Add RandomAccessMemory class for in-memory binary operations
This commit is contained in:
@@ -4,43 +4,21 @@ project(BinaryTable)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Core Binary Table Library
|
||||
add_library(binary_table
|
||||
# Main executable with integrated binary table implementation
|
||||
add_executable(main
|
||||
binary_table.h
|
||||
binary_table.cpp
|
||||
)
|
||||
|
||||
# Main Application
|
||||
add_executable(main main.cpp)
|
||||
target_link_libraries(main binary_table)
|
||||
|
||||
# Debug Test Executables
|
||||
add_executable(debug_multi_key debug/debug_multi_key.cpp)
|
||||
target_link_libraries(debug_multi_key binary_table)
|
||||
|
||||
add_executable(debug_alloc debug/debug_alloc.cpp)
|
||||
target_link_libraries(debug_alloc binary_table)
|
||||
|
||||
add_executable(debug_address_table debug/debug_address_table.cpp)
|
||||
target_link_libraries(debug_address_table binary_table)
|
||||
|
||||
add_executable(debug_step_by_step debug/debug_step_by_step.cpp)
|
||||
target_link_libraries(debug_step_by_step binary_table)
|
||||
|
||||
add_executable(debug_simple debug/debug_simple.cpp)
|
||||
target_link_libraries(debug_simple binary_table)
|
||||
|
||||
# Compiler Settings
|
||||
if(MSVC)
|
||||
target_compile_options(binary_table PRIVATE /W4)
|
||||
target_compile_options(main PRIVATE /W4)
|
||||
else()
|
||||
target_compile_options(binary_table PRIVATE -Wall -Wextra -pedantic)
|
||||
target_compile_options(main PRIVATE -Wall -Wextra -pedantic)
|
||||
# Apply warnings to debug executables too
|
||||
target_compile_options(debug_multi_key PRIVATE -Wall -Wextra -pedantic)
|
||||
target_compile_options(debug_alloc PRIVATE -Wall -Wextra -pedantic)
|
||||
target_compile_options(debug_address_table PRIVATE -Wall -Wextra -pedantic)
|
||||
target_compile_options(debug_step_by_step PRIVATE -Wall -Wextra -pedantic)
|
||||
target_compile_options(debug_simple PRIVATE -Wall -Wextra -pedantic)
|
||||
endif()
|
||||
|
||||
# Link required libraries
|
||||
if(UNIX AND NOT APPLE)
|
||||
# Only link stdc++fs on Linux, not macOS
|
||||
target_link_libraries(main PRIVATE stdc++fs)
|
||||
endif()
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace bt {
|
||||
|
||||
@@ -483,8 +486,25 @@ BinaryTable::~BinaryTable() {
|
||||
|
||||
void BinaryTable::initialize() {
|
||||
fseek(file_, 0, SEEK_SET);
|
||||
writeInt64(0, BT_Null.address()); // Address table pointer (8 bytes)
|
||||
writeInt32(8, 0); // Free list entry count (4 bytes)
|
||||
|
||||
// Magic number "SWPS" (0/4 bytes)
|
||||
const char magic[] = "SWPS";
|
||||
fwrite(magic, 1, 4, file_);
|
||||
|
||||
// Version (1.0 float16) (4/2 bytes)
|
||||
uint16_t version = 0x3C00; // 1.0 in float16 format
|
||||
uint8_t versionBytes[2] = {
|
||||
static_cast<uint8_t>(version & 0xFF),
|
||||
static_cast<uint8_t>((version >> 8) & 0xFF)
|
||||
};
|
||||
fwrite(versionBytes, 1, 2, file_);
|
||||
|
||||
// Address table pointer (null) (6/8 bytes)
|
||||
writeInt64(6, BT_Null.address());
|
||||
|
||||
// Free list count (0) (14/4 bytes)
|
||||
writeInt32(14, 0);
|
||||
|
||||
fflush(file_);
|
||||
}
|
||||
|
||||
@@ -600,7 +620,7 @@ void BinaryTable::setFilePosition(int64_t position) {
|
||||
|
||||
// Address table management
|
||||
std::unordered_map<int64_t, BT_Pointer> BinaryTable::getAddressTable() {
|
||||
int64_t tableAddress = readInt64(0);
|
||||
int64_t tableAddress = readInt64(6);
|
||||
DEBUG_PRINTLN("DEBUG: getAddressTable reading from address " << tableAddress);
|
||||
|
||||
if (tableAddress == -1) { // Null pointer
|
||||
@@ -674,7 +694,7 @@ void BinaryTable::setAddressTable(const std::unordered_map<int64_t, BT_Pointer>&
|
||||
}
|
||||
|
||||
// Read old table pointer FIRST to ensure we can clean it up later
|
||||
int64_t oldTablePointerAddress = readInt64(0);
|
||||
int64_t oldTablePointerAddress = readInt64(6);
|
||||
BT_Pointer oldTablePtr(oldTablePointerAddress);
|
||||
int32_t oldTableSize = 0;
|
||||
|
||||
@@ -729,7 +749,7 @@ void BinaryTable::setAddressTable(const std::unordered_map<int64_t, BT_Pointer>&
|
||||
fflush(file_);
|
||||
|
||||
// Atomically update header to point to new table
|
||||
writeInt64(0, newTableAddress.address());
|
||||
writeInt64(6, newTableAddress.address());
|
||||
fflush(file_);
|
||||
|
||||
// Only free old table after new one is successfully committed
|
||||
@@ -748,19 +768,15 @@ std::vector<BT_FreeListEntry> BinaryTable::getFreeList() {
|
||||
return freeListCache_;
|
||||
}
|
||||
|
||||
int64_t fileLength = getFileLength();
|
||||
if (fileLength < 4) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t entryCount = readInt32(fileLength - 4);
|
||||
int32_t entryCount = readInt32(14);
|
||||
if (entryCount == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int32_t entrySize = 8 + 4; // Pointer + Size
|
||||
int32_t freeListSize = entryCount * entrySize;
|
||||
int64_t freeListStart = fileLength - 4 - freeListSize;
|
||||
int64_t fileLength = getFileLength();
|
||||
int64_t freeListStart = fileLength - freeListSize;
|
||||
|
||||
std::vector<BT_FreeListEntry> freeList;
|
||||
for (int32_t i = 0; i < entryCount; i++) {
|
||||
@@ -781,65 +797,44 @@ void BinaryTable::setFreeList(const std::vector<BT_FreeListEntry>& list) {
|
||||
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)
|
||||
// Read OLD count from header (position 14)
|
||||
int32_t oldEntryCount = readInt32(14);
|
||||
|
||||
// Calculate old free list size (entries only, not count)
|
||||
int32_t oldListSize = oldEntryCount * (8 + 4);
|
||||
|
||||
// Remove old free list entries from EOF
|
||||
if (oldEntryCount > 0) {
|
||||
int32_t oldListSize = (oldEntryCount * (8 + 4)) + 4; // Entries + Count
|
||||
int64_t newFileLength = fileLength - oldListSize;
|
||||
DEBUG_PRINTLN("DEBUG: setFreeList - removing old free list, oldListSize=" << oldListSize << ", truncating to: " << newFileLength);
|
||||
truncateFile(newFileLength);
|
||||
fileLength = newFileLength; // Update file length
|
||||
int64_t currentLength = getFileLength();
|
||||
truncateFile(currentLength - oldListSize);
|
||||
}
|
||||
|
||||
// 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<uint8_t> buffer;
|
||||
|
||||
// Entries
|
||||
for (const auto& entry : list) {
|
||||
// Pointer (8 bytes, little endian)
|
||||
int64_t addr = entry.pointer.address();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
buffer.push_back(static_cast<uint8_t>((addr >> (i * 8)) & 0xFF));
|
||||
}
|
||||
// Size (4 bytes, little endian)
|
||||
int32_t size = entry.size;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buffer.push_back(static_cast<uint8_t>((size >> (i * 8)) & 0xFF));
|
||||
|
||||
// Write NEW count to header (position 14)
|
||||
writeInt32(14, static_cast<int32_t>(list.size()));
|
||||
|
||||
// Write NEW entries to EOF (if any)
|
||||
if (!list.empty()) {
|
||||
// Encode new free list entries
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
for (const auto& entry : list) {
|
||||
// Pointer (8 bytes, little endian)
|
||||
int64_t addr = entry.pointer.address();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
buffer.push_back(static_cast<uint8_t>((addr >> (i * 8)) & 0xFF));
|
||||
}
|
||||
// Size (4 bytes, little endian)
|
||||
int32_t size = entry.size;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buffer.push_back(static_cast<uint8_t>((size >> (i * 8)) & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
fseek(file_, 0, SEEK_END);
|
||||
fwrite(buffer.data(), 1, buffer.size(), file_);
|
||||
}
|
||||
|
||||
// Entry count (4 bytes, little endian)
|
||||
int32_t count = static_cast<int32_t>(list.size());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buffer.push_back(static_cast<uint8_t>((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
|
||||
}
|
||||
|
||||
void BinaryTable::truncateFile(int64_t newSize) {
|
||||
@@ -865,29 +860,24 @@ void BinaryTable::liftFreeList() {
|
||||
throw std::runtime_error("Free list is already lifted");
|
||||
}
|
||||
|
||||
// Cache the free list
|
||||
freeListCache_ = getFreeList();
|
||||
|
||||
// Remove free list from end of file
|
||||
int64_t fileLength = getFileLength();
|
||||
int32_t oldEntryCount = (fileLength >= 4) ? readInt32(fileLength - 4) : 0;
|
||||
// Read count from header (position 14)
|
||||
int32_t oldEntryCount = readInt32(14);
|
||||
|
||||
if (oldEntryCount > 0) {
|
||||
int32_t oldEntrySize = 8 + 4;
|
||||
int32_t oldFreeListSize = oldEntryCount * oldEntrySize + 4;
|
||||
int64_t newFileLength = fileLength - oldFreeListSize;
|
||||
int32_t oldFreeListSize = oldEntryCount * oldEntrySize; // Just entries, no count
|
||||
|
||||
// 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);
|
||||
}
|
||||
// Remove free list entries from EOF
|
||||
int64_t fileLength = getFileLength();
|
||||
truncateFile(fileLength - oldFreeListSize);
|
||||
}
|
||||
|
||||
// Clear count in header (position 14)
|
||||
writeInt32(14, 0);
|
||||
|
||||
freeListLifted_ = true;
|
||||
}
|
||||
|
||||
@@ -898,9 +888,7 @@ void BinaryTable::dropFreeList() {
|
||||
}
|
||||
|
||||
freeListLifted_ = false;
|
||||
DEBUG_PRINTLN("DEBUG: About to call setFreeList - this might corrupt the address table!");
|
||||
setFreeList(freeListCache_);
|
||||
DEBUG_PRINTLN("DEBUG: setFreeList completed");
|
||||
setFreeList(freeListCache_); // This now writes count to header and entries to EOF
|
||||
freeListCache_.clear();
|
||||
}
|
||||
|
||||
@@ -1106,4 +1094,150 @@ void BinaryTable::debugAddressTable(const std::string& context) {
|
||||
DEBUG_PRINTLN("=========================");
|
||||
}
|
||||
|
||||
} // namespace bt
|
||||
std::string binaryDump(const std::vector<uint8_t>& data) {
|
||||
std::ostringstream buffer;
|
||||
|
||||
for (size_t i = 0; i < data.size(); i += 16) {
|
||||
// Address
|
||||
buffer << "0x" << std::hex << std::setfill('0') << std::setw(4) << std::uppercase << i
|
||||
<< " (" << std::dec << std::setfill(' ') << std::setw(4) << i << ") | ";
|
||||
|
||||
// Hex bytes
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
buffer << std::hex << std::setfill('0') << std::setw(2) << std::uppercase
|
||||
<< static_cast<int>(data[i + j]) << " ";
|
||||
} else {
|
||||
buffer << " ";
|
||||
}
|
||||
}
|
||||
|
||||
buffer << " | ";
|
||||
|
||||
// Integer representation
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
buffer << std::dec << std::setfill(' ') << std::setw(3)
|
||||
<< static_cast<int>(data[i + j]) << " ";
|
||||
} else {
|
||||
buffer << " ";
|
||||
}
|
||||
}
|
||||
|
||||
buffer << " | ";
|
||||
|
||||
// ASCII representation
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
uint8_t byte = data[i + j];
|
||||
if (byte >= 32 && byte <= 126) {
|
||||
buffer << static_cast<char>(byte);
|
||||
} else {
|
||||
buffer << '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer << " | ";
|
||||
if (i + 16 < data.size()) {
|
||||
buffer << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
} // namespace bt
|
||||
|
||||
int main() {
|
||||
// Delete existing file if it exists
|
||||
if (std::filesystem::exists("example.bin")) {
|
||||
std::filesystem::remove("example.bin");
|
||||
}
|
||||
|
||||
bt::BinaryTable table("example.bin");
|
||||
table.initialize();
|
||||
|
||||
// Read file for dump
|
||||
std::ifstream file("example.bin", std::ios::binary);
|
||||
std::vector<uint8_t> fileData((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
std::cout << "File dump:\n";
|
||||
std::cout << bt::binaryDump(fileData) << "\n";
|
||||
std::cout << "File size: " << fileData.size() << " bytes\n";
|
||||
std::cout << " \n";
|
||||
|
||||
// Set values
|
||||
table.set("int_array", std::vector<int32_t>{6, 3, 9, 2, 5});
|
||||
table.set("float_array", std::vector<float>{1.5f, 2.5f, 3.5f});
|
||||
table.set("empty", std::vector<int32_t>{});
|
||||
|
||||
// Get arrays and modify elements
|
||||
auto intArray = table.getArray<int32_t>("int_array");
|
||||
auto floatArray = table.getArray<float>("float_array");
|
||||
|
||||
intArray.set(0, 1);
|
||||
floatArray.set(1, 4.5f);
|
||||
|
||||
std::cout << "int_array pointer: " << intArray.getPointer().address() << "\n";
|
||||
std::cout << "float_array pointer: " << floatArray.getPointer().address() << "\n";
|
||||
|
||||
intArray.add(10);
|
||||
floatArray.add(5.5f);
|
||||
|
||||
intArray.addAll({420, 69, 1337, 1738});
|
||||
floatArray.addAll({6.5f, 7.5f, 8.5f});
|
||||
|
||||
// Test the fetchSublist method
|
||||
auto intArraySublist = intArray.fetchSublist(0, 3);
|
||||
auto floatArraySublist = floatArray.fetchSublist(0, 2);
|
||||
std::cout << "Sublist int_array (0-3): ";
|
||||
for (auto val : intArraySublist) {
|
||||
std::cout << val << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
std::cout << "Sublist float_array (0-2): ";
|
||||
for (auto val : floatArraySublist) {
|
||||
std::cout << val << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
// Read back values
|
||||
auto readback1 = table.get<std::vector<int32_t>>("int_array");
|
||||
auto readback2 = table.get<std::vector<float>>("float_array");
|
||||
auto readback3 = table.get<std::vector<int32_t>>("empty");
|
||||
|
||||
std::cout << "Readback1: ";
|
||||
for (auto val : readback1) {
|
||||
std::cout << val << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
std::cout << "Readback2: ";
|
||||
for (auto val : readback2) {
|
||||
std::cout << val << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
std::cout << "Readback3: ";
|
||||
for (auto val : readback3) {
|
||||
std::cout << val << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
std::cout << " \n";
|
||||
|
||||
// Final file dump
|
||||
std::ifstream finalFile("example.bin", std::ios::binary);
|
||||
std::vector<uint8_t> finalFileData((std::istreambuf_iterator<char>(finalFile)),
|
||||
std::istreambuf_iterator<char>());
|
||||
finalFile.close();
|
||||
|
||||
std::cout << "File dump:\n";
|
||||
std::cout << bt::binaryDump(finalFileData) << "\n";
|
||||
std::cout << "File size: " << finalFileData.size() << " bytes\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<EFBFBD> 2025-26 by Benjamin Watt of IMBENJI.NET LIMITED - All rights reserved.
|
||||
|
||||
Use of this source code is governed by a MIT license that can be found in the LICENSE file.
|
||||
Use of this source code is governed by the Business Source License 1.1 that can be found in the LICENSE file.
|
||||
|
||||
This file is part of the SweepStore (formerly Binary Table) package for C++.
|
||||
|
||||
@@ -192,6 +192,9 @@ private:
|
||||
int64_t hashString(const std::string& str) const;
|
||||
|
||||
void truncateFile(int64_t newSize);
|
||||
void antiFreeListScope(std::function<void()> fn);
|
||||
void free(BT_Pointer pointer, int32_t size);
|
||||
BT_Pointer alloc(int32_t size);
|
||||
|
||||
// File I/O helpers
|
||||
int32_t readInt32(int64_t position);
|
||||
@@ -212,12 +215,9 @@ public:
|
||||
|
||||
void initialize();
|
||||
|
||||
// Memory management
|
||||
// Memory management
|
||||
void liftFreeList();
|
||||
void dropFreeList();
|
||||
void antiFreeListScope(std::function<void()> fn);
|
||||
void free(BT_Pointer pointer, int32_t size);
|
||||
BT_Pointer alloc(int32_t size);
|
||||
|
||||
// Data operations
|
||||
template<typename T>
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "../binary_table.h"
|
||||
|
||||
void printAddressTable(bt::BinaryTable& table) {
|
||||
// We can't access getAddressTable directly, so let's use a different approach
|
||||
// Try to retrieve all known keys and see what happens
|
||||
std::vector<std::string> keys = {"key1", "key2", "key3"};
|
||||
|
||||
for (const std::string& key : keys) {
|
||||
try {
|
||||
auto ref = table.getReference(key);
|
||||
std::cout << " " << key << " -> address " << ref.getPointer().address()
|
||||
<< " (type " << static_cast<int>(ref.getType()) << ")" << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " " << key << " -> ERROR: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace bt;
|
||||
|
||||
const std::string filename = "debug_addr_table.bin";
|
||||
if (std::filesystem::exists(filename)) {
|
||||
std::filesystem::remove(filename);
|
||||
}
|
||||
|
||||
BinaryTable table(filename);
|
||||
table.initialize();
|
||||
|
||||
std::cout << "=== Testing Address Table Corruption ===\n" << std::endl;
|
||||
|
||||
std::cout << "Initial state (empty):" << std::endl;
|
||||
printAddressTable(table);
|
||||
|
||||
std::cout << "\n1. After storing key1:" << std::endl;
|
||||
table.set<int32_t>("key1", 100);
|
||||
printAddressTable(table);
|
||||
|
||||
std::cout << "\n2. After storing key2:" << std::endl;
|
||||
table.set<int32_t>("key2", 200);
|
||||
printAddressTable(table);
|
||||
|
||||
std::cout << "\n3. After storing key3:" << std::endl;
|
||||
table.set<int32_t>("key3", 300);
|
||||
printAddressTable(table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "../binary_table.h"
|
||||
|
||||
int main() {
|
||||
using namespace bt;
|
||||
|
||||
const std::string filename = "debug_alloc.bin";
|
||||
if (std::filesystem::exists(filename)) {
|
||||
std::filesystem::remove(filename);
|
||||
}
|
||||
|
||||
BinaryTable table(filename);
|
||||
table.initialize();
|
||||
|
||||
std::cout << "=== Testing Memory Allocation Issues ===\n" << std::endl;
|
||||
|
||||
// Store first key and see what address it gets
|
||||
std::cout << "1. Storing first key..." << std::endl;
|
||||
table.set<int32_t>("key1", 100);
|
||||
|
||||
// Get the address where key1's value was stored
|
||||
auto addressTable1 = table.getReference("key1").getPointer();
|
||||
std::cout << " key1 value stored at: " << addressTable1.address() << std::endl;
|
||||
|
||||
// Store second key and see what addresses are used
|
||||
std::cout << "2. Storing second key..." << std::endl;
|
||||
table.set<int32_t>("key2", 200);
|
||||
|
||||
auto addressTable2 = table.getReference("key2").getPointer();
|
||||
std::cout << " key2 value stored at: " << addressTable2.address() << std::endl;
|
||||
|
||||
// Check if key1 is still accessible
|
||||
std::cout << "3. Checking if key1 is still accessible..." << std::endl;
|
||||
try {
|
||||
int32_t val1 = table.get<int32_t>("key1");
|
||||
std::cout << " ✅ key1 still works: " << val1 << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ key1 broken: " << e.what() << std::endl;
|
||||
|
||||
// Let's see what's actually stored at key1's address
|
||||
try {
|
||||
auto ref = table.getReference("key1");
|
||||
std::cout << " key1 type is: " << static_cast<int>(ref.getType()) << std::endl;
|
||||
} catch (const std::exception& e2) {
|
||||
std::cout << " Can't even get type: " << e2.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n=== Address Comparison ===\n" << std::endl;
|
||||
std::cout << "key1 address: " << addressTable1.address() << std::endl;
|
||||
std::cout << "key2 address: " << addressTable2.address() << std::endl;
|
||||
|
||||
if (addressTable1.address() == addressTable2.address()) {
|
||||
std::cout << "💥 SAME ADDRESS! This proves the bug!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Addresses are different, issue is elsewhere" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "../binary_table.h"
|
||||
|
||||
int main() {
|
||||
using namespace bt;
|
||||
|
||||
const std::string filename = "debug_multi.bin";
|
||||
if (std::filesystem::exists(filename)) {
|
||||
std::filesystem::remove(filename);
|
||||
}
|
||||
|
||||
BinaryTable table(filename);
|
||||
table.initialize();
|
||||
|
||||
std::cout << "=== Testing Multi-Key Storage ===" << std::endl;
|
||||
|
||||
// Store first key
|
||||
std::cout << "1. Storing first key..." << std::endl;
|
||||
table.set<int32_t>("key1", 100);
|
||||
|
||||
// Try to read it back
|
||||
try {
|
||||
int32_t val1 = table.get<int32_t>("key1");
|
||||
std::cout << " ✅ First key retrieved: " << val1 << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ First key failed: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Store second key - this is where it likely breaks
|
||||
std::cout << "2. Storing second key..." << std::endl;
|
||||
table.set<int32_t>("key2", 200);
|
||||
|
||||
// Try to read second key
|
||||
try {
|
||||
int32_t val2 = table.get<int32_t>("key2");
|
||||
std::cout << " ✅ Second key retrieved: " << val2 << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ Second key failed: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
// Try to read first key again - this will likely fail
|
||||
std::cout << "3. Re-reading first key..." << std::endl;
|
||||
try {
|
||||
int32_t val1_again = table.get<int32_t>("key1");
|
||||
std::cout << " ✅ First key still accessible: " << val1_again << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ First key now broken: " << e.what() << std::endl;
|
||||
std::cout << " 💥 CONFIRMED: Table breaks after storing 2+ keys!" << std::endl;
|
||||
}
|
||||
|
||||
// Store third key to see if pattern continues
|
||||
std::cout << "4. Storing third key..." << std::endl;
|
||||
try {
|
||||
table.set<int32_t>("key3", 300);
|
||||
int32_t val3 = table.get<int32_t>("key3");
|
||||
std::cout << " ✅ Third key works: " << val3 << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ Third key failed: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\n=== Conclusion ===" << std::endl;
|
||||
std::cout << "The issue is definitely in the address table management" << std::endl;
|
||||
std::cout << "when storing multiple keys. Single key = perfect," << std::endl;
|
||||
std::cout << "multiple keys = corruption." << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "../binary_table.h"
|
||||
|
||||
int main() {
|
||||
using namespace bt;
|
||||
|
||||
const std::string filename = "debug_simple.bin";
|
||||
if (std::filesystem::exists(filename)) {
|
||||
std::filesystem::remove(filename);
|
||||
}
|
||||
|
||||
BinaryTable table(filename);
|
||||
table.initialize();
|
||||
|
||||
std::cout << "1. Storing key1..." << std::endl;
|
||||
table.set<int32_t>("key1", 100);
|
||||
table.debugAddressTable("after key1");
|
||||
|
||||
std::cout << "2. Reading key1..." << std::endl;
|
||||
try {
|
||||
int32_t val = table.get<int32_t>("key1");
|
||||
std::cout << " ✅ key1 = " << val << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ key1 failed: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "3. Storing key2..." << std::endl;
|
||||
table.set<int32_t>("key2", 200);
|
||||
table.debugAddressTable("after key2");
|
||||
|
||||
std::cout << "4. Reading key2..." << std::endl;
|
||||
try {
|
||||
int32_t val = table.get<int32_t>("key2");
|
||||
std::cout << " ✅ key2 = " << val << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ key2 failed: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "5. Re-reading key1..." << std::endl;
|
||||
try {
|
||||
int32_t val = table.get<int32_t>("key1");
|
||||
std::cout << " ✅ key1 = " << val << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << " ❌ key1 failed: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include "../binary_table.h"
|
||||
|
||||
void dumpFile(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<uint8_t> data(size);
|
||||
file.read(reinterpret_cast<char*>(data.data()), size);
|
||||
|
||||
std::cout << "File size: " << size << " bytes" << std::endl;
|
||||
for (size_t i = 0; i < std::min(size, size_t(80)); i++) {
|
||||
if (i % 16 == 0) std::cout << std::hex << i << ": ";
|
||||
std::cout << std::hex << std::setfill('0') << std::setw(2) << (int)data[i] << " ";
|
||||
if (i % 16 == 15) std::cout << std::endl;
|
||||
}
|
||||
if (size % 16 != 0) std::cout << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace bt;
|
||||
|
||||
const std::string filename = "debug_step.bin";
|
||||
if (std::filesystem::exists(filename)) {
|
||||
std::filesystem::remove(filename);
|
||||
}
|
||||
|
||||
BinaryTable table(filename);
|
||||
table.initialize();
|
||||
|
||||
std::cout << "=== Step-by-step Address Table Debug ===\n" << std::endl;
|
||||
|
||||
std::cout << "After initialize():" << std::endl;
|
||||
dumpFile(filename);
|
||||
|
||||
std::cout << "\n1. Before storing key1:" << std::endl;
|
||||
// Try reading the address table header
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
int64_t addr;
|
||||
file.read(reinterpret_cast<char*>(&addr), 8);
|
||||
std::cout << "Address table pointer: " << addr << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\n2. Storing key1..." << std::endl;
|
||||
table.set<int32_t>("key1", 100);
|
||||
|
||||
std::cout << "After storing key1:" << std::endl;
|
||||
dumpFile(filename);
|
||||
|
||||
// Try reading the address table
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
int64_t addr;
|
||||
file.read(reinterpret_cast<char*>(&addr), 8);
|
||||
std::cout << "Address table pointer: " << addr << std::endl;
|
||||
|
||||
if (addr != -1) {
|
||||
file.seekg(addr);
|
||||
uint8_t type;
|
||||
int32_t count;
|
||||
file.read(reinterpret_cast<char*>(&type), 1);
|
||||
file.read(reinterpret_cast<char*>(&count), 4);
|
||||
std::cout << "Address table type: " << (int)type << ", count: " << count << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n3. Storing key2..." << std::endl;
|
||||
table.set<int32_t>("key2", 200);
|
||||
|
||||
std::cout << "After storing key2:" << std::endl;
|
||||
dumpFile(filename);
|
||||
|
||||
// Try reading the address table again
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
int64_t addr;
|
||||
file.read(reinterpret_cast<char*>(&addr), 8);
|
||||
std::cout << "Address table pointer: " << addr << std::endl;
|
||||
|
||||
if (addr != -1) {
|
||||
file.seekg(addr);
|
||||
uint8_t type;
|
||||
int32_t count;
|
||||
file.read(reinterpret_cast<char*>(&type), 1);
|
||||
file.read(reinterpret_cast<char*>(&count), 4);
|
||||
std::cout << "Address table type: " << (int)type << ", count: " << count << std::endl;
|
||||
|
||||
// Read the entries
|
||||
for (int32_t i = 0; i < count && i < 5; i++) {
|
||||
int64_t keyHash, valueAddr;
|
||||
file.read(reinterpret_cast<char*>(&keyHash), 8);
|
||||
file.read(reinterpret_cast<char*>(&valueAddr), 8);
|
||||
std::cout << "Entry " << i << ": hash=" << keyHash << ", addr=" << valueAddr << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
213
cpp/main.cpp
213
cpp/main.cpp
@@ -1,213 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include "binary_table.h"
|
||||
|
||||
void printBinaryDump(const std::vector<uint8_t>& data) {
|
||||
for (size_t i = 0; i < data.size(); i += 16) {
|
||||
// Address
|
||||
printf("0x%04X (%4zu) | ", static_cast<unsigned int>(i), i);
|
||||
|
||||
// Hex bytes
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
printf("%02X ", data[i + j]);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" | ");
|
||||
|
||||
// Integer representation
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
printf("%3d ", data[i + j]);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" | ");
|
||||
|
||||
// ASCII representation
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
uint8_t byte = data[i + j];
|
||||
if (byte >= 32 && byte <= 126) {
|
||||
printf("%c", static_cast<char>(byte));
|
||||
} else {
|
||||
printf(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf(" |\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> readFile(const std::string& path) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<uint8_t> data(size);
|
||||
file.read(reinterpret_cast<char*>(data.data()), size);
|
||||
return data;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace bt;
|
||||
|
||||
std::cout << "C++ Binary Table - Reading Dart Reference File" << std::endl;
|
||||
std::cout << "===============================================" << std::endl;
|
||||
|
||||
// Read the file created by Dart
|
||||
const std::string filename = "dart_reference.bin";
|
||||
if (!std::filesystem::exists(filename)) {
|
||||
std::cout << "❌ Reference file not found: " << filename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "📁 Reading reference file created by Dart..." << std::endl;
|
||||
auto fileData = readFile(filename);
|
||||
printBinaryDump(fileData);
|
||||
std::cout << "File size: " << fileData.size() << " bytes\n" << std::endl;
|
||||
|
||||
// Try to read the file with C++ implementation
|
||||
try {
|
||||
BinaryTable table(filename);
|
||||
|
||||
std::cout << "🔍 Testing C++ reading of Dart-created file..." << std::endl;
|
||||
|
||||
// Try to read the arrays that Dart created
|
||||
std::cout << "Attempting to read 'int_array'..." << std::endl;
|
||||
try {
|
||||
auto intArray = table.getArray<int32_t>("int_array");
|
||||
std::cout << "✅ int_array found, length: " << intArray.length() << std::endl;
|
||||
|
||||
if (intArray.length() > 0) {
|
||||
std::cout << "First few elements: ";
|
||||
int count = std::min(5, static_cast<int>(intArray.length()));
|
||||
for (int i = 0; i < count; i++) {
|
||||
std::cout << intArray[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "❌ Failed to read int_array: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\nAttempting to read 'float_array'..." << std::endl;
|
||||
try {
|
||||
auto floatArray = table.getArray<float>("float_array");
|
||||
std::cout << "✅ float_array found, length: " << floatArray.length() << std::endl;
|
||||
|
||||
if (floatArray.length() > 0) {
|
||||
std::cout << "First few elements: ";
|
||||
int count = std::min(5, static_cast<int>(floatArray.length()));
|
||||
for (int i = 0; i < count; i++) {
|
||||
std::cout << floatArray[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "❌ Failed to read float_array: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\nAttempting to read 'empty' array..." << std::endl;
|
||||
try {
|
||||
auto emptyArray = table.getArray<int32_t>("empty");
|
||||
std::cout << "✅ empty array found, length: " << emptyArray.length() << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "❌ Failed to read empty array: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "❌ Failed to read file: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\n" << std::string(50, '=') << std::endl;
|
||||
std::cout << "Testing C++ Writing -> C++ Reading" << std::endl;
|
||||
std::cout << std::string(50, '=') << std::endl;
|
||||
|
||||
// Test C++ writing by creating a simple file
|
||||
const std::string testFilename = "cpp_test.bin";
|
||||
if (std::filesystem::exists(testFilename)) {
|
||||
std::filesystem::remove(testFilename);
|
||||
}
|
||||
|
||||
try {
|
||||
BinaryTable writeTable(testFilename);
|
||||
writeTable.initialize();
|
||||
|
||||
std::cout << "📝 Writing simple data with C++..." << std::endl;
|
||||
|
||||
// Write very simple data first
|
||||
writeTable.set<int32_t>("test_int", 42);
|
||||
std::cout << "✅ Wrote integer" << std::endl;
|
||||
|
||||
// Read it back immediately
|
||||
int32_t readInt = writeTable.get<int32_t>("test_int");
|
||||
std::cout << "✅ Read back integer: " << readInt << std::endl;
|
||||
|
||||
// Write a simple array
|
||||
writeTable.set<std::vector<int32_t>>("simple_array", {1, 2, 3});
|
||||
std::cout << "✅ Wrote simple array" << std::endl;
|
||||
|
||||
auto readArray = writeTable.getArray<int32_t>("simple_array");
|
||||
std::cout << "✅ Read back array, length: " << readArray.length() << std::endl;
|
||||
|
||||
if (readArray.length() > 0) {
|
||||
std::cout << "Array elements: ";
|
||||
for (int i = 0; i < readArray.length(); i++) {
|
||||
std::cout << readArray[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Test array operations
|
||||
std::cout << "\n📝 Testing array operations..." << std::endl;
|
||||
readArray.set(0, 99); // Modify first element
|
||||
readArray.add(4); // Add element
|
||||
readArray.addAll({5, 6}); // Add multiple
|
||||
|
||||
std::cout << "After modifications, length: " << readArray.length() << std::endl;
|
||||
std::cout << "Elements: ";
|
||||
for (int i = 0; i < readArray.length(); i++) {
|
||||
std::cout << readArray[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Test sublist
|
||||
auto sublist = readArray.fetchSublist(0, 3);
|
||||
std::cout << "Sublist (0-3): ";
|
||||
for (auto val : sublist) {
|
||||
std::cout << val << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "\n🎉 C++ Implementation Status:" << std::endl;
|
||||
std::cout << "✅ File reading (Dart compatibility)" << std::endl;
|
||||
std::cout << "✅ File writing" << std::endl;
|
||||
std::cout << "✅ Basic data types (int, float, string)" << std::endl;
|
||||
std::cout << "✅ Array storage and retrieval" << std::endl;
|
||||
std::cout << "✅ Array operations (set, add, addAll)" << std::endl;
|
||||
std::cout << "✅ Array sublist fetching" << std::endl;
|
||||
std::cout << "✅ Type-safe template system" << std::endl;
|
||||
std::cout << "✅ Memory-efficient file access" << std::endl;
|
||||
std::cout << "✅ Full interoperability with Dart" << std::endl;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "❌ C++ write/read test failed: " << e.what() << std::endl;
|
||||
|
||||
// Show the file that was created
|
||||
if (std::filesystem::exists(testFilename)) {
|
||||
std::cout << "\nFile that was created:" << std::endl;
|
||||
auto data = readFile(testFilename);
|
||||
printBinaryDump(data);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include "binary_table.h"
|
||||
|
||||
void printBinaryDump(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cout << "Cannot open file for dump" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<uint8_t> data(size);
|
||||
file.read(reinterpret_cast<char*>(data.data()), size);
|
||||
file.close();
|
||||
|
||||
std::cout << "\n=== Binary Dump of " << filename << " (" << size << " bytes) ===" << std::endl;
|
||||
|
||||
for (size_t i = 0; i < data.size(); i += 16) {
|
||||
printf("0x%04X | ", static_cast<unsigned int>(i));
|
||||
|
||||
// Hex bytes
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
printf("%02X ", data[i + j]);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" | ");
|
||||
|
||||
// ASCII representation
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i + j < data.size()) {
|
||||
uint8_t byte = data[i + j];
|
||||
printf("%c", (byte >= 32 && byte <= 126) ? byte : '.');
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
std::cout << "=========================" << std::endl;
|
||||
}
|
||||
|
||||
// Test equivalent to Dart's main() function
|
||||
int main() {
|
||||
std::cout << "🧪 C++ Binary Table Parity Test (matching Dart behavior)" << std::endl;
|
||||
std::cout << "=========================================================" << std::endl;
|
||||
|
||||
const std::string filename = "cpp_parity_test.bin";
|
||||
|
||||
// Clean up any existing file
|
||||
std::filesystem::remove(filename);
|
||||
|
||||
try {
|
||||
bt::BinaryTable table(filename);
|
||||
table.initialize();
|
||||
|
||||
std::cout << "\n1. Testing basic data types..." << std::endl;
|
||||
|
||||
// Set basic values
|
||||
table.set<int32_t>("myInt", 42);
|
||||
table.set<float>("myFloat", 3.14f);
|
||||
table.set<std::string>("myString", "Hello, World!");
|
||||
|
||||
// Verify basic values
|
||||
assert(table.get<int32_t>("myInt") == 42);
|
||||
assert(table.get<float>("myFloat") == 3.14f);
|
||||
assert(table.get<std::string>("myString") == "Hello, World!");
|
||||
|
||||
std::cout << "✅ Basic data types work correctly" << std::endl;
|
||||
|
||||
std::cout << "\n2. Testing array operations..." << std::endl;
|
||||
|
||||
// Test array creation and access
|
||||
std::vector<int32_t> testArray = {10, 20, 30, 40, 50};
|
||||
table.set<std::vector<int32_t>>("myArray", testArray);
|
||||
|
||||
auto retrievedArray = table.get<std::vector<int32_t>>("myArray");
|
||||
assert(retrievedArray.size() == 5);
|
||||
for (size_t i = 0; i < retrievedArray.size(); i++) {
|
||||
assert(retrievedArray[i] == testArray[i]);
|
||||
}
|
||||
|
||||
std::cout << "✅ Array storage and retrieval work correctly" << std::endl;
|
||||
|
||||
// Test uniform array operations
|
||||
auto uniformArray = table.getArray<int32_t>("myArray");
|
||||
assert(uniformArray.length() == 5);
|
||||
assert(uniformArray[0] == 10);
|
||||
assert(uniformArray[4] == 50);
|
||||
|
||||
// Test array modification
|
||||
uniformArray.set(2, 999);
|
||||
assert(uniformArray[2] == 999);
|
||||
|
||||
// Test array extension
|
||||
uniformArray.add(60);
|
||||
assert(uniformArray.length() == 6);
|
||||
assert(uniformArray[5] == 60);
|
||||
|
||||
std::cout << "✅ Uniform array operations work correctly" << std::endl;
|
||||
|
||||
std::cout << "\n3. Testing multi-key operations (previously causing corruption)..." << std::endl;
|
||||
|
||||
// Add multiple keys to test address table stability
|
||||
table.set<int32_t>("key1", 100);
|
||||
table.set<int32_t>("key2", 200);
|
||||
table.set<int32_t>("key3", 300);
|
||||
table.set<std::string>("str1", "First");
|
||||
table.set<std::string>("str2", "Second");
|
||||
|
||||
// Verify all keys are accessible
|
||||
assert(table.get<int32_t>("key1") == 100);
|
||||
assert(table.get<int32_t>("key2") == 200);
|
||||
assert(table.get<int32_t>("key3") == 300);
|
||||
assert(table.get<std::string>("str1") == "First");
|
||||
assert(table.get<std::string>("str2") == "Second");
|
||||
|
||||
std::cout << "✅ Multi-key operations work without corruption" << std::endl;
|
||||
|
||||
std::cout << "\n4. Testing remove operations..." << std::endl;
|
||||
|
||||
// Test removal
|
||||
table.remove("key2");
|
||||
|
||||
// Verify removed key is gone
|
||||
try {
|
||||
table.get<int32_t>("key2");
|
||||
assert(false && "Should have thrown exception");
|
||||
} catch (const std::runtime_error&) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Verify other keys still work
|
||||
assert(table.get<int32_t>("key1") == 100);
|
||||
assert(table.get<int32_t>("key3") == 300);
|
||||
|
||||
std::cout << "✅ Remove operations work correctly" << std::endl;
|
||||
|
||||
std::cout << "\n5. Testing fetchSublist functionality..." << std::endl;
|
||||
|
||||
auto sublist = uniformArray.fetchSublist(1, 4);
|
||||
assert(sublist.size() == 3);
|
||||
assert(sublist[0] == 20); // myArray[1]
|
||||
assert(sublist[1] == 999); // myArray[2] (modified)
|
||||
assert(sublist[2] == 40); // myArray[3]
|
||||
|
||||
std::cout << "✅ fetchSublist works correctly" << std::endl;
|
||||
|
||||
std::cout << "\n6. Testing free list and truncation operations..." << std::endl;
|
||||
|
||||
// Create some data, then remove it to test free list
|
||||
table.set<int32_t>("temp1", 1000);
|
||||
table.set<int32_t>("temp2", 2000);
|
||||
table.set<int32_t>("temp3", 3000);
|
||||
|
||||
table.remove("temp1");
|
||||
table.remove("temp2");
|
||||
table.remove("temp3");
|
||||
|
||||
// Test truncation
|
||||
table.truncate();
|
||||
|
||||
// Verify original data still accessible
|
||||
assert(table.get<int32_t>("myInt") == 42);
|
||||
assert(table.get<std::string>("myString") == "Hello, World!");
|
||||
assert(table.get<int32_t>("key1") == 100);
|
||||
|
||||
std::cout << "✅ Free list and truncation work correctly" << std::endl;
|
||||
|
||||
std::cout << "\n🎉 ALL TESTS PASSED! C++ implementation has Dart parity!" << std::endl;
|
||||
|
||||
// Print final file dump for verification
|
||||
printBinaryDump(filename);
|
||||
|
||||
// Clean up
|
||||
std::filesystem::remove(filename);
|
||||
|
||||
return 0;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "❌ Test failed: " << e.what() << std::endl;
|
||||
|
||||
// Print file dump for debugging
|
||||
printBinaryDump(filename);
|
||||
|
||||
std::filesystem::remove(filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user