ElementSerializer.vala 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /**
  2. * Element binary serialization for Implexus storage layer.
  3. *
  4. * Provides serialization and deserialization of Invercargill.Element
  5. * values to/from binary format for persistent storage.
  6. */
  7. namespace Implexus.Storage {
  8. /**
  9. * Type codes for binary element serialization.
  10. */
  11. internal enum ElementTypeCode {
  12. NULL = 0x00,
  13. BOOL = 0x01,
  14. INT64 = 0x02,
  15. UINT64 = 0x03,
  16. DOUBLE = 0x04,
  17. STRING = 0x05,
  18. BINARY = 0x06,
  19. ARRAY = 0x07,
  20. DICTIONARY = 0x08;
  21. /**
  22. * Determines the type code from an Element's GLib.Type.
  23. */
  24. public static ElementTypeCode from_type(GLib.Type type) {
  25. if (type == typeof(bool)) {
  26. return BOOL;
  27. } else if (type == typeof(int64)) {
  28. return INT64;
  29. } else if (type == typeof(uint64)) {
  30. return UINT64;
  31. } else if (type == typeof(double)) {
  32. return DOUBLE;
  33. } else if (type == typeof(string)) {
  34. return STRING;
  35. } else if (type == typeof(Invercargill.BinaryData)) {
  36. return BINARY;
  37. } else if (type == typeof(Invercargill.Enumerable<Invercargill.Element>)) {
  38. return ARRAY;
  39. } else if (type == typeof(Invercargill.Properties)) {
  40. return DICTIONARY;
  41. } else {
  42. return NULL;
  43. }
  44. }
  45. }
  46. /**
  47. * Writes Element values to binary format.
  48. */
  49. public class ElementWriter : Object {
  50. private Invercargill.ByteComposition _buffer;
  51. /**
  52. * Creates a new ElementWriter.
  53. */
  54. public ElementWriter() {
  55. _buffer = new Invercargill.ByteComposition();
  56. }
  57. /**
  58. * Writes an element to the buffer.
  59. *
  60. * @param element The element to write
  61. */
  62. public void write_element(Invercargill.Element element) throws StorageError {
  63. // Check for null element
  64. if (element.is_null()) {
  65. write_null();
  66. return;
  67. }
  68. // Check the element's type and serialize accordingly
  69. var type = element.type();
  70. if (type == typeof(bool)) {
  71. _buffer.append_uint8(ElementTypeCode.BOOL);
  72. try {
  73. write_bool(element.as<bool?>());
  74. } catch (Invercargill.ElementError e) {
  75. throw new StorageError.CORRUPT_DATA("Failed to get bool value: %s".printf(e.message));
  76. }
  77. } else if (type == typeof(int64)) {
  78. _buffer.append_uint8(ElementTypeCode.INT64);
  79. try {
  80. int64? val = element.as<int64?>();
  81. write_int64(val == null ? 0 : (!) val);
  82. } catch (Invercargill.ElementError e) {
  83. throw new StorageError.CORRUPT_DATA("Failed to get int64 value: %s".printf(e.message));
  84. }
  85. } else if (type == typeof(uint64)) {
  86. _buffer.append_uint8(ElementTypeCode.UINT64);
  87. try {
  88. uint64? val = element.as<uint64?>();
  89. write_uint64(val == null ? 0 : (!) val);
  90. } catch (Invercargill.ElementError e) {
  91. throw new StorageError.CORRUPT_DATA("Failed to get uint64 value: %s".printf(e.message));
  92. }
  93. } else if (type == typeof(double)) {
  94. _buffer.append_uint8(ElementTypeCode.DOUBLE);
  95. try {
  96. double? val = element.as<double?>();
  97. write_double(val == null ? 0.0 : (!) val);
  98. } catch (Invercargill.ElementError e) {
  99. throw new StorageError.CORRUPT_DATA("Failed to get double value: %s".printf(e.message));
  100. }
  101. } else if (type == typeof(string)) {
  102. _buffer.append_uint8(ElementTypeCode.STRING);
  103. try {
  104. write_string(element.as<string>());
  105. } catch (Invercargill.ElementError e) {
  106. throw new StorageError.CORRUPT_DATA("Failed to get string value: %s".printf(e.message));
  107. }
  108. } else if (type == typeof(Invercargill.BinaryData)) {
  109. _buffer.append_uint8(ElementTypeCode.BINARY);
  110. try {
  111. write_binary(element.as<Invercargill.BinaryData>());
  112. } catch (Invercargill.ElementError e) {
  113. throw new StorageError.CORRUPT_DATA("Failed to get binary value: %s".printf(e.message));
  114. }
  115. } else if (type == typeof(Invercargill.Enumerable<Invercargill.Element>)) {
  116. _buffer.append_uint8(ElementTypeCode.ARRAY);
  117. try {
  118. write_array(element.as<Invercargill.Enumerable<Invercargill.Element>>());
  119. } catch (Invercargill.ElementError e) {
  120. throw new StorageError.CORRUPT_DATA("Failed to get array value: %s".printf(e.message));
  121. }
  122. } else if (type == typeof(Invercargill.Properties)) {
  123. _buffer.append_uint8(ElementTypeCode.DICTIONARY);
  124. try {
  125. write_dictionary(element.as<Invercargill.Properties>());
  126. } catch (Invercargill.ElementError e) {
  127. throw new StorageError.CORRUPT_DATA("Failed to get dictionary value: %s".printf(e.message));
  128. }
  129. } else {
  130. // Unknown type - serialize as null
  131. write_null();
  132. }
  133. }
  134. /**
  135. * Writes a null value.
  136. */
  137. public void write_null() {
  138. _buffer.append_uint8(ElementTypeCode.NULL);
  139. }
  140. /**
  141. * Writes a boolean value.
  142. */
  143. public void write_bool(bool value) {
  144. _buffer.append_uint8(value ? (uint8) 1 : (uint8) 0);
  145. }
  146. /**
  147. * Writes an int64 value (big-endian).
  148. */
  149. public void write_int64(int64 value) {
  150. _buffer.append_uint8((uint8) ((value >> 56) & 0xFF));
  151. _buffer.append_uint8((uint8) ((value >> 48) & 0xFF));
  152. _buffer.append_uint8((uint8) ((value >> 40) & 0xFF));
  153. _buffer.append_uint8((uint8) ((value >> 32) & 0xFF));
  154. _buffer.append_uint8((uint8) ((value >> 24) & 0xFF));
  155. _buffer.append_uint8((uint8) ((value >> 16) & 0xFF));
  156. _buffer.append_uint8((uint8) ((value >> 8) & 0xFF));
  157. _buffer.append_uint8((uint8) (value & 0xFF));
  158. }
  159. /**
  160. * Writes a uint64 value (big-endian).
  161. */
  162. public void write_uint64(uint64 value) {
  163. _buffer.append_uint8((uint8) ((value >> 56) & 0xFF));
  164. _buffer.append_uint8((uint8) ((value >> 48) & 0xFF));
  165. _buffer.append_uint8((uint8) ((value >> 40) & 0xFF));
  166. _buffer.append_uint8((uint8) ((value >> 32) & 0xFF));
  167. _buffer.append_uint8((uint8) ((value >> 24) & 0xFF));
  168. _buffer.append_uint8((uint8) ((value >> 16) & 0xFF));
  169. _buffer.append_uint8((uint8) ((value >> 8) & 0xFF));
  170. _buffer.append_uint8((uint8) (value & 0xFF));
  171. }
  172. /**
  173. * Writes a double value.
  174. */
  175. public void write_double(double value) {
  176. int64 bits = (int64) *(uint64*) &value;
  177. write_int64(bits);
  178. }
  179. /**
  180. * Writes a length value (int64).
  181. */
  182. public void write_length(int64 length) {
  183. write_int64(length);
  184. }
  185. /**
  186. * Writes a string value.
  187. */
  188. public void write_string(string value) {
  189. int64 len = value.length;
  190. write_length(len);
  191. _buffer.append_string(value);
  192. }
  193. /**
  194. * Writes binary data.
  195. */
  196. public void write_binary(Invercargill.BinaryData value) {
  197. var bytes = value.to_bytes();
  198. int64 len = (int64) bytes.length;
  199. write_length(len);
  200. _buffer.append_bytes(bytes);
  201. }
  202. /**
  203. * Writes an array of elements.
  204. */
  205. public void write_array(Invercargill.Enumerable<Invercargill.Element> collection) {
  206. uint count = collection.count();
  207. write_length((int64) count);
  208. foreach (var item in collection) {
  209. try {
  210. write_element(item);
  211. } catch (StorageError e) {
  212. // Skip invalid elements - write null
  213. write_null();
  214. }
  215. }
  216. }
  217. /**
  218. * Writes a dictionary of elements.
  219. */
  220. public void write_dictionary(Invercargill.Properties props) {
  221. uint count = props.count();
  222. write_length((int64) count);
  223. foreach (var kvp in props) {
  224. write_string(kvp.key);
  225. try {
  226. write_element(kvp.value);
  227. } catch (StorageError e) {
  228. // Write null for invalid elements
  229. write_null();
  230. }
  231. }
  232. }
  233. /**
  234. * Gets the written data as BinaryData.
  235. *
  236. * @return The binary data
  237. */
  238. public Invercargill.BinaryData to_binary_data() {
  239. return _buffer;
  240. }
  241. /**
  242. * Clears the buffer for reuse.
  243. */
  244. public void clear() {
  245. _buffer.clear();
  246. }
  247. }
  248. /**
  249. * Reads Element values from binary format.
  250. */
  251. public class ElementReader : Object {
  252. private Invercargill.BinaryData _data;
  253. private Invercargill.ReadOnlyAddressable<uint8> _buffer;
  254. private uint _position;
  255. private uint _size;
  256. /**
  257. * Creates a new ElementReader with the given data.
  258. *
  259. * @param data The binary data to read
  260. */
  261. public ElementReader(Invercargill.BinaryData data) {
  262. _data = data;
  263. _buffer = data.to_byte_buffer();
  264. _position = 0;
  265. _size = data.count();
  266. }
  267. /**
  268. * Current position in the data.
  269. */
  270. public uint position {
  271. get { return _position; }
  272. }
  273. /**
  274. * Size of the data.
  275. */
  276. public uint size {
  277. get { return _size; }
  278. }
  279. /**
  280. * Whether there is more data to read.
  281. */
  282. public bool has_more {
  283. get { return _position < _size; }
  284. }
  285. /**
  286. * Reads a single byte.
  287. */
  288. private uint8 read_byte() throws StorageError {
  289. if (_position >= _size) {
  290. throw new StorageError.CORRUPT_DATA("Unexpected end of data");
  291. }
  292. try {
  293. var byte_val = _buffer.get(_position);
  294. _position++;
  295. return byte_val;
  296. } catch (Invercargill.IndexError e) {
  297. throw new StorageError.CORRUPT_DATA("Failed to read byte: %s".printf(e.message));
  298. }
  299. }
  300. /**
  301. * Reads bytes from the data.
  302. *
  303. * @param length Number of bytes to read
  304. * @return The bytes read
  305. */
  306. private uint8[] read_bytes(uint length) throws StorageError {
  307. if (_position + length > _size) {
  308. throw new StorageError.CORRUPT_DATA("Unexpected end of data");
  309. }
  310. var bytes = new uint8[length];
  311. var slice = _data.slice(_position, _position + length);
  312. var slice_bytes = slice.to_bytes();
  313. Memory.copy(bytes, slice_bytes.get_data(), length);
  314. _position += length;
  315. return bytes;
  316. }
  317. /**
  318. * Reads an element from the data.
  319. *
  320. * @return The element read
  321. */
  322. public Invercargill.Element read_element() throws StorageError {
  323. if (_position >= _size) {
  324. throw new StorageError.CORRUPT_DATA("Unexpected end of data");
  325. }
  326. var type_code = (ElementTypeCode) read_byte();
  327. switch (type_code) {
  328. case ElementTypeCode.NULL:
  329. return new Invercargill.NullElement();
  330. case ElementTypeCode.BOOL:
  331. return new Invercargill.NativeElement<bool?>(read_bool());
  332. case ElementTypeCode.INT64:
  333. return new Invercargill.NativeElement<int64?>(read_int64());
  334. case ElementTypeCode.UINT64:
  335. return new Invercargill.NativeElement<uint64?>(read_uint64());
  336. case ElementTypeCode.DOUBLE:
  337. return new Invercargill.NativeElement<double?>(read_double());
  338. case ElementTypeCode.STRING:
  339. return new Invercargill.NativeElement<string>(read_string());
  340. case ElementTypeCode.BINARY:
  341. return new Invercargill.NativeElement<Invercargill.BinaryData>(read_binary());
  342. case ElementTypeCode.ARRAY:
  343. return read_array();
  344. case ElementTypeCode.DICTIONARY:
  345. return read_dictionary();
  346. default:
  347. throw new StorageError.CORRUPT_DATA("Unknown type code: %d".printf((int) type_code));
  348. }
  349. }
  350. /**
  351. * Reads a boolean value.
  352. */
  353. public bool read_bool() throws StorageError {
  354. return read_byte() != 0;
  355. }
  356. /**
  357. * Reads an int64 value (big-endian).
  358. */
  359. public int64 read_int64() throws StorageError {
  360. if (_position + 8 > _size) {
  361. throw new StorageError.CORRUPT_DATA("Unexpected end of data reading int64");
  362. }
  363. try {
  364. int64 result = (int64) (
  365. ((int64) _buffer.get(_position) << 56) |
  366. ((int64) _buffer.get(_position + 1) << 48) |
  367. ((int64) _buffer.get(_position + 2) << 40) |
  368. ((int64) _buffer.get(_position + 3) << 32) |
  369. ((int64) _buffer.get(_position + 4) << 24) |
  370. ((int64) _buffer.get(_position + 5) << 16) |
  371. ((int64) _buffer.get(_position + 6) << 8) |
  372. ((int64) _buffer.get(_position + 7))
  373. );
  374. _position += 8;
  375. return result;
  376. } catch (Invercargill.IndexError e) {
  377. throw new StorageError.CORRUPT_DATA("Failed to read int64: %s".printf(e.message));
  378. }
  379. }
  380. /**
  381. * Reads a uint64 value (big-endian).
  382. */
  383. public uint64 read_uint64() throws StorageError {
  384. if (_position + 8 > _size) {
  385. throw new StorageError.CORRUPT_DATA("Unexpected end of data reading uint64");
  386. }
  387. try {
  388. uint64 result = (uint64) (
  389. ((uint64) _buffer.get(_position) << 56) |
  390. ((uint64) _buffer.get(_position + 1) << 48) |
  391. ((uint64) _buffer.get(_position + 2) << 40) |
  392. ((uint64) _buffer.get(_position + 3) << 32) |
  393. ((uint64) _buffer.get(_position + 4) << 24) |
  394. ((uint64) _buffer.get(_position + 5) << 16) |
  395. ((uint64) _buffer.get(_position + 6) << 8) |
  396. ((uint64) _buffer.get(_position + 7))
  397. );
  398. _position += 8;
  399. return result;
  400. } catch (Invercargill.IndexError e) {
  401. throw new StorageError.CORRUPT_DATA("Failed to read uint64: %s".printf(e.message));
  402. }
  403. }
  404. /**
  405. * Reads a double value.
  406. */
  407. public double read_double() throws StorageError {
  408. int64 bits = read_int64();
  409. return *(double*) &bits;
  410. }
  411. /**
  412. * Reads a length value.
  413. */
  414. public int64 read_length() throws StorageError {
  415. return read_int64();
  416. }
  417. /**
  418. * Reads a string value.
  419. */
  420. public string read_string() throws StorageError {
  421. int64 len = read_length();
  422. if (len < 0) {
  423. throw new StorageError.CORRUPT_DATA("Negative string length");
  424. }
  425. var bytes = read_bytes((uint) len);
  426. // Create a properly null-terminated string from the bytes
  427. // We need to copy the data and add a null terminator
  428. var str_builder = new StringBuilder.sized((size_t)(len + 1));
  429. for (int64 i = 0; i < len; i++) {
  430. str_builder.append_c((char) bytes[i]);
  431. }
  432. return str_builder.str;
  433. }
  434. /**
  435. * Reads binary data.
  436. */
  437. public Invercargill.BinaryData read_binary() throws StorageError {
  438. int64 len = read_length();
  439. if (len < 0) {
  440. throw new StorageError.CORRUPT_DATA("Negative binary length");
  441. }
  442. var bytes = read_bytes((uint) len);
  443. return new Invercargill.DataStructures.ByteBuffer.from_byte_array(bytes);
  444. }
  445. /**
  446. * Reads an array of elements.
  447. */
  448. public Invercargill.NativeElement<Invercargill.Enumerable<Invercargill.Element>> read_array() throws StorageError {
  449. int64 count = read_length();
  450. if (count < 0) {
  451. throw new StorageError.CORRUPT_DATA("Negative array count");
  452. }
  453. var array = new Invercargill.DataStructures.Vector<Invercargill.Element>();
  454. for (int64 i = 0; i < count; i++) {
  455. var element = read_element();
  456. array.add(element);
  457. }
  458. return new Invercargill.NativeElement<Invercargill.Enumerable<Invercargill.Element>>(array);
  459. }
  460. /**
  461. * Reads a dictionary of elements.
  462. */
  463. public Invercargill.NativeElement<Invercargill.Properties> read_dictionary() throws StorageError {
  464. int64 count = read_length();
  465. if (count < 0) {
  466. throw new StorageError.CORRUPT_DATA("Negative dictionary count");
  467. }
  468. var props = new Invercargill.DataStructures.PropertyDictionary();
  469. for (int64 i = 0; i < count; i++) {
  470. string key = read_string();
  471. var value = read_element();
  472. try {
  473. props.set(key, value);
  474. } catch (Invercargill.IndexError e) {
  475. throw new StorageError.CORRUPT_DATA("Failed to set dictionary key: %s".printf(e.message));
  476. }
  477. }
  478. return new Invercargill.NativeElement<Invercargill.Properties>(props);
  479. }
  480. }
  481. }