diff --git a/src/world/PalettedContainer.h b/src/world/PalettedContainer.h index 743bf6a..30ab266 100644 --- a/src/world/PalettedContainer.h +++ b/src/world/PalettedContainer.h @@ -6,7 +6,8 @@ namespace Feather { - // A container that maps values from a local palette to a + // A container that maps values from a local palette to a global palette + // Implemented like Minecraft's BitStorage in 1.15.2 (with cell overflow) template struct PalettedContainer { @@ -25,7 +26,6 @@ namespace Feather uint size = MaxSize; uint8 bits = MaxBits; uint64 mask = 0; - uint valuesPerCell = 0; public: PalettedContainer(uint size, uint8 bits) : size(size), data() { SetBits(bits); } @@ -33,43 +33,51 @@ namespace Feather inline void SetBits(uint8 nbits) { bits = nbits; - mask = (1 << bits) - 1; - valuesPerCell = BITS_PER_CELL / bits; - - Log::Debug("PalettedContainer::SetBits: {}, mask = {:#b}, valuesPerCell = {}", bits, mask, valuesPerCell); + mask = (1UL << bits) - 1UL; } // TODO: optimize all math here - inline int CellIndex(int index) - { - return index / valuesPerCell; - } - // Update value, returns previous value // TODO: change from uint to const T& inline uint GetAndSet(int index, uint value) { + // Note: Java >> is signed, while >>> is used for zero-fill shift. + // Usage of signed >> instead of >>> has been marked. + + // Bit # we want to read from + int bitIndex = index * bits; + + // Cell index + uint cell = bitIndex >> 6; // java signed >> + // Next cell, if bits overflow + uint next = ((index + 1) * bits - 1) >> 6; // java signed >> + + uint shift = bitIndex ^ cell << 6; + auto [d, lock] = data.borrow(); + + // read previous value + uint64 cellValue = d[cell]; + uint prev = (uint)(d[cell] >> shift & mask); - int id = value; // palette.GetID(value); - int cellIndex = CellIndex(index); - uint64 shift = (index - cellIndex * valuesPerCell) * bits;//(index % valuesPerCell) * bits; + d[cell] &= ~(mask << shift); // clear target bits + d[cell] |= ((uint64)value & mask) << shift; // set target bits - Log::Debug("PalettedContainer::GetAndSet: index {} -> cell {}, [{}:{}]", index, cellIndex, shift, shift + bits); + // Bits overflow into next cell + if (next != cell) + { + uint shift1 = BITS_PER_CELL - shift; + uint shift2 = bits - shift1; - uint64 cell = d[cellIndex]; - int prev = (cell >> shift) & mask; + uint64 nextValue = d[next]; - //d[cellIndex] = ((cell & (mask << shift ^ 0xFFFFFFFFFFFFFFFFL)) | (uint64(id) & mask)) << shift; + prev |= (uint)(d[next] << shift1 & mask); + // TODO: not 100% on the math here + d[next] = d[next] >> shift2 << shift2 | ((uint64)value & mask) >> shift1; // last shift is java signed >> + } - d[cellIndex] &= ~(mask << shift); // clear target bits - d[cellIndex] |= ((uint64)id & mask) << shift; // set target bits - - //Log::Debug("Changed from {:#b} to {:#b}", cell, d[cellIndex]); - Log::Debug("Changed from {} (g: {}) to {} (g: {})", prev, palette.ByID(prev), value, palette.ByID(value)); - - return (uint)prev; // palette.ByID(prev); + return prev; } inline constexpr size_t GetNumLongs() { return NUM_LONGS; }