diff --git a/cpp/binary_table.h b/cpp/binary_table.h index 0848b32..b240abe 100644 --- a/cpp/binary_table.h +++ b/cpp/binary_table.h @@ -1,3 +1,4 @@ +#define BINARY_TABLE_MAIN #pragma once #include #include @@ -81,10 +82,10 @@ inline std::vector encodeValue(const BT_Value& value) { for (int i = 0; i < 4; ++i) buffer.push_back((v >> (i * 8)) & 0xFF); } else if (std::holds_alternative(value)) { buffer.push_back(static_cast(BT_Type::FLOAT)); - double v = std::get(value); - uint32_t asInt; - std::memcpy(&asInt, &v, 4); // Only use 4 bytes (float32) - for (int i = 0; i < 4; ++i) buffer.push_back((asInt >> (i * 8)) & 0xFF); + float v = static_cast(std::get(value)); + uint8_t bytes[4]; + std::memcpy(bytes, &v, 4); + buffer.insert(buffer.end(), bytes, bytes + 4); } else if (std::holds_alternative(value)) { buffer.push_back(static_cast(BT_Type::STRING)); const std::string& str = std::get(value); @@ -235,8 +236,9 @@ public: BT_Reference(BinaryTable* table, BT_Pointer pointer) : _table(table), _pointer(pointer) {} - BT_Value decodeValue(); - int size(); + // decodeValue returns BT_Value for primitives, nullptr for arrays (handled separately) + virtual BT_Value decodeValue(); + virtual int size(); std::string to_string() const { return _pointer.to_string(); } }; @@ -249,22 +251,86 @@ public: void set(int index, const BT_Value& value); void add(const BT_Value& value); void addAll(const std::vector& values); - int size() override; + int size(); BT_Type elementType(); std::string to_string(bool readValues = false); }; +// --- binaryDump utility --- +inline std::string binaryDump(const std::vector& data) { + std::ostringstream buffer; + for (size_t i = 0; i < data.size(); i += 16) { + // Address + buffer << "0x" << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << i; + buffer << " (" << std::dec << std::setw(4) << i << ") | "; + // Hex bytes + for (size_t j = 0; j < 16; ++j) { + if (i + j < data.size()) { + buffer << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (int)data[i + j] << " "; + } else { + buffer << " "; + } + } + buffer << " | "; + // Integer representation + for (size_t j = 0; j < 16; ++j) { + if (i + j < data.size()) { + buffer << std::dec << std::setw(3) << (int)data[i + j] << " "; + } else { + buffer << " "; + } + } + buffer << " | "; + // ASCII representation + for (size_t j = 0; j < 16; ++j) { + if (i + j < data.size()) { + int byte = data[i + j]; + if (byte >= 32 && byte <= 126) { + buffer << (char)byte; + } else { + buffer << '.'; + } + } + } + buffer << " | "; + if (i + 16 < data.size()) buffer << std::endl; + } + return buffer.str(); +} + // --- BT_Reference Implementation --- #include class BinaryTable { public: std::unique_ptr _file; + std::map _addressTable; BinaryTable(const std::string& path) : _file(std::make_unique(path)) {} // ...other members will be added later... + + // Set a value for a key + void set(const std::string& key, const BT_Value& value) { + int64_t keyHash = bt_hash(key); + std::vector valueBuffer = encodeValue(value); + // Append value to end of file + _file->setPosition(_file->length()); + int64_t valueAddress = _file->length(); + _file->write(valueBuffer); + _addressTable[keyHash] = BT_Pointer(valueAddress); + } + + // Retrieve the pointer for a given key + BT_Pointer getPointer(const std::string& key) { + int64_t keyHash = bt_hash(key); + auto it = _addressTable.find(keyHash); + if (it == _addressTable.end()) { + throw std::runtime_error("Key not found in address table: " + key); + } + return it->second; + } }; inline BT_Value BT_Reference::decodeValue() { - if (_pointer.is_null()) return {}; + if (_pointer.is_null()) throw std::runtime_error("Null pointer"); _table->_file->setPosition(_pointer.address); int typeId = _table->_file->readByte(); BT_Type type = BT_Type_from_id(typeId); @@ -277,8 +343,7 @@ inline BT_Value BT_Reference::decodeValue() { std::vector bytes = _table->_file->read(length); return std::string(bytes.begin(), bytes.end()); } else if (type == BT_Type::INTEGER_ARRAY || type == BT_Type::FLOAT_ARRAY) { - // Return a BT_UniformArray wrapper - return BT_UniformArray(_table, _pointer); + throw std::runtime_error("decodeValue() called on array type; use BT_UniformArray instead"); } else { throw std::runtime_error("Unsupported or unimplemented BT_Type in decodeValue"); } @@ -340,6 +405,51 @@ inline void BT_UniformArray::set(int index, const BT_Value& value) { _table->_file->write(valueBuffer); } +inline void BT_UniformArray::add(const BT_Value& value) { + addAll(std::vector{value}); +} + +inline void BT_UniformArray::addAll(const std::vector& values) { + // Read current array type and length + int oldLen = length(); + BT_Type type = elementType(); + if (values.empty()) return; + // Validate all new values are of the correct type + for (size_t i = 0; i < values.size(); i++) { + BT_Type newValueType; + if (std::holds_alternative(values[i])) newValueType = BT_Type::INTEGER; + else if (std::holds_alternative(values[i])) newValueType = BT_Type::FLOAT; + else throw std::runtime_error("Type mismatch or unsupported type in addAll"); + if (newValueType != type) { + throw std::runtime_error("Type mismatch in addAll: expected " + std::to_string((int)type) + ", got " + std::to_string((int)newValueType)); + } + } + // Read the full array buffer + int elemSize = 1 + BT_Type_size(type); + int oldBufferSize = 1 + 4 + oldLen * elemSize; + _table->_file->setPosition(_pointer.address); + std::vector fullBuffer = _table->_file->read(oldBufferSize); + // Encode new values and append + for (const auto& v : values) { + std::vector enc = encodeValue(v); + fullBuffer.insert(fullBuffer.end(), enc.begin(), enc.end()); + } + // Update length in buffer + int newLen = oldLen + (int)values.size(); + for (int i = 0; i < 4; ++i) fullBuffer[1 + i] = (newLen >> (i * 8)) & 0xFF; + // Append new buffer to file (simulate alloc) + _table->_file->setPosition(_table->_file->length()); + int64_t newAddress = _table->_file->length(); + _table->_file->write(fullBuffer); + // Update address table (in-memory only) + for (auto& kv : _table->_addressTable) { + if (kv.second == _pointer) { + kv.second = BT_Pointer(newAddress); + } + } + _pointer = BT_Pointer(newAddress); +} + inline int BT_UniformArray::size() { int len = length(); if (len == 0) return 1 + 4; @@ -433,7 +543,6 @@ inline std::string printBTValue(const BT_Value& v) { for (size_t i = 0; i < arr.size(); ++i) { if (i) oss << ", "; oss << arr[i]; } oss << "]"; return oss.str(); } - if (std::holds_alternative(v)) return std::get(v).to_string(true); return ""; } @@ -442,6 +551,14 @@ inline std::string printBTValue(const BT_Value& v) { #include #include +// Helper to get type from pointer +BT_Type get_type(BinaryTable& table, const BT_Pointer& ptr) { + if (ptr.is_null()) throw std::runtime_error("Null pointer"); + table._file->setPosition(ptr.address); + int typeId = table._file->readByte(); + return BT_Type_from_id(typeId); +} + int main() { const std::string filename = "example.bin"; std::remove(filename.c_str()); @@ -461,10 +578,16 @@ int main() { table.set("empty", std::vector{}); // Modify arrays - auto v1 = table.get("int_array"); - auto v2 = table.get("float_array"); - if (std::holds_alternative(v1)) { - BT_UniformArray intArr = std::get(v1); + auto int_ptr = table.getPointer("int_array"); + auto float_ptr = table.getPointer("float_array"); + auto empty_ptr = table.getPointer("empty"); + + BT_Type int_type = get_type(table, int_ptr); + BT_Type float_type = get_type(table, float_ptr); + BT_Type empty_type = get_type(table, empty_ptr); + + if (BT_Type_is_array(int_type)) { + BT_UniformArray intArr(&table, int_ptr); intArr.set(0, 1); intArr.add(10); intArr.addAll({420, 69, 1337, 1738}); @@ -473,8 +596,8 @@ int main() { } else { std::cout << "int_array is not a BT_UniformArray!\n"; } - if (std::holds_alternative(v2)) { - BT_UniformArray floatArr = std::get(v2); + if (BT_Type_is_array(float_type)) { + BT_UniformArray floatArr(&table, float_ptr); floatArr.set(1, 4.5); floatArr.add(5.5); floatArr.addAll({6.5, 7.5, 8.5}); @@ -483,9 +606,8 @@ int main() { } else { std::cout << "float_array is not a BT_UniformArray!\n"; } - auto v3 = table.get("empty"); - if (std::holds_alternative(v3)) { - BT_UniformArray emptyArr = std::get(v3); + if (BT_Type_is_array(empty_type)) { + BT_UniformArray emptyArr(&table, empty_ptr); std::cout << "Readback3: " << emptyArr.to_string(true) << std::endl; } else { std::cout << "empty is not a BT_UniformArray!\n";