292 lines
9.0 KiB
C++
292 lines
9.0 KiB
C++
/*
|
||
|
||
/$$$$$$ /$$ /$$ /$$$$$$$ /$$$$$$$$ /$$ /$$ /$$$$$ /$$$$$$ /$$ /$$ /$$$$$$$$ /$$$$$$$$
|
||
|_ $$_/| $$$ /$$$| $$__ $$| $$_____/| $$$ | $$ |__ $$|_ $$_/ | $$$ | $$| $$_____/|__ $$__/
|
||
| $$ | $$$$ /$$$$| $$ \ $$| $$ | $$$$| $$ | $$ | $$ | $$$$| $$| $$ | $$
|
||
| $$ | $$ $$/$$ $$| $$$$$$$ | $$$$$ | $$ $$ $$ | $$ | $$ | $$ $$ $$| $$$$$ | $$
|
||
| $$ | $$ $$$| $$| $$__ $$| $$__/ | $$ $$$$ /$$ | $$ | $$ | $$ $$$$| $$__/ | $$
|
||
| $$ | $$\ $ | $$| $$ \ $$| $$ | $$\ $$$| $$ | $$ | $$ | $$\ $$$| $$ | $$
|
||
/$$$$$$| $$ \/ | $$| $$$$$$$/| $$$$$$$$| $$ \ $$| $$$$$$/ /$$$$$$ /$$| $$ \ $$| $$$$$$$$ | $$
|
||
|______/|__/ |__/|_______/ |________/|__/ \__/ \______/ |______/|__/|__/ \__/|________/ |__/
|
||
|
||
<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,
|
||
FLOAT = 3,
|
||
STRING = 4,
|
||
INTEGER_ARRAY = 5,
|
||
FLOAT_ARRAY = 6
|
||
};
|
||
|
||
// 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;
|
||
case BT_Type::FLOAT: return 4;
|
||
case BT_Type::STRING: return -1;
|
||
case BT_Type::INTEGER_ARRAY: return -1;
|
||
case BT_Type::FLOAT_ARRAY: return -1;
|
||
}
|
||
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");
|
||
}
|
||
}
|
||
|
||
// Pointer class
|
||
class BT_Pointer {
|
||
private:
|
||
int64_t address_;
|
||
|
||
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_;
|
||
}
|
||
|
||
bool operator!=(const BT_Pointer& other) const {
|
||
return !(*this == other);
|
||
}
|
||
};
|
||
|
||
// Null pointer constant
|
||
const BT_Pointer BT_Null{-1};
|
||
|
||
// Free list entry
|
||
struct BT_FreeListEntry {
|
||
BT_Pointer pointer;
|
||
int32_t size;
|
||
|
||
BT_FreeListEntry(BT_Pointer ptr, int32_t sz) : pointer(ptr), size(sz) {}
|
||
};
|
||
|
||
// 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);
|
||
|
||
// 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 {
|
||
protected:
|
||
BinaryTable* table_;
|
||
BT_Pointer pointer_;
|
||
|
||
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_; }
|
||
};
|
||
|
||
// Uniform array class template
|
||
template<typename T>
|
||
class BT_UniformArray : public BT_Reference {
|
||
public:
|
||
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);
|
||
};
|
||
|
||
// 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:
|
||
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);
|
||
};
|
||
|
||
// 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>>();
|
||
|
||
// 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");
|
||
}
|
||
|
||
auto valueBuffer = encodeValue(value);
|
||
BT_Pointer valueAddress = alloc(static_cast<int32_t>(valueBuffer.size()));
|
||
|
||
writeBytes(valueAddress.address(), valueBuffer);
|
||
|
||
addressTable[keyHash] = valueAddress;
|
||
setAddressTable(addressTable);
|
||
});
|
||
}
|
||
|
||
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");
|
||
}
|
||
|
||
BT_Reference valueRef(this, it->second);
|
||
return valueRef.decodeValue<T>();
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
} // namespace bt
|