| OLD | NEW |
| (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 } |
| OLD | NEW |