Add fetchSublist method to BinaryTable for improved data retrieval
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
// --- BT_Type Enum ---
|
// --- BT_Type Enum ---
|
||||||
enum class BT_Type : int {
|
enum class BT_Type : int {
|
||||||
@@ -127,10 +128,11 @@ struct BT_FreeListEntry {
|
|||||||
class BT_File {
|
class BT_File {
|
||||||
public:
|
public:
|
||||||
std::fstream file;
|
std::fstream file;
|
||||||
|
mutable std::shared_mutex _rwlock;
|
||||||
BT_File(const std::string& path) {
|
BT_File(const std::string& path) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
file.open(path, std::ios::in | std::ios::out | std::ios::binary);
|
file.open(path, std::ios::in | std::ios::out | std::ios::binary);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
// Try to create the file if it doesn't exist
|
|
||||||
file.open(path, std::ios::out | std::ios::binary);
|
file.open(path, std::ios::out | std::ios::binary);
|
||||||
file.close();
|
file.close();
|
||||||
file.open(path, std::ios::in | std::ios::out | std::ios::binary);
|
file.open(path, std::ios::in | std::ios::out | std::ios::binary);
|
||||||
@@ -138,10 +140,12 @@ public:
|
|||||||
if (!file.is_open()) throw std::runtime_error("Failed to open file");
|
if (!file.is_open()) throw std::runtime_error("Failed to open file");
|
||||||
}
|
}
|
||||||
void setPosition(int64_t pos) {
|
void setPosition(int64_t pos) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
file.seekp(pos);
|
file.seekp(pos);
|
||||||
file.seekg(pos);
|
file.seekg(pos);
|
||||||
}
|
}
|
||||||
int64_t length() {
|
int64_t length() {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
auto cur = file.tellg();
|
auto cur = file.tellg();
|
||||||
file.seekg(0, std::ios::end);
|
file.seekg(0, std::ios::end);
|
||||||
int64_t len = file.tellg();
|
int64_t len = file.tellg();
|
||||||
@@ -150,20 +154,22 @@ public:
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> read(int size) {
|
std::vector<uint8_t> read(int size) {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
std::vector<uint8_t> buf(size);
|
std::vector<uint8_t> buf(size);
|
||||||
file.read(reinterpret_cast<char*>(buf.data()), size);
|
file.read(reinterpret_cast<char*>(buf.data()), size);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
void write(const std::vector<uint8_t>& buf) {
|
void write(const std::vector<uint8_t>& buf) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
file.write(reinterpret_cast<const char*>(buf.data()), buf.size());
|
file.write(reinterpret_cast<const char*>(buf.data()), buf.size());
|
||||||
}
|
}
|
||||||
int readInt(int size = 4) {
|
int readInt(int size = 4) {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
std::vector<uint8_t> buf = read(size);
|
std::vector<uint8_t> buf = read(size);
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (int i = size - 1; i >= 0; --i) {
|
for (int i = size - 1; i >= 0; --i) {
|
||||||
result = (result << 8) | buf[i];
|
result = (result << 8) | buf[i];
|
||||||
}
|
}
|
||||||
// Sign extend if MSB is set
|
|
||||||
int signBit = 1 << (size * 8 - 1);
|
int signBit = 1 << (size * 8 - 1);
|
||||||
if (result & signBit) {
|
if (result & signBit) {
|
||||||
result -= 1 << (size * 8);
|
result -= 1 << (size * 8);
|
||||||
@@ -171,6 +177,7 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
void writeInt(int value, int size = 4) {
|
void writeInt(int value, int size = 4) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
std::vector<uint8_t> buf(size);
|
std::vector<uint8_t> buf(size);
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
buf[i] = (value >> (i * 8)) & 0xFF;
|
buf[i] = (value >> (i * 8)) & 0xFF;
|
||||||
@@ -178,6 +185,7 @@ public:
|
|||||||
write(buf);
|
write(buf);
|
||||||
}
|
}
|
||||||
BT_Pointer readPointer() {
|
BT_Pointer readPointer() {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
int64_t addr = 0;
|
int64_t addr = 0;
|
||||||
std::vector<uint8_t> buf = read(8);
|
std::vector<uint8_t> buf = read(8);
|
||||||
for (int i = 7; i >= 0; --i) {
|
for (int i = 7; i >= 0; --i) {
|
||||||
@@ -186,6 +194,7 @@ public:
|
|||||||
return BT_Pointer(addr);
|
return BT_Pointer(addr);
|
||||||
}
|
}
|
||||||
void writePointer(const BT_Pointer& ptr) {
|
void writePointer(const BT_Pointer& ptr) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
int64_t addr = ptr.address;
|
int64_t addr = ptr.address;
|
||||||
std::vector<uint8_t> buf(8);
|
std::vector<uint8_t> buf(8);
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
@@ -194,33 +203,39 @@ public:
|
|||||||
write(buf);
|
write(buf);
|
||||||
}
|
}
|
||||||
float readFloat32() {
|
float readFloat32() {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
std::vector<uint8_t> buf = read(4);
|
std::vector<uint8_t> buf = read(4);
|
||||||
float val;
|
float val;
|
||||||
std::memcpy(&val, buf.data(), 4);
|
std::memcpy(&val, buf.data(), 4);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
void writeFloat32(float value) {
|
void writeFloat32(float value) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
uint8_t buf[4];
|
uint8_t buf[4];
|
||||||
std::memcpy(buf, &value, 4);
|
std::memcpy(buf, &value, 4);
|
||||||
write(std::vector<uint8_t>(buf, buf + 4));
|
write(std::vector<uint8_t>(buf, buf + 4));
|
||||||
}
|
}
|
||||||
double readFloat64() {
|
double readFloat64() {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
std::vector<uint8_t> buf = read(8);
|
std::vector<uint8_t> buf = read(8);
|
||||||
double val;
|
double val;
|
||||||
std::memcpy(&val, buf.data(), 8);
|
std::memcpy(&val, buf.data(), 8);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
void writeFloat64(double value) {
|
void writeFloat64(double value) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
uint8_t buf[8];
|
uint8_t buf[8];
|
||||||
std::memcpy(buf, &value, 8);
|
std::memcpy(buf, &value, 8);
|
||||||
write(std::vector<uint8_t>(buf, buf + 8));
|
write(std::vector<uint8_t>(buf, buf + 8));
|
||||||
}
|
}
|
||||||
uint8_t readByte() {
|
uint8_t readByte() {
|
||||||
|
std::shared_lock lock(_rwlock);
|
||||||
char c;
|
char c;
|
||||||
file.read(&c, 1);
|
file.read(&c, 1);
|
||||||
return static_cast<uint8_t>(c);
|
return static_cast<uint8_t>(c);
|
||||||
}
|
}
|
||||||
void writeByte(uint8_t b) {
|
void writeByte(uint8_t b) {
|
||||||
|
std::unique_lock lock(_rwlock);
|
||||||
char c = static_cast<char>(b);
|
char c = static_cast<char>(b);
|
||||||
file.write(&c, 1);
|
file.write(&c, 1);
|
||||||
}
|
}
|
||||||
@@ -254,6 +269,7 @@ public:
|
|||||||
int size();
|
int size();
|
||||||
BT_Type elementType();
|
BT_Type elementType();
|
||||||
std::string to_string(bool readValues = false);
|
std::string to_string(bool readValues = false);
|
||||||
|
std::vector<BT_Value> fetchSublist(int start = 0, int end = -1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- binaryDump utility ---
|
// --- binaryDump utility ---
|
||||||
@@ -489,6 +505,29 @@ inline std::string BT_UniformArray::to_string(bool readValues) {
|
|||||||
return oss.str();
|
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 ---
|
// --- Free List Encoding/Decoding ---
|
||||||
inline std::vector<uint8_t> encodeFreeList(const std::vector<BT_FreeListEntry>& freeList) {
|
inline std::vector<uint8_t> encodeFreeList(const std::vector<BT_FreeListEntry>& freeList) {
|
||||||
std::vector<uint8_t> buffer;
|
std::vector<uint8_t> buffer;
|
||||||
|
|||||||
@@ -363,6 +363,34 @@ class BT_UniformArray extends BT_Reference {
|
|||||||
addAll([value]);
|
addAll([value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<dynamic> fetchSublist([int start = 0, int end = -1]) {
|
||||||
|
|
||||||
|
// Read the full array by loading the full buffer of only whats needed
|
||||||
|
BT_Type? type = elementType;
|
||||||
|
if (type == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (type.size == -1) {
|
||||||
|
throw Exception("Types with variable size are not supported in uniform arrays. Use a non-uniform array instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
end = end == -1 ? length : end;
|
||||||
|
|
||||||
|
int bufferStart = 1 + 4 + start * (1 + type.size);
|
||||||
|
int bufferEnd = 1 + 4 + end * (1 + type.size);
|
||||||
|
int bufferSize = bufferEnd - bufferStart;
|
||||||
|
_table._file.setPositionSync(_pointer.address + bufferStart);
|
||||||
|
List<int> buffer = _table._file.readSync(bufferSize).toList();
|
||||||
|
|
||||||
|
List<dynamic> values = [];
|
||||||
|
for (int i = 0; i < (end - start); i++) {
|
||||||
|
int offset = i * (1 + type.size);
|
||||||
|
BT_Reference itemRef = BT_Reference(_table, BT_Pointer((_pointer.address + bufferStart) + offset));
|
||||||
|
values.add(itemRef.decodeValue());
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get size {
|
int get size {
|
||||||
|
|
||||||
@@ -875,6 +903,12 @@ void main() {
|
|||||||
table["int_array"].addAll([420, 69, 1337, 1738]);
|
table["int_array"].addAll([420, 69, 1337, 1738]);
|
||||||
table["float_array"].addAll([6.5, 7.5, 8.5]);
|
table["float_array"].addAll([6.5, 7.5, 8.5]);
|
||||||
|
|
||||||
|
// Test the full read method
|
||||||
|
var intArrayFull = table["int_array"].fetchSublist(0, 3);
|
||||||
|
var floatArrayFull = table["float_array"].fetchSublist(0, 2);
|
||||||
|
print("Full read int_array (0-3): $intArrayFull");
|
||||||
|
print("Full read float_array (0-2): $floatArrayFull");
|
||||||
|
|
||||||
var readback1 = table["int_array"];
|
var readback1 = table["int_array"];
|
||||||
var readback2 = table["float_array"];
|
var readback2 = table["float_array"];
|
||||||
var readback3 = table["empty"];
|
var readback3 = table["empty"];
|
||||||
|
|||||||
Reference in New Issue
Block a user