Refactor value encoding in BinaryTable for improved clarity and consistency

This commit is contained in:
ImBenji
2025-10-13 15:28:20 +01:00
parent 782e3fcd94
commit a3f4b3308b

View File

@@ -21,93 +21,93 @@ int64_t BinaryTable::hashString(const std::string& str) const {
std::vector<uint8_t> encodeValue(const int32_t& value) { std::vector<uint8_t> encodeValue(const int32_t& value) {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
buffer.push_back(static_cast<uint8_t>(BT_Type::INTEGER)); buffer.push_back(static_cast<uint8_t>(BT_Type::INTEGER));
// Little endian encoding // Little endian encoding
buffer.push_back(value & 0xFF); buffer.push_back(value & 0xFF);
buffer.push_back((value >> 8) & 0xFF); buffer.push_back((value >> 8) & 0xFF);
buffer.push_back((value >> 16) & 0xFF); buffer.push_back((value >> 16) & 0xFF);
buffer.push_back((value >> 24) & 0xFF); buffer.push_back((value >> 24) & 0xFF);
return buffer; return buffer;
} }
std::vector<uint8_t> encodeValue(const float& value) { std::vector<uint8_t> encodeValue(const float& value) {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT)); buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT));
// Convert float to bytes (little endian) // Convert float to bytes (little endian)
uint32_t floatBits; uint32_t floatBits;
std::memcpy(&floatBits, &value, sizeof(float)); std::memcpy(&floatBits, &value, sizeof(float));
buffer.push_back(floatBits & 0xFF); buffer.push_back(floatBits & 0xFF);
buffer.push_back((floatBits >> 8) & 0xFF); buffer.push_back((floatBits >> 8) & 0xFF);
buffer.push_back((floatBits >> 16) & 0xFF); buffer.push_back((floatBits >> 16) & 0xFF);
buffer.push_back((floatBits >> 24) & 0xFF); buffer.push_back((floatBits >> 24) & 0xFF);
return buffer; return buffer;
} }
std::vector<uint8_t> encodeValue(const std::string& value) { std::vector<uint8_t> encodeValue(const std::string& value) {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
buffer.push_back(static_cast<uint8_t>(BT_Type::STRING)); buffer.push_back(static_cast<uint8_t>(BT_Type::STRING));
// String length (little endian) // String length (little endian)
int32_t length = static_cast<int32_t>(value.length()); int32_t length = static_cast<int32_t>(value.length());
buffer.push_back(length & 0xFF); buffer.push_back(length & 0xFF);
buffer.push_back((length >> 8) & 0xFF); buffer.push_back((length >> 8) & 0xFF);
buffer.push_back((length >> 16) & 0xFF); buffer.push_back((length >> 16) & 0xFF);
buffer.push_back((length >> 24) & 0xFF); buffer.push_back((length >> 24) & 0xFF);
// String bytes // String bytes
for (char c : value) { for (char c : value) {
buffer.push_back(static_cast<uint8_t>(c)); buffer.push_back(static_cast<uint8_t>(c));
} }
return buffer; return buffer;
} }
std::vector<uint8_t> encodeValue(const std::vector<int32_t>& value) { std::vector<uint8_t> encodeValue(const std::vector<int32_t>& value) {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
buffer.push_back(static_cast<uint8_t>(BT_Type::INTEGER_ARRAY)); buffer.push_back(static_cast<uint8_t>(BT_Type::INTEGER_ARRAY));
// Array length (little endian) // Array length (little endian)
int32_t length = static_cast<int32_t>(value.size()); int32_t length = static_cast<int32_t>(value.size());
buffer.push_back(length & 0xFF); buffer.push_back(length & 0xFF);
buffer.push_back((length >> 8) & 0xFF); buffer.push_back((length >> 8) & 0xFF);
buffer.push_back((length >> 16) & 0xFF); buffer.push_back((length >> 16) & 0xFF);
buffer.push_back((length >> 24) & 0xFF); buffer.push_back((length >> 24) & 0xFF);
// Array elements // Array elements
for (const auto& item : value) { for (const auto& item : value) {
auto itemBuffer = encodeValue(item); auto itemBuffer = encodeValue(item);
buffer.insert(buffer.end(), itemBuffer.begin(), itemBuffer.end()); buffer.insert(buffer.end(), itemBuffer.begin(), itemBuffer.end());
} }
return buffer; return buffer;
} }
std::vector<uint8_t> encodeValue(const std::vector<float>& value) { std::vector<uint8_t> encodeValue(const std::vector<float>& value) {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT_ARRAY)); buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT_ARRAY));
// Array length (little endian) // Array length (little endian)
int32_t length = static_cast<int32_t>(value.size()); int32_t length = static_cast<int32_t>(value.size());
buffer.push_back(length & 0xFF); buffer.push_back(length & 0xFF);
buffer.push_back((length >> 8) & 0xFF); buffer.push_back((length >> 8) & 0xFF);
buffer.push_back((length >> 16) & 0xFF); buffer.push_back((length >> 16) & 0xFF);
buffer.push_back((length >> 24) & 0xFF); buffer.push_back((length >> 24) & 0xFF);
// Array elements // Array elements
for (const auto& item : value) { for (const auto& item : value) {
auto itemBuffer = encodeValue(item); auto itemBuffer = encodeValue(item);
buffer.insert(buffer.end(), itemBuffer.begin(), itemBuffer.end()); buffer.insert(buffer.end(), itemBuffer.begin(), itemBuffer.end());
} }
return buffer; return buffer;
} }
// BT_Reference implementation // BT_Reference implementation
BT_Reference::BT_Reference(BinaryTable* table, BT_Pointer pointer) BT_Reference::BT_Reference(BinaryTable* table, BT_Pointer pointer)
: table_(table), pointer_(pointer) {} : table_(table), pointer_(pointer) {}
template<> template<>
@@ -115,14 +115,14 @@ int32_t BT_Reference::decodeValue<int32_t>() {
if (pointer_.isNull()) { if (pointer_.isNull()) {
throw std::runtime_error("Null pointer"); throw std::runtime_error("Null pointer");
} }
table_->setFilePosition(pointer_.address()); table_->setFilePosition(pointer_.address());
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
if (static_cast<BT_Type>(typeId) != BT_Type::INTEGER) { if (static_cast<BT_Type>(typeId) != BT_Type::INTEGER) {
throw std::runtime_error("Type mismatch"); throw std::runtime_error("Type mismatch");
} }
return table_->readInt32(pointer_.address() + 1); return table_->readInt32(pointer_.address() + 1);
} }
@@ -131,14 +131,14 @@ float BT_Reference::decodeValue<float>() {
if (pointer_.isNull()) { if (pointer_.isNull()) {
throw std::runtime_error("Null pointer"); throw std::runtime_error("Null pointer");
} }
table_->setFilePosition(pointer_.address()); table_->setFilePosition(pointer_.address());
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
if (static_cast<BT_Type>(typeId) != BT_Type::FLOAT) { if (static_cast<BT_Type>(typeId) != BT_Type::FLOAT) {
throw std::runtime_error("Type mismatch"); throw std::runtime_error("Type mismatch");
} }
return table_->readFloat32(pointer_.address() + 1); return table_->readFloat32(pointer_.address() + 1);
} }
@@ -147,17 +147,17 @@ std::string BT_Reference::decodeValue<std::string>() {
if (pointer_.isNull()) { if (pointer_.isNull()) {
throw std::runtime_error("Null pointer"); throw std::runtime_error("Null pointer");
} }
table_->setFilePosition(pointer_.address()); table_->setFilePosition(pointer_.address());
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
if (static_cast<BT_Type>(typeId) != BT_Type::STRING) { if (static_cast<BT_Type>(typeId) != BT_Type::STRING) {
throw std::runtime_error("Type mismatch"); throw std::runtime_error("Type mismatch");
} }
int32_t length = table_->readInt32(pointer_.address() + 1); int32_t length = table_->readInt32(pointer_.address() + 1);
auto bytes = table_->readBytes(pointer_.address() + 5, length); auto bytes = table_->readBytes(pointer_.address() + 5, length);
return std::string(bytes.begin(), bytes.end()); return std::string(bytes.begin(), bytes.end());
} }
@@ -176,28 +176,28 @@ std::vector<int32_t> BT_Reference::decodeValue<std::vector<int32_t>>() {
if (pointer_.isNull()) { if (pointer_.isNull()) {
return {}; return {};
} }
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
BT_Type type = static_cast<BT_Type>(typeId); BT_Type type = static_cast<BT_Type>(typeId);
if (type != BT_Type::INTEGER_ARRAY) { if (type != BT_Type::INTEGER_ARRAY) {
throw std::runtime_error("Type mismatch - expected integer array"); throw std::runtime_error("Type mismatch - expected integer array");
} }
int32_t length = table_->readInt32(pointer_.address() + 1); int32_t length = table_->readInt32(pointer_.address() + 1);
std::vector<int32_t> result; std::vector<int32_t> result;
result.reserve(length); result.reserve(length);
// Each element is: type byte (1) + int32 data (4) = 5 bytes // Each element is: type byte (1) + int32 data (4) = 5 bytes
int64_t elementPos = pointer_.address() + 1 + 4; // Skip type and length int64_t elementPos = pointer_.address() + 1 + 4; // Skip type and length
for (int32_t i = 0; i < length; i++) { for (int32_t i = 0; i < length; i++) {
// Skip the type byte, read the int32 value // Skip the type byte, read the int32 value
int32_t value = table_->readInt32(elementPos + 1); int32_t value = table_->readInt32(elementPos + 1);
result.push_back(value); result.push_back(value);
elementPos += 5; // Move to next element elementPos += 5; // Move to next element
} }
return result; return result;
} }
@@ -206,28 +206,28 @@ std::vector<float> BT_Reference::decodeValue<std::vector<float>>() {
if (pointer_.isNull()) { if (pointer_.isNull()) {
return {}; return {};
} }
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
BT_Type type = static_cast<BT_Type>(typeId); BT_Type type = static_cast<BT_Type>(typeId);
if (type != BT_Type::FLOAT_ARRAY) { if (type != BT_Type::FLOAT_ARRAY) {
throw std::runtime_error("Type mismatch - expected float array"); throw std::runtime_error("Type mismatch - expected float array");
} }
int32_t length = table_->readInt32(pointer_.address() + 1); int32_t length = table_->readInt32(pointer_.address() + 1);
std::vector<float> result; std::vector<float> result;
result.reserve(length); result.reserve(length);
// Each element is: type byte (1) + float data (4) = 5 bytes // Each element is: type byte (1) + float data (4) = 5 bytes
int64_t elementPos = pointer_.address() + 1 + 4; // Skip type and length int64_t elementPos = pointer_.address() + 1 + 4; // Skip type and length
for (int32_t i = 0; i < length; i++) { for (int32_t i = 0; i < length; i++) {
// Skip the type byte, read the float value // Skip the type byte, read the float value
float value = table_->readFloat32(elementPos + 1); float value = table_->readFloat32(elementPos + 1);
result.push_back(value); result.push_back(value);
elementPos += 5; // Move to next element elementPos += 5; // Move to next element
} }
return result; return result;
} }
@@ -235,10 +235,10 @@ int32_t BT_Reference::size() const {
if (pointer_.isNull()) { if (pointer_.isNull()) {
return 0; return 0;
} }
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
BT_Type type = static_cast<BT_Type>(typeId); BT_Type type = static_cast<BT_Type>(typeId);
switch (type) { switch (type) {
case BT_Type::POINTER: case BT_Type::POINTER:
return 1 + 8; // Type byte + pointer return 1 + 8; // Type byte + pointer
@@ -267,7 +267,7 @@ BT_Type BT_Reference::getType() const {
if (pointer_.isNull()) { if (pointer_.isNull()) {
throw std::runtime_error("Null pointer"); throw std::runtime_error("Null pointer");
} }
uint8_t typeId = table_->readByte(pointer_.address()); uint8_t typeId = table_->readByte(pointer_.address());
return static_cast<BT_Type>(typeId); return static_cast<BT_Type>(typeId);
} }
@@ -278,15 +278,15 @@ int32_t BT_UniformArray<T>::length() const {
if (this->pointer_.isNull()) { if (this->pointer_.isNull()) {
return 0; return 0;
} }
try { try {
uint8_t typeId = this->table_->readByte(this->pointer_.address()); uint8_t typeId = this->table_->readByte(this->pointer_.address());
BT_Type type = static_cast<BT_Type>(typeId); BT_Type type = static_cast<BT_Type>(typeId);
if (!isArrayType(type)) { if (!isArrayType(type)) {
return 0; // Treat non-array as empty array instead of throwing return 0; // Treat non-array as empty array instead of throwing
} }
return this->table_->readInt32(this->pointer_.address() + 1); return this->table_->readInt32(this->pointer_.address() + 1);
} catch (...) { } catch (...) {
return 0; // If we can't read, treat as empty return 0; // If we can't read, treat as empty
@@ -298,20 +298,20 @@ T BT_UniformArray<T>::operator[](int32_t index) const {
if (this->pointer_.isNull()) { if (this->pointer_.isNull()) {
throw std::runtime_error("Null pointer"); throw std::runtime_error("Null pointer");
} }
int32_t len = length(); int32_t len = length();
if (index < 0 || index >= len) { if (index < 0 || index >= len) {
throw std::out_of_range("Index out of range"); throw std::out_of_range("Index out of range");
} }
// Determine element type and size // Determine element type and size
uint8_t elementTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4); uint8_t elementTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4);
BT_Type elementType = static_cast<BT_Type>(elementTypeId); BT_Type elementType = static_cast<BT_Type>(elementTypeId);
int32_t elementSize = 1 + getTypeSize(elementType); int32_t elementSize = 1 + getTypeSize(elementType);
int64_t itemAddress = this->pointer_.address() + 1 + 4 + index * elementSize; int64_t itemAddress = this->pointer_.address() + 1 + 4 + index * elementSize;
BT_Reference itemRef(this->table_, BT_Pointer(itemAddress)); BT_Reference itemRef(this->table_, BT_Pointer(itemAddress));
return itemRef.decodeValue<T>(); return itemRef.decodeValue<T>();
} }
@@ -320,26 +320,26 @@ void BT_UniformArray<T>::set(int32_t index, const T& value) {
if (this->pointer_.isNull()) { if (this->pointer_.isNull()) {
throw std::runtime_error("Null pointer"); throw std::runtime_error("Null pointer");
} }
int32_t len = length(); int32_t len = length();
if (index < 0 || index >= len) { if (index < 0 || index >= len) {
throw std::out_of_range("Index out of range"); throw std::out_of_range("Index out of range");
} }
// Validate type compatibility // Validate type compatibility
BT_Type expectedType = getTypeFromValue<T>(); BT_Type expectedType = getTypeFromValue<T>();
uint8_t elementTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4); uint8_t elementTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4);
BT_Type elementType = static_cast<BT_Type>(elementTypeId); BT_Type elementType = static_cast<BT_Type>(elementTypeId);
if (expectedType != elementType) { if (expectedType != elementType) {
throw std::runtime_error("Type mismatch"); throw std::runtime_error("Type mismatch");
} }
// Encode and write value // Encode and write value
auto valueBuffer = encodeValue(value); auto valueBuffer = encodeValue(value);
int32_t elementSize = 1 + getTypeSize(elementType); int32_t elementSize = 1 + getTypeSize(elementType);
int64_t itemAddress = this->pointer_.address() + 1 + 4 + index * elementSize; int64_t itemAddress = this->pointer_.address() + 1 + 4 + index * elementSize;
this->table_->writeBytes(itemAddress, valueBuffer); this->table_->writeBytes(itemAddress, valueBuffer);
} }
@@ -353,7 +353,7 @@ void BT_UniformArray<T>::addAll(const std::vector<T>& values) {
this->table_->antiFreeListScope([&]() { this->table_->antiFreeListScope([&]() {
// Get current element type or determine from new values // Get current element type or determine from new values
BT_Type elementType = getTypeFromValue<T>(); BT_Type elementType = getTypeFromValue<T>();
if (length() > 0) { if (length() > 0) {
uint8_t existingTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4); uint8_t existingTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4);
BT_Type existingType = static_cast<BT_Type>(existingTypeId); BT_Type existingType = static_cast<BT_Type>(existingTypeId);
@@ -361,7 +361,7 @@ void BT_UniformArray<T>::addAll(const std::vector<T>& values) {
throw std::runtime_error("Type mismatch"); throw std::runtime_error("Type mismatch");
} }
} }
// Validate all values are compatible // Validate all values are compatible
for (const auto& value : values) { for (const auto& value : values) {
(void)value; // Suppress unused variable warning (void)value; // Suppress unused variable warning
@@ -373,12 +373,12 @@ void BT_UniformArray<T>::addAll(const std::vector<T>& values) {
throw std::runtime_error("Variable size types not supported in uniform arrays"); throw std::runtime_error("Variable size types not supported in uniform arrays");
} }
} }
// Read current array buffer // Read current array buffer
int32_t currentLength = length(); int32_t currentLength = length();
int32_t elementSize = 1 + getTypeSize(elementType); int32_t elementSize = 1 + getTypeSize(elementType);
int32_t currentBufferSize = 1 + 4 + currentLength * elementSize; int32_t currentBufferSize = 1 + 4 + currentLength * elementSize;
std::vector<uint8_t> fullBuffer; std::vector<uint8_t> fullBuffer;
if (currentLength > 0) { if (currentLength > 0) {
fullBuffer = this->table_->readBytes(this->pointer_.address(), currentBufferSize); fullBuffer = this->table_->readBytes(this->pointer_.address(), currentBufferSize);
@@ -390,28 +390,28 @@ void BT_UniformArray<T>::addAll(const std::vector<T>& values) {
fullBuffer.push_back(0); fullBuffer.push_back(0);
fullBuffer.push_back(0); fullBuffer.push_back(0);
} }
// Add new values to buffer // Add new values to buffer
for (const auto& value : values) { for (const auto& value : values) {
auto valueBuffer = encodeValue(value); auto valueBuffer = encodeValue(value);
fullBuffer.insert(fullBuffer.end(), valueBuffer.begin(), valueBuffer.end()); fullBuffer.insert(fullBuffer.end(), valueBuffer.begin(), valueBuffer.end());
} }
// Update length in buffer // Update length in buffer
int32_t newLength = currentLength + static_cast<int32_t>(values.size()); int32_t newLength = currentLength + static_cast<int32_t>(values.size());
fullBuffer[1] = newLength & 0xFF; fullBuffer[1] = newLength & 0xFF;
fullBuffer[2] = (newLength >> 8) & 0xFF; fullBuffer[2] = (newLength >> 8) & 0xFF;
fullBuffer[3] = (newLength >> 16) & 0xFF; fullBuffer[3] = (newLength >> 16) & 0xFF;
fullBuffer[4] = (newLength >> 24) & 0xFF; fullBuffer[4] = (newLength >> 24) & 0xFF;
// Free old array if it exists // Free old array if it exists
if (!this->pointer_.isNull()) { if (!this->pointer_.isNull()) {
this->table_->free(this->pointer_, currentBufferSize); this->table_->free(this->pointer_, currentBufferSize);
} }
// Allocate new space // Allocate new space
BT_Pointer newPointer = this->table_->alloc(static_cast<int32_t>(fullBuffer.size())); BT_Pointer newPointer = this->table_->alloc(static_cast<int32_t>(fullBuffer.size()));
// Update any references in address table // Update any references in address table
auto addressTable = this->table_->getAddressTable(); auto addressTable = this->table_->getAddressTable();
for (auto& [key, value] : addressTable) { for (auto& [key, value] : addressTable) {
@@ -421,7 +421,7 @@ void BT_UniformArray<T>::addAll(const std::vector<T>& values) {
} }
this->table_->setAddressTable(addressTable); this->table_->setAddressTable(addressTable);
this->pointer_ = newPointer; this->pointer_ = newPointer;
// Write updated buffer // Write updated buffer
this->table_->writeBytes(newPointer.address(), fullBuffer); this->table_->writeBytes(newPointer.address(), fullBuffer);
}); });
@@ -433,30 +433,30 @@ std::vector<T> BT_UniformArray<T>::fetchSublist(int32_t start, int32_t end) {
if (len == 0) { if (len == 0) {
return {}; return {};
} }
if (end == -1) { if (end == -1) {
end = len; end = len;
} }
if (start < 0 || start >= len || end < start || end > len) { if (start < 0 || start >= len || end < start || end > len) {
throw std::out_of_range("Invalid range"); throw std::out_of_range("Invalid range");
} }
uint8_t elementTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4); uint8_t elementTypeId = this->table_->readByte(this->pointer_.address() + 1 + 4);
BT_Type elementType = static_cast<BT_Type>(elementTypeId); BT_Type elementType = static_cast<BT_Type>(elementTypeId);
int32_t elementSize = 1 + getTypeSize(elementType); int32_t elementSize = 1 + getTypeSize(elementType);
if (getTypeSize(elementType) == -1) { if (getTypeSize(elementType) == -1) {
throw std::runtime_error("Variable size types not supported in uniform arrays"); throw std::runtime_error("Variable size types not supported in uniform arrays");
} }
std::vector<T> result; std::vector<T> result;
for (int32_t i = start; i < end; i++) { for (int32_t i = start; i < end; i++) {
int64_t itemAddress = this->pointer_.address() + 1 + 4 + i * elementSize; int64_t itemAddress = this->pointer_.address() + 1 + 4 + i * elementSize;
BT_Reference itemRef(this->table_, BT_Pointer(itemAddress)); BT_Reference itemRef(this->table_, BT_Pointer(itemAddress));
result.push_back(itemRef.decodeValue<T>()); result.push_back(itemRef.decodeValue<T>());
} }
return result; return result;
} }
@@ -465,14 +465,14 @@ template class BT_UniformArray<int32_t>;
template class BT_UniformArray<float>; template class BT_UniformArray<float>;
// BinaryTable implementation // BinaryTable implementation
BinaryTable::BinaryTable(const std::string& path) BinaryTable::BinaryTable(const std::string& path)
: filePath_(path), freeListLifted_(false) { : filePath_(path), freeListLifted_(false) {
file_ = fopen(path.c_str(), "r+b"); file_ = fopen(path.c_str(), "r+b");
if (!file_) { if (!file_) {
// File doesn't exist, create it // File doesn't exist, create it
file_ = fopen(path.c_str(), "w+b"); file_ = fopen(path.c_str(), "w+b");
} }
} }
BinaryTable::~BinaryTable() { BinaryTable::~BinaryTable() {
@@ -493,7 +493,7 @@ int32_t BinaryTable::readInt32(int64_t position) {
fseek(file_, position, SEEK_SET); fseek(file_, position, SEEK_SET);
uint8_t bytes[4]; uint8_t bytes[4];
fread(bytes, 1, 4, file_); fread(bytes, 1, 4, file_);
return static_cast<int32_t>(bytes[0]) | return static_cast<int32_t>(bytes[0]) |
(static_cast<int32_t>(bytes[1]) << 8) | (static_cast<int32_t>(bytes[1]) << 8) |
(static_cast<int32_t>(bytes[2]) << 16) | (static_cast<int32_t>(bytes[2]) << 16) |
@@ -504,12 +504,12 @@ float BinaryTable::readFloat32(int64_t position) {
fseek(file_, position, SEEK_SET); fseek(file_, position, SEEK_SET);
uint8_t bytes[4]; uint8_t bytes[4];
fread(bytes, 1, 4, file_); fread(bytes, 1, 4, file_);
uint32_t floatBits = static_cast<uint32_t>(bytes[0]) | uint32_t floatBits = static_cast<uint32_t>(bytes[0]) |
(static_cast<uint32_t>(bytes[1]) << 8) | (static_cast<uint32_t>(bytes[1]) << 8) |
(static_cast<uint32_t>(bytes[2]) << 16) | (static_cast<uint32_t>(bytes[2]) << 16) |
(static_cast<uint32_t>(bytes[3]) << 24); (static_cast<uint32_t>(bytes[3]) << 24);
float result; float result;
std::memcpy(&result, &floatBits, sizeof(float)); std::memcpy(&result, &floatBits, sizeof(float));
return result; return result;
@@ -519,12 +519,12 @@ int64_t BinaryTable::readInt64(int64_t position) {
fseek(file_, position, SEEK_SET); fseek(file_, position, SEEK_SET);
uint8_t bytes[8]; uint8_t bytes[8];
fread(bytes, 1, 8, file_); fread(bytes, 1, 8, file_);
int64_t result = 0; int64_t result = 0;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
result |= static_cast<int64_t>(bytes[i]) << (i * 8); result |= static_cast<int64_t>(bytes[i]) << (i * 8);
} }
return result; return result;
} }
@@ -557,7 +557,7 @@ void BinaryTable::writeFloat32(int64_t position, float value) {
fseek(file_, position, SEEK_SET); fseek(file_, position, SEEK_SET);
uint32_t floatBits; uint32_t floatBits;
std::memcpy(&floatBits, &value, sizeof(float)); std::memcpy(&floatBits, &value, sizeof(float));
uint8_t bytes[4] = { uint8_t bytes[4] = {
static_cast<uint8_t>(floatBits & 0xFF), static_cast<uint8_t>(floatBits & 0xFF),
static_cast<uint8_t>((floatBits >> 8) & 0xFF), static_cast<uint8_t>((floatBits >> 8) & 0xFF),
@@ -602,59 +602,59 @@ void BinaryTable::setFilePosition(int64_t position) {
std::unordered_map<int64_t, BT_Pointer> BinaryTable::getAddressTable() { std::unordered_map<int64_t, BT_Pointer> BinaryTable::getAddressTable() {
int64_t tableAddress = readInt64(0); int64_t tableAddress = readInt64(0);
DEBUG_PRINTLN("DEBUG: getAddressTable reading from address " << tableAddress); DEBUG_PRINTLN("DEBUG: getAddressTable reading from address " << tableAddress);
if (tableAddress == -1) { // Null pointer if (tableAddress == -1) { // Null pointer
return {}; return {};
} }
// Validate table address is within file bounds // Validate table address is within file bounds
int64_t fileLength = getFileLength(); int64_t fileLength = getFileLength();
if (tableAddress < 0 || tableAddress >= fileLength) { if (tableAddress < 0 || tableAddress >= fileLength) {
DEBUG_PRINTLN("DEBUG: Address table pointer is out of bounds: " << tableAddress << " (file length: " << fileLength << ")"); DEBUG_PRINTLN("DEBUG: Address table pointer is out of bounds: " << tableAddress << " (file length: " << fileLength << ")");
throw std::runtime_error("Address table pointer is corrupted - out of bounds"); throw std::runtime_error("Address table pointer is corrupted - out of bounds");
} }
try { try {
uint8_t typeId = readByte(tableAddress); uint8_t typeId = readByte(tableAddress);
if (static_cast<BT_Type>(typeId) != BT_Type::ADDRESS_TABLE) { if (static_cast<BT_Type>(typeId) != BT_Type::ADDRESS_TABLE) {
DEBUG_PRINTLN("DEBUG: Invalid type ID at address table location: " << (int)typeId); DEBUG_PRINTLN("DEBUG: Invalid type ID at address table location: " << (int)typeId);
// Address table might not be valid yet, return empty // Address table might not be valid yet, return empty
return {}; return {};
} }
int32_t tableCount = readInt32(tableAddress + 1); int32_t tableCount = readInt32(tableAddress + 1);
// Validate table count is reasonable // Validate table count is reasonable
if (tableCount < 0 || tableCount > 1000000) { // Arbitrary but reasonable limit if (tableCount < 0 || tableCount > 1000000) { // Arbitrary but reasonable limit
DEBUG_PRINTLN("DEBUG: Suspicious address table count: " << tableCount); DEBUG_PRINTLN("DEBUG: Suspicious address table count: " << tableCount);
throw std::runtime_error("Address table appears corrupted - invalid entry count"); throw std::runtime_error("Address table appears corrupted - invalid entry count");
} }
// Validate the entire table fits within file bounds // Validate the entire table fits within file bounds
int64_t requiredSize = 1 + 4 + tableCount * (8 + 8); // Type + count + entries int64_t requiredSize = 1 + 4 + tableCount * (8 + 8); // Type + count + entries
if (tableAddress + requiredSize > fileLength) { if (tableAddress + requiredSize > fileLength) {
DEBUG_PRINTLN("DEBUG: Address table extends beyond file bounds"); DEBUG_PRINTLN("DEBUG: Address table extends beyond file bounds");
throw std::runtime_error("Address table appears corrupted - extends beyond file"); throw std::runtime_error("Address table appears corrupted - extends beyond file");
} }
std::unordered_map<int64_t, BT_Pointer> addressTable; std::unordered_map<int64_t, BT_Pointer> addressTable;
for (int32_t i = 0; i < tableCount; i++) { for (int32_t i = 0; i < tableCount; i++) {
int64_t offset = tableAddress + 1 + 4 + i * (8 + 8); int64_t offset = tableAddress + 1 + 4 + i * (8 + 8);
int64_t keyHash = readInt64(offset); int64_t keyHash = readInt64(offset);
int64_t valueAddress = readInt64(offset + 8); int64_t valueAddress = readInt64(offset + 8);
// Validate each value address is within bounds (or null) // Validate each value address is within bounds (or null)
if (valueAddress != -1 && (valueAddress < 0 || valueAddress >= fileLength)) { if (valueAddress != -1 && (valueAddress < 0 || valueAddress >= fileLength)) {
DEBUG_PRINTLN("DEBUG: Invalid value address in entry " << i << ": " << valueAddress); DEBUG_PRINTLN("DEBUG: Invalid value address in entry " << i << ": " << valueAddress);
throw std::runtime_error("Address table entry contains invalid pointer"); throw std::runtime_error("Address table entry contains invalid pointer");
} }
DEBUG_PRINTLN(" Reading entry " << i << ": hash " << keyHash << " -> address " << valueAddress); DEBUG_PRINTLN(" Reading entry " << i << ": hash " << keyHash << " -> address " << valueAddress);
addressTable[keyHash] = BT_Pointer(valueAddress); addressTable[keyHash] = BT_Pointer(valueAddress);
} }
return addressTable; return addressTable;
} catch (const std::runtime_error&) { } catch (const std::runtime_error&) {
// Re-throw runtime errors (our validation failures) // Re-throw runtime errors (our validation failures)
@@ -672,12 +672,12 @@ void BinaryTable::setAddressTable(const std::unordered_map<int64_t, BT_Pointer>&
for (const auto& [key, value] : table) { for (const auto& [key, value] : table) {
DEBUG_PRINTLN(" Writing hash " << key << " -> address " << value.address()); DEBUG_PRINTLN(" Writing hash " << key << " -> address " << value.address());
} }
// Read old table pointer FIRST to ensure we can clean it up later // Read old table pointer FIRST to ensure we can clean it up later
int64_t oldTablePointerAddress = readInt64(0); int64_t oldTablePointerAddress = readInt64(0);
BT_Pointer oldTablePtr(oldTablePointerAddress); BT_Pointer oldTablePtr(oldTablePointerAddress);
int32_t oldTableSize = 0; int32_t oldTableSize = 0;
// Calculate old table size if it exists // Calculate old table size if it exists
if (!oldTablePtr.isNull()) { if (!oldTablePtr.isNull()) {
try { try {
@@ -689,19 +689,19 @@ void BinaryTable::setAddressTable(const std::unordered_map<int64_t, BT_Pointer>&
oldTablePtr = BT_Null; oldTablePtr = BT_Null;
} }
} }
// Build buffer manually (matching Dart implementation exactly) // Build buffer manually (matching Dart implementation exactly)
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
// Type byte // Type byte
buffer.push_back(static_cast<uint8_t>(BT_Type::ADDRESS_TABLE)); buffer.push_back(static_cast<uint8_t>(BT_Type::ADDRESS_TABLE));
// Table count (little endian, 4 bytes) // Table count (little endian, 4 bytes)
int32_t count = static_cast<int32_t>(table.size()); int32_t count = static_cast<int32_t>(table.size());
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
buffer.push_back(static_cast<uint8_t>((count >> (i * 8)) & 0xFF)); buffer.push_back(static_cast<uint8_t>((count >> (i * 8)) & 0xFF));
} }
// Table entries // Table entries
for (const auto& [key, value] : table) { for (const auto& [key, value] : table) {
// Key hash (little endian, 8 bytes) // Key hash (little endian, 8 bytes)
@@ -714,23 +714,24 @@ void BinaryTable::setAddressTable(const std::unordered_map<int64_t, BT_Pointer>&
buffer.push_back(static_cast<uint8_t>((addr >> (i * 8)) & 0xFF)); buffer.push_back(static_cast<uint8_t>((addr >> (i * 8)) & 0xFF));
} }
} }
// Allocate and write new address table // Allocate and write new address table
BT_Pointer newTableAddress = alloc(static_cast<int32_t>(buffer.size())); BT_Pointer newTableAddress = alloc(static_cast<int32_t>(buffer.size()));
setFilePosition(newTableAddress.address()); setFilePosition(newTableAddress.address());
size_t written = fwrite(buffer.data(), 1, buffer.size(), file_); size_t written = fwrite(buffer.data(), 1, buffer.size(), file_);
(void)written; // Suppress unused variable warning in release builds
if (written != buffer.size()) { if (written != buffer.size()) {
throw std::runtime_error("Failed to write complete address table"); throw std::runtime_error("Failed to write complete address table");
} }
// Ensure new table is written to disk before updating header // Ensure new table is written to disk before updating header
fflush(file_); fflush(file_);
// Atomically update header to point to new table // Atomically update header to point to new table
writeInt64(0, newTableAddress.address()); writeInt64(0, newTableAddress.address());
fflush(file_); fflush(file_);
// Only free old table after new one is successfully committed // Only free old table after new one is successfully committed
DEBUG_PRINTLN("DEBUG: oldTablePtr.isNull()=" << oldTablePtr.isNull() << ", oldTablePtr.address()=" << oldTablePtr.address() << ", newTableAddress=" << newTableAddress.address()); DEBUG_PRINTLN("DEBUG: oldTablePtr.isNull()=" << oldTablePtr.isNull() << ", oldTablePtr.address()=" << oldTablePtr.address() << ", newTableAddress=" << newTableAddress.address());
if (!oldTablePtr.isNull() && oldTablePtr != newTableAddress) { if (!oldTablePtr.isNull() && oldTablePtr != newTableAddress) {
@@ -746,21 +747,21 @@ std::vector<BT_FreeListEntry> BinaryTable::getFreeList() {
if (freeListLifted_) { if (freeListLifted_) {
return freeListCache_; return freeListCache_;
} }
int64_t fileLength = getFileLength(); int64_t fileLength = getFileLength();
if (fileLength < 4) { if (fileLength < 4) {
return {}; return {};
} }
int32_t entryCount = readInt32(fileLength - 4); int32_t entryCount = readInt32(fileLength - 4);
if (entryCount == 0) { if (entryCount == 0) {
return {}; return {};
} }
int32_t entrySize = 8 + 4; // Pointer + Size int32_t entrySize = 8 + 4; // Pointer + Size
int32_t freeListSize = entryCount * entrySize; int32_t freeListSize = entryCount * entrySize;
int64_t freeListStart = fileLength - 4 - freeListSize; int64_t freeListStart = fileLength - 4 - freeListSize;
std::vector<BT_FreeListEntry> freeList; std::vector<BT_FreeListEntry> freeList;
for (int32_t i = 0; i < entryCount; i++) { for (int32_t i = 0; i < entryCount; i++) {
int64_t offset = freeListStart + i * entrySize; int64_t offset = freeListStart + i * entrySize;
@@ -768,7 +769,7 @@ std::vector<BT_FreeListEntry> BinaryTable::getFreeList() {
int32_t size = readInt32(offset + 8); int32_t size = readInt32(offset + 8);
freeList.emplace_back(BT_Pointer(pointerAddress), size); freeList.emplace_back(BT_Pointer(pointerAddress), size);
} }
return freeList; return freeList;
} }
@@ -779,18 +780,18 @@ void BinaryTable::setFreeList(const std::vector<BT_FreeListEntry>& list) {
DEBUG_PRINTLN("DEBUG: setFreeList early return - just updating cache"); DEBUG_PRINTLN("DEBUG: setFreeList early return - just updating cache");
return; return;
} }
// Always remove old free list first (matching Dart behavior) // Always remove old free list first (matching Dart behavior)
int64_t fileLength = getFileLength(); int64_t fileLength = getFileLength();
DEBUG_PRINTLN("DEBUG: setFreeList fileLength=" << fileLength); DEBUG_PRINTLN("DEBUG: setFreeList fileLength=" << fileLength);
// Calculate old free list size to remove // Calculate old free list size to remove
int32_t oldEntryCount = 0; int32_t oldEntryCount = 0;
if (fileLength >= 4) { if (fileLength >= 4) {
oldEntryCount = readInt32(fileLength - 4); oldEntryCount = readInt32(fileLength - 4);
} }
DEBUG_PRINTLN("DEBUG: setFreeList oldEntryCount=" << oldEntryCount); DEBUG_PRINTLN("DEBUG: setFreeList oldEntryCount=" << oldEntryCount);
// Remove old free list (matching Dart: always truncate first) // Remove old free list (matching Dart: always truncate first)
if (oldEntryCount > 0) { if (oldEntryCount > 0) {
int32_t oldListSize = (oldEntryCount * (8 + 4)) + 4; // Entries + Count int32_t oldListSize = (oldEntryCount * (8 + 4)) + 4; // Entries + Count
@@ -799,19 +800,19 @@ void BinaryTable::setFreeList(const std::vector<BT_FreeListEntry>& list) {
truncateFile(newFileLength); truncateFile(newFileLength);
fileLength = newFileLength; // Update file length fileLength = newFileLength; // Update file length
} }
// If the new free list is empty, we're done (old list already removed) // If the new free list is empty, we're done (old list already removed)
if (list.empty()) { if (list.empty()) {
DEBUG_PRINTLN("DEBUG: setFreeList - empty list, old list removed, done"); DEBUG_PRINTLN("DEBUG: setFreeList - empty list, old list removed, done");
return; return;
} }
// Write new free list at end of file // Write new free list at end of file
int64_t newLogicalEnd = fileLength; int64_t newLogicalEnd = fileLength;
// Encode new free list // Encode new free list
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
// Entries // Entries
for (const auto& entry : list) { for (const auto& entry : list) {
// Pointer (8 bytes, little endian) // Pointer (8 bytes, little endian)
@@ -825,18 +826,18 @@ void BinaryTable::setFreeList(const std::vector<BT_FreeListEntry>& list) {
buffer.push_back(static_cast<uint8_t>((size >> (i * 8)) & 0xFF)); buffer.push_back(static_cast<uint8_t>((size >> (i * 8)) & 0xFF));
} }
} }
// Entry count (4 bytes, little endian) // Entry count (4 bytes, little endian)
int32_t count = static_cast<int32_t>(list.size()); int32_t count = static_cast<int32_t>(list.size());
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
buffer.push_back(static_cast<uint8_t>((count >> (i * 8)) & 0xFF)); buffer.push_back(static_cast<uint8_t>((count >> (i * 8)) & 0xFF));
} }
// Write at the logical end position // Write at the logical end position
fseek(file_, newLogicalEnd, SEEK_SET); fseek(file_, newLogicalEnd, SEEK_SET);
fwrite(buffer.data(), 1, buffer.size(), file_); fwrite(buffer.data(), 1, buffer.size(), file_);
fflush(file_); fflush(file_);
// Update logical file length // Update logical file length
// File will be extended automatically by write operations // File will be extended automatically by write operations
} }
@@ -845,14 +846,15 @@ void BinaryTable::truncateFile(int64_t newSize) {
// Actually truncate the file (matching Dart behavior) // Actually truncate the file (matching Dart behavior)
DEBUG_PRINTLN("DEBUG: truncateFile - truncating to " << newSize); DEBUG_PRINTLN("DEBUG: truncateFile - truncating to " << newSize);
fclose(file_); fclose(file_);
try { try {
std::filesystem::resize_file(filePath_, newSize); std::filesystem::resize_file(filePath_, newSize);
DEBUG_PRINTLN("DEBUG: truncateFile - resize successful"); DEBUG_PRINTLN("DEBUG: truncateFile - resize successful");
} catch (const std::exception& e) { } catch (const std::exception& e) {
DEBUG_PRINTLN("DEBUG: truncateFile - resize failed: " << e.what()); DEBUG_PRINTLN("DEBUG: truncateFile - resize failed: " << e.what());
(void)e; // Suppress unused variable warning in release builds
} }
file_ = fopen(filePath_.c_str(), "r+b"); file_ = fopen(filePath_.c_str(), "r+b");
DEBUG_PRINTLN("DEBUG: truncateFile - reopen: success=" << (file_ != nullptr)); DEBUG_PRINTLN("DEBUG: truncateFile - reopen: success=" << (file_ != nullptr));
} }
@@ -862,30 +864,30 @@ void BinaryTable::liftFreeList() {
if (freeListLifted_) { if (freeListLifted_) {
throw std::runtime_error("Free list is already lifted"); throw std::runtime_error("Free list is already lifted");
} }
freeListCache_ = getFreeList(); freeListCache_ = getFreeList();
// Remove free list from end of file // Remove free list from end of file
int64_t fileLength = getFileLength(); int64_t fileLength = getFileLength();
int32_t oldEntryCount = (fileLength >= 4) ? readInt32(fileLength - 4) : 0; int32_t oldEntryCount = (fileLength >= 4) ? readInt32(fileLength - 4) : 0;
if (oldEntryCount > 0) { if (oldEntryCount > 0) {
int32_t oldEntrySize = 8 + 4; int32_t oldEntrySize = 8 + 4;
int32_t oldFreeListSize = oldEntryCount * oldEntrySize + 4; int32_t oldFreeListSize = oldEntryCount * oldEntrySize + 4;
int64_t newFileLength = fileLength - oldFreeListSize; int64_t newFileLength = fileLength - oldFreeListSize;
// Store current file position to restore later if needed // Store current file position to restore later if needed
long currentPos = ftell(file_); long currentPos = ftell(file_);
// Properly truncate the file // Properly truncate the file
truncateFile(newFileLength); truncateFile(newFileLength);
// Restore file position if it's still valid // Restore file position if it's still valid
if (currentPos >= 0 && currentPos < newFileLength) { if (currentPos >= 0 && currentPos < newFileLength) {
fseek(file_, currentPos, SEEK_SET); fseek(file_, currentPos, SEEK_SET);
} }
} }
freeListLifted_ = true; freeListLifted_ = true;
} }
@@ -894,7 +896,7 @@ void BinaryTable::dropFreeList() {
if (!freeListLifted_) { if (!freeListLifted_) {
throw std::runtime_error("Free list is not lifted"); throw std::runtime_error("Free list is not lifted");
} }
freeListLifted_ = false; freeListLifted_ = false;
DEBUG_PRINTLN("DEBUG: About to call setFreeList - this might corrupt the address table!"); DEBUG_PRINTLN("DEBUG: About to call setFreeList - this might corrupt the address table!");
setFreeList(freeListCache_); setFreeList(freeListCache_);
@@ -920,37 +922,37 @@ void BinaryTable::free(BT_Pointer pointer, int32_t size) {
DEBUG_PRINTLN("DEBUG: free() THROWING EXCEPTION - free list not lifted!"); DEBUG_PRINTLN("DEBUG: free() THROWING EXCEPTION - free list not lifted!");
throw std::runtime_error("Free list must be lifted before freeing memory"); throw std::runtime_error("Free list must be lifted before freeing memory");
} }
if (pointer.isNull() || size <= 0) { if (pointer.isNull() || size <= 0) {
throw std::invalid_argument("Cannot free null pointer or zero size"); throw std::invalid_argument("Cannot free null pointer or zero size");
} }
// Fetch current free list (matching Dart exactly) // Fetch current free list (matching Dart exactly)
std::vector<BT_FreeListEntry> freeList = freeListCache_; std::vector<BT_FreeListEntry> freeList = freeListCache_;
// Add new free entry // Add new free entry
freeList.emplace_back(pointer, size); freeList.emplace_back(pointer, size);
// Merge contiguous free entries (matching Dart logic exactly) // Merge contiguous free entries (matching Dart logic exactly)
auto mergeContiguousFreeBlocks = [](std::vector<BT_FreeListEntry> freeList) -> std::vector<BT_FreeListEntry> { auto mergeContiguousFreeBlocks = [](std::vector<BT_FreeListEntry> freeList) -> std::vector<BT_FreeListEntry> {
if (freeList.empty()) return {}; if (freeList.empty()) return {};
// Create a copy and sort by address to check for contiguous blocks // Create a copy and sort by address to check for contiguous blocks
std::vector<BT_FreeListEntry> sorted = freeList; std::vector<BT_FreeListEntry> sorted = freeList;
std::sort(sorted.begin(), sorted.end(), std::sort(sorted.begin(), sorted.end(),
[](const BT_FreeListEntry& a, const BT_FreeListEntry& b) { [](const BT_FreeListEntry& a, const BT_FreeListEntry& b) {
return a.pointer.address() < b.pointer.address(); return a.pointer.address() < b.pointer.address();
}); });
std::vector<BT_FreeListEntry> merged; std::vector<BT_FreeListEntry> merged;
for (const auto& entry : sorted) { for (const auto& entry : sorted) {
if (merged.empty()) { if (merged.empty()) {
// First entry, just add it // First entry, just add it
merged.emplace_back(entry.pointer, entry.size); merged.emplace_back(entry.pointer, entry.size);
} else { } else {
auto& last = merged.back(); auto& last = merged.back();
// Check if current entry is contiguous with the last merged entry // Check if current entry is contiguous with the last merged entry
if (last.pointer.address() + last.size == entry.pointer.address()) { if (last.pointer.address() + last.size == entry.pointer.address()) {
// Merge: extend the size of the last entry // Merge: extend the size of the last entry
@@ -961,12 +963,12 @@ void BinaryTable::free(BT_Pointer pointer, int32_t size) {
} }
} }
} }
return merged; return merged;
}; };
freeList = mergeContiguousFreeBlocks(freeList); freeList = mergeContiguousFreeBlocks(freeList);
// Update free list // Update free list
freeListCache_ = freeList; freeListCache_ = freeList;
} }
@@ -975,21 +977,21 @@ BT_Pointer BinaryTable::alloc(int32_t size) {
if (!freeListLifted_) { if (!freeListLifted_) {
throw std::runtime_error("Free list must be lifted before allocation"); throw std::runtime_error("Free list must be lifted before allocation");
} }
// Find suitable free block // Find suitable free block
auto it = std::find_if(freeListCache_.begin(), freeListCache_.end(), auto it = std::find_if(freeListCache_.begin(), freeListCache_.end(),
[size](const BT_FreeListEntry& entry) { [size](const BT_FreeListEntry& entry) {
return entry.size >= size; return entry.size >= size;
}); });
if (it == freeListCache_.end()) { if (it == freeListCache_.end()) {
// No suitable block, allocate at end of file // No suitable block, allocate at end of file
int64_t allocPos = getFileLength(); int64_t allocPos = getFileLength();
return BT_Pointer(allocPos); return BT_Pointer(allocPos);
} }
BT_Pointer result = it->pointer; BT_Pointer result = it->pointer;
if (it->size == size) { if (it->size == size) {
// Exact fit, remove block // Exact fit, remove block
freeListCache_.erase(it); freeListCache_.erase(it);
@@ -998,7 +1000,7 @@ BT_Pointer BinaryTable::alloc(int32_t size) {
it->pointer = BT_Pointer(it->pointer.address() + size); it->pointer = BT_Pointer(it->pointer.address() + size);
it->size -= size; it->size -= size;
} }
return result; return result;
} }
@@ -1006,12 +1008,12 @@ BT_Pointer BinaryTable::alloc(int32_t size) {
BT_Reference BinaryTable::getReference(const std::string& key) { BT_Reference BinaryTable::getReference(const std::string& key) {
auto addressTable = getAddressTable(); auto addressTable = getAddressTable();
int64_t keyHash = hashString(key); int64_t keyHash = hashString(key);
auto it = addressTable.find(keyHash); auto it = addressTable.find(keyHash);
if (it == addressTable.end()) { if (it == addressTable.end()) {
throw std::runtime_error("Key does not exist"); throw std::runtime_error("Key does not exist");
} }
return BT_Reference(this, it->second); return BT_Reference(this, it->second);
} }
@@ -1019,15 +1021,15 @@ void BinaryTable::remove(const std::string& key) {
antiFreeListScope([&]() { antiFreeListScope([&]() {
auto addressTable = getAddressTable(); auto addressTable = getAddressTable();
int64_t keyHash = hashString(key); int64_t keyHash = hashString(key);
auto it = addressTable.find(keyHash); auto it = addressTable.find(keyHash);
if (it == addressTable.end()) { if (it == addressTable.end()) {
throw std::runtime_error("Key does not exist"); throw std::runtime_error("Key does not exist");
} }
BT_Reference valueRef(this, it->second); BT_Reference valueRef(this, it->second);
free(it->second, valueRef.size()); free(it->second, valueRef.size());
addressTable.erase(it); addressTable.erase(it);
setAddressTable(addressTable); setAddressTable(addressTable);
}); });
@@ -1037,26 +1039,26 @@ void BinaryTable::truncate() {
antiFreeListScope([&]() { antiFreeListScope([&]() {
// Relocate address table // Relocate address table
setAddressTable(getAddressTable()); setAddressTable(getAddressTable());
// Check if last free block is at end of file // Check if last free block is at end of file
auto freeList = getFreeList(); auto freeList = getFreeList();
if (freeList.empty()) { if (freeList.empty()) {
return; return;
} }
std::sort(freeList.begin(), freeList.end(), std::sort(freeList.begin(), freeList.end(),
[](const BT_FreeListEntry& a, const BT_FreeListEntry& b) { [](const BT_FreeListEntry& a, const BT_FreeListEntry& b) {
return a.pointer.address() < b.pointer.address(); return a.pointer.address() < b.pointer.address();
}); });
const auto& lastEntry = freeList.back(); const auto& lastEntry = freeList.back();
int64_t fileEnd = getFileLength(); int64_t fileEnd = getFileLength();
int64_t expectedEnd = lastEntry.pointer.address() + lastEntry.size; int64_t expectedEnd = lastEntry.pointer.address() + lastEntry.size;
if (expectedEnd == fileEnd) { if (expectedEnd == fileEnd) {
freeList.pop_back(); freeList.pop_back();
setFreeList(freeList); setFreeList(freeList);
// Actually truncate file (matching Dart behavior) // Actually truncate file (matching Dart behavior)
truncateFile(lastEntry.pointer.address()); truncateFile(lastEntry.pointer.address());
} }
@@ -1070,31 +1072,34 @@ void BinaryTable::debugAddressTable(const std::string& context) {
DEBUG_PRINT(" (" << context << ")"); DEBUG_PRINT(" (" << context << ")");
} }
DEBUG_PRINTLN(" ==="); DEBUG_PRINTLN(" ===");
auto addressTable = getAddressTable(); auto addressTable = getAddressTable();
DEBUG_PRINTLN("Address table has " << addressTable.size() << " entries"); DEBUG_PRINTLN("Address table has " << addressTable.size() << " entries");
for (const auto& [hash, pointer] : addressTable) { for (const auto& [hash, pointer] : addressTable) {
DEBUG_PRINTLN(" Hash " << hash << " -> Address " << pointer.address()); DEBUG_PRINTLN(" Hash " << hash << " -> Address " << pointer.address());
if (!pointer.isNull()) { if (!pointer.isNull()) {
try { try {
uint8_t typeByte = readByte(pointer.address()); uint8_t typeByte = readByte(pointer.address());
DEBUG_PRINTLN(" Type byte: " << (int)typeByte); DEBUG_PRINTLN(" Type byte: " << (int)typeByte);
if (typeByte == 2) { // INTEGER if (typeByte == 2) { // INTEGER
int32_t value = readInt32(pointer.address() + 1); int32_t value = readInt32(pointer.address() + 1);
DEBUG_PRINTLN(" Value: " << value); DEBUG_PRINTLN(" Value: " << value);
(void)value; // Suppress unused variable warning in release builds
} else { } else {
DEBUG_PRINT(" Raw bytes: "); DEBUG_PRINT(" Raw bytes: ");
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
uint8_t byte = readByte(pointer.address() + i); uint8_t byte = readByte(pointer.address() + i);
DEBUG_PRINT(std::hex << (int)byte << " "); DEBUG_PRINT(std::hex << (int)byte << " ");
(void)byte; // Suppress unused variable warning in release builds
} }
DEBUG_PRINTLN(std::dec); DEBUG_PRINTLN(std::dec);
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
DEBUG_PRINTLN(" Error reading data: " << e.what()); DEBUG_PRINTLN(" Error reading data: " << e.what());
(void)e; // Suppress unused variable warning in release builds
} }
} }
} }