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