encode-shared.js 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /**
  2. * Parse a compact encode trie string into a Map structure used for encoding.
  3. *
  4. * Format per entry (ascending code points using delta encoding):
  5. * <diffBase36>[&name;][{<children>}] -- diff omitted when 0
  6. * Where diff = currentKey - previousKey - 1 (first entry stores absolute key).
  7. * `&name;` is the entity value (already wrapped); a following `{` denotes children.
  8. */
  9. export function parseEncodeTrie(serialized) {
  10. const top = new Map();
  11. const totalLength = serialized.length;
  12. let cursor = 0;
  13. let lastTopKey = -1;
  14. function readDiff() {
  15. const start = cursor;
  16. while (cursor < totalLength) {
  17. const char = serialized.charAt(cursor);
  18. if ((char < "0" || char > "9") && (char < "a" || char > "z")) {
  19. break;
  20. }
  21. cursor++;
  22. }
  23. if (cursor === start)
  24. return 0;
  25. return Number.parseInt(serialized.slice(start, cursor), 36);
  26. }
  27. function readEntity() {
  28. if (serialized[cursor] !== "&") {
  29. throw new Error(`Child entry missing value near index ${cursor}`);
  30. }
  31. // Cursor currently points at '&'
  32. const start = cursor;
  33. const end = serialized.indexOf(";", cursor + 1);
  34. if (end === -1) {
  35. throw new Error(`Unterminated entity starting at index ${start}`);
  36. }
  37. cursor = end + 1; // Move past ';'
  38. return serialized.slice(start, cursor); // Includes & ... ;
  39. }
  40. while (cursor < totalLength) {
  41. const keyDiff = readDiff();
  42. const key = lastTopKey === -1 ? keyDiff : lastTopKey + keyDiff + 1;
  43. let value;
  44. if (serialized[cursor] === "&")
  45. value = readEntity();
  46. if (serialized[cursor] === "{") {
  47. cursor++; // Skip '{'
  48. // Parse first child
  49. let diff = readDiff();
  50. let childKey = diff; // First key (lastChildKey = -1)
  51. const firstValue = readEntity();
  52. if (serialized[cursor] === "{") {
  53. throw new Error("Unexpected nested '{' beyond depth 2");
  54. }
  55. // If end of block -> single child optimization
  56. if (serialized[cursor] === "}") {
  57. top.set(key, { value, next: childKey, nextValue: firstValue });
  58. cursor++; // Skip '}'
  59. }
  60. else {
  61. const childMap = new Map();
  62. childMap.set(childKey, firstValue);
  63. let lastChildKey = childKey;
  64. while (cursor < totalLength && serialized[cursor] !== "}") {
  65. diff = readDiff();
  66. childKey = lastChildKey + diff + 1;
  67. const childValue = readEntity();
  68. if (serialized[cursor] === "{") {
  69. throw new Error("Unexpected nested '{' beyond depth 2");
  70. }
  71. childMap.set(childKey, childValue);
  72. lastChildKey = childKey;
  73. }
  74. if (serialized[cursor] !== "}") {
  75. throw new Error("Unterminated child block");
  76. }
  77. cursor++; // Skip '}'
  78. top.set(key, { value, next: childMap });
  79. }
  80. }
  81. else if (value === undefined) {
  82. throw new Error(`Malformed encode trie: missing value at index ${cursor}`);
  83. }
  84. else {
  85. top.set(key, value);
  86. }
  87. lastTopKey = key;
  88. }
  89. return top;
  90. }
  91. //# sourceMappingURL=encode-shared.js.map