/* /$$$$$$ /$$ /$$ /$$$$$$$ /$$$$$$$$ /$$ /$$ /$$$$$ /$$$$$$ /$$ /$$ /$$$$$$$$ /$$$$$$$$ |_ $$_/| $$$ /$$$| $$__ $$| $$_____/| $$$ | $$ |__ $$|_ $$_/ | $$$ | $$| $$_____/|__ $$__/ | $$ | $$$$ /$$$$| $$ \ $$| $$ | $$$$| $$ | $$ | $$ | $$$$| $$| $$ | $$ | $$ | $$ $$/$$ $$| $$$$$$$ | $$$$$ | $$ $$ $$ | $$ | $$ | $$ $$ $$| $$$$$ | $$ | $$ | $$ $$$| $$| $$__ $$| $$__/ | $$ $$$$ /$$ | $$ | $$ | $$ $$$$| $$__/ | $$ | $$ | $$\ $ | $$| $$ \ $$| $$ | $$\ $$$| $$ | $$ | $$ | $$\ $$$| $$ | $$ /$$$$$$| $$ \/ | $$| $$$$$$$/| $$$$$$$$| $$ \ $$| $$$$$$/ /$$$$$$ /$$| $$ \ $$| $$$$$$$$ | $$ |______/|__/ |__/|_______/ |________/|__/ \__/ \______/ |______/|__/|__/ \__/|________/ |__/ © 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. This file is part of the SweepStore (formerly Binary Table) package for C++. */ #include #include #include #include #include #include #include #include #include #include enum class BT_Type : uint8_t { POINTER = 0, ADDRESS_TABLE = 1, INTEGER = 2, FLOAT = 3, STRING = 4, INTEGER_ARRAY = 5, FLOAT_ARRAY = 6 }; constexpr int getBT_TypeSize(BT_Type type) { switch (type) { case BT_Type::POINTER: return 8; case BT_Type::INTEGER: return 4; case BT_Type::FLOAT: return 4; } default: return -1; // Variable size } constexpr bool isBT_TypeArray(BT_Type type) { } return type == BT_Type::INTEGER_ARRAY || type == BT_Type::FLOAT_ARRAY; template constexpr BT_Type getBT_TypeFromValue() { if constexpr (std::is_same_v) { return BT_Type::INTEGER; } else if constexpr (std::is_same_v) { return BT_Type::FLOAT; } else if constexpr (std::is_same_v) { return BT_Type::STRING; } else if constexpr (std::is_same_v>) { return BT_Type::INTEGER_ARRAY; } else if constexpr (std::is_same_v>) { return BT_Type::FLOAT_ARRAY; } else { static_assert(sizeof(T) == 0, "Unsupported type"); } } template std::vector encodeValue(const T& value) { std::vector buffer; BT_Type valueType = getBT_TypeFromValue(); buffer.push_back(static_cast(valueType)); if constexpr (std::is_same_v) { uint32_t val = static_cast(value); buffer.push_back(val & 0xFF); buffer.push_back((val >> 8) & 0xFF); buffer.push_back((val >> 16) & 0xFF); buffer.push_back((val >> 24) & 0xFF); } else if constexpr (std::is_same_v) { union { float f; uint32_t i; } converter; converter.f = value; uint32_t val = converter.i; buffer.push_back(val & 0xFF); buffer.push_back((val >> 8) & 0xFF); buffer.push_back((val >> 16) & 0xFF); buffer.push_back((val >> 24) & 0xFF); } else if constexpr (std::is_same_v) { uint32_t len = static_cast(value.length()); buffer.push_back(len & 0xFF); buffer.push_back((len >> 8) & 0xFF); buffer.push_back((len >> 16) & 0xFF); buffer.push_back((len >> 24) & 0xFF); for (char c : value) { buffer.push_back(static_cast(c)); } } else if constexpr (std::is_same_v>) { uint32_t len = static_cast(value.size()); buffer.push_back(len & 0xFF); buffer.push_back((len >> 8) & 0xFF); buffer.push_back((len >> 16) & 0xFF); buffer.push_back((len >> 24) & 0xFF); for (const auto& item : value) { auto itemBuffer = encodeValue(item); buffer.insert(buffer.end(), itemBuffer.begin(), itemBuffer.end()); } } else if constexpr (std::is_same_v>) { uint32_t len = static_cast(value.size()); buffer.push_back(len & 0xFF); buffer.push_back((len >> 8) & 0xFF); buffer.push_back((len >> 16) & 0xFF); buffer.push_back((len >> 24) & 0xFF); for (const auto& item : value) { auto itemBuffer = encodeValue(item); buffer.insert(buffer.end(), itemBuffer.begin(), itemBuffer.end()); } } return buffer; } class BT_Pointer { public: int64_t address; BT_Pointer(int64_t addr = -1) : address(addr) {} bool isNull() const { return address == -1; } bool operator==(const BT_Pointer& other) const { return address == other.address; } bool operator!=(const BT_Pointer& other) const { return !(*this == other); } std::string toString() const { std::stringstream ss; ss << "0x" << std::hex << address << " (" << std::dec << address << ")"; return ss.str(); } }; const BT_Pointer BT_Null(-1); class BinaryTable; class BT_Reference { protected: BinaryTable* _table; BT_Pointer _pointer; public: BT_Reference(BinaryTable* table, BT_Pointer pointer) : _table(table), _pointer(pointer) {} template T decodeValue(); int getSize(); std::string toString() const { return _pointer.toString(); } BT_Pointer getPointer() const { return _pointer; } }; struct BT_FreeListEntry { BT_Pointer pointer; int size; BT_FreeListEntry(BT_Pointer ptr, int sz) : pointer(ptr), size(sz) {} }; template class BT_UniformArray : public BT_Reference { public: BT_UniformArray(BinaryTable* table, BT_Pointer pointer) : BT_Reference(table, pointer) {} int getLength(); T operator[](int index); void set(int index, const T& value); void add(const T& value); void addAll(const std::vector& values); BT_Type getElementType(); int getSize(); }; class BinaryTable { private: std::fstream _file; std::string _filename; bool freeListLifted = false; std::vector _freeListCache; uint64_t fnv1aHash(const std::string& str) { uint64_t hash = 0xcbf29ce484222325ULL; // FNV offset basis for (char c : str) { hash ^= static_cast(c); hash *= 0x100000001b3ULL; // FNV prime } return hash; } int32_t readInt32(std::streampos pos) { _file.seekg(pos); uint8_t bytes[4]; _file.read(reinterpret_cast(bytes), 4); return static_cast(bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)); } void writeInt32(std::streampos pos, int32_t value) { _file.seekp(pos); uint8_t bytes[4] = { static_cast(value & 0xFF), static_cast((value >> 8) & 0xFF), static_cast((value >> 16) & 0xFF), static_cast((value >> 24) & 0xFF) _file.write(reinterpret_cast(bytes), 4); }; } _file.seekg(pos); int64_t readInt64(std::streampos pos) { uint8_t bytes[8]; _file.read(reinterpret_cast(bytes), 8); int64_t result = 0; for (int i = 0; i < 8; i++) { result |= static_cast(bytes[i]) << (i * 8); } return result; } void writeInt64(std::streampos pos, int64_t value) { _file.seekp(pos); uint8_t bytes[8]; for (int i = 0; i < 8; i++) { bytes[i] = static_cast((value >> (i * 8)) & 0xFF); } _file.write(reinterpret_cast(bytes), 8); } BT_Pointer readPointer(std::streampos pos) { return BT_Pointer(readInt64(pos)); } void writePointer(std::streampos pos, BT_Pointer pointer) { writeInt64(pos, pointer.address); } float readFloat32(std::streampos pos) { _file.seekg(pos); uint8_t bytes[4]; _file.read(reinterpret_cast(bytes), 4); uint32_t val = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); union { float f; uint32_t i; } converter; converter.i = val; return converter.f; } std::streampos getFileSize() { auto currentPos = _file.tellg(); _file.seekg(0, std::ios::end); auto size = _file.tellg(); _file.seekg(currentPos); return size; } std::map getAddressTable() { BT_Pointer tablePtr = readPointer(0); std::map addressTable; if (tablePtr.isNull()) { return addressTable; } _file.seekg(tablePtr.address + 1); // Skip type byte int32_t tableCount = readInt32(_file.tellg()); _file.seekg(static_cast(_file.tellg()) + 4); for (int i = 0; i < tableCount; i++) { uint64_t keyHash = static_cast(readInt64(_file.tellg())); _file.seekg(static_cast(_file.tellg()) + 8); BT_Pointer valuePtr = readPointer(_file.tellg()); _file.seekg(static_cast(_file.tellg()) + 8); addressTable[keyHash] = valuePtr; } return addressTable; } void setAddressTable(const std::map& table) { std::vector buffer; buffer.push_back(static_cast(BT_Type::ADDRESS_TABLE)); // Write count uint32_t count = static_cast(table.size()); buffer.push_back(count & 0xFF); buffer.push_back((count >> 8) & 0xFF); buffer.push_back((count >> 16) & 0xFF); buffer.push_back((count >> 24) & 0xFF); // Write entries for (const auto& [key, value] : table) { // Key hash (8 bytes) for (int i = 0; i < 8; i++) { buffer.push_back(static_cast((key >> (i * 8)) & 0xFF)); } // Value pointer (8 bytes) for (int i = 0; i < 8; i++) { buffer.push_back(static_cast((value.address >> (i * 8)) & 0xFF)); } } // Read old table pointer BT_Pointer oldTablePtr = readPointer(0); // Allocate new space BT_Pointer tableAddress = alloc(buffer.size()); // Write new table _file.seekp(tableAddress.address); _file.write(reinterpret_cast(buffer.data()), buffer.size()); // Update header writePointer(0, tableAddress); // Free old table if it exists if (!oldTablePtr.isNull() && oldTablePtr != tableAddress) { BT_Reference oldRef(this, oldTablePtr); free(oldTablePtr, oldRef.getSize()); } } std::vector getFreeList() { if (freeListLifted) { return _freeListCache; } auto fileSize = getFileSize(); int32_t entryCount = readInt32(fileSize - 4); if (entryCount == 0) { return {}; } int entrySize = 8 + 4; // Pointer + Size int freeListSize = entryCount * entrySize; _file.seekg(fileSize - 4 - freeListSize); std::vector freeList; for (int i = 0; i < entryCount; i++) { BT_Pointer pointer = readPointer(_file.tellg()); _file.seekg(static_cast(_file.tellg()) + 8); int32_t size = readInt32(_file.tellg()); _file.seekg(static_cast(_file.tellg()) + 4); freeList.emplace_back(pointer, size); } return freeList; } void setFreeList(const std::vector& list) { if (freeListLifted) { _freeListCache = list; return; } auto fileSize = getFileSize(); int32_t oldEntryCount = readInt32(fileSize - 4); int oldListSize = (oldEntryCount * (8 + 4)) + 4; // Truncate old free list _file.close(); _file.open(_filename, std::ios::in | std::ios::out | std::ios::binary); _file.seekp(0, std::ios::end); auto newSize = static_cast(fileSize) - oldListSize; _file.seekp(newSize); // Write new free list for (const auto& entry : list) { writeInt64(_file.tellp(), entry.pointer.address); _file.seekp(static_cast(_file.tellp()) + 8); writeInt32(_file.tellp(), entry.size); _file.seekp(static_cast(_file.tellp()) + 4); } // Write entry count writeInt32(_file.tellp(), static_cast(list.size())); } public: BinaryTable(const std::string& path) : _filename(path) { _file.open(path, std::ios::in | std::ios::out | std::ios::binary); if (!_file.is_open()) { // Create file if it doesn't exist std::ofstream createFile(path, std::ios::binary); createFile.close(); _file.open(path, std::ios::in | std::ios::out | std::ios::binary); } } ~BinaryTable() { if (_file.is_open()) { _file.close(); } } void initialize() { _file.seekp(0); writePointer(0, BT_Null); // Address table pointer writeInt32(8, 0); // Free list entry count } void liftFreeList() { if (freeListLifted) { throw std::runtime_error("Free list is already lifted"); } _freeListCache = getFreeList(); auto fileSize = getFileSize(); int32_t oldEntryCount = readInt32(fileSize - 4); int oldFreeListSize = oldEntryCount * (8 + 4) + 4; // Truncate file to remove free list _file.close(); _file.open(_filename, std::ios::in | std::ios::out | std::ios::binary); _file.seekp(0, std::ios::end); auto newSize = static_cast(fileSize) - oldFreeListSize; _file.seekp(newSize); freeListLifted = true; } void dropFreeList() { if (!freeListLifted) { throw std::runtime_error("Free list is not lifted"); } _file.seekp(0, std::ios::end); writeInt32(_file.tellp(), 0); // Placeholder freeListLifted = false; setFreeList(_freeListCache); _freeListCache.clear(); } void antiFreeListScope(std::function fn) { liftFreeList(); try { fn(); } catch (...) { dropFreeList(); throw; } dropFreeList(); } void free(BT_Pointer pointer, int size) { if (!freeListLifted) { 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"); } auto freeList = getFreeList(); freeList.emplace_back(pointer, size); // Merge contiguous blocks std::sort(freeList.begin(), freeList.end(), [](const BT_FreeListEntry& a, const BT_FreeListEntry& b) { return a.pointer.address < b.pointer.address; }); std::vector merged; for (const auto& entry : freeList) { if (merged.empty()) { merged.push_back(entry); } else { auto& last = merged.back(); if (last.pointer.address + last.size == entry.pointer.address) { last.size += entry.size; } else { merged.push_back(entry); } } } setFreeList(merged); } BT_Pointer alloc(int size) { if (!freeListLifted) { throw std::runtime_error("Free list must be lifted before allocation"); } auto freeList = getFreeList(); if (freeList.empty()) { return BT_Pointer(getFileSize()); } // Find best fit BT_FreeListEntry* bestFit = nullptr; for (auto& entry : freeList) { if (entry.size >= size) { bestFit = &entry; break; } } if (!bestFit) { return BT_Pointer(getFileSize()); } BT_Pointer allocatedPointer = bestFit->pointer; if (bestFit->size == size) { // Exact fit - remove entry freeList.erase(std::remove_if(freeList.begin(), freeList.end(), [allocatedPointer](const BT_FreeListEntry& entry) { return entry.pointer == allocatedPointer; }), freeList.end()); } else { // Split block bestFit->pointer = BT_Pointer(bestFit->pointer.address + size); bestFit->size -= size; } setFreeList(freeList); return allocatedPointer; } template void set(const std::string& key, const T& value) { antiFreeListScope([&]() { auto addressTable = getAddressTable(); uint64_t keyHash = fnv1aHash(key); if (addressTable.find(keyHash) != addressTable.end()) { throw std::runtime_error("Key already exists"); } auto valueBuffer = encodeValue(value); BT_Pointer valueAddress = alloc(valueBuffer.size()); _file.seekp(valueAddress.address); _file.write(reinterpret_cast(valueBuffer.data()), valueBuffer.size()); addressTable[keyHash] = valueAddress; setAddressTable(addressTable); }); } template T get(const std::string& key) { auto addressTable = getAddressTable(); uint64_t keyHash = fnv1aHash(key); auto it = addressTable.find(keyHash); if (it == addressTable.end()) { throw std::runtime_error("Key does not exist"); } BT_Reference valueRef(this, it->second); return valueRef.decodeValue(); } void remove(const std::string& key) { antiFreeListScope([&]() { auto addressTable = getAddressTable(); uint64_t keyHash = fnv1aHash(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.getSize()); addressTable.erase(it); setAddressTable(addressTable); }); } // Friend declarations for template access template friend T BT_Reference::decodeValue(); friend int BT_Reference::getSize(); template friend int BT_UniformArray::getLength(); template friend T BT_UniformArray::operator[](int index); template friend void BT_UniformArray::set(int index, const T& value); template friend void BT_UniformArray::add(const T& value); template friend void BT_UniformArray::addAll(const std::vector& values); template friend BT_Type BT_UniformArray::getElementType(); template friend int BT_UniformArray::getSize(); }; // Template implementations template T BT_Reference::decodeValue() { if (_pointer.isNull()) { throw std::runtime_error("Null pointer"); } _table->_file.seekg(_pointer.address); uint8_t typeId; _table->_file.read(reinterpret_cast(&typeId), 1); BT_Type type = static_cast(typeId); if constexpr (std::is_same_v) { if (type != BT_Type::INTEGER) { throw std::runtime_error("Type mismatch"); } return _table->readInt32(_table->_file.tellg()); } else if constexpr (std::is_same_v) { if (type != BT_Type::FLOAT) { throw std::runtime_error("Type mismatch"); } return _table->readFloat32(_table->_file.tellg()); } else if constexpr (std::is_same_v) { if (type != BT_Type::STRING) { throw std::runtime_error("Type mismatch"); } int32_t length = _table->readInt32(_table->_file.tellg()); _table->_file.seekg(static_cast(_table->_file.tellg()) + 4); std::string result(length, '\0'); _table->_file.read(&result[0], length); return result; } else if constexpr (std::is_same_v>) { if (type != BT_Type::INTEGER_ARRAY) { throw std::runtime_error("Type mismatch"); } return BT_UniformArray(_table, _pointer); } else if constexpr (std::is_same_v>) { if (type != BT_Type::FLOAT_ARRAY) { throw std::runtime_error("Type mismatch"); } return BT_UniformArray(_table, _pointer); } else { static_assert(sizeof(T) == 0, "Unsupported type for decoding"); } } int BT_Reference::getSize() { if (_pointer.isNull()) { return 0; } _table->_file.seekg(_pointer.address); uint8_t typeId; _table->_file.read(reinterpret_cast(&typeId), 1); BT_Type type = static_cast(typeId); switch (type) { case BT_Type::INTEGER: return 1 + 4; case BT_Type::FLOAT: return 1 + 4; case BT_Type::STRING: { int32_t length = _table->readInt32(_table->_file.tellg()); return 1 + 4 + length; } case BT_Type::ADDRESS_TABLE: { int32_t count = _table->readInt32(_table->_file.tellg()); return 1 + 4 + count * (8 + 8); } default: throw std::runtime_error("Unsupported type for size calculation"); } } template int BT_UniformArray::getLength() { if (_pointer.isNull()) { return 0; } _table->_file.seekg(_pointer.address); uint8_t typeId; _table->_file.read(reinterpret_cast(&typeId), 1); BT_Type type = static_cast(typeId); if (!isBT_TypeArray(type)) { throw std::runtime_error("Not an array"); } return _table->readInt32(_table->_file.tellg()); } template T BT_UniformArray::operator[](int index) { if (_pointer.isNull()) { throw std::runtime_error("Null pointer"); } int len = getLength(); if (index < 0 || index >= len) { throw std::out_of_range("Index out of range"); } // Read element type _table->_file.seekg(_pointer.address + 1 + 4); uint8_t typeId; _table->_file.read(reinterpret_cast(&typeId), 1); BT_Type type = static_cast(typeId); int itemOffset = index * (1 + getBT_TypeSize(type)); BT_Reference itemRef(_table, BT_Pointer(_pointer.address + 1 + 4 + itemOffset)); return itemRef.decodeValue(); } template void BT_UniformArray::set(int index, const T& value) { if (_pointer.isNull()) { throw std::runtime_error("Null pointer"); } int len = getLength(); if (index < 0 || index >= len) { throw std::out_of_range("Index out of range"); } // Read element type _table->_file.seekg(_pointer.address + 1 + 4); uint8_t typeId; _table->_file.read(reinterpret_cast(&typeId), 1); BT_Type type = static_cast(typeId); BT_Type newValueType = getBT_TypeFromValue(); if (newValueType != type) { } int itemOffset = index * (1 + getBT_TypeSize(type)); BT_Pointer itemPointer(_pointer.address + 1 + 4 + itemOffset); auto valueBuffer = encodeValue(value); _table->_file.seekp(itemPointer.address); _table->_file.write(reinterpret_cast(valueBuffer.data()), valueBuffer.size()); } template void BT_UniformArray::add(const T& value) { addAll({value}); } template void BT_UniformArray::addAll(const std::vector& values) { _table->antiFreeListScope([&]() { BT_Type elementType = getElementType(); // Validate types for (const auto& value : values) { BT_Type newValueType = getBT_TypeFromValue(); if (newValueType != elementType) { throw std::runtime_error("Type mismatch"); } } // Read current array int currentLength = getLength(); int elementSize = 1 + getBT_TypeSize(elementType); int bufferSize = 1 + 4 + currentLength * elementSize; _table->_file.seekg(_pointer.address); std::vector fullBuffer(bufferSize); _table->_file.read(reinterpret_cast(fullBuffer.data()), bufferSize); // Add new values for (const auto& value : values) { auto valueBuffer = encodeValue(value); fullBuffer.insert(fullBuffer.end(), valueBuffer.begin(), valueBuffer.end()); } // Update length int newLength = currentLength + values.size(); uint32_t len = static_cast(newLength); fullBuffer[1] = len & 0xFF; fullBuffer[2] = (len >> 8) & 0xFF; fullBuffer[3] = (len >> 16) & 0xFF; fullBuffer[4] = (len >> 24) & 0xFF; // Free old array _table->free(_pointer, getSize()); // Allocate new space BT_Pointer newPointer = _table->alloc(fullBuffer.size()); // Update address table references auto addressTable = _table->getAddressTable(); for (auto& [key, value] : addressTable) { if (value == _pointer) { value = newPointer; } } _table->setAddressTable(addressTable); _pointer = newPointer; // Write updated buffer _table->_file.seekp(newPointer.address); _table->_file.write(reinterpret_cast(fullBuffer.data()), fullBuffer.size()); }); } template BT_Type BT_UniformArray::getElementType() { if (getLength() == 0) { return getBT_TypeFromValue(); } _table->_file.seekg(_pointer.address + 1 + 4); uint8_t typeId; _table->_file.read(reinterpret_cast(&typeId), 1); return static_cast(typeId); } template int BT_UniformArray::getSize() { int len = getLength(); if (len == 0) { return 1 + 4; } BT_Type elementType = getElementType(); return 1 + 4 + len * (1 + getBT_TypeSize(elementType)); } std::string binaryDump(const std::vector& data) { std::stringstream buffer; for (size_t i = 0; i < data.size(); i += 16) { // Address buffer << "0x" << std::setfill('0') << std::setw(4) << std::hex << std::uppercase << i << " (" << std::setfill(' ') << std::setw(4) << std::dec << i << ") | "; // Hex bytes for (int j = 0; j < 16; j++) { if (i + j < data.size()) { buffer << std::setfill('0') << std::setw(2) << std::hex << std::uppercase << static_cast(data[i + j]) << " "; } else { buffer << " "; } } buffer << " | "; // Integer representation for (int j = 0; j < 16; j++) { if (i + j < data.size()) { buffer << std::setfill(' ') << std::setw(3) << std::dec << static_cast(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(byte); } else { buffer << '.'; } } } buffer << " | "; if (i + 16 < data.size()) buffer << "\n"; } return buffer.str(); } int main() { // Remove existing file std::remove("example.bin"); BinaryTable table("example.bin"); table.initialize(); // Read file for dump std::ifstream file("example.bin", std::ios::binary); std::vector fileData((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); std::cout << "File dump:\n"; std::cout << binaryDump(fileData) << "\n"; std::cout << "File size: " << fileData.size() << " bytes\n\n"; // Set values table.set("int_array", std::vector{6, 3, 9, 2, 5}); table.set("float_array", std::vector{1.5f, 2.5f, 3.5f}); table.set("empty", std::vector{}); // Get arrays and modify auto intArray = table.get>("int_array"); auto floatArray = table.get>("float_array"); intArray.set(0, 1); floatArray.set(1, 4.5f); std::cout << "int_array pointer: " << intArray.getPointer().toString() << "\n"; std::cout << "float_array pointer: " << floatArray.getPointer().toString() << "\n"; intArray.add(10); floatArray.add(5.5f); intArray.addAll({420, 69, 1337, 1738}); floatArray.addAll({6.5f, 7.5f, 8.5f}); // Read back values auto readback1 = table.get>("int_array"); auto readback2 = table.get>("float_array"); auto readback3 = table.get>("empty"); std::cout << "Readback1 length: " << readback1.getLength() << "\n"; std::cout << "Readback2 length: " << readback2.getLength() << "\n"; std::cout << "Readback3 length: " << readback3.getLength() << "\n"; // Print some values if (readback1.getLength() > 0) { std::cout << "First int: " << readback1[0] << "\n"; } if (readback2.getLength() > 0) { std::cout << "First float: " << readback2[0] << "\n"; } // Final file dump std::ifstream finalFile("example.bin", std::ios::binary); std::vector finalData((std::istreambuf_iterator(finalFile)), std::istreambuf_iterator()); finalFile.close(); std::cout << "\nFile dump:\n"; std::cout << binaryDump(finalData) << "\n"; std::cout << "File size: " << finalData.size() << " bytes\n"; return 0; }