TG9six 03a642d635 first push
first push
2025-09-06 17:17:39 +04:00

222 lines
6.3 KiB
C#

// Perfect Culling (C) 2021 Patrick König
//
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Koenigz.PerfectCulling.IO
{
/// <summary>
/// Allows to write individual bits in an intuitive way.
/// Used for efficient data storage.
/// </summary>
public class BitStreamWriter
{
public byte[] Buffer => m_buf;
public int Length => m_currentBufPos;
const uint SingleBitMask = 0x00000001;
private byte[] m_buf = new byte[65536];
private byte m_currentByte;
private int m_currenBitPos = 0;
private int m_currentBufPos = 0;
public void Reset()
{
m_currentByte = 0;
m_currenBitPos = 0;
m_currentBufPos = 0;
}
public int Write(uint value, int bits)
{
#if UNITY_EDITOR
if (bits > 32 || bits < 0)
{
throw new SystemException($"Invalid number of bits: {bits}");
}
#endif
int bitsWritten = 0;
for (int i = 0; i < bits; ++i)
{
uint singleBit = (value >> i) & SingleBitMask;
WriteBit(singleBit);
++bitsWritten;
}
return bitsWritten;
}
public void Flush()
{
if (m_currenBitPos != 0)
{
AppendCurrentByte();
}
m_currentByte = 0;
m_currenBitPos = 0;
}
private void WriteBit(uint singleBit)
{
if (m_currenBitPos == 8)
{
AppendCurrentByte();
m_currentByte = 0;
m_currenBitPos = 0;
}
if (singleBit != 0)
{
m_currentByte |= (byte) (singleBit << m_currenBitPos);
}
++m_currenBitPos;
}
private void AppendCurrentByte()
{
m_buf[m_currentBufPos] = m_currentByte;
++m_currentBufPos;
}
}
/// <summary>
/// Allows to read individual bits in an intuitive way.
/// Used for efficient data storage.
/// </summary>
public class BitStreamReader
{
private const int NumberOfBitsInByte = 8;
private byte[] m_buffer;
private int m_currentBits;
private int m_bufPos;
private static readonly byte[] BitMask = new byte[]
{
0,
0b0000_0001,
0b0000_0011,
0b0000_0111,
0b0000_1111,
0b0001_1111,
0b0011_1111,
0b0111_1111,
0b1111_1111,
};
public uint Read(int bits)
{
#if UNITY_EDITOR
if (bits > 8 || bits <= 0)
{
throw new SystemException($"Invalid number of bits: {bits}");
}
#endif
uint value = 0;
#pragma warning disable 162
if (BitstreamConfig.ReadMultipleBitsInBitStream)
{
if (m_currentBits == 0)
{
++m_bufPos;
m_currentBits = NumberOfBitsInByte;
}
if (m_currentBits >= bits)
{
// We got more or the same amount of bits available. We can read them all in a single call
value = (uint)((m_buffer[m_bufPos] >> (NumberOfBitsInByte - m_currentBits)) & BitMask[bits]);
m_currentBits -= bits;
}
else
{
int prevBitCount = m_currentBits;
// We got less than the requested amount of bits available. We first read the rest of the current byte...
uint firstRead = (uint)(m_buffer[m_bufPos] >> (NumberOfBitsInByte - m_currentBits));
bits -= m_currentBits;
++m_bufPos;
m_currentBits = NumberOfBitsInByte - bits;
// ... and read the remaining bits from the following byte.
uint secondRead = (uint)(m_buffer[m_bufPos] & BitMask[bits]);
value = (secondRead << (prevBitCount)) | firstRead;
}
}
else
{
// Unrolled loop
switch (bits)
{
case 1: value |= ReadBit() << 0;break;
case 2: value |= ReadBit() << 0 | ReadBit() << 1;break;
case 3: value |= ReadBit() << 0 | ReadBit() << 1 | ReadBit() << 2;break;
case 4: value |= ReadBit() << 0 | ReadBit() << 1 | ReadBit() << 2 | ReadBit() << 3;break;
case 5: value |= ReadBit() << 0 | ReadBit() << 1 | ReadBit() << 2 | ReadBit() << 3 | ReadBit() << 4;break;
case 6: value |= ReadBit() << 0 | ReadBit() << 1 | ReadBit() << 2 | ReadBit() << 3 | ReadBit() << 4 | ReadBit() << 5;break;
case 7: value |= ReadBit() << 0 | ReadBit() << 1 | ReadBit() << 2 | ReadBit() << 3 | ReadBit() << 4 | ReadBit() << 5 | ReadBit() << 6;break;
case 8: value |= ReadBit() << 0 | ReadBit() << 1 | ReadBit() << 2 | ReadBit() << 3 | ReadBit() << 4 | ReadBit() << 5 | ReadBit() << 6 | ReadBit() << 7;break;
}
}
#pragma warning restore 162
return value;
}
public void Reset(byte[] buffer)
{
m_buffer = buffer;
m_bufPos = 0;
m_currentBits = NumberOfBitsInByte;
}
private uint ReadBit()
{
if (m_currentBits == 0)
{
++m_bufPos;
m_currentBits = NumberOfBitsInByte - 1;
return (uint) (m_buffer[m_bufPos] >> 0) & 1;
}
uint value = (uint) (m_buffer[m_bufPos] >> (NumberOfBitsInByte - m_currentBits)) & 1;
--m_currentBits;
return value;
}
}
public static class BitstreamConfig
{
public const bool ReadMultipleBitsInBitStream = true;
}
}