Matt Jeanneret | cab955f | 2019-04-10 15:45:57 -0400 | [diff] [blame] | 1 | // Package bitmap implements (thread-safe) bitmap functions and abstractions. |
| 2 | // |
| 3 | // Installation |
| 4 | // |
| 5 | // go get github.com/boljen/go-bitmap |
| 6 | package bitmap |
| 7 | |
| 8 | import "sync" |
| 9 | |
| 10 | var ( |
| 11 | tA = [8]byte{1, 2, 4, 8, 16, 32, 64, 128} |
| 12 | tB = [8]byte{254, 253, 251, 247, 239, 223, 191, 127} |
| 13 | ) |
| 14 | |
| 15 | func dataOrCopy(d []byte, c bool) []byte { |
| 16 | if !c { |
| 17 | return d |
| 18 | } |
| 19 | ndata := make([]byte, len(d)) |
| 20 | copy(ndata, d) |
| 21 | return ndata |
| 22 | } |
| 23 | |
| 24 | // NewSlice creates a new byteslice with length l (in bits). |
| 25 | // The actual size in bits might be up to 7 bits larger because |
| 26 | // they are stored in a byteslice. |
| 27 | func NewSlice(l int) []byte { |
| 28 | remainder := l % 8 |
| 29 | if remainder != 0 { |
| 30 | remainder = 1 |
| 31 | } |
| 32 | return make([]byte, l/8+remainder) |
| 33 | } |
| 34 | |
| 35 | // Get returns the value of bit i from map m. |
| 36 | // It doesn't check the bounds of the slice. |
| 37 | func Get(m []byte, i int) bool { |
| 38 | return m[i/8]&tA[i%8] != 0 |
| 39 | } |
| 40 | |
| 41 | // Set sets bit i of map m to value v. |
| 42 | // It doesn't check the bounds of the slice. |
| 43 | func Set(m []byte, i int, v bool) { |
| 44 | index := i / 8 |
| 45 | bit := i % 8 |
| 46 | if v { |
| 47 | m[index] = m[index] | tA[bit] |
| 48 | } else { |
| 49 | m[index] = m[index] & tB[bit] |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | // GetBit returns the value of bit i of byte b. |
| 54 | // The bit index must be between 0 and 7. |
| 55 | func GetBit(b byte, i int) bool { |
| 56 | return b&tA[i] != 0 |
| 57 | } |
| 58 | |
| 59 | // SetBit sets bit i of byte b to value v. |
| 60 | // The bit index must be between 0 and 7. |
| 61 | func SetBit(b byte, i int, v bool) byte { |
| 62 | if v { |
| 63 | return b | tA[i] |
| 64 | } |
| 65 | return b & tB[i] |
| 66 | } |
| 67 | |
| 68 | // SetBitRef sets bit i of byte *b to value v. |
| 69 | func SetBitRef(b *byte, i int, v bool) { |
| 70 | if v { |
| 71 | *b = *b | tA[i] |
| 72 | } else { |
| 73 | *b = *b & tB[i] |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // Len returns the length (in bits) of the provided byteslice. |
| 78 | // It will always be a multipile of 8 bits. |
| 79 | func Len(m []byte) int { |
| 80 | return len(m) * 8 |
| 81 | } |
| 82 | |
| 83 | // Bitmap is a byteslice with bitmap functions. |
| 84 | // Creating one form existing data is as simple as bitmap := Bitmap(data). |
| 85 | type Bitmap []byte |
| 86 | |
| 87 | // New creates a new Bitmap instance with length l (in bits). |
| 88 | func New(l int) Bitmap { |
| 89 | return NewSlice(l) |
| 90 | } |
| 91 | |
| 92 | // Len wraps around the Len function. |
| 93 | func (b Bitmap) Len() int { |
| 94 | return Len(b) |
| 95 | } |
| 96 | |
| 97 | // Get wraps around the Get function. |
| 98 | func (b Bitmap) Get(i int) bool { |
| 99 | return Get(b, i) |
| 100 | } |
| 101 | |
| 102 | // Set wraps around the Set function. |
| 103 | func (b Bitmap) Set(i int, v bool) { |
| 104 | Set(b, i, v) |
| 105 | } |
| 106 | |
| 107 | // Data returns the data of the bitmap. |
| 108 | // If copy is false the actual underlying slice will be returned. |
| 109 | func (b Bitmap) Data(copy bool) []byte { |
| 110 | return dataOrCopy(b, copy) |
| 111 | } |
| 112 | |
| 113 | // Threadsafe implements thread-safe read- and write locking for the bitmap. |
| 114 | type Threadsafe struct { |
| 115 | bm Bitmap |
| 116 | mu sync.RWMutex |
| 117 | } |
| 118 | |
| 119 | // TSFromData creates a new Threadsafe using the provided data. |
| 120 | // If copy is true the actual slice will be used. |
| 121 | func TSFromData(data []byte, copy bool) *Threadsafe { |
| 122 | return &Threadsafe{ |
| 123 | bm: Bitmap(dataOrCopy(data, copy)), |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | // NewTS creates a new Threadsafe instance. |
| 128 | func NewTS(length int) *Threadsafe { |
| 129 | return &Threadsafe{ |
| 130 | bm: New(length), |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | // Data returns the data of the bitmap. |
| 135 | // If copy is false the actual underlying slice will be returned. |
| 136 | func (b *Threadsafe) Data(copy bool) []byte { |
| 137 | b.mu.RLock() |
| 138 | data := dataOrCopy(b.bm, copy) |
| 139 | b.mu.RUnlock() |
| 140 | return data |
| 141 | } |
| 142 | |
| 143 | // Len wraps around the Len function. |
| 144 | func (b Threadsafe) Len() int { |
| 145 | b.mu.RLock() |
| 146 | l := b.bm.Len() |
| 147 | b.mu.RUnlock() |
| 148 | return l |
| 149 | } |
| 150 | |
| 151 | // Get wraps around the Get function. |
| 152 | func (b Threadsafe) Get(i int) bool { |
| 153 | b.mu.RLock() |
| 154 | v := b.bm.Get(i) |
| 155 | b.mu.RUnlock() |
| 156 | return v |
| 157 | } |
| 158 | |
| 159 | // Set wraps around the Set function. |
| 160 | func (b Threadsafe) Set(i int, v bool) { |
| 161 | b.mu.Lock() |
| 162 | b.bm.Set(i, v) |
| 163 | b.mu.Unlock() |
| 164 | } |
| 165 | |
| 166 | // Concurrent is a bitmap implementation that achieves thread-safety |
| 167 | // using atomic operations along with some unsafe. |
| 168 | // It performs atomic operations on 32bits of data. |
| 169 | type Concurrent []byte |
| 170 | |
| 171 | // NewConcurrent returns a concurrent bitmap. |
| 172 | // It will create a bitmap |
| 173 | func NewConcurrent(l int) Concurrent { |
| 174 | remainder := l % 8 |
| 175 | if remainder != 0 { |
| 176 | remainder = 1 |
| 177 | } |
| 178 | return make([]byte, l/8+remainder, l/8+remainder+3) |
| 179 | } |
| 180 | |
| 181 | // Get wraps around the Get function. |
| 182 | func (c Concurrent) Get(b int) bool { |
| 183 | return Get(c, b) |
| 184 | } |
| 185 | |
| 186 | // Set wraps around the SetAtomic function. |
| 187 | func (c Concurrent) Set(b int, v bool) { |
| 188 | SetAtomic(c, b, v) |
| 189 | } |
| 190 | |
| 191 | // Len wraps around the Len function. |
| 192 | func (c Concurrent) Len() int { |
| 193 | return Len(c) |
| 194 | } |
| 195 | |
| 196 | // Data returns the data of the bitmap. |
| 197 | // If copy is false the actual underlying slice will be returned. |
| 198 | func (c Concurrent) Data(copy bool) []byte { |
| 199 | return dataOrCopy(c, copy) |
| 200 | } |