/** * Element binary serialization for Implexus storage layer. * * Provides serialization and deserialization of Invercargill.Element * values to/from binary format for persistent storage. */ namespace Implexus.Storage { /** * Type codes for binary element serialization. */ internal enum ElementTypeCode { NULL = 0x00, BOOL = 0x01, INT64 = 0x02, UINT64 = 0x03, DOUBLE = 0x04, STRING = 0x05, BINARY = 0x06, ARRAY = 0x07, DICTIONARY = 0x08; /** * Determines the type code from an Element's GLib.Type. */ public static ElementTypeCode from_type(GLib.Type type) { if (type == typeof(bool)) { return BOOL; } else if (type == typeof(int64)) { return INT64; } else if (type == typeof(uint64)) { return UINT64; } else if (type == typeof(double)) { return DOUBLE; } else if (type == typeof(string)) { return STRING; } else if (type == typeof(Invercargill.BinaryData)) { return BINARY; } else if (type == typeof(Invercargill.Enumerable)) { return ARRAY; } else if (type == typeof(Invercargill.Properties)) { return DICTIONARY; } else { return NULL; } } } /** * Writes Element values to binary format. */ public class ElementWriter : Object { private Invercargill.ByteComposition _buffer; /** * Creates a new ElementWriter. */ public ElementWriter() { _buffer = new Invercargill.ByteComposition(); } /** * Writes an element to the buffer. * * @param element The element to write */ public void write_element(Invercargill.Element element) throws StorageError { // Check for null element if (element.is_null()) { write_null(); return; } // Check the element's type and serialize accordingly var type = element.type(); if (type == typeof(bool)) { _buffer.append_uint8(ElementTypeCode.BOOL); try { write_bool(element.as()); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get bool value: %s".printf(e.message)); } } else if (type == typeof(int64)) { _buffer.append_uint8(ElementTypeCode.INT64); try { int64? val = element.as(); write_int64(val == null ? 0 : (!) val); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get int64 value: %s".printf(e.message)); } } else if (type == typeof(uint64)) { _buffer.append_uint8(ElementTypeCode.UINT64); try { uint64? val = element.as(); write_uint64(val == null ? 0 : (!) val); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get uint64 value: %s".printf(e.message)); } } else if (type == typeof(double)) { _buffer.append_uint8(ElementTypeCode.DOUBLE); try { double? val = element.as(); write_double(val == null ? 0.0 : (!) val); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get double value: %s".printf(e.message)); } } else if (type == typeof(string)) { _buffer.append_uint8(ElementTypeCode.STRING); try { write_string(element.as()); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get string value: %s".printf(e.message)); } } else if (type == typeof(Invercargill.BinaryData)) { _buffer.append_uint8(ElementTypeCode.BINARY); try { write_binary(element.as()); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get binary value: %s".printf(e.message)); } } else if (type == typeof(Invercargill.Enumerable)) { _buffer.append_uint8(ElementTypeCode.ARRAY); try { write_array(element.as>()); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get array value: %s".printf(e.message)); } } else if (type == typeof(Invercargill.Properties)) { _buffer.append_uint8(ElementTypeCode.DICTIONARY); try { write_dictionary(element.as()); } catch (Invercargill.ElementError e) { throw new StorageError.CORRUPT_DATA("Failed to get dictionary value: %s".printf(e.message)); } } else { // Unknown type - serialize as null write_null(); } } /** * Writes a null value. */ public void write_null() { _buffer.append_uint8(ElementTypeCode.NULL); } /** * Writes a boolean value. */ public void write_bool(bool value) { _buffer.append_uint8(value ? (uint8) 1 : (uint8) 0); } /** * Writes an int64 value (big-endian). */ public void write_int64(int64 value) { _buffer.append_uint8((uint8) ((value >> 56) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 48) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 40) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 32) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 24) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 16) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 8) & 0xFF)); _buffer.append_uint8((uint8) (value & 0xFF)); } /** * Writes a uint64 value (big-endian). */ public void write_uint64(uint64 value) { _buffer.append_uint8((uint8) ((value >> 56) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 48) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 40) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 32) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 24) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 16) & 0xFF)); _buffer.append_uint8((uint8) ((value >> 8) & 0xFF)); _buffer.append_uint8((uint8) (value & 0xFF)); } /** * Writes a double value. */ public void write_double(double value) { int64 bits = (int64) *(uint64*) &value; write_int64(bits); } /** * Writes a length value (int64). */ public void write_length(int64 length) { write_int64(length); } /** * Writes a string value. */ public void write_string(string value) { int64 len = value.length; write_length(len); _buffer.append_string(value); } /** * Writes binary data. */ public void write_binary(Invercargill.BinaryData value) { var bytes = value.to_bytes(); int64 len = (int64) bytes.length; write_length(len); _buffer.append_bytes(bytes); } /** * Writes an array of elements. */ public void write_array(Invercargill.Enumerable collection) { uint count = collection.count(); write_length((int64) count); foreach (var item in collection) { try { write_element(item); } catch (StorageError e) { // Skip invalid elements - write null write_null(); } } } /** * Writes a dictionary of elements. */ public void write_dictionary(Invercargill.Properties props) { uint count = props.count(); write_length((int64) count); foreach (var kvp in props) { write_string(kvp.key); try { write_element(kvp.value); } catch (StorageError e) { // Write null for invalid elements write_null(); } } } /** * Gets the written data as BinaryData. * * @return The binary data */ public Invercargill.BinaryData to_binary_data() { return _buffer; } /** * Clears the buffer for reuse. */ public void clear() { _buffer.clear(); } } /** * Reads Element values from binary format. */ public class ElementReader : Object { private Invercargill.BinaryData _data; private Invercargill.ReadOnlyAddressable _buffer; private uint _position; private uint _size; /** * Creates a new ElementReader with the given data. * * @param data The binary data to read */ public ElementReader(Invercargill.BinaryData data) { _data = data; _buffer = data.to_byte_buffer(); _position = 0; _size = data.count(); } /** * Current position in the data. */ public uint position { get { return _position; } } /** * Size of the data. */ public uint size { get { return _size; } } /** * Whether there is more data to read. */ public bool has_more { get { return _position < _size; } } /** * Reads a single byte. */ private uint8 read_byte() throws StorageError { if (_position >= _size) { throw new StorageError.CORRUPT_DATA("Unexpected end of data"); } try { var byte_val = _buffer.get(_position); _position++; return byte_val; } catch (Invercargill.IndexError e) { throw new StorageError.CORRUPT_DATA("Failed to read byte: %s".printf(e.message)); } } /** * Reads bytes from the data. * * @param length Number of bytes to read * @return The bytes read */ private uint8[] read_bytes(uint length) throws StorageError { if (_position + length > _size) { throw new StorageError.CORRUPT_DATA("Unexpected end of data"); } var bytes = new uint8[length]; var slice = _data.slice(_position, _position + length); var slice_bytes = slice.to_bytes(); Memory.copy(bytes, slice_bytes.get_data(), length); _position += length; return bytes; } /** * Reads an element from the data. * * @return The element read */ public Invercargill.Element read_element() throws StorageError { if (_position >= _size) { throw new StorageError.CORRUPT_DATA("Unexpected end of data"); } var type_code = (ElementTypeCode) read_byte(); switch (type_code) { case ElementTypeCode.NULL: return new Invercargill.NullElement(); case ElementTypeCode.BOOL: return new Invercargill.NativeElement(read_bool()); case ElementTypeCode.INT64: return new Invercargill.NativeElement(read_int64()); case ElementTypeCode.UINT64: return new Invercargill.NativeElement(read_uint64()); case ElementTypeCode.DOUBLE: return new Invercargill.NativeElement(read_double()); case ElementTypeCode.STRING: return new Invercargill.NativeElement(read_string()); case ElementTypeCode.BINARY: return new Invercargill.NativeElement(read_binary()); case ElementTypeCode.ARRAY: return read_array(); case ElementTypeCode.DICTIONARY: return read_dictionary(); default: throw new StorageError.CORRUPT_DATA("Unknown type code: %d".printf((int) type_code)); } } /** * Reads a boolean value. */ public bool read_bool() throws StorageError { return read_byte() != 0; } /** * Reads an int64 value (big-endian). */ public int64 read_int64() throws StorageError { if (_position + 8 > _size) { throw new StorageError.CORRUPT_DATA("Unexpected end of data reading int64"); } try { int64 result = (int64) ( ((int64) _buffer.get(_position) << 56) | ((int64) _buffer.get(_position + 1) << 48) | ((int64) _buffer.get(_position + 2) << 40) | ((int64) _buffer.get(_position + 3) << 32) | ((int64) _buffer.get(_position + 4) << 24) | ((int64) _buffer.get(_position + 5) << 16) | ((int64) _buffer.get(_position + 6) << 8) | ((int64) _buffer.get(_position + 7)) ); _position += 8; return result; } catch (Invercargill.IndexError e) { throw new StorageError.CORRUPT_DATA("Failed to read int64: %s".printf(e.message)); } } /** * Reads a uint64 value (big-endian). */ public uint64 read_uint64() throws StorageError { if (_position + 8 > _size) { throw new StorageError.CORRUPT_DATA("Unexpected end of data reading uint64"); } try { uint64 result = (uint64) ( ((uint64) _buffer.get(_position) << 56) | ((uint64) _buffer.get(_position + 1) << 48) | ((uint64) _buffer.get(_position + 2) << 40) | ((uint64) _buffer.get(_position + 3) << 32) | ((uint64) _buffer.get(_position + 4) << 24) | ((uint64) _buffer.get(_position + 5) << 16) | ((uint64) _buffer.get(_position + 6) << 8) | ((uint64) _buffer.get(_position + 7)) ); _position += 8; return result; } catch (Invercargill.IndexError e) { throw new StorageError.CORRUPT_DATA("Failed to read uint64: %s".printf(e.message)); } } /** * Reads a double value. */ public double read_double() throws StorageError { int64 bits = read_int64(); return *(double*) &bits; } /** * Reads a length value. */ public int64 read_length() throws StorageError { return read_int64(); } /** * Reads a string value. */ public string read_string() throws StorageError { int64 len = read_length(); if (len < 0) { throw new StorageError.CORRUPT_DATA("Negative string length"); } var bytes = read_bytes((uint) len); // Create a properly null-terminated string from the bytes // We need to copy the data and add a null terminator var str_builder = new StringBuilder.sized((size_t)(len + 1)); for (int64 i = 0; i < len; i++) { str_builder.append_c((char) bytes[i]); } return str_builder.str; } /** * Reads binary data. */ public Invercargill.BinaryData read_binary() throws StorageError { int64 len = read_length(); if (len < 0) { throw new StorageError.CORRUPT_DATA("Negative binary length"); } var bytes = read_bytes((uint) len); return new Invercargill.DataStructures.ByteBuffer.from_byte_array(bytes); } /** * Reads an array of elements. */ public Invercargill.NativeElement> read_array() throws StorageError { int64 count = read_length(); if (count < 0) { throw new StorageError.CORRUPT_DATA("Negative array count"); } var array = new Invercargill.DataStructures.Vector(); for (int64 i = 0; i < count; i++) { var element = read_element(); array.add(element); } return new Invercargill.NativeElement>(array); } /** * Reads a dictionary of elements. */ public Invercargill.NativeElement read_dictionary() throws StorageError { int64 count = read_length(); if (count < 0) { throw new StorageError.CORRUPT_DATA("Negative dictionary count"); } var props = new Invercargill.DataStructures.PropertyDictionary(); for (int64 i = 0; i < count; i++) { string key = read_string(); var value = read_element(); try { props.set(key, value); } catch (Invercargill.IndexError e) { throw new StorageError.CORRUPT_DATA("Failed to set dictionary key: %s".printf(e.message)); } } return new Invercargill.NativeElement(props); } } }