diff --git a/README.md b/README.md index cd91935b..c47198e7 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ instance methods, and class methods that are supported. To use this module directly (without browserify), install it: ```bash -npm install buffer +npm install @caspertech/buffer ``` This module was previously called **native-buffer-browserify**, but please use **buffer** diff --git a/index.d.ts b/index.d.ts index 07096a2f..bf81d88f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -7,6 +7,7 @@ export class Buffer extends Uint8Array { compare(otherBuffer: Uint8Array, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number; copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; slice(start?: number, end?: number): Buffer; + subarray(start?: number, end?: number): Buffer; writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; @@ -20,15 +21,15 @@ export class Buffer extends Uint8Array { readUInt16BE(offset: number, noAssert?: boolean): number; readUInt32LE(offset: number, noAssert?: boolean): number; readUInt32BE(offset: number, noAssert?: boolean): number; - readBigUInt64LE(offset: number): BigInt; - readBigUInt64BE(offset: number): BigInt; + readBigUInt64LE(offset: number): bigint; + readBigUInt64BE(offset: number): bigint; readInt8(offset: number, noAssert?: boolean): number; readInt16LE(offset: number, noAssert?: boolean): number; readInt16BE(offset: number, noAssert?: boolean): number; readInt32LE(offset: number, noAssert?: boolean): number; readInt32BE(offset: number, noAssert?: boolean): number; - readBigInt64LE(offset: number): BigInt; - readBigInt64BE(offset: number): BigInt; + readBigInt64LE(offset: number): bigint; + readBigInt64BE(offset: number): bigint; readFloatLE(offset: number, noAssert?: boolean): number; readFloatBE(offset: number, noAssert?: boolean): number; readDoubleLE(offset: number, noAssert?: boolean): number; @@ -42,15 +43,15 @@ export class Buffer extends Uint8Array { writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; - writeBigUInt64LE(value: number, offset: number): BigInt; - writeBigUInt64BE(value: number, offset: number): BigInt; + writeBigUInt64LE(value: bigint, offset: number): number; + writeBigUInt64BE(value: bigint, offset: number): number; writeInt8(value: number, offset: number, noAssert?: boolean): number; writeInt16LE(value: number, offset: number, noAssert?: boolean): number; writeInt16BE(value: number, offset: number, noAssert?: boolean): number; writeInt32LE(value: number, offset: number, noAssert?: boolean): number; writeInt32BE(value: number, offset: number, noAssert?: boolean): number; - writeBigInt64LE(value: number, offset: number): BigInt; - writeBigInt64BE(value: number, offset: number): BigInt; + writeBigInt64LE(value: bigint, offset: number): number; + writeBigInt64BE(value: bigint, offset: number): number; writeFloatLE(value: number, offset: number, noAssert?: boolean): number; writeFloatBE(value: number, offset: number, noAssert?: boolean): number; writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; diff --git a/index.js b/index.js index cc199262..0d8b52d2 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,8 @@ const customInspectSymbol = ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation : null +const decoderUTF8 = new TextDecoder('utf8') + exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer exports.INSPECT_MAX_BYTES = 50 @@ -22,6 +24,16 @@ exports.INSPECT_MAX_BYTES = 50 const K_MAX_LENGTH = 0x7fffffff exports.kMaxLength = K_MAX_LENGTH +// not used, but value is added for maintain api compatability +// max length will vary from browser to browser, but using a likely expected value circa Node v8 +const K_STRING_MAX_LENGTH = 2**28 - 1 +exports.kStringMaxLength = K_STRING_MAX_LENGTH + +exports.constants = { + MAX_LENGTH: K_MAX_LENGTH, + MAX_STRING_LENGTH: K_STRING_MAX_LENGTH +} + /** * If `Buffer.TYPED_ARRAY_SUPPORT`: * === true Use Uint8Array implementation (fastest) @@ -376,6 +388,7 @@ Buffer.isEncoding = function isEncoding (encoding) { case 'ascii': case 'latin1': case 'binary': + case 'base64url': case 'base64': case 'ucs2': case 'ucs-2': @@ -533,8 +546,9 @@ function slowToString (encoding, start, end) { case 'binary': return latin1Slice(this, start, end) + case 'base64url': case 'base64': - return base64Slice(this, start, end) + return base64Slice(this, start, end, encoding) case 'ucs2': case 'ucs-2': @@ -844,9 +858,12 @@ function hexWrite (buf, string, offset, length) { } let i for (i = 0; i < length; ++i) { - const parsed = parseInt(string.substr(i * 2, 2), 16) - if (numberIsNaN(parsed)) return i - buf[offset + i] = parsed + const a = hexCharValueTable[string[i * 2]] + const b = hexCharValueTable[string[i * 2 + 1]] + if (a === undefined || b === undefined) { + return i + } + buf[offset + i] = a << 4 | b } return i } @@ -859,8 +876,9 @@ function asciiWrite (buf, string, offset, length) { return blitBuffer(asciiToBytes(string), buf, offset, length) } -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) +function base64Write (buf, string, offset, length, encoding) { + const b64 = encoding === 'base64url' ? base64urlToBase64(string) : string + return blitBuffer(base64ToBytes(b64), buf, offset, length) } function ucs2Write (buf, string, offset, length) { @@ -918,9 +936,11 @@ Buffer.prototype.write = function write (string, offset, length, encoding) { case 'binary': return asciiWrite(this, string, offset, length) + case 'base64url': case 'base64': + // console.log(encoding, '::', string) // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) + return base64Write(this, string, offset, length, encoding) case 'ucs2': case 'ucs-2': @@ -943,15 +963,24 @@ Buffer.prototype.toJSON = function toJSON () { } } -function base64Slice (buf, start, end) { +function base64Slice (buf, start, end, encoding) { + let b64 if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) + b64 = base64.fromByteArray(buf) } else { - return base64.fromByteArray(buf.slice(start, end)) + b64 = base64.fromByteArray(buf.slice(start, end)) } + return encoding === 'base64url' ? base64urlFromBase64(b64) : b64 } +// For smaller buffers than this TextDecoder#decode appears +// to have more overhead than doing it in Javascript directly. +const TEXT_DECODER_THRESHOLD = 7000 + function utf8Slice (buf, start, end) { + if ((end - start) > TEXT_DECODER_THRESHOLD) { + return decoderUTF8.decode(buf.slice(start, end)) + } end = Math.min(buf.length, end) const res = [] @@ -1215,14 +1244,14 @@ Buffer.prototype.readBigUInt64LE = defineBigIntMethod(function readBigUInt64LE ( } const lo = first + - this[++offset] * 2 ** 8 + - this[++offset] * 2 ** 16 + - this[++offset] * 2 ** 24 + this[++offset] * 256 + + this[++offset] * 65536 + + this[++offset] * 16777216 const hi = this[++offset] + - this[++offset] * 2 ** 8 + - this[++offset] * 2 ** 16 + - last * 2 ** 24 + this[++offset] * 256 + + this[++offset] * 65536 + + last * 16777216 return BigInt(lo) + (BigInt(hi) << BigInt(32)) }) @@ -1236,14 +1265,14 @@ Buffer.prototype.readBigUInt64BE = defineBigIntMethod(function readBigUInt64BE ( boundsError(offset, this.length - 8) } - const hi = first * 2 ** 24 + - this[++offset] * 2 ** 16 + - this[++offset] * 2 ** 8 + + const hi = first * 16777216 + + this[++offset] * 65536 + + this[++offset] * 256 + this[++offset] - const lo = this[++offset] * 2 ** 24 + - this[++offset] * 2 ** 16 + - this[++offset] * 2 ** 8 + + const lo = this[++offset] * 16777216 + + this[++offset] * 65536 + + this[++offset] * 256 + last return (BigInt(hi) << BigInt(32)) + BigInt(lo) @@ -1336,15 +1365,15 @@ Buffer.prototype.readBigInt64LE = defineBigIntMethod(function readBigInt64LE (of } const val = this[offset + 4] + - this[offset + 5] * 2 ** 8 + - this[offset + 6] * 2 ** 16 + + this[offset + 5] * 256 + + this[offset + 6] * 65536 + (last << 24) // Overflow return (BigInt(val) << BigInt(32)) + BigInt(first + - this[++offset] * 2 ** 8 + - this[++offset] * 2 ** 16 + - this[++offset] * 2 ** 24) + this[++offset] * 256 + + this[++offset] * 65536 + + this[++offset] * 16777216) }) Buffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE (offset) { @@ -1357,14 +1386,14 @@ Buffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE (of } const val = (first << 24) + // Overflow - this[++offset] * 2 ** 16 + - this[++offset] * 2 ** 8 + + this[++offset] * 65536 + + this[++offset] * 256 + this[++offset] return (BigInt(val) << BigInt(32)) + - BigInt(this[++offset] * 2 ** 24 + - this[++offset] * 2 ** 16 + - this[++offset] * 2 ** 8 + + BigInt(this[++offset] * 16777216 + + this[++offset] * 65536 + + this[++offset] * 256 + last) }) @@ -1861,11 +1890,11 @@ E('ERR_OUT_OF_RANGE', function (str, range, input) { let msg = `The value of "${str}" is out of range.` let received = input - if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + if (Number.isInteger(input) && Math.abs(input) > 4294967296) { received = addNumericalSeparator(String(input)) } else if (typeof input === 'bigint') { received = String(input) - if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) { + if (input > BigInt(4294967296) || input < -(BigInt(4294967296))) { received = addNumericalSeparator(received) } received += 'n' @@ -1939,6 +1968,23 @@ function boundsError (value, length, type) { const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g +const BASE64_CHAR_62 = '+' +const BASE64_CHAR_63 = '/' +const BASE64URL_CHAR_62 = '-' +const BASE64URL_CHAR_63 = '_' + +function base64urlToBase64 (str) { + return str + .replaceAll(BASE64URL_CHAR_62, BASE64_CHAR_62) + .replaceAll(BASE64URL_CHAR_63, BASE64_CHAR_63) +} + +function base64urlFromBase64 (str) { + return str + .replaceAll(BASE64_CHAR_62, BASE64URL_CHAR_62) + .replaceAll(BASE64_CHAR_63, BASE64URL_CHAR_63) +} + function base64clean (str) { // Node takes equal signs as end of the Base64 encoding str = str.split('=')[0] @@ -2098,6 +2144,17 @@ const hexSliceLookupTable = (function () { return table })() +// Lookup table for Buffer.from(x, 'hex') +const hexCharValueTable = (function () { + const alphabet = '0123456789abcdefABCDEF' + const table = {} + for (let i = 0; i < 22; ++i) { + // ABCDEF should be same value as abcdef + table[alphabet[i]] = i < 16 ? i : i - 6 + } + return table +})() + // Return not function with Error if BigInt not supported function defineBigIntMethod (fn) { return typeof BigInt === 'undefined' ? BufferBigIntNotDefined : fn diff --git a/package.json b/package.json index ca1ad9a7..742db3d2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "buffer", + "name": "@caspertech/buffer", "description": "Node.js Buffer API, for the browser", - "version": "6.0.3", + "version": "6.0.8", "author": { "name": "Feross Aboukhadijeh", "email": "feross@feross.org", @@ -66,7 +66,9 @@ "test-browser-new": "airtap -- test/*.js test/node/*.js", "test-browser-new-local": "airtap --local -- test/*.js test/node/*.js", "test-node": "tape test/*.js test/node/*.js", - "update-authors": "./bin/update-authors.sh" + "update-authors": "./bin/update-authors.sh", + "lint": "standard", + "lint:fix": "standard --fix" }, "standard": { "ignore": [ diff --git a/perf/readUtf8.js b/perf/readUtf8.js index a0109ff8..e78db9bc 100644 --- a/perf/readUtf8.js +++ b/perf/readUtf8.js @@ -5,15 +5,25 @@ var suite = util.suite() // 256 random bytes var array = [ 152, 130, 206, 23, 243, 238, 197, 44, 27, 86, 208, 36, 163, 184, 164, 21, 94, 242, 178, 46, 25, 26, 253, 178, 72, 147, 207, 112, 236, 68, 179, 190, 29, 83, 239, 147, 125, 55, 143, 19, 157, 68, 157, 58, 212, 224, 150, 39, 128, 24, 94, 225, 120, 121, 75, 192, 112, 19, 184, 142, 203, 36, 43, 85, 26, 147, 227, 139, 242, 186, 57, 78, 11, 102, 136, 117, 180, 210, 241, 92, 3, 215, 54, 167, 249, 1, 44, 225, 146, 86, 2, 42, 68, 21, 47, 238, 204, 153, 216, 252, 183, 66, 222, 255, 15, 202, 16, 51, 134, 1, 17, 19, 209, 76, 238, 38, 76, 19, 7, 103, 249, 5, 107, 137, 64, 62, 170, 57, 16, 85, 179, 193, 97, 86, 166, 196, 36, 148, 138, 193, 210, 69, 187, 38, 242, 97, 195, 219, 252, 244, 38, 1, 197, 18, 31, 246, 53, 47, 134, 52, 105, 72, 43, 239, 128, 203, 73, 93, 199, 75, 222, 220, 166, 34, 63, 236, 11, 212, 76, 243, 171, 110, 78, 39, 205, 204, 6, 177, 233, 212, 243, 0, 33, 41, 122, 118, 92, 252, 0, 157, 108, 120, 70, 137, 100, 223, 243, 171, 232, 66, 126, 111, 142, 33, 3, 39, 117, 27, 107, 54, 1, 217, 227, 132, 13, 166, 3, 73, 53, 127, 225, 236, 134, 219, 98, 214, 125, 148, 24, 64, 142, 111, 231, 194, 42, 150, 185, 10, 182, 163, 244, 19, 4, 59, 135, 16 ] -var browserBuffer = new BrowserBuffer(array) -var nodeBuffer = new Buffer(array) +function addTest(size) { + let arr = array; + for(var i = 0; i < size; i++) { + arr = arr.concat(array); + } -suite - .add('BrowserBuffer#readUtf8', function () { - browserBuffer.toString() - }) + var browserBuffer = BrowserBuffer.from(arr) + var nodeBuffer = Buffer.from(arr) + suite + .add('BrowserBuffer#readUtf8 ' + nodeBuffer.byteLength, function () { + browserBuffer.toString() + }) -if (!process.browser) suite - .add('NodeBuffer#readUtf8', function () { - nodeBuffer.toString() - }) + if (!process.browser) suite + .add('NodeBuffer#readUtf8 ' + nodeBuffer.byteLength, function () { + nodeBuffer.toString() + }) +} + +for(var i = 0; i < 6; i++) { + addTest(i * 10); +} \ No newline at end of file diff --git a/test/base64.js b/test/base64.js index 977225b3..ebd2de0c 100644 --- a/test/base64.js +++ b/test/base64.js @@ -53,3 +53,21 @@ test('base64: high byte', function (t) { ) t.end() }) + +test('base64url: convert to/from base64', function (t) { + const base64url = '8J-Ps--4j_Cfj7PvuI8=' + const base64 = '8J+Ps++4j/Cfj7PvuI8=' + const text = '🏳️🏳️' + + const base64urlBuf = new B(base64url, 'base64url') + t.equal(base64urlBuf.toString('base64'), base64) + t.equal(base64urlBuf.toString(), text) + + const base64Buf = new B(base64, 'base64') + t.equal(base64Buf.toString('base64url'), base64url) + t.equal(base64Buf.toString(), text) + + const buf = new B(text) + t.equal(buf.toString('base64url'), base64url) + t.end() +}) diff --git a/test/node/test-buffer-badhex.js b/test/node/test-buffer-badhex.js index a6388e31..439ec7ca 100644 --- a/test/node/test-buffer-badhex.js +++ b/test/node/test-buffer-badhex.js @@ -5,7 +5,7 @@ const assert = require('assert'); // Test hex strings and bad hex strings { - const buf = Buffer.alloc(4); + let buf = Buffer.alloc(4); assert.strictEqual(buf.length, 4); assert.deepStrictEqual(buf, new Buffer([0, 0, 0, 0])); assert.strictEqual(buf.write('abcdxx', 0, 'hex'), 2); @@ -14,6 +14,13 @@ const assert = require('assert'); assert.strictEqual(buf.write('abcdef01', 0, 'hex'), 4); assert.deepStrictEqual(buf, new Buffer([0xab, 0xcd, 0xef, 0x01])); assert.strictEqual(buf.toString('hex'), 'abcdef01'); + // Node Buffer behavior check + // > Buffer.from('abc def01','hex') + // + buf = Buffer.alloc(4); + assert.strictEqual(buf.write('abc def01', 0, 'hex'), 1); + assert.deepStrictEqual(buf, new Buffer([0xab, 0x0, 0x0, 0x0])); + assert.strictEqual(buf.toString('hex'), 'ab000000'); const copy = Buffer.from(buf.toString('hex'), 'hex'); assert.strictEqual(buf.toString('hex'), copy.toString('hex')); diff --git a/test/node/test-buffer.js b/test/node/test-buffer.js index 3a8c4527..4397ce26 100644 --- a/test/node/test-buffer.js +++ b/test/node/test-buffer.js @@ -4,6 +4,7 @@ var Buffer = require('../../').Buffer; var common = { skip: function () {} }; var assert = require('assert'); +const buffer = require('../../'); var Buffer = require('../../').Buffer; var SlowBuffer = require('../../').SlowBuffer; @@ -442,10 +443,10 @@ for (var i = 0; i < Buffer.byteLength(utf8String); i++) { { // Bug regression test var testValue = '\u00F6\u65E5\u672C\u8A9E'; // ö日本語 - var buffer = new Buffer(32); - var size = buffer.write(testValue, 0, 'utf8'); -// console.log('bytes written to buffer: ' + size); - var slice = buffer.toString('utf8', 0, size); + var testBuffer = new Buffer(32); + var size = testBuffer.write(testValue, 0, 'utf8'); +// console.log('bytes written to testBuffer: ' + size); + var slice = testBuffer.toString('utf8', 0, size); assert.equal(slice, testValue); } @@ -1050,12 +1051,12 @@ Buffer(Buffer(0), 0, 0); // GH-5110 { - var buffer = new Buffer('test'); - var string = JSON.stringify(buffer); + var testBuffer = new Buffer('test'); + var string = JSON.stringify(testBuffer); assert.strictEqual(string, '{"type":"Buffer","data":[116,101,115,116]}'); - assert.deepStrictEqual(buffer, JSON.parse(string, function(key, value) { + assert.deepStrictEqual(testBuffer, JSON.parse(string, function(key, value) { return value && value.type === 'Buffer' ? new Buffer(value.data) : value; @@ -1498,3 +1499,9 @@ assert.throws(() => Buffer(-100), assert.throws(() => Buffer(-1), '"size" argument must not be negative'); +// Verify constants +assert.equal(0x7fffffff, buffer.kMaxLength) +assert.equal(buffer.kMaxLength, buffer.constants.MAX_LENGTH) + +assert.equal(2**28 - 1, buffer.kStringMaxLength) +assert.equal(buffer.kStringMaxLength, buffer.constants.MAX_STRING_LENGTH) diff --git a/test/to-string.js b/test/to-string.js index 3f146a8e..376affa0 100644 --- a/test/to-string.js +++ b/test/to-string.js @@ -162,12 +162,24 @@ test('utf8 replacement chars (3 byte sequences)', function (t) { ) t.equal( new B([0xE0, 0xAC]).toString(), - '\uFFFD\uFFFD' + '\uFFFD' ) t.equal( new B([0xE0, 0xAC, 0xB9]).toString(), '\u0B39' ) + t.equal( + new B([0xE0, 64]).toString(), + '\uFFFD@' + ) + t.equal( + new B([0xE0, 0xAC, 64]).toString(), + '\uFFFD@' + ) + t.equal( + new B([0xE0, 0xAC, 0xB9, 64]).toString(), + '\u0B39@' + ) t.end() }) @@ -178,11 +190,11 @@ test('utf8 replacement chars (4 byte sequences)', function (t) { ) t.equal( new B([0xF4, 0x8F]).toString(), - '\uFFFD\uFFFD' + '\uFFFD' ) t.equal( new B([0xF4, 0x8F, 0x80]).toString(), - '\uFFFD\uFFFD\uFFFD' + '\uFFFD' ) t.equal( new B([0xF4, 0x8F, 0x80, 0x84]).toString(), @@ -201,8 +213,17 @@ test('utf8 replacement chars (4 byte sequences)', function (t) { test('utf8 replacement chars on 256 random bytes', function (t) { t.equal( + // generated via + // let str = Buffer.from([...]).toString() + // let encoded = ""; + // for (let i = 0; i < str.length; i++) { + // encoded += + // "\\u" + str.charCodeAt(i).toString(16).toUpperCase().padStart(4, 0); + // } + // console.log(encoded); + new B([152, 130, 206, 23, 243, 238, 197, 44, 27, 86, 208, 36, 163, 184, 164, 21, 94, 242, 178, 46, 25, 26, 253, 178, 72, 147, 207, 112, 236, 68, 179, 190, 29, 83, 239, 147, 125, 55, 143, 19, 157, 68, 157, 58, 212, 224, 150, 39, 128, 24, 94, 225, 120, 121, 75, 192, 112, 19, 184, 142, 203, 36, 43, 85, 26, 147, 227, 139, 242, 186, 57, 78, 11, 102, 136, 117, 180, 210, 241, 92, 3, 215, 54, 167, 249, 1, 44, 225, 146, 86, 2, 42, 68, 21, 47, 238, 204, 153, 216, 252, 183, 66, 222, 255, 15, 202, 16, 51, 134, 1, 17, 19, 209, 76, 238, 38, 76, 19, 7, 103, 249, 5, 107, 137, 64, 62, 170, 57, 16, 85, 179, 193, 97, 86, 166, 196, 36, 148, 138, 193, 210, 69, 187, 38, 242, 97, 195, 219, 252, 244, 38, 1, 197, 18, 31, 246, 53, 47, 134, 52, 105, 72, 43, 239, 128, 203, 73, 93, 199, 75, 222, 220, 166, 34, 63, 236, 11, 212, 76, 243, 171, 110, 78, 39, 205, 204, 6, 177, 233, 212, 243, 0, 33, 41, 122, 118, 92, 252, 0, 157, 108, 120, 70, 137, 100, 223, 243, 171, 232, 66, 126, 111, 142, 33, 3, 39, 117, 27, 107, 54, 1, 217, 227, 132, 13, 166, 3, 73, 53, 127, 225, 236, 134, 219, 98, 214, 125, 148, 24, 64, 142, 111, 231, 194, 42, 150, 185, 10, 182, 163, 244, 19, 4, 59, 135, 16]).toString(), - '\uFFFD\uFFFD\uFFFD\u0017\uFFFD\uFFFD\uFFFD\u002C\u001B\u0056\uFFFD\u0024\uFFFD\uFFFD\uFFFD\u0015\u005E\uFFFD\uFFFD\u002E\u0019\u001A\uFFFD\uFFFD\u0048\uFFFD\uFFFD\u0070\uFFFD\u0044\uFFFD\uFFFD\u001D\u0053\uFFFD\uFFFD\u007D\u0037\uFFFD\u0013\uFFFD\u0044\uFFFD\u003A\uFFFD\uFFFD\uFFFD\u0027\uFFFD\u0018\u005E\uFFFD\u0078\u0079\u004B\uFFFD\u0070\u0013\uFFFD\uFFFD\uFFFD\u0024\u002B\u0055\u001A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0039\u004E\u000B\u0066\uFFFD\u0075\uFFFD\uFFFD\uFFFD\u005C\u0003\uFFFD\u0036\uFFFD\uFFFD\u0001\u002C\uFFFD\uFFFD\u0056\u0002\u002A\u0044\u0015\u002F\uFFFD\u0319\uFFFD\uFFFD\uFFFD\u0042\uFFFD\uFFFD\u000F\uFFFD\u0010\u0033\uFFFD\u0001\u0011\u0013\uFFFD\u004C\uFFFD\u0026\u004C\u0013\u0007\u0067\uFFFD\u0005\u006B\uFFFD\u0040\u003E\uFFFD\u0039\u0010\u0055\uFFFD\uFFFD\u0061\u0056\uFFFD\uFFFD\u0024\uFFFD\uFFFD\uFFFD\uFFFD\u0045\uFFFD\u0026\uFFFD\u0061\uFFFD\uFFFD\uFFFD\uFFFD\u0026\u0001\uFFFD\u0012\u001F\uFFFD\u0035\u002F\uFFFD\u0034\u0069\u0048\u002B\uFFFD\uFFFD\uFFFD\u0049\u005D\uFFFD\u004B\uFFFD\u0726\u0022\u003F\uFFFD\u000B\uFFFD\u004C\uFFFD\uFFFD\u006E\u004E\u0027\uFFFD\uFFFD\u0006\uFFFD\uFFFD\uFFFD\uFFFD\u0000\u0021\u0029\u007A\u0076\u005C\uFFFD\u0000\uFFFD\u006C\u0078\u0046\uFFFD\u0064\uFFFD\uFFFD\uFFFD\uFFFD\u0042\u007E\u006F\uFFFD\u0021\u0003\u0027\u0075\u001B\u006B\u0036\u0001\uFFFD\uFFFD\uFFFD\u000D\uFFFD\u0003\u0049\u0035\u007F\uFFFD\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u007D\uFFFD\u0018\u0040\uFFFD\u006F\uFFFD\uFFFD\u002A\uFFFD\uFFFD\u000A\uFFFD\uFFFD\uFFFD\u0013\u0004\u003B\uFFFD\u0010' + '\uFFFD\uFFFD\uFFFD\u0017\uFFFD\uFFFD\uFFFD\u002C\u001B\u0056\uFFFD\u0024\uFFFD\uFFFD\uFFFD\u0015\u005E\uFFFD\u002E\u0019\u001A\uFFFD\uFFFD\u0048\uFFFD\uFFFD\u0070\uFFFD\u0044\uFFFD\uFFFD\u001D\u0053\uFFFD\u007D\u0037\uFFFD\u0013\uFFFD\u0044\uFFFD\u003A\uFFFD\uFFFD\uFFFD\u0027\uFFFD\u0018\u005E\uFFFD\u0078\u0079\u004B\uFFFD\u0070\u0013\uFFFD\uFFFD\uFFFD\u0024\u002B\u0055\u001A\uFFFD\uFFFD\uFFFD\u0039\u004E\u000B\u0066\uFFFD\u0075\uFFFD\uFFFD\uFFFD\u005C\u0003\uFFFD\u0036\uFFFD\uFFFD\u0001\u002C\uFFFD\u0056\u0002\u002A\u0044\u0015\u002F\uFFFD\u0319\uFFFD\uFFFD\uFFFD\u0042\uFFFD\uFFFD\u000F\uFFFD\u0010\u0033\uFFFD\u0001\u0011\u0013\uFFFD\u004C\uFFFD\u0026\u004C\u0013\u0007\u0067\uFFFD\u0005\u006B\uFFFD\u0040\u003E\uFFFD\u0039\u0010\u0055\uFFFD\uFFFD\u0061\u0056\uFFFD\uFFFD\u0024\uFFFD\uFFFD\uFFFD\uFFFD\u0045\uFFFD\u0026\uFFFD\u0061\uFFFD\uFFFD\uFFFD\uFFFD\u0026\u0001\uFFFD\u0012\u001F\uFFFD\u0035\u002F\uFFFD\u0034\u0069\u0048\u002B\uFFFD\uFFFD\u0049\u005D\uFFFD\u004B\uFFFD\u0726\u0022\u003F\uFFFD\u000B\uFFFD\u004C\uFFFD\u006E\u004E\u0027\uFFFD\uFFFD\u0006\uFFFD\uFFFD\uFFFD\uFFFD\u0000\u0021\u0029\u007A\u0076\u005C\uFFFD\u0000\uFFFD\u006C\u0078\u0046\uFFFD\u0064\uFFFD\uFFFD\uFFFD\u0042\u007E\u006F\uFFFD\u0021\u0003\u0027\u0075\u001B\u006B\u0036\u0001\uFFFD\uFFFD\u000D\uFFFD\u0003\u0049\u0035\u007F\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u007D\uFFFD\u0018\u0040\uFFFD\u006F\uFFFD\uFFFD\u002A\uFFFD\uFFFD\u000A\uFFFD\uFFFD\uFFFD\u0013\u0004\u003B\uFFFD\u0010' ) t.end() })