github.com/tunabay/go-bitarray@v1.3.1/buffer.go (about) 1 // Copyright (c) 2021 Hirotsuna Mizuno. All rights reserved. 2 // Use of this source code is governed by the MIT license that can be found in 3 // the LICENSE file. 4 5 package bitarray 6 7 import ( 8 "fmt" 9 ) 10 11 // Buffer is a bit array buffer whose contents can be updated by partial reading 12 // and writing with an offset. It is not safe for concurrent use by multiple 13 // goroutines. The zero value for Buffer represents a zero length buffer that 14 // can be resized and used. 15 type Buffer struct { 16 b []byte // nil only for zero length 17 nBits int 18 off int 19 } 20 21 // NewBuffer creates a Buffer with the specified bit length. 22 func NewBuffer(nBits int) *Buffer { 23 switch { 24 case nBits < 0: 25 panicf("NewBuffer: negative nBits %d.", nBits) 26 case nBits == 0: 27 return &Buffer{} 28 } 29 30 return &Buffer{ 31 b: allocByteSlice((nBits + 7) >> 3), 32 nBits: nBits, 33 } 34 } 35 36 // NewBufferFromBitArray creates a new Buffer with the same bit length and 37 // initial content as the specified BitArray. 38 func NewBufferFromBitArray(ba BitArrayer) *Buffer { 39 if ba == nil { 40 return &Buffer{} 41 } 42 bab := ba.BitArray() 43 buf := NewBuffer(bab.Len()) 44 if 0 < buf.nBits { 45 copy(buf.b, bab.b) 46 } 47 return buf 48 } 49 50 // NewBufferFromByteSlice creates a new Buffer that references an existing byte 51 // slice b. The created Buffer references b without copying it, therefore, 52 // changes to the buffer affect b and vice versa. The length of the buffer 53 // created will be len(b) * 8. NewBufferFromByteSlice is useful when reading or 54 // writing a subpart of a byte slice as a bit array without copying or 55 // bit-shifting. 56 func NewBufferFromByteSlice(b []byte) *Buffer { 57 return NewBufferFromByteSlicePartial(b, 0, len(b)<<3) 58 } 59 60 // NewBufferFromByteSlicePartial is identical to NewBufferFromByteSlice except 61 // that it creates a buffer with the first bit specified by off, and the length 62 // specified by nBits. 63 func NewBufferFromByteSlicePartial(b []byte, off, nBits int) *Buffer { 64 switch { 65 case off < 0: 66 panicf("NewBufferFromByteSlice: negative off %d.", nBits) 67 case nBits < 0: 68 panicf("NewBufferFromByteSlice: negative nBits %d.", nBits) 69 case len(b)<<3 < off+nBits: 70 panicf("NewBufferFromByteSlice: out of range: off=%d, nBits=%d > len=%d.", off, nBits, len(b)) 71 case nBits == 0: 72 return &Buffer{} 73 } 74 return &Buffer{b: b[off>>3:], nBits: nBits, off: off & 7} 75 } 76 77 // IsZero returns whether the Buffer is zero length. 78 func (buf *Buffer) IsZero() bool { 79 return buf.Len() == 0 80 } 81 82 // Len returns the number of bits contained in the buffer. 83 func (buf *Buffer) Len() int { 84 if buf == nil { 85 return 0 86 } 87 return buf.nBits 88 } 89 90 // Clone clones the Buffer with its content. 91 func (buf *Buffer) Clone() *Buffer { 92 if buf.Len() == 0 { 93 return &Buffer{} 94 } 95 b := make([]byte, len(buf.b)) 96 copy(b, buf.b) 97 98 return &Buffer{b: b, nBits: buf.nBits, off: buf.off} 99 } 100 101 // BitArray creates an imuurable BitArray from the current content. 102 func (buf *Buffer) BitArray() *BitArray { 103 return NewFromBytes(buf.b, buf.off, buf.nBits) 104 } 105 106 // String returns the string representation of the current content. 107 func (buf Buffer) String() string { 108 sb := make([]byte, buf.nBits) 109 for i := 0; i < buf.nBits; i++ { 110 sb[i] = '0' + buf.b[(buf.off+i)>>3]>>(7-(buf.off+i)&7)&1 111 } 112 return string(sb) 113 } 114 115 // Resize resizes the Buffer to the size specified by nBits. When expanding, all 116 // bits in the new range to be extended are initialized with 0. When shrinking, 117 // the extra bits are truncated. In either case, the align specifies whether to 118 // fix the MSBs or the LSBs. 119 // 120 // Resize always reallocates internal memory. That is, the buffers created by 121 // Slice method or NewBufferFromByteSlice break their relationship with the 122 // parent buffer or slice by calling this Resize, even if nBits is equivalent to 123 // or less than its current size. 124 func (buf *Buffer) Resize(nBits int, align Alignment) { 125 switch { 126 case nBits < 0: 127 panicf("Resize: negative nBits %d.", nBits) 128 case nBits == 0: 129 buf.b = nil 130 buf.nBits = 0 131 buf.off = 0 132 return 133 } 134 135 b := allocByteSlice((nBits + 7) >> 3) 136 if buf.nBits == 0 { 137 buf.b = b 138 buf.nBits = nBits 139 buf.off = 0 140 return 141 } 142 if align == AlignLeft { 143 if nBits < buf.nBits { // shrink 144 copyBits(b, buf.b, 0, buf.off, nBits) 145 } else { // extend 146 copyBits(b, buf.b, 0, buf.off, buf.nBits) 147 } 148 } else { 149 if nBits < buf.nBits { // shrink 150 copyBits(b, buf.b, 0, buf.off+buf.nBits-nBits, nBits) 151 } else { // extend 152 copyBits(b, buf.b, nBits-buf.nBits, buf.off, buf.nBits) 153 } 154 } 155 156 buf.b = b 157 buf.nBits = nBits 158 buf.off = 0 159 } 160 161 // FillBits sets all the bits in the buffer to the value bit, 0 or 1. 162 func (buf *Buffer) FillBits(bit byte) { 163 buf.FillBitsAt(0, buf.nBits, bit) 164 } 165 166 // FillBitsAt sets the nBits bits starting at off to the value bit. 167 func (buf *Buffer) FillBitsAt(off, nBits int, bit byte) { 168 switch { 169 case off < 0: 170 panicf("FillBitsAt: negative off %d.", off) 171 case nBits < 0: 172 panicf("FillBitsAt: negative nBits %d.", nBits) 173 case buf.nBits < off+nBits: 174 panicf("FillBitsAt: out of range: off=%d + nBits=%d > len=%d.", off, nBits, buf.nBits) 175 case bit&1 == 0: 176 clearBits(buf.b, buf.off+off, nBits) 177 default: 178 setBits(buf.b, buf.off+off, nBits) 179 } 180 } 181 182 // Format implements the fmt.Formatter interface to format Buffer value using 183 // the standard fmt.Printf family functions. 184 func (buf Buffer) Format(s fmt.State, verb rune) { buf.BitArray().Format(s, verb) }