UNPKG

prismarine-chunk

Version:
162 lines (140 loc) 4.51 kB
// const assert = require('assert') const neededBits = require('./neededBits') class BitArray { constructor (options) { if (options === null) { return } // assert(options.bitsPerValue > 0, 'bits per value must at least 1') // assert(options.bitsPerValue <= 32, 'bits per value exceeds 32') if (!options.data) { options.data = new Uint32Array(Math.ceil((options.capacity * options.bitsPerValue) / 64) * 2) } const valueMask = (1 << options.bitsPerValue) - 1 this.data = options.data.buffer ? new Uint32Array(options.data.buffer) : Uint32Array.from(options.data) this.capacity = options.capacity this.bitsPerValue = options.bitsPerValue this.valueMask = valueMask } toJson () { return JSON.stringify({ data: Array.from(this.data), capacity: this.capacity, bitsPerValue: this.bitsPerValue, valueMask: this.valueMask }) } static fromJson (j) { return new BitArray(JSON.parse(j)) } toArray () { const array = [] for (let i = 0; i < this.capacity; i++) { array.push(this.get(i)) } return array } static fromArray (array, bitsPerValue) { const data = [] let i = 0 let curLong = 0 let curBit = 0 while (i < array.length) { curLong |= array[i] << curBit curBit += bitsPerValue if (curBit > 32) { data.push(curLong & 0xffffffff) curBit -= 32 curLong = array[i] >>> (bitsPerValue - curBit) } i++ } if (curBit > 0) { data.push(curLong) } const bitarray = new BitArray(null) bitarray.data = data bitarray.capacity = array.length bitarray.bitsPerValue = bitsPerValue bitarray.valueMask = (1 << bitsPerValue) - 1 return bitarray } get (index) { // assert(index >= 0 && index < this.capacity, 'index is out of bounds') const bitIndex = index * this.bitsPerValue const startLongIndex = bitIndex >>> 5 const startLong = this.data[startLongIndex] const indexInStartLong = bitIndex & 31 let result = startLong >>> indexInStartLong const endBitOffset = indexInStartLong + this.bitsPerValue if (endBitOffset > 32) { // Value stretches across multiple longs const endLong = this.data[startLongIndex + 1] result |= endLong << (32 - indexInStartLong) } return result & this.valueMask } set (index, value) { // assert(index >= 0 && index < this.capacity, 'index is out of bounds') // assert(value <= this.valueMask, 'value does not fit into bits per value') const bitIndex = index * this.bitsPerValue const startLongIndex = bitIndex >>> 5 const indexInStartLong = bitIndex & 31 // Clear bits of this value first this.data[startLongIndex] = ((this.data[startLongIndex] & ~(this.valueMask << indexInStartLong)) | ((value & this.valueMask) << indexInStartLong)) >>> 0 const endBitOffset = indexInStartLong + this.bitsPerValue if (endBitOffset > 32) { // Value stretches across multiple longs this.data[startLongIndex + 1] = ((this.data[startLongIndex + 1] & ~((1 << (endBitOffset - 32)) - 1)) | (value >> (32 - indexInStartLong))) >>> 0 } } resizeTo (newBitsPerValue) { // assert(newBitsPerValue > 0, 'bits per value must at least 1') // assert(newBitsPerValue <= 32, 'bits per value exceeds 32') const newArr = new BitArray({ bitsPerValue: newBitsPerValue, capacity: this.capacity }) for (let i = 0; i < this.capacity; ++i) { const value = this.get(i) if (neededBits(value) > newBitsPerValue) { throw new Error( "existing value in BitArray can't fit in new bits per value" ) } newArr.set(i, value) } return newArr } length () { return this.data.length / 2 } readBuffer (smartBuffer, size = this.data.length) { if (size !== this.data.length) { this.data = new Uint32Array(size) this.readOffset = size * 4 return } for (let i = 0; i < this.data.length; i += 2) { this.data[i + 1] = smartBuffer.readUInt32BE() this.data[i] = smartBuffer.readUInt32BE() } return this } writeBuffer (smartBuffer) { for (let i = 0; i < this.data.length; i += 2) { smartBuffer.writeUInt32BE(this.data[i + 1]) smartBuffer.writeUInt32BE(this.data[i]) } return this } getBitsPerValue () { return this.bitsPerValue } } module.exports = BitArray