Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1198)

Side by Side Diff: common/chunkstream/buffer.go

Issue 1413923013: Add `chunk` segmented data library. (Closed) Base URL: https://github.com/luci/luci-go@logdog-review-streamserver
Patch Set: Use errors in pacnic. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | common/chunkstream/buffer_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package chunkstream
6
7 import (
8 "errors"
9 )
10
11 // Buffer is a collection of ordered Chunks that can cheaply read and shifted
12 // as if it were a continuous byte stream.
13 //
14 // A Buffer is not goroutine-safe.
15 //
16 // The primary means of interacting with a Buffer is to construct a View and
17 // then use it to access the Buffer's contents. Views can be used concurrently,
18 // and View operations are goroutine-safe.
19 type Buffer struct {
20 // First is a pointer to the first Chunk node in the buffer.
21 first *chunkNode
22 // Last is a pointer to the last Chunk node in the buffer.
23 last *chunkNode
24
25 // size is the total number of bytes in the Buffer.
26 size int64
27
28 // fidx is the current byte offset in first.
29 fidx int
30 }
31
32 // Append adds additional Chunks to the buffer.
33 //
34 // After completion, the Chunk is now owned by the Buffer and should not be used
35 // anymore externally.
36 func (b *Buffer) Append(c ...Chunk) {
37 for _, chunk := range c {
38 b.appendChunk(chunk)
39 }
40 }
41
42 func (b *Buffer) appendChunk(c Chunk) {
43 // Ignore/discard zero-length data.
44 if len(c.Bytes()) == 0 {
45 c.Release()
46 return
47 }
48
49 cn := newChunkNode(c)
50 cn.next = nil
51 if b.last == nil {
52 // First node.
53 b.first = cn
54 } else {
55 b.last.next = cn
56 }
57 b.last = cn
58 b.size += int64(cn.length())
59 }
60
61 // Bytes constructs a byte slice containing the contents of the Buffer.
62 //
63 // This is a potentially expensive operation, and should generally be used only
64 // for debugging and tests, as it defeats most of the purpose of this package.
65 func (b *Buffer) Bytes() []byte {
66 if b.Len() == 0 {
67 return nil
68 }
69
70 m := make([]byte, 0, b.Len())
71 idx := b.fidx
72 for cur := b.first; cur != nil; cur = cur.next {
73 m = append(m, cur.Bytes()[idx:]...)
74 idx = 0
75 }
76 return m
77 }
78
79 // Len returns the total amount of data in the buffer.
80 func (b *Buffer) Len() int64 {
81 return b.size
82 }
83
84 // FirstChunk returns the first Chunk in the Buffer, or nil if the Buffer has
85 // no Chunks.
86 func (b *Buffer) FirstChunk() Chunk {
87 if b.first == nil {
88 return nil
89 }
90 return b.first.Chunk
91 }
92
93 // View returns a View instance bound to this Buffer and spanning all data
94 // currently in the Buffer.
95 //
96 // The View is no longer valid after Consume is called on the Buffer.
97 func (b *Buffer) View() *View {
98 return b.ViewLimit(b.size)
99 }
100
101 // ViewLimit constructs a View instance, but artifically constrains it to
102 // read at most the specified number of bytes.
103 //
104 // This is useful when reading a subset of the data into a Buffer, as ReadFrom
105 // does not allow a size to be specified.
106 func (b *Buffer) ViewLimit(limit int64) *View {
107 if limit > b.size {
108 limit = b.size
109 }
110
111 return &View{
112 cur: b.first,
113 cidx: b.fidx,
114 size: limit,
115
116 b: b,
117 }
118 }
119
120 // Consume removes the specified number of bytes from the beginning of the
121 // Buffer. If Consume skips past all of the data in a Chunk is no longer needed,
122 // it is Release()d.
123 func (b *Buffer) Consume(c int64) {
124 if c == 0 {
125 return
126 }
127
128 if c > b.size {
129 panic(errors.New("consuming more data than available"))
130 }
131 b.size -= c
132
133 for c > 0 {
134 // Do we consume the entire chunk?
135 if int64(b.first.length()-b.fidx) > c {
136 // No. Advance our chunk index and terminate.
137 b.fidx += int(c)
138 break
139 }
140
141 n := b.first
142 c -= int64(n.length() - b.fidx)
143 b.first = n.next
144 b.fidx = 0
145 if b.first == nil {
146 b.last = nil
147 }
148
149 // Release our node. We must not reference it after this.
150 n.release()
151 }
152 }
OLDNEW
« no previous file with comments | « no previous file | common/chunkstream/buffer_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698