diff --git a/cpp/binary_table.h b/cpp/binary_table.h index b240abe..b8e60d0 100644 --- a/cpp/binary_table.h +++ b/cpp/binary_table.h @@ -11,6 +11,7 @@ #include #include #include +#include // --- BT_Type Enum --- enum class BT_Type : int { @@ -127,10 +128,11 @@ struct BT_FreeListEntry { 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()) { - // Try to create the file if it doesn't exist file.open(path, std::ios::out | std::ios::binary); file.close(); 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"); } 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(); @@ -150,20 +154,22 @@ public: return len; } std::vector read(int size) { + std::shared_lock lock(_rwlock); std::vector buf(size); file.read(reinterpret_cast(buf.data()), size); return buf; } void write(const std::vector& buf) { + std::unique_lock lock(_rwlock); file.write(reinterpret_cast(buf.data()), buf.size()); } int readInt(int size = 4) { + std::shared_lock lock(_rwlock); std::vector buf = read(size); int result = 0; for (int i = size - 1; i >= 0; --i) { result = (result << 8) | buf[i]; } - // Sign extend if MSB is set int signBit = 1 << (size * 8 - 1); if (result & signBit) { result -= 1 << (size * 8); @@ -171,6 +177,7 @@ public: return result; } void writeInt(int value, int size = 4) { + std::unique_lock lock(_rwlock); std::vector buf(size); for (int i = 0; i < size; ++i) { buf[i] = (value >> (i * 8)) & 0xFF; @@ -178,6 +185,7 @@ public: write(buf); } BT_Pointer readPointer() { + std::shared_lock lock(_rwlock); int64_t addr = 0; std::vector buf = read(8); for (int i = 7; i >= 0; --i) { @@ -186,6 +194,7 @@ public: return BT_Pointer(addr); } void writePointer(const BT_Pointer& ptr) { + std::unique_lock lock(_rwlock); int64_t addr = ptr.address; std::vector buf(8); for (int i = 0; i < 8; ++i) { @@ -194,33 +203,39 @@ public: write(buf); } float readFloat32() { + std::shared_lock lock(_rwlock); std::vector 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(buf, buf + 4)); } double readFloat64() { + std::shared_lock lock(_rwlock); std::vector 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(buf, buf + 8)); } uint8_t readByte() { + std::shared_lock lock(_rwlock); char c; file.read(&c, 1); return static_cast(c); } void writeByte(uint8_t b) { + std::unique_lock lock(_rwlock); char c = static_cast(b); file.write(&c, 1); } @@ -254,6 +269,7 @@ public: int size(); BT_Type elementType(); std::string to_string(bool readValues = false); + std::vector fetchSublist(int start = 0, int end = -1); }; // --- binaryDump utility --- @@ -489,6 +505,29 @@ inline std::string BT_UniformArray::to_string(bool readValues) { return oss.str(); } +inline std::vector 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 buffer = _table->_file->read(bufferSize); + std::vector 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 encodeFreeList(const std::vector& freeList) { std::vector buffer; diff --git a/dart/lib/binary_table.dart b/dart/lib/binary_table.dart index 81c55e4..778fe5f 100644 --- a/dart/lib/binary_table.dart +++ b/dart/lib/binary_table.dart @@ -363,6 +363,34 @@ class BT_UniformArray extends BT_Reference { addAll([value]); } + List 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 buffer = _table._file.readSync(bufferSize).toList(); + + List 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 int get size { @@ -875,6 +903,12 @@ void main() { table["int_array"].addAll([420, 69, 1337, 1738]); 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 readback2 = table["float_array"]; var readback3 = table["empty"];