|
15 | 15 | * limitations under the License.
|
16 | 16 | */
|
17 | 17 |
|
| 18 | +import { ByteString } from '../util/byte_string'; |
| 19 | + |
| 20 | +/** These constants are taken from the backend. */ |
| 21 | +const MIN_SURROGATE = '\uD800'; |
| 22 | +const MAX_SURROGATE = '\uDBFF'; |
| 23 | + |
| 24 | +const ESCAPE1 = 0x00; |
| 25 | +const NULL_BYTE = 0xff; // Combined with ESCAPE1 |
| 26 | +const SEPARATOR = 0x01; // Combined with ESCAPE1 |
| 27 | + |
| 28 | +const ESCAPE2 = 0xff; |
| 29 | +const INFINITY = 0xff; // Combined with ESCAPE2 |
| 30 | +const FF_BYTE = 0x00; // Combined with ESCAPE2 |
| 31 | + |
18 | 32 | const LONG_SIZE = 64;
|
19 | 33 | const BYTE_SIZE = 8;
|
20 | 34 |
|
@@ -96,6 +110,70 @@ export class OrderedCodeWriter {
|
96 | 110 | buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
|
97 | 111 | position = 0;
|
98 | 112 |
|
| 113 | + /** |
| 114 | + * Writes utf8 bytes into this byte sequence, ascending. |
| 115 | + * |
| 116 | + * This is a more efficent version of writeBytesAscending(str.getBytes(UTF_8)); |
| 117 | + */ |
| 118 | + writeUtf8Ascending(sequence: string): void { |
| 119 | + const utf16Length = sequence.length; |
| 120 | + let i = 0; |
| 121 | + while (i < utf16Length) { |
| 122 | + const char = sequence[i]; |
| 123 | + const charCode = sequence.charCodeAt(i); |
| 124 | + if (charCode < 0x80) { |
| 125 | + this.writeByteAscending(charCode); |
| 126 | + } else if (charCode < 0x800) { |
| 127 | + this.writeByteAscending((0xf >> 6) | (charCode >>> 6)); |
| 128 | + this.writeByteAscending(0x80 | (0x3f & charCode)); |
| 129 | + } else if (char < MIN_SURROGATE || MAX_SURROGATE < char) { |
| 130 | + this.writeByteAscending((0xf >> 5) | (charCode >>> 12)); |
| 131 | + this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6))); |
| 132 | + this.writeByteAscending(0x80 | (0x3f & charCode)); |
| 133 | + } else { |
| 134 | + ++i; // Skip the low surrogate. |
| 135 | + this.writeByteAscending((0xf >> 4) | (charCode >>> 18)); |
| 136 | + this.writeByteAscending(0x80 | (0x3f & (charCode >>> 12))); |
| 137 | + this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6))); |
| 138 | + this.writeByteAscending(0x80 | (0x3f & charCode)); |
| 139 | + } |
| 140 | + ++i; |
| 141 | + } |
| 142 | + this.writeSeparatorAscending(); |
| 143 | + } |
| 144 | + |
| 145 | + /** |
| 146 | + * Writes utf8 bytes into this byte sequence, descending. |
| 147 | + * |
| 148 | + * This is a more efficent version of writeBytesDescending(str.getBytes(UTF_8)); |
| 149 | + */ |
| 150 | + writeUtf8Descending(sequence: string): void { |
| 151 | + const utf16Length = sequence.length; |
| 152 | + let i = 0; |
| 153 | + while (i < utf16Length) { |
| 154 | + const char = sequence[i]; |
| 155 | + const charCode = sequence.charCodeAt(i); |
| 156 | + if (charCode < 0x80) { |
| 157 | + this.writeByteDescending(charCode); |
| 158 | + } else if (charCode < 0x800) { |
| 159 | + this.writeByteDescending((0xf >> 6) | (charCode >>> 6)); |
| 160 | + this.writeByteDescending(0x80 | (0x3f & charCode)); |
| 161 | + } else if (char < MIN_SURROGATE || MAX_SURROGATE < char) { |
| 162 | + this.writeByteDescending((0xf >> 5) | (charCode >>> 12)); |
| 163 | + this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6))); |
| 164 | + this.writeByteDescending(0x80 | (0x3f & charCode)); |
| 165 | + } else { |
| 166 | + ++i; // Skip the low surrogate. |
| 167 | + this.writeByteDescending((0xf >> 4) | (charCode >>> 18)); |
| 168 | + this.writeByteDescending(0x80 | (0x3f & (charCode >>> 12))); |
| 169 | + this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6))); |
| 170 | + this.writeByteDescending(0x80 | (0x3f & charCode)); |
| 171 | + } |
| 172 | + ++i; |
| 173 | + } |
| 174 | + this.writeSeparatorDescending(); |
| 175 | + } |
| 176 | + |
99 | 177 | writeNumberAscending(val: number): void {
|
100 | 178 | const value = this.toOrderedBits(val);
|
101 | 179 | const len = unsignedNumLength(value);
|
@@ -143,6 +221,56 @@ export class OrderedCodeWriter {
|
143 | 221 | return this.buffer.slice(0, this.position);
|
144 | 222 | }
|
145 | 223 |
|
| 224 | + /** |
| 225 | + * Writes a single byte ascending to the buffer, doing proper escaping as described in [ ]. |
| 226 | + */ |
| 227 | + private writeByteAscending(b: number): void { |
| 228 | + if (b === ESCAPE1) { |
| 229 | + this.writeEscapedByteAscending(ESCAPE1); |
| 230 | + this.writeEscapedByteAscending(NULL_BYTE); |
| 231 | + } else if (b === ESCAPE2) { |
| 232 | + this.writeEscapedByteAscending(ESCAPE2); |
| 233 | + this.writeEscapedByteAscending(FF_BYTE); |
| 234 | + } else { |
| 235 | + this.writeEscapedByteAscending(b); |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + /** |
| 240 | + * Writes a single byte descending to the buffer, doing proper escaping as described in [ ]. |
| 241 | + */ |
| 242 | + private writeByteDescending(b: number): void { |
| 243 | + if (b === ESCAPE1) { |
| 244 | + this.writeEscapedByteDescending(ESCAPE1); |
| 245 | + this.writeEscapedByteDescending(NULL_BYTE); |
| 246 | + } else if (b === ESCAPE2) { |
| 247 | + this.writeEscapedByteDescending(ESCAPE2); |
| 248 | + this.writeEscapedByteDescending(FF_BYTE); |
| 249 | + } else { |
| 250 | + this.writeEscapedByteDescending(b); |
| 251 | + } |
| 252 | + } |
| 253 | + |
| 254 | + private writeSeparatorAscending(): void { |
| 255 | + this.writeEscapedByteAscending(ESCAPE1); |
| 256 | + this.writeEscapedByteAscending(SEPARATOR); |
| 257 | + } |
| 258 | + |
| 259 | + private writeSeparatorDescending(): void { |
| 260 | + this.writeEscapedByteDescending(ESCAPE1); |
| 261 | + this.writeEscapedByteDescending(SEPARATOR); |
| 262 | + } |
| 263 | + |
| 264 | + private writeEscapedByteAscending(b: number): void { |
| 265 | + this.ensureAvailable(1); |
| 266 | + this.buffer[this.position++] = b; |
| 267 | + } |
| 268 | + |
| 269 | + private writeEscapedByteDescending(b: number): void { |
| 270 | + this.ensureAvailable(1); |
| 271 | + this.buffer[this.position++] = ~b; |
| 272 | + } |
| 273 | + |
146 | 274 | private ensureAvailable(bytes: number): void {
|
147 | 275 | const minCapacity = bytes + this.position;
|
148 | 276 | if (minCapacity <= this.buffer.length) {
|
|
0 commit comments