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

Unified 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: Code review comments. 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | common/chunkstream/buffer_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: common/chunkstream/buffer.go
diff --git a/common/chunkstream/buffer.go b/common/chunkstream/buffer.go
new file mode 100644
index 0000000000000000000000000000000000000000..f76fc78cb3134f1adae2ea3c92cdec347abfab1d
--- /dev/null
+++ b/common/chunkstream/buffer.go
@@ -0,0 +1,148 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package chunkstream
+
+// Buffer is a collection of ordered Chunks that can cheaply read and shifted
+// as if it were a continuous byte stream.
+//
+// A Buffer is not goroutine-safe.
+//
+// The primary means of interacting with a Buffer is to construct a View and
+// then use it to access the Buffer's contents. Views can be used concurrently,
+// and View operations are goroutine-safe.
+type Buffer struct {
+ // First is a pointer to the first Chunk node in the buffer.
+ first *chunkNode
+ // Last is a pointer to the last Chunk node in the buffer.
+ last *chunkNode
+
+ // size is the total number of bytes in the Buffer.
+ size int64
+
+ // fidx is the current byte offset in first.
+ fidx int
+}
+
+// Append adds additional Chunks to the buffer.
+//
+// After completion, the Chunk is now owned by the Buffer and should not be used
+// anymore externally.
+func (b *Buffer) Append(c ...Chunk) {
+ for _, chunk := range c {
+ b.appendChunk(chunk)
iannucci 2015/11/18 23:51:42 maybe allocate a mini-list of chunks and then appe
dnj (Google) 2015/11/19 00:19:55 Yeah it's not a bad idea, save some pointer updati
+ }
+}
+
+func (b *Buffer) appendChunk(c Chunk) {
+ // Ignore/discard zero-length data.
+ if len(c.Bytes()) == 0 {
+ c.Release()
+ return
+ }
+
+ cn := newChunkNode(c)
+ cn.next = nil
+ if b.last == nil {
+ // First node.
+ b.first = cn
+ } else {
+ b.last.next = cn
+ }
+ b.last = cn
+ b.size += int64(cn.length())
+}
+
+// Bytes constructs a byte slice containing the contents of the Buffer.
+//
+// This is a potentially expensive operation, and should generally be used only
+// for debugging and tests, as it defeats most of the purpose of this package.
+func (b *Buffer) Bytes() []byte {
+ if b.Len() == 0 {
+ return nil
+ }
+
+ m := make([]byte, 0, b.Len())
+ idx := b.fidx
+ for cur := b.first; cur != nil; cur = cur.next {
+ m = append(m, cur.Bytes()[idx:]...)
+ idx = 0
+ }
+ return m
+}
+
+// Len returns the total amount of data in the buffer.
+func (b *Buffer) Len() int64 {
+ return b.size
+}
+
+// FirstChunk returns the first Chunk in the Buffer, or nil if the Buffer has
+// no Chunks.
+func (b *Buffer) FirstChunk() Chunk {
+ if b.first == nil {
+ return nil
+ }
+ return b.first.Chunk
+}
+
+// View returns a View instance bound to this Buffer and spanning all data
+// currently in the Buffer.
+//
+// The View is no longer valid after Consume is called on the Buffer.
+func (b *Buffer) View() *View {
+ return b.ViewLimit(b.size)
+}
+
+// ViewLimit constructs a View instance, but artifically constrains it to
+// read at most the specified number of bytes.
+//
+// This is useful when reading a subset of the data into a Buffer, as ReadFrom
+// does not allow a size to be specified.
+func (b *Buffer) ViewLimit(limit int64) *View {
+ if limit > b.size {
+ limit = b.size
+ }
+
+ return &View{
+ cur: b.first,
+ cidx: b.fidx,
+ size: limit,
+
+ b: b,
+ }
+}
+
+// Consume removes the specified number of bytes from the beginning of the
+// Buffer. If Consume skips past all of the data in a Chunk is no longer needed,
+// it is Release()d.
+func (b *Buffer) Consume(c int64) {
+ if c == 0 {
+ return
+ }
+
+ if c > b.size {
+ panic("consuming more data than available")
iannucci 2015/11/18 23:51:43 should we panic with errors? or is raw strings OK?
dnj (Google) 2015/11/19 00:19:55 TBH I'm not sure. I don't think an error would add
+ }
+ b.size -= c
+
+ for c > 0 {
+ // Do we consume the entire chunk?
+ if int64(b.first.length()-b.fidx) > c {
+ // No. Advance our chunk index and terminate.
+ b.fidx += int(c)
+ break
+ }
+
+ n := b.first
+ c -= int64(n.length() - b.fidx)
+ b.first = n.next
+ b.fidx = 0
+ if b.first == nil {
+ b.last = nil
+ }
+
+ // Release our node. We must not reference it after this.
+ n.release()
+ }
+}
« 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