智慧水务管理系统 - 精河县供水工程综合管理平台

encode-shared.ts 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /**
  2. * A node inside the encoding trie used by `encode.ts`.
  3. *
  4. * There are two physical shapes to minimize allocations and lookup cost:
  5. *
  6. * 1. Leaf node (string)
  7. * - A plain string (already in the form `"&name;"`).
  8. * - Represents a terminal match with no children.
  9. *
  10. * 2. Branch / value node (object)
  11. */
  12. export type EncodeTrieNode =
  13. | string
  14. | {
  15. /**
  16. * Entity value for the current code point sequence (wrapped: `&...;`).
  17. * Present when the path to this node itself is a valid named entity.
  18. */
  19. value: string | undefined;
  20. /** If a number, the next code unit of the only next character. */
  21. next: number | Map<number, EncodeTrieNode>;
  22. /** If next is a number, `nextValue` contains the entity value. */
  23. nextValue?: string;
  24. };
  25. /**
  26. * Parse a compact encode trie string into a Map structure used for encoding.
  27. *
  28. * Format per entry (ascending code points using delta encoding):
  29. * <diffBase36>[&name;][{<children>}] -- diff omitted when 0
  30. * Where diff = currentKey - previousKey - 1 (first entry stores absolute key).
  31. * `&name;` is the entity value (already wrapped); a following `{` denotes children.
  32. */
  33. export function parseEncodeTrie(
  34. serialized: string,
  35. ): Map<number, EncodeTrieNode> {
  36. const top = new Map<number, EncodeTrieNode>();
  37. const totalLength = serialized.length;
  38. let cursor = 0;
  39. let lastTopKey = -1;
  40. function readDiff(): number {
  41. const start = cursor;
  42. while (cursor < totalLength) {
  43. const char = serialized.charAt(cursor);
  44. if ((char < "0" || char > "9") && (char < "a" || char > "z")) {
  45. break;
  46. }
  47. cursor++;
  48. }
  49. if (cursor === start) return 0;
  50. return Number.parseInt(serialized.slice(start, cursor), 36);
  51. }
  52. function readEntity(): string {
  53. if (serialized[cursor] !== "&") {
  54. throw new Error(`Child entry missing value near index ${cursor}`);
  55. }
  56. // Cursor currently points at '&'
  57. const start = cursor;
  58. const end = serialized.indexOf(";", cursor + 1);
  59. if (end === -1) {
  60. throw new Error(`Unterminated entity starting at index ${start}`);
  61. }
  62. cursor = end + 1; // Move past ';'
  63. return serialized.slice(start, cursor); // Includes & ... ;
  64. }
  65. while (cursor < totalLength) {
  66. const keyDiff = readDiff();
  67. const key = lastTopKey === -1 ? keyDiff : lastTopKey + keyDiff + 1;
  68. let value: string | undefined;
  69. if (serialized[cursor] === "&") value = readEntity();
  70. if (serialized[cursor] === "{") {
  71. cursor++; // Skip '{'
  72. // Parse first child
  73. let diff = readDiff();
  74. let childKey = diff; // First key (lastChildKey = -1)
  75. const firstValue = readEntity();
  76. if (serialized[cursor] === "{") {
  77. throw new Error("Unexpected nested '{' beyond depth 2");
  78. }
  79. // If end of block -> single child optimization
  80. if (serialized[cursor] === "}") {
  81. top.set(key, { value, next: childKey, nextValue: firstValue });
  82. cursor++; // Skip '}'
  83. } else {
  84. const childMap = new Map<number, EncodeTrieNode>();
  85. childMap.set(childKey, firstValue);
  86. let lastChildKey = childKey;
  87. while (cursor < totalLength && serialized[cursor] !== "}") {
  88. diff = readDiff();
  89. childKey = lastChildKey + diff + 1;
  90. const childValue = readEntity();
  91. if (serialized[cursor] === "{") {
  92. throw new Error("Unexpected nested '{' beyond depth 2");
  93. }
  94. childMap.set(childKey, childValue);
  95. lastChildKey = childKey;
  96. }
  97. if (serialized[cursor] !== "}") {
  98. throw new Error("Unterminated child block");
  99. }
  100. cursor++; // Skip '}'
  101. top.set(key, { value, next: childMap });
  102. }
  103. } else if (value === undefined) {
  104. throw new Error(
  105. `Malformed encode trie: missing value at index ${cursor}`,
  106. );
  107. } else {
  108. top.set(key, value);
  109. }
  110. lastTopKey = key;
  111. }
  112. return top;
  113. }