Add fetchSublist method to BinaryTable for improved data retrieval
This commit is contained in:
@@ -1,20 +1,44 @@
|
||||
#define BINARY_TABLE_MAIN
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <shared_mutex>
|
||||
/*
|
||||
|
||||
/$$$$$$ /$$ /$$ /$$$$$$$ /$$$$$$$$ /$$ /$$ /$$$$$ /$$$$$$ /$$ /$$ /$$$$$$$$ /$$$$$$$$
|
||||
|_ $$_/| $$$ /$$$| $$__ $$| $$_____/| $$$ | $$ |__ $$|_ $$_/ | $$$ | $$| $$_____/|__ $$__/
|
||||
| $$ | $$$$ /$$$$| $$ \ $$| $$ | $$$$| $$ | $$ | $$ | $$$$| $$| $$ | $$
|
||||
| $$ | $$ $$/$$ $$| $$$$$$$ | $$$$$ | $$ $$ $$ | $$ | $$ | $$ $$ $$| $$$$$ | $$
|
||||
| $$ | $$ $$$| $$| $$__ $$| $$__/ | $$ $$$$ /$$ | $$ | $$ | $$ $$$$| $$__/ | $$
|
||||
| $$ | $$\ $ | $$| $$ \ $$| $$ | $$\ $$$| $$ | $$ | $$ | $$\ $$$| $$ | $$
|
||||
/$$$$$$| $$ \/ | $$| $$$$$$$/| $$$$$$$$| $$ \ $$| $$$$$$/ /$$$$$$ /$$| $$ \ $$| $$$$$$$$ | $$
|
||||
|______/|__/ |__/|_______/ |________/|__/ \__/ \______/ |______/|__/|__/ \__/|________/ |__/
|
||||
|
||||
// --- BT_Type Enum ---
|
||||
enum class BT_Type : int {
|
||||
<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.
|
||||
|
||||
This file is part of the SweepStore (formerly Binary Table) package for C++.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <variant>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
|
||||
namespace bt {
|
||||
|
||||
// Forward declarations
|
||||
class BinaryTable;
|
||||
class BT_Reference;
|
||||
template<typename T> class BT_UniformArray;
|
||||
|
||||
// Type enumeration matching Dart version
|
||||
enum class BT_Type : uint8_t {
|
||||
POINTER = 0,
|
||||
ADDRESS_TABLE = 1,
|
||||
INTEGER = 2,
|
||||
@@ -24,8 +48,9 @@ enum class BT_Type : int {
|
||||
FLOAT_ARRAY = 6
|
||||
};
|
||||
|
||||
inline int BT_Type_size(BT_Type t) {
|
||||
switch (t) {
|
||||
// Size mapping for types
|
||||
constexpr int getTypeSize(BT_Type type) {
|
||||
switch (type) {
|
||||
case BT_Type::POINTER: return 8;
|
||||
case BT_Type::ADDRESS_TABLE: return -1;
|
||||
case BT_Type::INTEGER: return 4;
|
||||
@@ -33,632 +58,235 @@ inline int BT_Type_size(BT_Type t) {
|
||||
case BT_Type::STRING: return -1;
|
||||
case BT_Type::INTEGER_ARRAY: return -1;
|
||||
case BT_Type::FLOAT_ARRAY: return -1;
|
||||
default: throw std::invalid_argument("Invalid BT_Type");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if type is array type
|
||||
constexpr bool isArrayType(BT_Type type) {
|
||||
return type == BT_Type::INTEGER_ARRAY || type == BT_Type::FLOAT_ARRAY;
|
||||
}
|
||||
|
||||
// Type deduction helpers
|
||||
template<typename T>
|
||||
constexpr BT_Type getTypeFromValue() {
|
||||
if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, int>) {
|
||||
return BT_Type::INTEGER;
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
return BT_Type::FLOAT;
|
||||
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||
return BT_Type::STRING;
|
||||
} else if constexpr (std::is_same_v<T, std::vector<int32_t>> || std::is_same_v<T, std::vector<int>>) {
|
||||
return BT_Type::INTEGER_ARRAY;
|
||||
} else if constexpr (std::is_same_v<T, std::vector<float>>) {
|
||||
return BT_Type::FLOAT_ARRAY;
|
||||
} else {
|
||||
static_assert(sizeof(T) == 0, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool BT_Type_is_array(BT_Type t) {
|
||||
return t == BT_Type::INTEGER_ARRAY || t == BT_Type::FLOAT_ARRAY;
|
||||
}
|
||||
// Pointer class
|
||||
class BT_Pointer {
|
||||
private:
|
||||
int64_t address_;
|
||||
|
||||
inline BT_Type BT_Type_from_id(int id) {
|
||||
if (id < 0 || id > 6) throw std::invalid_argument("Invalid BT_Type id");
|
||||
return static_cast<BT_Type>(id);
|
||||
}
|
||||
|
||||
// --- FNV-1a Hash ---
|
||||
inline int64_t bt_hash(const std::string& str) {
|
||||
uint64_t hash = 0xcbf29ce484222325ULL;
|
||||
for (unsigned char c : str) {
|
||||
hash ^= c;
|
||||
hash *= 0x100000001b3ULL;
|
||||
public:
|
||||
explicit BT_Pointer(int64_t address = -1) : address_(address) {}
|
||||
|
||||
bool isNull() const { return address_ == -1; }
|
||||
int64_t address() const { return address_; }
|
||||
|
||||
bool operator==(const BT_Pointer& other) const {
|
||||
return address_ == other.address_;
|
||||
}
|
||||
return static_cast<int64_t>(hash);
|
||||
}
|
||||
|
||||
// --- BT_Pointer ---
|
||||
struct BT_Pointer {
|
||||
int64_t address;
|
||||
BT_Pointer(int64_t addr = -1) : address(addr) {}
|
||||
bool is_null() 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 to_string() const {
|
||||
std::ostringstream oss;
|
||||
oss << "0x" << std::hex << address << " (" << std::dec << address << ")";
|
||||
return oss.str();
|
||||
|
||||
bool operator!=(const BT_Pointer& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
const BT_Pointer BT_Null(-1);
|
||||
|
||||
// --- BT_Value Type ---
|
||||
using BT_Value = std::variant<int, double, std::string, std::vector<int>, std::vector<double>>;
|
||||
// Null pointer constant
|
||||
const BT_Pointer BT_Null{-1};
|
||||
|
||||
// --- encodeValue ---
|
||||
inline std::vector<uint8_t> encodeValue(const BT_Value& value) {
|
||||
std::vector<uint8_t> buffer;
|
||||
if (std::holds_alternative<int>(value)) {
|
||||
buffer.push_back(static_cast<uint8_t>(BT_Type::INTEGER));
|
||||
int v = std::get<int>(value);
|
||||
for (int i = 0; i < 4; ++i) buffer.push_back((v >> (i * 8)) & 0xFF);
|
||||
} else if (std::holds_alternative<double>(value)) {
|
||||
buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT));
|
||||
float v = static_cast<float>(std::get<double>(value));
|
||||
uint8_t bytes[4];
|
||||
std::memcpy(bytes, &v, 4);
|
||||
buffer.insert(buffer.end(), bytes, bytes + 4);
|
||||
} else if (std::holds_alternative<std::string>(value)) {
|
||||
buffer.push_back(static_cast<uint8_t>(BT_Type::STRING));
|
||||
const std::string& str = std::get<std::string>(value);
|
||||
int len = static_cast<int>(str.size());
|
||||
for (int i = 0; i < 4; ++i) buffer.push_back((len >> (i * 8)) & 0xFF);
|
||||
buffer.insert(buffer.end(), str.begin(), str.end());
|
||||
} else if (std::holds_alternative<std::vector<int>>(value)) {
|
||||
buffer.push_back(static_cast<uint8_t>(BT_Type::INTEGER_ARRAY));
|
||||
const auto& arr = std::get<std::vector<int>>(value);
|
||||
int len = static_cast<int>(arr.size());
|
||||
for (int i = 0; i < 4; ++i) buffer.push_back((len >> (i * 8)) & 0xFF);
|
||||
for (int v : arr) {
|
||||
auto enc = encodeValue(v);
|
||||
buffer.insert(buffer.end(), enc.begin(), enc.end());
|
||||
}
|
||||
} else if (std::holds_alternative<std::vector<double>>(value)) {
|
||||
buffer.push_back(static_cast<uint8_t>(BT_Type::FLOAT_ARRAY));
|
||||
const auto& arr = std::get<std::vector<double>>(value);
|
||||
int len = static_cast<int>(arr.size());
|
||||
for (int i = 0; i < 4; ++i) buffer.push_back((len >> (i * 8)) & 0xFF);
|
||||
for (double v : arr) {
|
||||
auto enc = encodeValue(v);
|
||||
buffer.insert(buffer.end(), enc.begin(), enc.end());
|
||||
}
|
||||
} else {
|
||||
throw std::invalid_argument("Unsupported BT_Value type");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// --- BT_FreeListEntry ---
|
||||
// Free list entry
|
||||
struct BT_FreeListEntry {
|
||||
BT_Pointer pointer;
|
||||
int size;
|
||||
BT_FreeListEntry(BT_Pointer p, int s) : pointer(p), size(s) {}
|
||||
int32_t size;
|
||||
|
||||
BT_FreeListEntry(BT_Pointer ptr, int32_t sz) : pointer(ptr), size(sz) {}
|
||||
};
|
||||
|
||||
// --- File I/O Helpers ---
|
||||
class BT_File {
|
||||
public:
|
||||
std::fstream file;
|
||||
mutable std::shared_mutex _rwlock;
|
||||
BT_File(const std::string& path) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
file.open(path, std::ios::in | std::ios::out | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
file.open(path, std::ios::out | std::ios::binary);
|
||||
file.close();
|
||||
file.open(path, std::ios::in | std::ios::out | std::ios::binary);
|
||||
}
|
||||
if (!file.is_open()) throw std::runtime_error("Failed to open file");
|
||||
}
|
||||
void setPosition(int64_t pos) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
file.seekp(pos);
|
||||
file.seekg(pos);
|
||||
}
|
||||
int64_t length() {
|
||||
std::shared_lock lock(_rwlock);
|
||||
auto cur = file.tellg();
|
||||
file.seekg(0, std::ios::end);
|
||||
int64_t len = file.tellg();
|
||||
file.seekg(cur);
|
||||
file.seekp(cur);
|
||||
return len;
|
||||
}
|
||||
std::vector<uint8_t> read(int size) {
|
||||
std::shared_lock lock(_rwlock);
|
||||
std::vector<uint8_t> buf(size);
|
||||
file.read(reinterpret_cast<char*>(buf.data()), size);
|
||||
return buf;
|
||||
}
|
||||
void write(const std::vector<uint8_t>& buf) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
file.write(reinterpret_cast<const char*>(buf.data()), buf.size());
|
||||
}
|
||||
int readInt(int size = 4) {
|
||||
std::shared_lock lock(_rwlock);
|
||||
std::vector<uint8_t> buf = read(size);
|
||||
int result = 0;
|
||||
for (int i = size - 1; i >= 0; --i) {
|
||||
result = (result << 8) | buf[i];
|
||||
}
|
||||
int signBit = 1 << (size * 8 - 1);
|
||||
if (result & signBit) {
|
||||
result -= 1 << (size * 8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void writeInt(int value, int size = 4) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
std::vector<uint8_t> buf(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
buf[i] = (value >> (i * 8)) & 0xFF;
|
||||
}
|
||||
write(buf);
|
||||
}
|
||||
BT_Pointer readPointer() {
|
||||
std::shared_lock lock(_rwlock);
|
||||
int64_t addr = 0;
|
||||
std::vector<uint8_t> buf = read(8);
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
addr = (addr << 8) | buf[i];
|
||||
}
|
||||
return BT_Pointer(addr);
|
||||
}
|
||||
void writePointer(const BT_Pointer& ptr) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
int64_t addr = ptr.address;
|
||||
std::vector<uint8_t> buf(8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
buf[i] = (addr >> (i * 8)) & 0xFF;
|
||||
}
|
||||
write(buf);
|
||||
}
|
||||
float readFloat32() {
|
||||
std::shared_lock lock(_rwlock);
|
||||
std::vector<uint8_t> buf = read(4);
|
||||
float val;
|
||||
std::memcpy(&val, buf.data(), 4);
|
||||
return val;
|
||||
}
|
||||
void writeFloat32(float value) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
uint8_t buf[4];
|
||||
std::memcpy(buf, &value, 4);
|
||||
write(std::vector<uint8_t>(buf, buf + 4));
|
||||
}
|
||||
double readFloat64() {
|
||||
std::shared_lock lock(_rwlock);
|
||||
std::vector<uint8_t> buf = read(8);
|
||||
double val;
|
||||
std::memcpy(&val, buf.data(), 8);
|
||||
return val;
|
||||
}
|
||||
void writeFloat64(double value) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
uint8_t buf[8];
|
||||
std::memcpy(buf, &value, 8);
|
||||
write(std::vector<uint8_t>(buf, buf + 8));
|
||||
}
|
||||
uint8_t readByte() {
|
||||
std::shared_lock lock(_rwlock);
|
||||
char c;
|
||||
file.read(&c, 1);
|
||||
return static_cast<uint8_t>(c);
|
||||
}
|
||||
void writeByte(uint8_t b) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
char c = static_cast<char>(b);
|
||||
file.write(&c, 1);
|
||||
}
|
||||
};
|
||||
// Value encoding functions
|
||||
std::vector<uint8_t> encodeValue(const int32_t& value);
|
||||
std::vector<uint8_t> encodeValue(const float& value);
|
||||
std::vector<uint8_t> encodeValue(const std::string& value);
|
||||
std::vector<uint8_t> encodeValue(const std::vector<int32_t>& value);
|
||||
std::vector<uint8_t> encodeValue(const std::vector<float>& value);
|
||||
|
||||
// --- BT_Reference ---
|
||||
class BinaryTable; // Forward declaration
|
||||
// Template wrapper for encoding
|
||||
template<typename T>
|
||||
std::vector<uint8_t> encodeValue(const T& value) {
|
||||
return encodeValue(value);
|
||||
}
|
||||
|
||||
// Reference class for handling stored values
|
||||
class BT_Reference {
|
||||
public:
|
||||
BinaryTable* _table;
|
||||
BT_Pointer _pointer;
|
||||
BT_Reference(BinaryTable* table, BT_Pointer pointer)
|
||||
: _table(table), _pointer(pointer) {}
|
||||
protected:
|
||||
BinaryTable* table_;
|
||||
BT_Pointer pointer_;
|
||||
|
||||
// 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(); }
|
||||
public:
|
||||
BT_Reference(BinaryTable* table, BT_Pointer pointer);
|
||||
|
||||
template<typename T>
|
||||
T decodeValue();
|
||||
|
||||
int32_t size() const;
|
||||
BT_Type getType() const;
|
||||
bool isNull() const { return pointer_.isNull(); }
|
||||
BT_Pointer getPointer() const { return pointer_; }
|
||||
};
|
||||
|
||||
// --- BT_UniformArray ---
|
||||
// Uniform array class template
|
||||
template<typename T>
|
||||
class BT_UniformArray : public BT_Reference {
|
||||
public:
|
||||
using BT_Reference::BT_Reference;
|
||||
int length();
|
||||
BT_Value operator[](int index);
|
||||
void set(int index, const BT_Value& value);
|
||||
void add(const BT_Value& value);
|
||||
void addAll(const std::vector<BT_Value>& values);
|
||||
int size();
|
||||
BT_Type elementType();
|
||||
std::string to_string(bool readValues = false);
|
||||
std::vector<BT_Value> fetchSublist(int start = 0, int end = -1);
|
||||
BT_UniformArray(BinaryTable* table, BT_Pointer pointer) : BT_Reference(table, pointer) {}
|
||||
|
||||
int32_t length() const;
|
||||
T operator[](int32_t index) const;
|
||||
void set(int32_t index, const T& value);
|
||||
void add(const T& value);
|
||||
void addAll(const std::vector<T>& values);
|
||||
std::vector<T> fetchSublist(int32_t start = 0, int32_t end = -1);
|
||||
};
|
||||
|
||||
// --- 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 ---
|
||||
#include <memory>
|
||||
// Main BinaryTable class
|
||||
class BinaryTable {
|
||||
private:
|
||||
std::fstream file_;
|
||||
std::string filePath_;
|
||||
|
||||
// Free list management
|
||||
bool freeListLifted_;
|
||||
std::vector<BT_FreeListEntry> freeListCache_;
|
||||
|
||||
// Internal methods
|
||||
std::unordered_map<int64_t, BT_Pointer> getAddressTable();
|
||||
void setAddressTable(const std::unordered_map<int64_t, BT_Pointer>& table);
|
||||
std::vector<BT_FreeListEntry> getFreeList();
|
||||
void setFreeList(const std::vector<BT_FreeListEntry>& list);
|
||||
int64_t hashString(const std::string& str) const;
|
||||
|
||||
void truncateFile(int64_t newSize);
|
||||
|
||||
// File I/O helpers
|
||||
int32_t readInt32(int64_t position);
|
||||
float readFloat32(int64_t position);
|
||||
int64_t readInt64(int64_t position);
|
||||
uint8_t readByte(int64_t position);
|
||||
std::vector<uint8_t> readBytes(int64_t position, int32_t count);
|
||||
|
||||
void writeInt32(int64_t position, int32_t value);
|
||||
void writeFloat32(int64_t position, float value);
|
||||
void writeInt64(int64_t position, int64_t value);
|
||||
void writeByte(int64_t position, uint8_t value);
|
||||
void writeBytes(int64_t position, const std::vector<uint8_t>& data);
|
||||
|
||||
public:
|
||||
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)) {}
|
||||
// ...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;
|
||||
}
|
||||
explicit BinaryTable(const std::string& path);
|
||||
~BinaryTable();
|
||||
|
||||
void initialize();
|
||||
|
||||
// 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>
|
||||
void set(const std::string& key, const T& value);
|
||||
|
||||
template<typename T>
|
||||
T get(const std::string& key);
|
||||
|
||||
BT_Reference getReference(const std::string& key);
|
||||
|
||||
template<typename T>
|
||||
BT_UniformArray<T> getArray(const std::string& key);
|
||||
|
||||
void remove(const std::string& key);
|
||||
void truncate();
|
||||
|
||||
// Debug methods
|
||||
void debugAddressTable(const std::string& context = "");
|
||||
|
||||
// File access for reference classes
|
||||
friend class BT_Reference;
|
||||
template<typename T> friend class BT_UniformArray;
|
||||
|
||||
int64_t getFileLength();
|
||||
void setFilePosition(int64_t position);
|
||||
};
|
||||
|
||||
inline BT_Value BT_Reference::decodeValue() {
|
||||
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);
|
||||
if (type == BT_Type::INTEGER) {
|
||||
return _table->_file->readInt(4);
|
||||
} else if (type == BT_Type::FLOAT) {
|
||||
return static_cast<double>(_table->_file->readFloat32());
|
||||
} else if (type == BT_Type::STRING) {
|
||||
int length = _table->_file->readInt(4);
|
||||
std::vector<uint8_t> bytes = _table->_file->read(length);
|
||||
return std::string(bytes.begin(), bytes.end());
|
||||
} else if (type == BT_Type::INTEGER_ARRAY || type == BT_Type::FLOAT_ARRAY) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
// Template specializations for decodeValue
|
||||
template<> int32_t BT_Reference::decodeValue<int32_t>();
|
||||
template<> float BT_Reference::decodeValue<float>();
|
||||
template<> std::string BT_Reference::decodeValue<std::string>();
|
||||
template<> std::vector<int32_t> BT_Reference::decodeValue<std::vector<int32_t>>();
|
||||
template<> std::vector<float> BT_Reference::decodeValue<std::vector<float>>();
|
||||
template<> BT_UniformArray<int32_t> BT_Reference::decodeValue<BT_UniformArray<int32_t>>();
|
||||
template<> BT_UniformArray<float> BT_Reference::decodeValue<BT_UniformArray<float>>();
|
||||
|
||||
inline int BT_Reference::size() {
|
||||
if (_pointer.is_null()) return 0;
|
||||
_table->_file->setPosition(_pointer.address);
|
||||
int typeId = _table->_file->readByte();
|
||||
BT_Type type = BT_Type_from_id(typeId);
|
||||
if (type == BT_Type::INTEGER || type == BT_Type::FLOAT) {
|
||||
return 1 + 4;
|
||||
} else if (type == BT_Type::STRING) {
|
||||
int length = _table->_file->readInt(4);
|
||||
return 1 + 4 + length;
|
||||
} else if (type == BT_Type::ADDRESS_TABLE) {
|
||||
int count = _table->_file->readInt(4);
|
||||
return 1 + 4 + count * (8 + BT_Type_size(BT_Type::POINTER));
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported BT_Type for size()");
|
||||
}
|
||||
}
|
||||
|
||||
// --- BT_UniformArray Implementation ---
|
||||
inline int BT_UniformArray::length() {
|
||||
if (_pointer.is_null()) return 0;
|
||||
_table->_file->setPosition(_pointer.address);
|
||||
int typeId = _table->_file->readByte();
|
||||
BT_Type type = BT_Type_from_id(typeId);
|
||||
if (!BT_Type_is_array(type)) throw std::runtime_error("Not an array");
|
||||
return _table->_file->readInt(4);
|
||||
}
|
||||
|
||||
inline BT_Value BT_UniformArray::operator[](int index) {
|
||||
if (_pointer.is_null()) throw std::runtime_error("Null pointer");
|
||||
int len = length();
|
||||
if (index < 0 || index >= len) throw std::out_of_range("Index out of range");
|
||||
_table->_file->setPosition(_pointer.address + 1 + 4);
|
||||
int typeId = _table->_file->readByte();
|
||||
BT_Type type = BT_Type_from_id(typeId);
|
||||
int itemOffset = index * (1 + BT_Type_size(type));
|
||||
BT_Reference itemRef(_table, BT_Pointer((_pointer.address + 1 + 4) + itemOffset));
|
||||
return itemRef.decodeValue();
|
||||
}
|
||||
|
||||
inline void BT_UniformArray::set(int index, const BT_Value& value) {
|
||||
if (_pointer.is_null()) throw std::runtime_error("Null pointer");
|
||||
int len = length();
|
||||
if (index < 0 || index >= len) throw std::out_of_range("Index out of range");
|
||||
_table->_file->setPosition(_pointer.address + 1 + 4);
|
||||
int typeId = _table->_file->readByte();
|
||||
BT_Type type = BT_Type_from_id(typeId);
|
||||
if (BT_Type_size(type) == -1) throw std::runtime_error("Variable size types not supported in uniform arrays");
|
||||
// Type check omitted for brevity
|
||||
int itemOffset = index * (1 + BT_Type_size(type));
|
||||
BT_Pointer itemPointer((_pointer.address + 1 + 4) + itemOffset);
|
||||
std::vector<uint8_t> valueBuffer = encodeValue(value);
|
||||
_table->_file->setPosition(itemPointer.address);
|
||||
_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));
|
||||
// Template method implementations for BinaryTable
|
||||
template<typename T>
|
||||
void BinaryTable::set(const std::string& key, const T& value) {
|
||||
antiFreeListScope([&]() {
|
||||
auto addressTable = getAddressTable();
|
||||
int64_t keyHash = hashString(key);
|
||||
|
||||
if (addressTable.find(keyHash) != addressTable.end()) {
|
||||
throw std::runtime_error("Key already exists");
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
auto valueBuffer = encodeValue(value);
|
||||
BT_Pointer valueAddress = alloc(static_cast<int32_t>(valueBuffer.size()));
|
||||
|
||||
writeBytes(valueAddress.address(), valueBuffer);
|
||||
|
||||
addressTable[keyHash] = valueAddress;
|
||||
setAddressTable(addressTable);
|
||||
});
|
||||
}
|
||||
|
||||
inline int BT_UniformArray::size() {
|
||||
int len = length();
|
||||
if (len == 0) return 1 + 4;
|
||||
_table->_file->setPosition(_pointer.address);
|
||||
int typeId = _table->_file->readByte();
|
||||
BT_Type type = BT_Type_from_id(typeId);
|
||||
if (BT_Type_is_array(type)) {
|
||||
return 1 + 4 + len * (1 + BT_Type_size(elementType()));
|
||||
template<typename T>
|
||||
T BinaryTable::get(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::size();
|
||||
|
||||
BT_Reference valueRef(this, it->second);
|
||||
return valueRef.decodeValue<T>();
|
||||
}
|
||||
|
||||
inline BT_Type BT_UniformArray::elementType() {
|
||||
if (length() == 0) return BT_Type::INTEGER; // Default/fallback
|
||||
_table->_file->setPosition(_pointer.address + 1 + 4);
|
||||
int typeId = _table->_file->readByte();
|
||||
return BT_Type_from_id(typeId);
|
||||
template<typename T>
|
||||
BT_UniformArray<T> BinaryTable::getArray(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_UniformArray<T>(this, it->second);
|
||||
}
|
||||
|
||||
inline std::string BT_UniformArray::to_string(bool readValues) {
|
||||
std::ostringstream oss;
|
||||
int len = length();
|
||||
if (!readValues) {
|
||||
oss << "Uniform Array of length " << len;
|
||||
return oss.str();
|
||||
}
|
||||
oss << "Uniform Array: [";
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
BT_Value v = (*this)[i];
|
||||
if (std::holds_alternative<int>(v)) oss << std::get<int>(v);
|
||||
else if (std::holds_alternative<double>(v)) oss << std::get<double>(v);
|
||||
else if (std::holds_alternative<std::string>(v)) oss << '"' << std::get<std::string>(v) << '"';
|
||||
else oss << "?";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
inline std::vector<BT_Value> BT_UniformArray::fetchSublist(int start, int end) {
|
||||
int len = length();
|
||||
if (len == 0) return {};
|
||||
if (start < 0 || start > len) throw std::out_of_range("fetchSublist: start out of range");
|
||||
if (end == -1) end = len;
|
||||
if (end < start || end > len) throw std::out_of_range("fetchSublist: end out of range");
|
||||
BT_Type type = elementType();
|
||||
if (BT_Type_size(type) == -1) throw std::runtime_error("Types with variable size are not supported in uniform arrays");
|
||||
int elemSize = 1 + BT_Type_size(type);
|
||||
int bufferStart = 1 + 4 + start * elemSize;
|
||||
int bufferEnd = 1 + 4 + end * elemSize;
|
||||
int bufferSize = bufferEnd - bufferStart;
|
||||
_table->_file->setPosition(_pointer.address + bufferStart);
|
||||
std::vector<uint8_t> buffer = _table->_file->read(bufferSize);
|
||||
std::vector<BT_Value> values;
|
||||
for (int i = 0; i < (end - start); ++i) {
|
||||
int offset = i * elemSize;
|
||||
BT_Reference itemRef(_table, BT_Pointer((_pointer.address + bufferStart) + offset));
|
||||
values.push_back(itemRef.decodeValue());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// --- Free List Encoding/Decoding ---
|
||||
inline std::vector<uint8_t> encodeFreeList(const std::vector<BT_FreeListEntry>& freeList) {
|
||||
std::vector<uint8_t> buffer;
|
||||
for (const auto& entry : freeList) {
|
||||
// Pointer (8 bytes, little-endian)
|
||||
int64_t addr = entry.pointer.address;
|
||||
for (int i = 0; i < 8; ++i) buffer.push_back((addr >> (i * 8)) & 0xFF);
|
||||
// Size (4 bytes, little-endian)
|
||||
int size = entry.size;
|
||||
for (int i = 0; i < 4; ++i) buffer.push_back((size >> (i * 8)) & 0xFF);
|
||||
}
|
||||
// Entry count (4 bytes, little-endian)
|
||||
int count = static_cast<int>(freeList.size());
|
||||
for (int i = 0; i < 4; ++i) buffer.push_back((count >> (i * 8)) & 0xFF);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline std::vector<BT_FreeListEntry> decodeFreeList(const std::vector<uint8_t>& buffer) {
|
||||
std::vector<BT_FreeListEntry> freeList;
|
||||
if (buffer.size() < 4) return freeList;
|
||||
int count = 0;
|
||||
for (int i = 0; i < 4; ++i) count |= (buffer[buffer.size() - 4 + i] << (i * 8));
|
||||
if (count == 0) return freeList;
|
||||
int entrySize = 8 + 4;
|
||||
int freeListSize = count * entrySize;
|
||||
if (buffer.size() < static_cast<size_t>(freeListSize + 4)) return freeList;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int offset = i * entrySize;
|
||||
int64_t addr = 0;
|
||||
for (int j = 0; j < 8; ++j) addr |= (static_cast<int64_t>(buffer[offset + j]) << (j * 8));
|
||||
int size = 0;
|
||||
for (int j = 0; j < 4; ++j) size |= (buffer[offset + 8 + j] << (j * 8));
|
||||
freeList.emplace_back(BT_Pointer(addr), size);
|
||||
}
|
||||
return freeList;
|
||||
}
|
||||
|
||||
// Helper to print BT_Value variant
|
||||
inline std::string printBTValue(const BT_Value& v) {
|
||||
if (std::holds_alternative<int>(v)) return std::to_string(std::get<int>(v));
|
||||
if (std::holds_alternative<double>(v)) return std::to_string(std::get<double>(v));
|
||||
if (std::holds_alternative<std::string>(v)) return '"' + std::get<std::string>(v) + '"';
|
||||
if (std::holds_alternative<std::vector<int>>(v)) {
|
||||
const auto& arr = std::get<std::vector<int>>(v);
|
||||
std::ostringstream oss; oss << "[";
|
||||
for (size_t i = 0; i < arr.size(); ++i) { if (i) oss << ", "; oss << arr[i]; }
|
||||
oss << "]"; return oss.str();
|
||||
}
|
||||
if (std::holds_alternative<std::vector<double>>(v)) {
|
||||
const auto& arr = std::get<std::vector<double>>(v);
|
||||
std::ostringstream oss; oss << "[";
|
||||
for (size_t i = 0; i < arr.size(); ++i) { if (i) oss << ", "; oss << arr[i]; }
|
||||
oss << "]"; return oss.str();
|
||||
}
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
// --- Main function for testing ---
|
||||
#ifdef BINARY_TABLE_MAIN
|
||||
#include <iostream>
|
||||
#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() {
|
||||
const std::string filename = "example.bin";
|
||||
std::remove(filename.c_str());
|
||||
std::ofstream(filename).close();
|
||||
BinaryTable table(filename);
|
||||
|
||||
std::cout << "File dump:" << std::endl;
|
||||
{
|
||||
std::ifstream f(filename, std::ios::binary);
|
||||
std::vector<uint8_t> data((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||
std::cout << binaryDump(data) << std::endl;
|
||||
std::cout << "File size: " << data.size() << " bytes\n" << std::endl;
|
||||
}
|
||||
|
||||
table.set("int_array", std::vector<int>{6, 3, 9, 2, 5});
|
||||
table.set("float_array", std::vector<double>{1.5, 2.5, 3.5});
|
||||
table.set("empty", std::vector<int>{});
|
||||
|
||||
// Modify arrays
|
||||
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});
|
||||
std::cout << "int_array pointer: " << intArr._pointer.to_string() << std::endl;
|
||||
std::cout << "Readback1: " << intArr.to_string(true) << std::endl;
|
||||
} else {
|
||||
std::cout << "int_array is not a BT_UniformArray!\n";
|
||||
}
|
||||
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});
|
||||
std::cout << "float_array pointer: " << floatArr._pointer.to_string() << std::endl;
|
||||
std::cout << "Readback2: " << floatArr.to_string(true) << std::endl;
|
||||
} else {
|
||||
std::cout << "float_array is not a BT_UniformArray!\n";
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
std::cout << "\nFile dump:" << std::endl;
|
||||
{
|
||||
std::ifstream f(filename, std::ios::binary);
|
||||
std::vector<uint8_t> data((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
||||
std::cout << binaryDump(data) << std::endl;
|
||||
std::cout << "File size: " << data.size() << " bytes" << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
} // namespace bt
|
||||
Reference in New Issue
Block a user