Enhance BinaryTable functionality with improved value encoding and array handling
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
#define BINARY_TABLE_MAIN
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -81,10 +82,10 @@ inline std::vector<uint8_t> encodeValue(const BT_Value& value) {
|
|||||||
for (int i = 0; i < 4; ++i) buffer.push_back((v >> (i * 8)) & 0xFF);
|
for (int i = 0; i < 4; ++i) buffer.push_back((v >> (i * 8)) & 0xFF);
|
||||||
} else if (std::holds_alternative<double>(value)) {
|
} else if (std::holds_alternative<double>(value)) {
|
||||||
buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT));
|
buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT));
|
||||||
double v = std::get<double>(value);
|
float v = static_cast<float>(std::get<double>(value));
|
||||||
uint32_t asInt;
|
uint8_t bytes[4];
|
||||||
std::memcpy(&asInt, &v, 4); // Only use 4 bytes (float32)
|
std::memcpy(bytes, &v, 4);
|
||||||
for (int i = 0; i < 4; ++i) buffer.push_back((asInt >> (i * 8)) & 0xFF);
|
buffer.insert(buffer.end(), bytes, bytes + 4);
|
||||||
} else if (std::holds_alternative<std::string>(value)) {
|
} else if (std::holds_alternative<std::string>(value)) {
|
||||||
buffer.push_back(static_cast<uint8_t>(BT_Type::STRING));
|
buffer.push_back(static_cast<uint8_t>(BT_Type::STRING));
|
||||||
const std::string& str = std::get<std::string>(value);
|
const std::string& str = std::get<std::string>(value);
|
||||||
@@ -235,8 +236,9 @@ public:
|
|||||||
BT_Reference(BinaryTable* table, BT_Pointer pointer)
|
BT_Reference(BinaryTable* table, BT_Pointer pointer)
|
||||||
: _table(table), _pointer(pointer) {}
|
: _table(table), _pointer(pointer) {}
|
||||||
|
|
||||||
BT_Value decodeValue();
|
// decodeValue returns BT_Value for primitives, nullptr for arrays (handled separately)
|
||||||
int size();
|
virtual BT_Value decodeValue();
|
||||||
|
virtual int size();
|
||||||
std::string to_string() const { return _pointer.to_string(); }
|
std::string to_string() const { return _pointer.to_string(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -249,22 +251,86 @@ public:
|
|||||||
void set(int index, const BT_Value& value);
|
void set(int index, const BT_Value& value);
|
||||||
void add(const BT_Value& value);
|
void add(const BT_Value& value);
|
||||||
void addAll(const std::vector<BT_Value>& values);
|
void addAll(const std::vector<BT_Value>& values);
|
||||||
int size() override;
|
int size();
|
||||||
BT_Type elementType();
|
BT_Type elementType();
|
||||||
std::string to_string(bool readValues = false);
|
std::string to_string(bool readValues = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- binaryDump utility ---
|
||||||
|
inline 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::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 ---
|
// --- BT_Reference Implementation ---
|
||||||
#include <memory>
|
#include <memory>
|
||||||
class BinaryTable {
|
class BinaryTable {
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<BT_File> _file;
|
std::unique_ptr<BT_File> _file;
|
||||||
|
std::map<int64_t, BT_Pointer> _addressTable;
|
||||||
BinaryTable(const std::string& path) : _file(std::make_unique<BT_File>(path)) {}
|
BinaryTable(const std::string& path) : _file(std::make_unique<BT_File>(path)) {}
|
||||||
// ...other members will be added later...
|
// ...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<uint8_t> 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() {
|
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);
|
_table->_file->setPosition(_pointer.address);
|
||||||
int typeId = _table->_file->readByte();
|
int typeId = _table->_file->readByte();
|
||||||
BT_Type type = BT_Type_from_id(typeId);
|
BT_Type type = BT_Type_from_id(typeId);
|
||||||
@@ -277,8 +343,7 @@ inline BT_Value BT_Reference::decodeValue() {
|
|||||||
std::vector<uint8_t> bytes = _table->_file->read(length);
|
std::vector<uint8_t> bytes = _table->_file->read(length);
|
||||||
return std::string(bytes.begin(), bytes.end());
|
return std::string(bytes.begin(), bytes.end());
|
||||||
} else if (type == BT_Type::INTEGER_ARRAY || type == BT_Type::FLOAT_ARRAY) {
|
} else if (type == BT_Type::INTEGER_ARRAY || type == BT_Type::FLOAT_ARRAY) {
|
||||||
// Return a BT_UniformArray wrapper
|
throw std::runtime_error("decodeValue() called on array type; use BT_UniformArray instead");
|
||||||
return BT_UniformArray(_table, _pointer);
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Unsupported or unimplemented BT_Type in decodeValue");
|
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);
|
_table->_file->write(valueBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void BT_UniformArray::add(const BT_Value& value) {
|
||||||
|
addAll(std::vector<BT_Value>{value});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void BT_UniformArray::addAll(const std::vector<BT_Value>& 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<int>(values[i])) newValueType = BT_Type::INTEGER;
|
||||||
|
else if (std::holds_alternative<double>(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<uint8_t> fullBuffer = _table->_file->read(oldBufferSize);
|
||||||
|
// Encode new values and append
|
||||||
|
for (const auto& v : values) {
|
||||||
|
std::vector<uint8_t> 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() {
|
inline int BT_UniformArray::size() {
|
||||||
int len = length();
|
int len = length();
|
||||||
if (len == 0) return 1 + 4;
|
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]; }
|
for (size_t i = 0; i < arr.size(); ++i) { if (i) oss << ", "; oss << arr[i]; }
|
||||||
oss << "]"; return oss.str();
|
oss << "]"; return oss.str();
|
||||||
}
|
}
|
||||||
if (std::holds_alternative<BT_UniformArray>(v)) return std::get<BT_UniformArray>(v).to_string(true);
|
|
||||||
return "<unknown>";
|
return "<unknown>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,6 +551,14 @@ inline std::string printBTValue(const BT_Value& v) {
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
// 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() {
|
int main() {
|
||||||
const std::string filename = "example.bin";
|
const std::string filename = "example.bin";
|
||||||
std::remove(filename.c_str());
|
std::remove(filename.c_str());
|
||||||
@@ -461,10 +578,16 @@ int main() {
|
|||||||
table.set("empty", std::vector<int>{});
|
table.set("empty", std::vector<int>{});
|
||||||
|
|
||||||
// Modify arrays
|
// Modify arrays
|
||||||
auto v1 = table.get("int_array");
|
auto int_ptr = table.getPointer("int_array");
|
||||||
auto v2 = table.get("float_array");
|
auto float_ptr = table.getPointer("float_array");
|
||||||
if (std::holds_alternative<BT_UniformArray>(v1)) {
|
auto empty_ptr = table.getPointer("empty");
|
||||||
BT_UniformArray intArr = std::get<BT_UniformArray>(v1);
|
|
||||||
|
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.set(0, 1);
|
||||||
intArr.add(10);
|
intArr.add(10);
|
||||||
intArr.addAll({420, 69, 1337, 1738});
|
intArr.addAll({420, 69, 1337, 1738});
|
||||||
@@ -473,8 +596,8 @@ int main() {
|
|||||||
} else {
|
} else {
|
||||||
std::cout << "int_array is not a BT_UniformArray!\n";
|
std::cout << "int_array is not a BT_UniformArray!\n";
|
||||||
}
|
}
|
||||||
if (std::holds_alternative<BT_UniformArray>(v2)) {
|
if (BT_Type_is_array(float_type)) {
|
||||||
BT_UniformArray floatArr = std::get<BT_UniformArray>(v2);
|
BT_UniformArray floatArr(&table, float_ptr);
|
||||||
floatArr.set(1, 4.5);
|
floatArr.set(1, 4.5);
|
||||||
floatArr.add(5.5);
|
floatArr.add(5.5);
|
||||||
floatArr.addAll({6.5, 7.5, 8.5});
|
floatArr.addAll({6.5, 7.5, 8.5});
|
||||||
@@ -483,9 +606,8 @@ int main() {
|
|||||||
} else {
|
} else {
|
||||||
std::cout << "float_array is not a BT_UniformArray!\n";
|
std::cout << "float_array is not a BT_UniformArray!\n";
|
||||||
}
|
}
|
||||||
auto v3 = table.get("empty");
|
if (BT_Type_is_array(empty_type)) {
|
||||||
if (std::holds_alternative<BT_UniformArray>(v3)) {
|
BT_UniformArray emptyArr(&table, empty_ptr);
|
||||||
BT_UniformArray emptyArr = std::get<BT_UniformArray>(v3);
|
|
||||||
std::cout << "Readback3: " << emptyArr.to_string(true) << std::endl;
|
std::cout << "Readback3: " << emptyArr.to_string(true) << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "empty is not a BT_UniformArray!\n";
|
std::cout << "empty is not a BT_UniformArray!\n";
|
||||||
|
|||||||
Reference in New Issue
Block a user