1/* 2 * Copyright (c) 2024 Republic of Reinvention BV <[email protected]>. All Rights Reserved. 3 * This file is part of project "cinematek-filmout-site" and can not be copied and/or distributed without 4 * the express permission and written consent of the legal responsible of Republic of Reinvention BV. 5 */ 6 7/** 8 * Created by bram on 7/28/15. 9 */ 10base.plugin("base.core.Bbor62", ["constants.base.core", function (BaseConstants) 11{ 12 // ----- ByteStream.java ----- 13 class ByteStream 14 { 15 constructor() 16 { 17 // use Uint8Array instead of byte[] for more efficient byte storage 18 this.buffer = new Uint8Array(8); 19 this.readPos = 0; 20 this.readBitPos = 0; 21 this.writePos = 0; 22 this.writeBitPos = 0; 23 this.writeBuf = 0; 24 } 25 26 read(numBits) 27 { 28 if (numBits <= 0 || numBits > 32) { 29 throw new Error("Number of bits must be between 1 and 32"); 30 } 31 32 var result = 0; 33 var bitsRemaining = numBits; 34 35 while (bitsRemaining > 0) { 36 var currentValue = this.buffer[this.readPos]; 37 var bitsAvailable = 8 - this.readBitPos; 38 var bitsToRead = Math.min(bitsRemaining, bitsAvailable); 39 var mask = (1 << bitsToRead) - 1; 40 var shift = bitsAvailable - bitsToRead; 41 var extractedBits = (currentValue >>> shift) & mask; 42 43 result = (result << bitsToRead) | extractedBits; 44 bitsRemaining -= bitsToRead; 45 this.readBitPos += bitsToRead; 46 47 if (this.readBitPos >= 8) { 48 this.readPos++; 49 this.readBitPos -= 8; 50 } 51 } 52 53 return result; 54 } 55 56 hasNext(numBits) 57 { 58 return (this.readPos * 8 + this.readBitPos + numBits) <= this.writePos * 8; 59 } 60 61 write(value, numBits) 62 { 63 if (numBits <= 0 || numBits > 32) { 64 throw new Error("Number of bits must be between 1 and 32"); 65 } 66 if (value >= (1 << numBits)) { 67 throw new Error("Value is too large for the specified number of bits"); 68 } 69 70 while (numBits > 0) { 71 var bitsAvailable = 8 - this.writeBitPos; 72 var bitsToWrite = Math.min(numBits, bitsAvailable); 73 var remainingBits = numBits - bitsToWrite; 74 75 var mask = (1 << bitsToWrite) - 1; 76 var val = (value >>> remainingBits) & mask; 77 78 this.writeBuf = (this.writeBuf << bitsToWrite) | val; 79 this.writeBitPos += bitsToWrite; 80 81 if (this.writeBitPos === 8) { 82 this._assertSpaceFor(1); 83 this.buffer[this.writePos++] = this.writeBuf; 84 this.writeBuf = 0; 85 this.writeBitPos = 0; 86 } 87 88 numBits = remainingBits; 89 } 90 } 91 92 flush() 93 { 94 if (this.writeBitPos > 0) { 95 throw new Error("Remaining bits left in the byte stream, this shouldn't happen; " + this.writeBitPos); 96 } 97 } 98 99 length() 100 { 101 return this.writePos; 102 } 103 104 _assertSpaceFor(numBytes) 105 { 106 if (this.readPos > 0) { 107 var newBuffer = new Uint8Array(this.buffer.length); 108 // JS for arraycopy 109 newBuffer.set(this.buffer.subarray(this.readPos, this.buffer.length), 0); 110 this.buffer = newBuffer; 111 this.writePos -= this.readPos; 112 this.readPos = 0; 113 } 114 115 if (this.writePos + numBytes > this.buffer.length) { 116 var newBuffer = new Uint8Array(this.buffer.length * 2); 117 newBuffer.set(this.buffer); 118 this.buffer = newBuffer; 119 } 120 } 121 } 122
123 // ----- WrappedByteReader.java ----- 124 class WrappedByteReader 125 { 126 constructor(stream, byteNum) 127 { 128 this.stream = stream; 129 this.byteNum = byteNum; 130 this.bitsRead = 0; 131 this.bytesRead = 0; 132 } 133 134 read(numBits) 135 { 136 var retVal = this.stream.read(numBits); 137 138 this.bitsRead += numBits; 139 // note that in JS, Math.floor() is the same as Math.floorDiv() 140 this.bytesRead += Math.floor(this.bitsRead / 8); 141 this.bitsRead = this.bitsRead % 8; 142 143 return retVal; 144 } 145 146 hasNext(numBits) 147 { 148 var newBitsRead = this.bitsRead + numBits; 149 // note that in JS, Math.floor() is the same as Math.floorDiv() 150 var newBytesRead = this.bytesRead + Math.floor(newBitsRead / 8); 151 newBitsRead = newBitsRead % 8; 152 153 return newBytesRead < this.byteNum || (newBytesRead === this.byteNum && newBitsRead === 0); 154 } 155 } 156 157 // ----- FlexDict.java ----- 158 class FlexDict 159 { 160 // Note: used Number.MAX_SAFE_INTEGER instead of Integer.MAX_VALUE 161 constructor(staticDict, enableDynamic = true, maxSize = Number.MAX_SAFE_INTEGER) 162 { 163 this.staticDict = staticDict; 164 // note that we use a Map, because plain objects (eg. {}): 165 // - didn't guarantee property order before ES2015 (now they do for string keys) 166 // - don't keep size 167 // - behave differently for string keys as for int keys 168 this.dynamicDict = new Map(); 169 this.enableDynamic = enableDynamic; 170 this.maxSize = maxSize; 171 } 172 173 get(key) 174 { 175 return this.staticDict.has(key) ? this.staticDict.get(key) : this.dynamicDict.get(key); 176 } 177 178 hasKey(key) 179 { 180 return this.staticDict.has(key) || this.dynamicDict.has(key); 181 } 182 183 add(key, val) 184 { 185 if (this.enableDynamic && this.size() < this.maxSize && !this.hasKey(key)) { 186 this.dynamicDict.set(key, val); 187 } 188 } 189 190 size() 191 { 192 return this.staticDict.size + this.dynamicDict.size; 193 } 194 195 reset() 196 { 197 this.dynamicDict.clear(); 198 } 199 } 200 201 // ----- BaseXStream.java ----- 202 class BaseXStream 203 { 204 // Note that this is a static getter property 205 // (methods that look like properties but actually execute code when accessed) 206 static get BITS_PER_BYTE() 207 { 208 return 8; 209 } 210 211 static log2ceil(x) 212 { 213 return 32 - Math.clz32(x - 1); 214 } 215 } 216 217 class BaseXStreamEncoder 218 { 219 constructor(consumer, config) 220 { 221 this.consumer = consumer || function () 222 { 223 }; 224 this.config = config || BaseXStream.DEFAULT_CONFIG; 225 this.buffer = 0; 226 this.bitsInBuffer = 0; 227 this.byteModulo = 0; 228 } 229 230 write(value, numBits) 231 { 232 if (numBits <= 0 || numBits > 32) { 233 throw new Error("Number of bits must be between 1 and 32"); 234 } 235 // note that 1 << numBits returns 1, because bits shifts in JS only have 32 bit precision 236 if (numBits === 32) { 237 if (value >= 4294967296) { 238 throw new Error("Value is too large for the specified number of bits"); 239 } 240 } 241 // numBits is guaranteed < 32 242 else if (value >= (1 << numBits)) { 243 throw new Error("Value is too large for the specified number of bits"); 244 } 245 246 while (numBits > 0) { 247 var bitsAvailable = this.config.bitsPerBlock - this.bitsInBuffer; 248 var bitsToWrite = Math.min(numBits, bitsAvailable); 249 var remainingBits = numBits - bitsToWrite; 250 251 var mask = (1 << bitsToWrite) - 1; 252 var val = (value >>> remainingBits) & mask; 253 254 this.buffer = (this.buffer << bitsToWrite) | val; 255 this.bitsInBuffer += bitsToWrite;
256 this.byteModulo = (this.byteModulo + bitsToWrite) % BaseXStream.BITS_PER_BYTE; 257 258 if (this.bitsInBuffer === this.config.bitsPerBlock) { 259 if (this.config.enableBitSqueezing) { 260 while (remainingBits > 0) { 261 var extraBitVal = (value >>> (remainingBits - 1)) & 1; 262 var tryBuffer = (this.buffer << 1) | extraBitVal; 263 264 if (tryBuffer > this.config.maxBlockValue && tryBuffer < this.config.maxBlockCapacity) { 265 this.buffer = tryBuffer; 266 this.bitsInBuffer++; 267 this.byteModulo = (this.byteModulo + 1) % BaseXStream.BITS_PER_BYTE; 268 remainingBits--; 269 } 270 else { 271 break; 272 } 273 } 274 } 275 276 this.consumer(this.encode(this.buffer, false)); 277 this.buffer = 0; 278 this.bitsInBuffer = 0; 279 } 280 281 numBits = remainingBits; 282 } 283 } 284 285 flush() 286 { 287 if (this.bitsInBuffer > 0) { 288 if (this.byteModulo === 0) { 289 var mask = (1 << this.bitsInBuffer) - 1; 290 var valueToEncode = this.buffer & mask; 291 var lastCode = this.encode(valueToEncode, true); 292 this.consumer(lastCode); 293 } 294 else { 295 throw new Error("Stream is not byte aligned"); 296 } 297 } 298 299 this.buffer = 0; 300 this.bitsInBuffer = 0; 301 } 302 303 encode(valueToEncode, finalBlock) 304 { 305 var result = ''; 306 307 do { 308 var remainder = valueToEncode % this.config.radix; 309 valueToEncode = Math.floor(valueToEncode / this.config.radix); 310 result = this.config.dict.charAt(remainder) + result; 311 } while (valueToEncode !== 0); 312 313 if (!finalBlock) { 314 while (result.length < this.config.charsPerBlock) { 315 result = this.config.dict.charAt(0) + result; 316 } 317 } 318 else { 319 while (BaseXStream.log2ceil(Math.pow(this.config.radix, result.length) - 1) < this.bitsInBuffer) { 320 result = this.config.dict.charAt(0) + result; 321 } 322 } 323 324 return result; 325 } 326 } 327 328 BaseXStream.Encoder = BaseXStreamEncoder; 329 330 class BaseXStreamDecoder 331 { 332 constructor(input, config) 333 { 334 this.config = config || BaseXStream.DEFAULT_CONFIG; 335 this.buffer = 0; 336 this.bitsInBuffer = 0; 337 this.byteModulo = 0; 338 this.currentBlock = ''; 339 this.blockVal = 0; 340 this.blockPos = this.config.charsPerBlock - 1; 341 this.finalBlockLut = this.buildFinalBlockLut(); 342 343 this.inputPos = 0; 344 this.readBuffer = new ByteStream(); 345 this.input = input; 346 } 347 348 read(numBits) 349 { 350 return this.assertBits(numBits).read(numBits); 351 } 352 353 hasNext(numBits) 354 { 355 return this.readBuffer.hasNext(numBits) || this.inputPos < this.input.length; 356 } 357 358 assertBits(numBits) 359 { 360 while (!this.readBuffer.hasNext(numBits) && this.inputPos < this.input.length) { 361 var lastChar = this.inputPos === this.input.length - 1; 362 this.readChar(this.input.charAt(this.inputPos++), lastChar); 363 } 364 return this.readBuffer; 365 } 366 367 readChar(c, lastChar) 368 { 369 // note that dictRev is a simple object 370 var index = this.config.dictRev[c]; 371 if (index === undefined) { 372 throw new Error("Invalid character: " + c); 373 } 374 375 this.blockVal = this.blockVal + (index * Math.pow(this.config.radix, this.blockPos--)); 376 this.currentBlock += c; 377 378 if (this.blockPos < 0) { 379 var bitsInBlock = lastChar ? this.getLastBlockBitLength() : this.config.bitsPerBlock; 380 this.decodeBlock(bitsInBlock); 381 382 this.currentBlock = ''; 383 this.blockVal = 0; 384 this.blockPos = this.config.charsPerBlock - 1; 385 } 386 387 if (lastChar) { 388 if (this.currentBlock.length > 0) { 389 this.blockVal = 0; 390 this.blockPos = this.currentBlock.length - 1; 391 392 for (var i = 0; i < this.currentBlock.length; i++) { 393 this.blockVal = this.blockVal + (this.config.dictRev[this.currentBlock.charAt(i)] * Math.pow(this.config.radix, this.blockPos--)); 394 } 395 396 var lastBlockBits = this.getLastBlockBitLength(); 397 this.decodeBlock(lastBlockBits); 398 } 399 400 if (this.bitsInBuffer > 0) { 401 throw new Error("Bitstream is not byte aligned"); 402 } 403 404 this.buffer = 0; 405 this.bitsInBuffer = 0; 406 this.byteModulo = 0; 407 } 408 } 409 410 decodeBlock(numBits) 411 { 412 var extraBits = 0; 413 while (this.blockVal >>> extraBits > this.config.maxBlockValue) { 414 numBits++; 415 extraBits++; 416 } 417 418 var mask = (1 << numBits) - 1; 419 this.buffer = (this.buffer << numBits) | (this.blockVal & mask); 420 this.bitsInBuffer += numBits; 421 this.byteModulo = (this.byteModulo + numBits) % BaseXStream.BITS_PER_BYTE; 422 423 while (this.bitsInBuffer >= BaseXStream.BITS_PER_BYTE) { 424 var bitsNotToDecode = this.bitsInBuffer - BaseXStream.BITS_PER_BYTE; 425 var valueToDecode = this.buffer >>> bitsNotToDecode; 426 this.readBuffer.write(valueToDecode, BaseXStream.BITS_PER_BYTE); 427 var m = (1 << bitsNotToDecode) - 1; 428 this.buffer &= m; 429 this.bitsInBuffer = bitsNotToDecode; 430 } 431 } 432 433 getLastBlockBitLength() 434 { 435 if (this.byteModulo === 0 && this.currentBlock.length === this.config.charsPerBlock) { 436 return this.config.bitsPerBlock; 437 } 438 else { 439 // see buildFinalBlockLut() notes 440 var entry = this.byteModulo + ',' + this.currentBlock.length; 441 if (this.finalBlockLut.has(entry)) { 442 return this.finalBlockLut.get(entry); 443 } 444 else { 445 throw new Error("Invalid last block combination: startPos=" + this.byteModulo + ", numChars=" + this.currentBlock.length); 446 } 447 } 448 } 449 450 buildFinalBlockLut() 451 { 452 var retVal = new Map(); 453 454 for (var numChars = 1; numChars <= this.config.charsPerBlock; numChars++) { 455 var minValue = Math.pow(this.config.radix, numChars - 1); 456 var minBits = Math.max(BaseXStream.log2ceil(minValue), 1); 457 var maxValue = Math.pow(this.config.radix, numChars) - 1; 458 var maxBits = Math.min(BaseXStream.log2ceil(maxValue), this.config.bitsPerBlock); 459 460 for (var modulo = 0; modulo < BaseXStream.BITS_PER_BYTE; modulo++) { 461 for (var bitLength = minBits; bitLength <= maxBits; bitLength++) {
462 if ((modulo + bitLength) % BaseXStream.BITS_PER_BYTE === 0) { 463 // watch out: JS maps test equality by reference, not value, so we can't use an array pair here 464 // instead, let's convert to a string 465 var startState = modulo + ',' + numChars; 466 if (!retVal.has(startState)) { 467 retVal.set(startState, bitLength); 468 } 469 else { 470 throw new Error("Configuration cannot guarantee unique final block bit length"); 471 } 472 } 473 } 474 } 475 } 476 477 return retVal; 478 } 479 } 480 481 BaseXStream.Decoder = BaseXStreamDecoder; 482 483 BaseXStream.DEFAULT_CONFIG = { 484 // note that this is a string 485 dict: BaseConstants.BASE62_ALPHABET, 486 charsPerBlock: 4, 487 enableBitSqueezing: true, 488 // and this is an object 489 dictRev: null, 490 radix: null, 491 bitsPerBlock: null, 492 maxBlockValue: null, 493 maxBlockCapacity: null, 494 495 init() 496 { 497 this.dictRev = {}; 498 for (var i = 0; i < this.dict.length; i++) { 499 this.dictRev[this.dict.charAt(i)] = i; 500 } 501 502 this.radix = this.dict.length; 503 var bitsPerChar = Math.log(this.radix) / Math.log(2); 504 this.bitsPerBlock = Math.floor(bitsPerChar * this.charsPerBlock); 505 this.maxBlockValue = Math.pow(2, this.bitsPerBlock) - 1; 506 this.maxBlockCapacity = Math.pow(this.radix, this.charsPerBlock); 507 } 508 }; 509 BaseXStream.DEFAULT_CONFIG.init(); 510 511 // ----- LZW.java ----- 512 class LZW 513 { 514 static get ASCII_ESCAPE_IDX() 515 { 516 return 0; 517 } 518 519 static get ASCII_ESCAPE_VAL() 520 { 521 return String.fromCharCode(LZW.ASCII_ESCAPE_IDX); 522 } 523 524 static get UNICODE_ESCAPE_IDX() 525 { 526 return 1; 527 } 528 529 static get UNICODE_ESCAPE_VAL() 530 { 531 return String.fromCharCode(LZW.UNICODE_ESCAPE_IDX); 532 } 533 534 constructor(config) 535 { 536 this.config = config || LZW.DEFAULT_CONFIG; 537 this.encodeDict = null; 538 this.decodeDict = null; 539 } 540 541 compress(input, output) 542 { 543 if (!input || input.length === 0) { 544 throw new Error("Please provide input"); 545 } 546 547 if (this.encodeDict === null) { 548 this.encodeDict = new FlexDict(this.config.staticDict, this.config.enableDynamicDict, this.config.maxDictSize); 549 } 550 var ctx = new LZW.Ctx(this.encodeDict); 551 552 var last = input.charAt(0); 553 for (var i = 1; i < input.length; i++) { 554 var current = input.charAt(i); 555 var lastPlusCurrent = last + current; 556 557 if (ctx.dict.hasKey(lastPlusCurrent)) { 558 last = lastPlusCurrent; 559 } 560 else { 561 var isNew = this.write(output, ctx, last); 562 this.checkReset(ctx.dict); 563 564 if (isNew) { 565 ctx.dict.add(last, ctx.dict.size()); 566 } 567 568 ctx.dict.add(lastPlusCurrent, ctx.dict.size()); 569 last = current; 570 } 571 } 572 573 var isNew = this.write(output, ctx, last); 574 this.checkReset(ctx.dict); 575 576 if (isNew) { 577 ctx.dict.add(last, ctx.dict.size()); 578 } 579 580 if (this.config.byteAlignMode && ctx.byteAlignVal > 0) { 581 var bitsUntilFullByte = 8 - ctx.byteAlignVal; 582 var bitsNeededForSeq = this.bitsNeeded(ctx.dict.size() - 1); 583 var bitsToWrite = Math.min(bitsNeededForSeq, bitsUntilFullByte); 584 output.write(LZW.ASCII_ESCAPE_IDX, bitsToWrite); 585 bitsUntilFullByte -= bitsToWrite; 586 587 if (bitsUntilFullByte > 0) { 588 output.write(0, bitsUntilFullByte); 589 } 590 ctx.byteAlignVal = 0; 591 } 592 } 593 594 decompress(input) 595 { 596 var retVal = ''; 597 598 if (this.decodeDict === null) { 599 this.decodeDict = new FlexDict(this.config.staticDictRev, this.config.enableDynamicDict, this.config.maxDictSize); 600 } 601 var ctx = new LZW.Ctx(this.decodeDict); 602 603 var nextIndex = ctx.dict.size() - 1; 604 var last = null; 605 var current; 606 607 while ((current = this.read(input, ctx, nextIndex, last)) !== null) { 608 retVal += current.value; 609 610 if (last !== null) { 611 ctx.dict.add(ctx.dict.size(), last + current.value.charAt(0)); 612 } 613 614 this.checkReset(ctx.dict); 615 616 if (current.isNew) { 617 ctx.dict.add(ctx.dict.size(), current.value); 618 } 619 620 last = current.value; 621 622 if (this.config.enableDynamicDict) { 623 nextIndex = (ctx.dict.size() - 1) + 1; 624 } 625 } 626 627 return retVal; 628 } 629 630 write(output, ctx, value) 631 { 632 var isNew = false;
633 var code = ctx.dict.get(value); 634 var bitsNeeded = this.bitsNeeded(ctx.dict.size() - 1); 635 636 if (code !== undefined) { 637 if (code === LZW.ASCII_ESCAPE_IDX || code === LZW.UNICODE_ESCAPE_IDX) { 638 output.write(LZW.ASCII_ESCAPE_IDX, bitsNeeded); 639 output.write(code, 8); 640 } 641 else { 642 output.write(code, bitsNeeded); 643 } 644 } 645 else { 646 if (value.length === 1) { 647 var c = value.charCodeAt(0); 648 649 if (c < 256) { 650 output.write(LZW.ASCII_ESCAPE_IDX, bitsNeeded); 651 output.write(c, 8); 652 } 653 else { 654 output.write(LZW.UNICODE_ESCAPE_IDX, bitsNeeded); 655 output.write(c, 16); 656 } 657 658 isNew = true; 659 } 660 else { 661 throw new Error("This shouldn't happen, all new values > 1 should be in the dict; " + value); 662 } 663 } 664 665 ctx.byteAlignVal = (ctx.byteAlignVal + bitsNeeded) % 8; 666 667 return isNew; 668 } 669 670 read(input, ctx, nextIndex, last) 671 { 672 var retVal = null; 673 var bitsNeeded = this.bitsNeeded(nextIndex); 674 675 if (input.hasNext(bitsNeeded)) { 676 var code = input.read(bitsNeeded); 677 ctx.byteAlignVal = (ctx.byteAlignVal + bitsNeeded) % 8; 678 679 retVal = new LZW.Token(); 680 if (code === LZW.ASCII_ESCAPE_IDX || code === LZW.UNICODE_ESCAPE_IDX) { 681 if (this.config.byteAlignMode && !input.hasNext(8)) { 682 if (ctx.byteAlignVal > 0) { 683 input.read(8 - ctx.byteAlignVal); 684 } 685 retVal = null; 686 } 687 else { 688 // Using String.fromCharCode instead of String.valueOf for character conversion 689 retVal.value = String.fromCharCode(input.read(code === LZW.ASCII_ESCAPE_IDX ? 8 : 16)); 690 retVal.isNew = retVal.value !== LZW.ASCII_ESCAPE_VAL && retVal.value !== LZW.UNICODE_ESCAPE_VAL; 691 } 692 } 693 else if (ctx.dict.hasKey(code)) { 694 retVal.value = ctx.dict.get(code); 695 } 696 else if (code === nextIndex) { 697 retVal.value = last + last.charAt(0); 698 } 699 else { 700 throw new Error("Invalid compressed data"); 701 } 702 } 703 else { 704 if (this.config.byteAlignMode && ctx.byteAlignVal > 0) { 705 input.read(8 - ctx.byteAlignVal); 706 } 707 } 708 709 return retVal; 710 } 711 712 checkReset(dict) 713 { 714 if (this.config.enableDynamicDict && this.config.enableDictReset && dict.size() >= this.config.maxDictSize) { 715 dict.reset(); 716 } 717 } 718 719 bitsNeeded(code) 720 { 721 return code === 0 ? 1 : 32 - Math.clz32(code); 722 } 723 } 724 725 class LZWToken 726 { 727 constructor() 728 { 729 this.value = null; 730 this.isNew = false; 731 } 732 } 733 734 LZW.Token = LZWToken; 735 736 class LZWCtx 737 { 738 constructor(dict) 739 { 740 this.dict = dict; 741 this.byteAlignVal = 0; 742 } 743 } 744 745 LZW.Ctx = LZWCtx; 746 747 LZW.DEFAULT_CONFIG = { 748 INITIAL_DICT: [ 749 LZW.ASCII_ESCAPE_VAL, 750 LZW.UNICODE_ESCAPE_VAL, 751 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 752 " ", ".", ",", 753 "e", "a", "r", "i", "o", "t", "n", "s", "l", "c", 754 "u", "d", "p", "m", "h", "g", "b", "f", "v", "k", 755 "w", "j", "q", "x", "y", "z", 756 "A", "M", "S", "C", "P", "D", "B", "R", "L", "T", 757 "E", "N", "H", "G", "F", "W", "I", "J", "K", "O",
758 "V", "U", "Q", "X", "Y", "Z", 759 "th", "en", "er", "in", "es", "on", "an", "re", "st", "le" 760 ], 761 762 enableDynamicDict: true, 763 maxDictSize: Math.pow(2, 10) - 1, 764 enableDictReset: true, 765 byteAlignMode: true, 766 staticDict: new Map(), 767 staticDictRev: new Map(), 768 769 init() 770 { 771 for (var i = 0; i < this.INITIAL_DICT.length; i++) { 772 var c = this.INITIAL_DICT[i]; 773 if (!this.staticDict.has(c)) { 774 this.staticDict.set(c, i); 775 this.staticDictRev.set(i, c); 776 } 777 else { 778 throw new Error("Duplicate dict character detected '" + c + "'"); 779 } 780 } 781 }, 782 }; 783 LZW.DEFAULT_CONFIG.init(); 784 785 // ----- Bbor.java ----- 786 class Bbor 787 { 788 static get POW_2_16() 789 { 790 return Math.pow(2, 16); 791 } 792 793 static get POW_2_32() 794 { 795 return Math.pow(2, 32); 796 } 797 798 static get JS_MAX_SAFE_INTEGER() 799 { 800 return Number.MAX_SAFE_INTEGER; 801 } 802 } 803 804 Bbor.Token = { 805 END_OBJECT: 'END_OBJECT', 806 START_OBJECT: 'START_OBJECT', 807 END_ARRAY: 'END_ARRAY', 808 START_ARRAY: 'START_ARRAY', 809 FIELD_NAME: 'FIELD_NAME', 810 VALUE_STRING: 'VALUE_STRING', 811 VALUE_NUMBER_INT: 'VALUE_NUMBER_INT', 812 VALUE_NUMBER_FLOAT: 'VALUE_NUMBER_FLOAT', 813 VALUE_TRUE: 'VALUE_TRUE', 814 VALUE_FALSE: 'VALUE_FALSE', 815 VALUE_NULL: 'VALUE_NULL' 816 }; 817 818 Bbor.TokenRev = { 819 START_OBJECT: Bbor.Token.END_OBJECT, 820 START_ARRAY: Bbor.Token.END_ARRAY 821 }; 822 823 Bbor.Primitive = { 824 POSITIVE_INTEGER: 'POSITIVE_INTEGER', 825 NEGATIVE_INTEGER: 'NEGATIVE_INTEGER', 826 BYTE_STRING: 'BYTE_STRING', 827 TEXT_STRING: 'TEXT_STRING', 828 POSITIVE_BIGNUM: 'POSITIVE_BIGNUM', 829 NEGATIVE_BIGNUM: 'NEGATIVE_BIGNUM', 830 BOOLEAN: 'BOOLEAN', 831 NULL: 'NULL', 832 UNDEFINED: 'UNDEFINED', 833 BYTE: 'BYTE', 834 FLOAT16: 'FLOAT16', 835 FLOAT32: 'FLOAT32', 836 FLOAT64: 'FLOAT64' 837 }; 838 839 class BborCtx 840 { 841 constructor(prev, token, size) 842 { 843 this.prev = prev; 844 this.token = token; 845 this.size = size; 846 this.pos = -1; 847 this.field = null; 848 } 849 } 850 851 Bbor.Ctx = BborCtx; 852 853 class BborEncoder 854 { 855 constructor(config) 856 { 857 this.config = config || Bbor.DEFAULT_CONFIG; 858 this.fields = new FlexDict(this.config.staticFields); 859 } 860 861 write(outputStream, compressor, value) 862 { 863 if (value === null) { 864 return this.writeNull(outputStream); 865 } 866 867 if (value === undefined) { 868 return this.writeUndefined(outputStream); 869 } 870 871 if (typeof value === 'boolean') { 872 return this.writeBoolean(outputStream, value); 873 } 874 875 if (typeof value === 'number') { 876 return this.writeNumber(outputStream, value); 877 } 878 879 if (typeof value === 'string') { 880 return this.writeString(outputStream, compressor, value); 881 } 882 883 if (value instanceof Uint8Array) { 884 return this.writeBytes(outputStream, value); 885 } 886 887 if (Array.isArray(value)) { 888 this.writeStartArray(outputStream, value.length); 889 for (var i = 0; i < value.length; i++) { 890 this.write(outputStream, compressor, value[i]); 891 } 892 this.writeEndArray(outputStream); 893 return this; 894 } 895 896 if (typeof value === 'object') { 897 var keys = Object.keys(value); 898 this.writeStartObject(outputStream, keys.length); 899 for (var i = 0; i < keys.length; i++) { 900 this.writeFieldName(outputStream, compressor, keys[i]); 901 this.write(outputStream, compressor, value[keys[i]]); 902 } 903 this.writeEndObject(outputStream); 904 return this; 905 } 906 907 if (typeof value === 'function') { 908 // update 2990725: decided to add this as a NOOP 909 // so we don't need to add logic to skip the functions 910 // in some exotic objects (like fullcalendar events), 911 // but know they won't get serialized (could be a future todo or config setting)! 912 return this; 913 } 914 915 throw new Error("Unsupported type: " + typeof value); 916 } 917 918 writeNull(outputStream) 919 { 920 this.writeUint8(outputStream, 0xf6); 921 return this; 922 } 923
924 writeUndefined(outputStream) 925 { 926 this.writeUint8(outputStream, 0xf7); 927 return this; 928 } 929 930 writeBoolean(outputStream, value) 931 { 932 this.writeUint8(outputStream, value ? 0xf5 : 0xf4); 933 return this; 934 } 935 936 /** 937 * Watch out: JS has only a single numeric type: Number, 938 * which is a double-precision 64-bit floating point number (IEEE 754) 939 * All numbers are stored as 64-bit floating point numbers (doubles). 940 * Note that a side effect of this Java port is that the toPositiveBigNum() below 941 * will never trigger, since we already checked for <= Number.MAX_SAFE_INTEGER. 942 */ 943 writeNumber(outputStream, value) 944 { 945 if (Math.floor(value) === value && Math.abs(value) <= Number.MAX_SAFE_INTEGER) { 946 if (value >= 0) { 947 if (value <= Bbor.JS_MAX_SAFE_INTEGER) { 948 this.writeTypeAndLength(outputStream, 0, value); 949 return this; 950 } 951 // should never trigger 952 else { 953 this.writeUint8(outputStream, 0xc2); 954 var bytes = this.toPositiveBigNum(value); 955 this.writeTypeAndLength(outputStream, 2, bytes.length); 956 for (var i = 0; i < bytes.length; i++) { 957 this.writeUint8(outputStream, bytes[i]); 958 } 959 return this; 960 } 961 } 962 else { 963 if (-Bbor.JS_MAX_SAFE_INTEGER <= value) { 964 this.writeTypeAndLength(outputStream, 1, -1 - value); 965 return this; 966 } 967 // should never trigger 968 else { 969 this.writeUint8(outputStream, 0xc3); 970 var bytes = this.toPositiveBigNum(-1 - value); 971 this.writeTypeAndLength(outputStream, 2, bytes.length); 972 for (var i = 0; i < bytes.length; i++) { 973 this.writeUint8(outputStream, bytes[i]); 974 } 975 return this; 976 } 977 } 978 } 979 980 // little trick to reduce the precision of a decimal to 32-bits 981 var float32 = new Float32Array([value])[0]; 982 if (Math.abs((value - float32) / value) < 1e-7) { 983 this.writeUint8(outputStream, 0xfa); 984 this.writeFloat32(outputStream, value); 985 } 986 else { 987 this.writeUint8(outputStream, 0xfb); 988 this.writeFloat64(outputStream, value); 989 } 990 991 return this; 992 } 993 994 writeString(outputStream, compressor, value) 995 { 996 // in JS, strings are represented as 16-bit Unicode characters (UTF-16), 997 // just like in Java, so nothing to change here when compression is enabled 998 if (this.config.enableStringCompression && compressor && value.length > 0) { 999 var byteStream = new ByteStream(); 1000 compressor.compress(value, byteStream); 1001 byteStream.flush(); 1002 this.writeTypeAndLength(outputStream, 3, byteStream.length()); 1003 while (byteStream.hasNext(8)) { 1004 this.writeUint8(outputStream, byteStream.read(8)); 1005 } 1006 return this; 1007 } 1008 else { 1009 // equivalent of String.getBytes(), returns Uint8Array 1010 // (see original cbor.js for alternative, raw implementation) 1011 var utf8Data = new TextEncoder().encode(value); 1012 this.writeTypeAndLength(outputStream, 3, utf8Data.length); 1013 for (var i = 0; i < utf8Data.length; i++) { 1014 this.writeUint8(outputStream, utf8Data[i]); 1015 } 1016 return this; 1017 } 1018 } 1019 1020 writeBytes(outputStream, value) 1021 { 1022 this.writeTypeAndLength(outputStream, 2, value.length); 1023 for (var i = 0; i < value.length; i++) { 1024 this.writeUint8(outputStream, value[i]); 1025 } 1026 return this; 1027 } 1028 1029 writeFieldName(outputStream, compressor, value) 1030 {
1031 if (this.config.enableKeyMapping) { 1032 if (!this.fields.hasKey(value)) { 1033 this.writeString(outputStream, compressor, value); 1034 this.fields.add(value, this.fields.size()); 1035 } 1036 else { 1037 this.writeNumber(outputStream, this.fields.get(value)); 1038 } 1039 } 1040 else { 1041 this.writeString(outputStream, compressor, value); 1042 } 1043 return this; 1044 } 1045 1046 writeStartArray(outputStream, size) 1047 { 1048 this.writeTypeAndLength(outputStream, 4, size); 1049 return this; 1050 } 1051 1052 writeEndArray(outputStream) 1053 { 1054 return this; 1055 } 1056 1057 writeStartObject(outputStream, size) 1058 { 1059 this.writeTypeAndLength(outputStream, 5, size); 1060 return this; 1061 } 1062 1063 writeEndObject(outputStream) 1064 { 1065 return this; 1066 } 1067 1068 writeTypeAndLength(outputStream, type, length) 1069 { 1070 if (length < 24) { 1071 this.writeUint8(outputStream, (type << 5) | length); 1072 } 1073 else if (length < 0x100) { 1074 this.writeUint8(outputStream, (type << 5) | 24); 1075 this.writeUint8(outputStream, length); 1076 } 1077 else if (length < 0x10000) { 1078 this.writeUint8(outputStream, (type << 5) | 25); 1079 this.writeUint16(outputStream, length); 1080 } 1081 else if (length < 0x100000000) { 1082 this.writeUint8(outputStream, (type << 5) | 26); 1083 this.writeUint32(outputStream, length); 1084 } 1085 else { 1086 this.writeUint8(outputStream, (type << 5) | 27); 1087 this.writeUint64(outputStream, length); 1088 } 1089 } 1090 1091 writeUint8(outputStream, value) 1092 { 1093 outputStream.write(value, 8); 1094 } 1095 1096 writeUint16(outputStream, value) 1097 { 1098 outputStream.write(value, 16); 1099 } 1100 1101 writeUint32(outputStream, value) 1102 { 1103 outputStream.write(value, 32); 1104 } 1105 1106 writeUint64(outputStream, value) 1107 { 1108 var low = value % Bbor.POW_2_32; 1109 var high = (value - low) / Bbor.POW_2_32; 1110 outputStream.write(high, 32); 1111 outputStream.write(low, 32); 1112 } 1113 1114 writeFloat32(outputStream, value) 1115 { 1116 this.writeUint32(outputStream, this.floatToIntBits(value)); 1117 } 1118 1119 // this is a bit different from the Java implementation because we wanted to avoid BigInt 1120 // and return two 32-bit numbers instead 1121 writeFloat64(outputStream, value) 1122 { 1123 var bits = this.doubleToLongBits(value); 1124 outputStream.write(bits.high, 32); 1125 outputStream.write(bits.low, 32); 1126 } 1127 1128 toPositiveBigNum(value) 1129 { 1130 var bytes = new Array(8); 1131 for (var i = 0; i < 8; i++) { 1132 bytes[7 - i] = value & 0xFF; 1133 value = value >>> 8; 1134 } 1135 1136 var startIndex = 0; 1137 while (startIndex < 7 && bytes[startIndex] === 0) { 1138 startIndex++; 1139 } 1140 1141 return bytes.slice(startIndex); 1142 } 1143 1144 // JS port of Float.floatToIntBits() 1145 floatToIntBits(value) 1146 { 1147 // Create an ArrayBuffer with 4 bytes (32 bits) 1148 var buffer = new ArrayBuffer(4); 1149 // Create a DataView for binary manipulation 1150 var view = new DataView(buffer); 1151 // Store the float32 in the buffer 1152 view.setFloat32(0, value, false); // false = big-endian, as used in IEEE 754 1153 // Retrieve the 32-bit integer representation 1154 return view.getInt32(0, false); // false = big-endian 1155 } 1156 1157 // JS port of Double.doubleToLongBits() 1158 doubleToLongBits(value) 1159 { 1160 // Create an ArrayBuffer with 8 bytes (64 bits) 1161 var buffer = new ArrayBuffer(8); 1162 // Create a DataView for binary manipulation 1163 var view = new DataView(buffer); 1164 // Store the float64 (double) in the buffer 1165 view.setFloat64(0, value, false); // false = big-endian, as used in IEEE 754
1166 // Retrieve the 64-bit representation as two 32-bit integers 1167 // since JavaScript doesn't natively support 64-bit integers, 1168 // and we want to avoid BigInt because too recent 1169 return { 1170 high: view.getUint32(0, false), // Top 32 bits 1171 low: view.getUint32(4, false) // Bottom 32 bits 1172 }; 1173 } 1174 } 1175 Bbor.Encoder = BborEncoder; 1176 1177 class BborDecoder 1178 { 1179 constructor(config) 1180 { 1181 this.config = config || Bbor.DEFAULT_CONFIG; 1182 this.fields = new FlexDict(this.config.staticFieldsRev); 1183 this.ctx = null; 1184 this.primitive = null; 1185 this.value = null; 1186 this.readingValue = false; 1187 this.end = false; 1188 } 1189 1190 read(base62Decoder, compressor) 1191 { 1192 var token = this.readNext(base62Decoder, compressor); 1193 if (token === null) { 1194 return Bbor.Decoder.END; 1195 } 1196 1197 switch (token) { 1198 case Bbor.Token.START_OBJECT: 1199 // let's use a plain object instead of new Map() because string-based keys 1200 // retain insertion order as of ES2015, so this should be equal to LinkedHashMap 1201 var obj = {}; 1202 var next; 1203 while ((next = this.read(base62Decoder, compressor)) !== Bbor.Decoder.END) { 1204 obj[next] = this.read(base62Decoder, compressor); 1205 } 1206 return obj; 1207 1208 case Bbor.Token.START_ARRAY: 1209 var arr = []; 1210 var el; 1211 while ((el = this.read(base62Decoder, compressor)) !== Bbor.Decoder.END) { 1212 arr.push(el); 1213 } 1214 return arr; 1215 1216 case Bbor.Token.END_OBJECT: 1217 case Bbor.Token.END_ARRAY: 1218 return Bbor.Decoder.END; 1219 1220 default: 1221 return this.value; 1222 } 1223 } 1224 1225 readNext(inputStream, compressor) 1226 { 1227 var t, initialByte, majorType, additionalInfo, length; 1228 var retVal, bytes, positive, high, low; 1229 var i; 1230 1231 if (!this.readingValue) { 1232 if (this.ctx !== null) { 1233 t = this.ctx.token; 1234 if (t === Bbor.Token.START_OBJECT || t === Bbor.Token.START_ARRAY) { 1235 this.ctx.pos++; 1236 if (this.ctx.pos === this.ctx.size) { 1237 this.ctx = this.ctx.prev; 1238 this.end = this.ctx === null; 1239 return Bbor.TokenRev[t]; 1240 } 1241 } 1242 } 1243 else { 1244 if (this.end) { 1245 return null; 1246 } 1247 } 1248 } 1249 1250 initialByte = this.readUint8(inputStream); 1251 majorType = initialByte >>> 5; 1252 additionalInfo = initialByte & 0x1f; 1253 length = this.readLength(inputStream, additionalInfo); 1254 1255 this.value = null; 1256 this.primitive = null; 1257 1258 switch (majorType) { 1259 case 0: 1260 // in case of a 64bit number, length will hold the high/low parts and we need to rebuild it 1261 if (additionalInfo === 27) { 1262 this.value = (length.high * Bbor.POW_2_32) + length.low; 1263 } 1264 else { 1265 this.value = length; 1266 } 1267 this.primitive = Bbor.Primitive.POSITIVE_INTEGER; 1268 retVal = Bbor.Token.VALUE_NUMBER_INT; 1269 break; 1270 1271 case 1: 1272 this.value = -1 - length; 1273 this.primitive = Bbor.Primitive.NEGATIVE_INTEGER; 1274 retVal = Bbor.Token.VALUE_NUMBER_INT; 1275 break; 1276 1277 case 2: 1278 bytes = new Uint8Array(length); 1279 for (i = 0; i < bytes.length; i++) { 1280 bytes[i] = inputStream.read(8); 1281 } 1282 this.value = bytes; 1283 this.primitive = Bbor.Primitive.BYTE_STRING; 1284 retVal = Bbor.Token.START_ARRAY; 1285 break; 1286 1287 case 3: 1288 this.value = this.readString(inputStream, compressor, length); 1289 this.primitive = Bbor.Primitive.TEXT_STRING; 1290 retVal = Bbor.Token.VALUE_STRING; 1291 break; 1292 1293 case 4: 1294 this.ctx = new Bbor.Ctx(this.ctx, Bbor.Token.START_ARRAY, length); 1295 retVal = Bbor.Token.START_ARRAY; 1296 break; 1297 1298 case 5: 1299 this.ctx = new Bbor.Ctx(this.ctx, Bbor.Token.START_OBJECT, length * 2); 1300 retVal = Bbor.Token.START_OBJECT; 1301 break; 1302 1303 case 6: 1304 switch (additionalInfo) { 1305 case 2: 1306 case 3: 1307 positive = additionalInfo === 2; 1308 this.readingValue = true; 1309 if (this.readNext(inputStream, compressor) === Bbor.Token.START_ARRAY) { 1310 this.value = positive ? this.fromPositiveBigNum(this.value) : 1311 -1 - this.fromPositiveBigNum(this.value); 1312 this.primitive = positive ? Bbor.Primitive.POSITIVE_BIGNUM : Bbor.Primitive.NEGATIVE_BIGNUM; 1313 this.readingValue = false;
1314 retVal = Bbor.Token.VALUE_NUMBER_INT; 1315 } 1316 else { 1317 throw new Error("Encountered bignum, but the next token isn't a byte array"); 1318 } 1319 break; 1320 default: 1321 throw new Error("Unimplemented tag value: " + additionalInfo); 1322 } 1323 break; 1324 1325 case 7: 1326 switch (additionalInfo) { 1327 case 20: 1328 this.value = false; 1329 this.primitive = Bbor.Primitive.BOOLEAN; 1330 retVal = Bbor.Token.VALUE_FALSE; 1331 break; 1332 case 21: 1333 this.value = true; 1334 this.primitive = Bbor.Primitive.BOOLEAN; 1335 retVal = Bbor.Token.VALUE_TRUE; 1336 break; 1337 case 22: 1338 this.value = null; 1339 this.primitive = Bbor.Primitive.NULL; 1340 retVal = Bbor.Token.VALUE_NULL; 1341 break; 1342 case 23: 1343 this.value = undefined; 1344 this.primitive = Bbor.Primitive.UNDEFINED; 1345 retVal = Bbor.Token.VALUE_NULL; 1346 break; 1347 case 24: 1348 this.value = this.readUint8(inputStream); 1349 this.primitive = Bbor.Primitive.BYTE; 1350 retVal = Bbor.Token.VALUE_NUMBER_INT; 1351 break; 1352 case 25: 1353 throw new Error("Half-precision float not implemented"); 1354 case 26: 1355 this.value = this.intBitsToFloat(length); 1356 this.primitive = Bbor.Primitive.FLOAT32; 1357 retVal = Bbor.Token.VALUE_NUMBER_FLOAT; 1358 break; 1359 case 27: 1360 // note that length will hold the high/low bits here 1361 this.value = this.longBitsToDouble(length); 1362 this.primitive = Bbor.Primitive.FLOAT64; 1363 retVal = Bbor.Token.VALUE_NUMBER_FLOAT; 1364 break; 1365 default: 1366 throw new Error("Unimplemented floating-point or simple value: " + additionalInfo); 1367 } 1368 break; 1369 1370 default: 1371 throw new Error("Unknown major type: " + majorType); 1372 } 1373 1374 if (this.ctx !== null && this.ctx.token === Bbor.Token.START_OBJECT && this.ctx.pos % 2 === 0) { 1375 this.primitive = null; 1376 this.value = this.parseField(this.value); 1377 this.ctx.field = this.value; 1378 retVal = Bbor.Token.FIELD_NAME; 1379 } 1380 1381 return retVal; 1382 } 1383 1384 readLength(inputStream, additionalInfo) 1385 { 1386 if (additionalInfo < 24) { 1387 return additionalInfo; 1388 } 1389 switch (additionalInfo) { 1390 case 24: 1391 return this.readUint8(inputStream); 1392 case 25: 1393 return this.readUint16(inputStream); 1394 case 26: 1395 return this.readUint32(inputStream); 1396 case 27: 1397 return this.readUint64(inputStream); 1398 default: 1399 throw new Error("Invalid additional info: " + additionalInfo); 1400 } 1401 } 1402 1403 readString(inputStream, compressor, length) 1404 { 1405 var bytes, i; 1406 if (this.config.enableStringCompression && compressor && length > 0) { 1407 return compressor.decompress(new WrappedByteReader(inputStream, length)); 1408 } 1409 else { 1410 bytes = new Uint8Array(length); 1411 for (i = 0; i < length; i++) { 1412 bytes[i] = inputStream.read(8); 1413 } 1414 return new TextDecoder().decode(bytes); 1415 } 1416 } 1417 1418 readUint8(inputStream) 1419 { 1420 return inputStream.read(8) & 0xFF; 1421 } 1422 1423 readUint16(inputStream) 1424 { 1425 return inputStream.read(16) & 0xFFFF; 1426 } 1427 1428 readUint32(inputStream) 1429 { 1430 var high = this.readUint16(inputStream); 1431 var low = this.readUint16(inputStream); 1432 return (high * Bbor.POW_2_16) + low; 1433 } 1434 1435 readUint64(inputStream) 1436 { 1437 // see encoder: since there's no support for 64-bit integers, let's return a split 1438 // Also note we don't need to reconstruct the parts using POW_2_32 because we'll write them to the raw bitstream 1439 // The >>> 0 is crucial here - it forces JavaScript to interpret the number as an unsigned 32-bit integer.
1440 // Without it, the negative value would be added instead of the intended unsigned value. 1441 return { 1442 high: inputStream.read(32) >>> 0, 1443 low: inputStream.read(32) >>> 0, 1444 }; 1445 } 1446 1447 fromPositiveBigNum(bytes) 1448 { 1449 var result = 0; 1450 for (var i = 0; i < bytes.length; i++) { 1451 result = (result << 8) | (bytes[i] & 0xFF); 1452 } 1453 return result; 1454 } 1455 1456 parseField(nameOrIdx) 1457 { 1458 if (this.config.enableKeyMapping) { 1459 if (typeof nameOrIdx === 'string') { 1460 this.fields.add(this.fields.size(), nameOrIdx); 1461 } 1462 else if (typeof nameOrIdx === 'number') { 1463 nameOrIdx = this.fields.get(nameOrIdx); 1464 } 1465 else { 1466 throw new Error("Invalid key type: " + typeof nameOrIdx); 1467 } 1468 } 1469 return nameOrIdx; 1470 } 1471 1472 intBitsToFloat(bits) 1473 { 1474 // Create an ArrayBuffer with 4 bytes (32 bits) 1475 var buffer = new ArrayBuffer(4); 1476 // Create a DataView for binary manipulation 1477 var view = new DataView(buffer); 1478 // Store the 32-bit integer in the buffer 1479 view.setInt32(0, bits, false); // false = big-endian, as used in IEEE 754 1480 // Retrieve the floating-point number from the buffer 1481 var value = view.getFloat32(0, false); // false = big-endian 1482 1483 // The reason for 7 digits is that IEEE 754 single-precision floats can represent approximately 6-7 decimal digits of precision. 1484 // Java automatically handles this for you because it maintains the 32-bit float type, 1485 // while in JavaScript we need to handle the display precision explicitly since it converts everything to 64-bit doubles. 1486 // the Number() is needed because toPrecision() returns a string (and it removed trailing zeros) 1487 // Note that we only apply this for decimal values or we'll lose int precision since this is also called 1488 // for (large) integers like Number.MAX_SAFE_INTEGER + 1 1489 if (Math.floor(value) !== value) { 1490 value = Number(value.toPrecision(7)); 1491 } 1492 1493 return value; 1494 } 1495 1496 longBitsToDouble(bits) 1497 { 1498 // Create an ArrayBuffer with 8 bytes (64 bits) 1499 var buffer = new ArrayBuffer(8); 1500 // Create a DataView for binary manipulation 1501 var view = new DataView(buffer); 1502 // Store the high 32 bits and low 32 bits in the buffer 1503 view.setUint32(0, bits.high, false); // Top 32 bits (big-endian) 1504 view.setUint32(4, bits.low, false); // Bottom 32 bits (big-endian) 1505 // Retrieve the floating-point number from the buffer 1506 return view.getFloat64(0, false); // false = big-endian 1507 } 1508 } 1509 1510 Bbor.Decoder = BborDecoder; 1511 // Empty objects compared with === or Object.is() return false unless they reference the exact same object. 1512 Bbor.Decoder.END = {}; 1513 1514 Bbor.DEFAULT_CONFIG = { 1515 1516 INITIAL_FIELDS: [ 1517 "id", 1518 "type", 1519 "name", 1520 "status", 1521 "count", 1522 "data", 1523 "value", 1524 "error", 1525 "response", 1526 "version" 1527 ], 1528 1529 enableKeyMapping: true, 1530 enableStringCompression: true, 1531 staticFields: new Map(), 1532 staticFieldsRev: new Map(), 1533 1534 init() 1535 { 1536 for (var i = 0; i < this.INITIAL_FIELDS.length; i++) { 1537 var c = this.INITIAL_FIELDS[i]; 1538 if (!this.staticFields.has(c)) { 1539 this.staticFields.set(c, i); 1540 this.staticFieldsRev.set(i, c); 1541 } 1542 else { 1543 throw new Error("Duplicate static field detected '" + c + "'"); 1544 } 1545 } 1546 }, 1547 }; 1548 Bbor.DEFAULT_CONFIG.init(); 1549 1550 // ----- MAIN ----- 1551 this.encode = function (obj) 1552 { 1553 var retVal = ""; 1554 var base62Encoder = new BaseXStream.Encoder(function (str) 1555 { 1556 retVal += str; 1557 }); 1558 new Bbor.Encoder().write(base62Encoder, new LZW(), obj); 1559 base62Encoder.flush(); 1560 return retVal; 1561 }; 1562 1563 this.decode = function (str) 1564 { 1565 return new Bbor.Decoder().read(new BaseXStream.Decoder(str), new LZW()); 1566 }; 1567 1568}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.