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

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: 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') | common/chunkstream/chunk.go » ('J')
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 // Buffer is a collection of ordered Chunks that can cheaply read and shifted
8 // as if it were a continuous byte stream.
9 //
10 // A Buffer is not goroutine-safe. However, data can be Append()ed to a Buffer
11 // without affecting Readers that were previously generated from it so long as
12 // no data is Consume()d from the Buffer. The act of consuming data invalidates
13 // any Reader state, and using a Reader constructed prior to a Consume() call
14 // after the Consume() call is completed is an error and may result in a panic.
iannucci 2015/11/05 01:10:07 So I think the following operations are mutually e
dnj 2015/11/13 23:22:03 Yep. I'll update the documentation to be more clea
15 type Buffer struct {
16 // First is a pointer to the first Chunk node in the buffer.
17 first *chunkNode
18 // Last is a pointer to the last Chunk node in the buffer.
19 last *chunkNode
20
21 // size is the total number of bytes in the Buffer.
22 size int64
23
24 // fidx is the current byte offset in first.
25 fidx int
26
27 // gen is a monotonically-increasing generation number. It increases eve ry
28 // time data is consumed from the Buffer via Consume.
29 gen int64
30 }
31
32 // Append adds another Chunk to the buffer.
iannucci 2015/11/05 01:10:07 another Chunk -> one or more Chunks
dnj 2015/11/13 23:22:03 Done.
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 c.Len() == 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(c.Len())
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 // Reader returns a Reader instance bound to this Buffer.
94 //
95 // If the limit is greater than zero, the Reader's limit will be set.
iannucci 2015/11/05 01:10:07 I think this docstring is stale.
dnj 2015/11/13 23:22:03 Done.
96 //
97 // The Reader is no longer valid after Consume is called.
98 func (b *Buffer) Reader() *Reader {
99 return b.ReaderLimit(b.size)
100 }
101
102 // ReaderLimit constructs a Reader instance, but artifically constrains it to
103 // read at most the specified number of bytes.
104 //
105 // This is useful when reading a subset of the data into a Buffer, as ReadFrom
106 // does not allow a size to be specified.
107 func (b *Buffer) ReaderLimit(limit int64) *Reader {
108 if limit > b.size {
109 limit = b.size
iannucci 2015/11/05 01:10:07 hm.. why? what if you plan to Append stuff, since
dnj 2015/11/13 23:22:03 Clearer with "View", but it's basically a snapshot
110 }
111
112 return &Reader{
113 cur: b.first,
114 cidx: b.fidx,
115 size: limit,
116
117 gen: b.gen,
118 b: b,
119 }
120 }
121
122 // Consume removes the specified number of bytes from the beginning of the
123 // Buffer. If Consume skips past all of the data in a Chunk is no longer needed,
124 // it is Release()d.
125 //
126 // Consume invalidates any current Reader instances. Using a Reader generated
127 // from before a Consume call is an error and will result in a panic.
iannucci 2015/11/05 01:10:07 nice! glad to see this is enforced with gen :)
dnj 2015/11/13 23:22:03 Removed gen. :(
128 func (b *Buffer) Consume(c int64) {
129 if c == 0 {
130 return
131 }
132
133 if c > b.size {
134 panic("consuming more data than available")
135 }
136 b.size -= c
137
138 for c > 0 {
139 // Do we consume the entire chunk?
140 if int64(b.first.Len()-b.fidx) > c {
141 // No. Advance our chunk index and terminate.
142 b.fidx += int(c)
143 break
144 }
145
146 n := b.first
147 c -= int64(n.Len() - b.fidx)
148 b.first = n.next
149 b.fidx = 0
150 if b.first == nil {
151 b.last = nil
152 }
153
154 // Release our node. We must not reference it after this.
155 n.release()
156 }
157 b.gen++
158 }
OLDNEW
« no previous file with comments | « no previous file | common/chunkstream/buffer_test.go » ('j') | common/chunkstream/chunk.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698