⛏️ index : haiku.git

/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "BitBuffer.h"


// #pragma mark - BitReader


struct BitBuffer::BitReader {
	const uint8*	data;
	uint64			bitSize;
	uint32			bitOffset;

	BitReader(const uint8* data, uint64 bitSize, uint32 bitOffset)
		:
		data(data),
		bitSize(bitSize),
		bitOffset(bitOffset)
	{
	}

	uint8 ReadByte()
	{
		uint8 byte = *data;
		data++;
		bitSize -= 8;

		if (bitOffset == 0)
			return byte;

		return (byte << bitOffset) | (*data >> (8 - bitOffset));
	}

	uint8 ReadBits(uint32 count)
	{
		uint8 byte = *data;
		bitSize -= count;
		bitOffset += count;

		if (bitOffset <= 8) {
			if (bitOffset == 8) {
				bitOffset = 0;
				data++;
				return byte & ((1 << count) - 1);
			}

			return (byte >> (8 - bitOffset)) & ((1 << count) - 1);
		}

		data++;
		bitOffset -= 8;
		return ((byte << bitOffset) | (*data >> (8 - bitOffset)))
			& ((1 << count) - 1);
	}
};


// #pragma mark - BitBuffer


BitBuffer::BitBuffer()
	:
	fMissingBits(0)
{
}


BitBuffer::~BitBuffer()
{
}


bool
BitBuffer::AddBytes(const void* data, size_t size)
{
	if (size == 0)
		return true;

	if (fMissingBits == 0) {
		size_t oldSize = fBytes.Size();
		if (!fBytes.AddUninitialized(size))
			return false;

		memcpy(fBytes.Elements() + oldSize, data, size);
		return true;
	}

	return AddBits(data, (uint64)size * 8, 0);
}


bool
BitBuffer::AddBits(const void* _data, uint64 bitSize, uint32 bitOffset)
{
	if (bitSize == 0)
		return true;

	const uint8* data = (const uint8*)_data + bitOffset / 8;
	bitOffset %= 8;

	BitReader reader(data, bitSize, bitOffset);

	// handle special case first: no more bits than missing
	size_t oldSize = fBytes.Size();
	if (fMissingBits > 0 && bitSize <= fMissingBits) {
		fMissingBits -= bitSize;
		uint8 bits = reader.ReadBits(bitSize) << fMissingBits;
		fBytes[oldSize - 1] |= bits;
		return true;
	}

	// resize the buffer
	if (!fBytes.AddUninitialized((reader.bitSize - fMissingBits + 7) / 8))
		return false;

	// fill in missing bits
	if (fMissingBits > 0) {
		fBytes[oldSize - 1] |= reader.ReadBits(fMissingBits);
		fMissingBits = 0;
	}

	// read full bytes as long as we can
	uint8* buffer = fBytes.Elements() + oldSize;
	while (reader.bitSize >= 8) {
		*buffer = reader.ReadByte();
		buffer++;
	}

	// If we have left-over bits, write a partial byte.
	if (reader.bitSize > 0) {
		fMissingBits = 8 - reader.bitSize;
		*buffer = reader.ReadBits(reader.bitSize) << fMissingBits;
	}

	return true;
}


bool
BitBuffer::AddZeroBits(uint64 bitSize)
{
	if (bitSize == 0)
		return true;

	// handle special case first: no more bits than missing
	size_t oldSize = fBytes.Size();
	if (fMissingBits > 0 && bitSize <= fMissingBits) {
		fMissingBits -= bitSize;
		return true;
	}

	// resize the buffer
	if (!fBytes.AddUninitialized((bitSize - fMissingBits + 7) / 8))
		return false;

	// fill in missing bits
	if (fMissingBits > 0) {
		bitSize -= fMissingBits;
		fMissingBits = 0;
	}

	// zero the remaining bytes, including a potentially partial last byte
	uint8* buffer = fBytes.Elements() + oldSize;
	memset(buffer, 0, (bitSize + 7) / 8);
	bitSize %= 8;

	if (bitSize > 0)
		fMissingBits = 8 - bitSize;

	return true;
}