github.com/consideritdone/landslidecore@v0.0.0-20230718131026-a8b21c5cf8a7/libs/bits/bit_array.go (about) 1 package bits 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "regexp" 7 "strings" 8 "sync" 9 10 tmmath "github.com/consideritdone/landslidecore/libs/math" 11 tmrand "github.com/consideritdone/landslidecore/libs/rand" 12 tmprotobits "github.com/consideritdone/landslidecore/proto/tendermint/libs/bits" 13 ) 14 15 // BitArray is a thread-safe implementation of a bit array. 16 type BitArray struct { 17 mtx sync.Mutex 18 Bits int `json:"bits"` // NOTE: persisted via reflect, must be exported 19 Elems []uint64 `json:"elems"` // NOTE: persisted via reflect, must be exported 20 } 21 22 // NewBitArray returns a new bit array. 23 // It returns nil if the number of bits is zero. 24 func NewBitArray(bits int) *BitArray { 25 if bits <= 0 { 26 return nil 27 } 28 return &BitArray{ 29 Bits: bits, 30 Elems: make([]uint64, (bits+63)/64), 31 } 32 } 33 34 // Size returns the number of bits in the bitarray 35 func (bA *BitArray) Size() int { 36 if bA == nil { 37 return 0 38 } 39 return bA.Bits 40 } 41 42 // GetIndex returns the bit at index i within the bit array. 43 // The behavior is undefined if i >= bA.Bits 44 func (bA *BitArray) GetIndex(i int) bool { 45 if bA == nil { 46 return false 47 } 48 bA.mtx.Lock() 49 defer bA.mtx.Unlock() 50 return bA.getIndex(i) 51 } 52 53 func (bA *BitArray) getIndex(i int) bool { 54 if i >= bA.Bits { 55 return false 56 } 57 return bA.Elems[i/64]&(uint64(1)<<uint(i%64)) > 0 58 } 59 60 // SetIndex sets the bit at index i within the bit array. 61 // The behavior is undefined if i >= bA.Bits 62 func (bA *BitArray) SetIndex(i int, v bool) bool { 63 if bA == nil { 64 return false 65 } 66 bA.mtx.Lock() 67 defer bA.mtx.Unlock() 68 return bA.setIndex(i, v) 69 } 70 71 func (bA *BitArray) setIndex(i int, v bool) bool { 72 if i >= bA.Bits { 73 return false 74 } 75 if v { 76 bA.Elems[i/64] |= (uint64(1) << uint(i%64)) 77 } else { 78 bA.Elems[i/64] &= ^(uint64(1) << uint(i%64)) 79 } 80 return true 81 } 82 83 // Copy returns a copy of the provided bit array. 84 func (bA *BitArray) Copy() *BitArray { 85 if bA == nil { 86 return nil 87 } 88 bA.mtx.Lock() 89 defer bA.mtx.Unlock() 90 return bA.copy() 91 } 92 93 func (bA *BitArray) copy() *BitArray { 94 c := make([]uint64, len(bA.Elems)) 95 copy(c, bA.Elems) 96 return &BitArray{ 97 Bits: bA.Bits, 98 Elems: c, 99 } 100 } 101 102 func (bA *BitArray) copyBits(bits int) *BitArray { 103 c := make([]uint64, (bits+63)/64) 104 copy(c, bA.Elems) 105 return &BitArray{ 106 Bits: bits, 107 Elems: c, 108 } 109 } 110 111 // Or returns a bit array resulting from a bitwise OR of the two bit arrays. 112 // If the two bit-arrys have different lengths, Or right-pads the smaller of the two bit-arrays with zeroes. 113 // Thus the size of the return value is the maximum of the two provided bit arrays. 114 func (bA *BitArray) Or(o *BitArray) *BitArray { 115 if bA == nil && o == nil { 116 return nil 117 } 118 if bA == nil && o != nil { 119 return o.Copy() 120 } 121 if o == nil { 122 return bA.Copy() 123 } 124 bA.mtx.Lock() 125 o.mtx.Lock() 126 c := bA.copyBits(tmmath.MaxInt(bA.Bits, o.Bits)) 127 smaller := tmmath.MinInt(len(bA.Elems), len(o.Elems)) 128 for i := 0; i < smaller; i++ { 129 c.Elems[i] |= o.Elems[i] 130 } 131 bA.mtx.Unlock() 132 o.mtx.Unlock() 133 return c 134 } 135 136 // And returns a bit array resulting from a bitwise AND of the two bit arrays. 137 // If the two bit-arrys have different lengths, this truncates the larger of the two bit-arrays from the right. 138 // Thus the size of the return value is the minimum of the two provided bit arrays. 139 func (bA *BitArray) And(o *BitArray) *BitArray { 140 if bA == nil || o == nil { 141 return nil 142 } 143 bA.mtx.Lock() 144 o.mtx.Lock() 145 defer func() { 146 bA.mtx.Unlock() 147 o.mtx.Unlock() 148 }() 149 return bA.and(o) 150 } 151 152 func (bA *BitArray) and(o *BitArray) *BitArray { 153 c := bA.copyBits(tmmath.MinInt(bA.Bits, o.Bits)) 154 for i := 0; i < len(c.Elems); i++ { 155 c.Elems[i] &= o.Elems[i] 156 } 157 return c 158 } 159 160 // Not returns a bit array resulting from a bitwise Not of the provided bit array. 161 func (bA *BitArray) Not() *BitArray { 162 if bA == nil { 163 return nil // Degenerate 164 } 165 bA.mtx.Lock() 166 defer bA.mtx.Unlock() 167 return bA.not() 168 } 169 170 func (bA *BitArray) not() *BitArray { 171 c := bA.copy() 172 for i := 0; i < len(c.Elems); i++ { 173 c.Elems[i] = ^c.Elems[i] 174 } 175 return c 176 } 177 178 // Sub subtracts the two bit-arrays bitwise, without carrying the bits. 179 // Note that carryless subtraction of a - b is (a and not b). 180 // The output is the same as bA, regardless of o's size. 181 // If bA is longer than o, o is right padded with zeroes 182 func (bA *BitArray) Sub(o *BitArray) *BitArray { 183 if bA == nil || o == nil { 184 // TODO: Decide if we should do 1's complement here? 185 return nil 186 } 187 bA.mtx.Lock() 188 o.mtx.Lock() 189 // output is the same size as bA 190 c := bA.copyBits(bA.Bits) 191 // Only iterate to the minimum size between the two. 192 // If o is longer, those bits are ignored. 193 // If bA is longer, then skipping those iterations is equivalent 194 // to right padding with 0's 195 smaller := tmmath.MinInt(len(bA.Elems), len(o.Elems)) 196 for i := 0; i < smaller; i++ { 197 // &^ is and not in golang 198 c.Elems[i] &^= o.Elems[i] 199 } 200 bA.mtx.Unlock() 201 o.mtx.Unlock() 202 return c 203 } 204 205 // IsEmpty returns true iff all bits in the bit array are 0 206 func (bA *BitArray) IsEmpty() bool { 207 if bA == nil { 208 return true // should this be opposite? 209 } 210 bA.mtx.Lock() 211 defer bA.mtx.Unlock() 212 for _, e := range bA.Elems { 213 if e > 0 { 214 return false 215 } 216 } 217 return true 218 } 219 220 // IsFull returns true iff all bits in the bit array are 1. 221 func (bA *BitArray) IsFull() bool { 222 if bA == nil { 223 return true 224 } 225 bA.mtx.Lock() 226 defer bA.mtx.Unlock() 227 228 // Check all elements except the last 229 for _, elem := range bA.Elems[:len(bA.Elems)-1] { 230 if (^elem) != 0 { 231 return false 232 } 233 } 234 235 // Check that the last element has (lastElemBits) 1's 236 lastElemBits := (bA.Bits+63)%64 + 1 237 lastElem := bA.Elems[len(bA.Elems)-1] 238 return (lastElem+1)&((uint64(1)<<uint(lastElemBits))-1) == 0 239 } 240 241 // PickRandom returns a random index for a set bit in the bit array. 242 // If there is no such value, it returns 0, false. 243 // It uses the global randomness in `random.go` to get this index. 244 func (bA *BitArray) PickRandom() (int, bool) { 245 if bA == nil { 246 return 0, false 247 } 248 249 bA.mtx.Lock() 250 trueIndices := bA.getTrueIndices() 251 bA.mtx.Unlock() 252 253 if len(trueIndices) == 0 { // no bits set to true 254 return 0, false 255 } 256 257 return trueIndices[tmrand.Intn(len(trueIndices))], true 258 } 259 260 func (bA *BitArray) getTrueIndices() []int { 261 trueIndices := make([]int, 0, bA.Bits) 262 curBit := 0 263 numElems := len(bA.Elems) 264 // set all true indices 265 for i := 0; i < numElems-1; i++ { 266 elem := bA.Elems[i] 267 if elem == 0 { 268 curBit += 64 269 continue 270 } 271 for j := 0; j < 64; j++ { 272 if (elem & (uint64(1) << uint64(j))) > 0 { 273 trueIndices = append(trueIndices, curBit) 274 } 275 curBit++ 276 } 277 } 278 // handle last element 279 lastElem := bA.Elems[numElems-1] 280 numFinalBits := bA.Bits - curBit 281 for i := 0; i < numFinalBits; i++ { 282 if (lastElem & (uint64(1) << uint64(i))) > 0 { 283 trueIndices = append(trueIndices, curBit) 284 } 285 curBit++ 286 } 287 return trueIndices 288 } 289 290 // String returns a string representation of BitArray: BA{<bit-string>}, 291 // where <bit-string> is a sequence of 'x' (1) and '_' (0). 292 // The <bit-string> includes spaces and newlines to help people. 293 // For a simple sequence of 'x' and '_' characters with no spaces or newlines, 294 // see the MarshalJSON() method. 295 // Example: "BA{_x_}" or "nil-BitArray" for nil. 296 func (bA *BitArray) String() string { 297 return bA.StringIndented("") 298 } 299 300 // StringIndented returns the same thing as String(), but applies the indent 301 // at every 10th bit, and twice at every 50th bit. 302 func (bA *BitArray) StringIndented(indent string) string { 303 if bA == nil { 304 return "nil-BitArray" 305 } 306 bA.mtx.Lock() 307 defer bA.mtx.Unlock() 308 return bA.stringIndented(indent) 309 } 310 311 func (bA *BitArray) stringIndented(indent string) string { 312 lines := []string{} 313 bits := "" 314 for i := 0; i < bA.Bits; i++ { 315 if bA.getIndex(i) { 316 bits += "x" 317 } else { 318 bits += "_" 319 } 320 if i%100 == 99 { 321 lines = append(lines, bits) 322 bits = "" 323 } 324 if i%10 == 9 { 325 bits += indent 326 } 327 if i%50 == 49 { 328 bits += indent 329 } 330 } 331 if len(bits) > 0 { 332 lines = append(lines, bits) 333 } 334 return fmt.Sprintf("BA{%v:%v}", bA.Bits, strings.Join(lines, indent)) 335 } 336 337 // Bytes returns the byte representation of the bits within the bitarray. 338 func (bA *BitArray) Bytes() []byte { 339 bA.mtx.Lock() 340 defer bA.mtx.Unlock() 341 342 numBytes := (bA.Bits + 7) / 8 343 bytes := make([]byte, numBytes) 344 for i := 0; i < len(bA.Elems); i++ { 345 elemBytes := [8]byte{} 346 binary.LittleEndian.PutUint64(elemBytes[:], bA.Elems[i]) 347 copy(bytes[i*8:], elemBytes[:]) 348 } 349 return bytes 350 } 351 352 // Update sets the bA's bits to be that of the other bit array. 353 // The copying begins from the begin of both bit arrays. 354 func (bA *BitArray) Update(o *BitArray) { 355 if bA == nil || o == nil { 356 return 357 } 358 359 bA.mtx.Lock() 360 o.mtx.Lock() 361 copy(bA.Elems, o.Elems) 362 o.mtx.Unlock() 363 bA.mtx.Unlock() 364 } 365 366 // MarshalJSON implements json.Marshaler interface by marshaling bit array 367 // using a custom format: a string of '-' or 'x' where 'x' denotes the 1 bit. 368 func (bA *BitArray) MarshalJSON() ([]byte, error) { 369 if bA == nil { 370 return []byte("null"), nil 371 } 372 373 bA.mtx.Lock() 374 defer bA.mtx.Unlock() 375 376 bits := `"` 377 for i := 0; i < bA.Bits; i++ { 378 if bA.getIndex(i) { 379 bits += `x` 380 } else { 381 bits += `_` 382 } 383 } 384 bits += `"` 385 return []byte(bits), nil 386 } 387 388 var bitArrayJSONRegexp = regexp.MustCompile(`\A"([_x]*)"\z`) 389 390 // UnmarshalJSON implements json.Unmarshaler interface by unmarshaling a custom 391 // JSON description. 392 func (bA *BitArray) UnmarshalJSON(bz []byte) error { 393 b := string(bz) 394 if b == "null" { 395 // This is required e.g. for encoding/json when decoding 396 // into a pointer with pre-allocated BitArray. 397 bA.Bits = 0 398 bA.Elems = nil 399 return nil 400 } 401 402 // Validate 'b'. 403 match := bitArrayJSONRegexp.FindStringSubmatch(b) 404 if match == nil { 405 return fmt.Errorf("bitArray in JSON should be a string of format %q but got %s", bitArrayJSONRegexp.String(), b) 406 } 407 bits := match[1] 408 409 // Construct new BitArray and copy over. 410 numBits := len(bits) 411 bA2 := NewBitArray(numBits) 412 for i := 0; i < numBits; i++ { 413 if bits[i] == 'x' { 414 bA2.SetIndex(i, true) 415 } 416 } 417 *bA = *bA2 //nolint:govet 418 return nil 419 } 420 421 // ToProto converts BitArray to protobuf 422 func (bA *BitArray) ToProto() *tmprotobits.BitArray { 423 if bA == nil || len(bA.Elems) == 0 { 424 return nil 425 } 426 427 return &tmprotobits.BitArray{ 428 Bits: int64(bA.Bits), 429 Elems: bA.Elems, 430 } 431 } 432 433 // FromProto sets a protobuf BitArray to the given pointer. 434 func (bA *BitArray) FromProto(protoBitArray *tmprotobits.BitArray) { 435 if protoBitArray == nil { 436 bA = nil 437 return 438 } 439 440 bA.Bits = int(protoBitArray.Bits) 441 if len(protoBitArray.Elems) > 0 { 442 bA.Elems = protoBitArray.Elems 443 } 444 }