From 9acd8cd8ac99dcb3471d966cc10236a57d56c3b7 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 24 Jun 2019 12:23:38 -0400 Subject: [PATCH 01/24] Preliminary work to support UTF-32. --- .gitignore | 1 + encodings/index.js | 3 +- encodings/utf32.js | 410 +++++++++++++++++++ package-lock.json | 994 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- test/utf32-test.js | 90 ++++ 6 files changed, 1498 insertions(+), 2 deletions(-) create mode 100644 encodings/utf32.js create mode 100644 package-lock.json create mode 100644 test/utf32-test.js diff --git a/.gitignore b/.gitignore index 9808b12..e849c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ wiki *~ *sublime-* coverage +/.idea diff --git a/encodings/index.js b/encodings/index.js index e304003..d95c244 100644 --- a/encodings/index.js +++ b/encodings/index.js @@ -4,6 +4,7 @@ // We support Browserify by skipping automatic module discovery and requiring modules directly. var modules = [ require("./internal"), + require("./utf32"), require("./utf16"), require("./utf7"), require("./sbcs-codec"), @@ -13,7 +14,7 @@ var modules = [ require("./dbcs-data"), ]; -// Put all encoding/alias/codec definitions to single object and export it. +// Put all encoding/alias/codec definitions to single object and export it. for (var i = 0; i < modules.length; i++) { var module = modules[i]; for (var enc in module) diff --git a/encodings/utf32.js b/encodings/utf32.js new file mode 100644 index 0000000..598522a --- /dev/null +++ b/encodings/utf32.js @@ -0,0 +1,410 @@ +'use strict'; + +var Buffer = require('safer-buffer').Buffer; +var BOMChar = '\uFEFF'; + +// == UTF32-LE codec. ========================================================== + +exports.utf32le = Utf32LECodec; + +function Utf32LECodec(options, iconv) { + this.iconv = iconv; +} + +Utf32LECodec.prototype.encoder = Utf32LEEncoder; +Utf32LECodec.prototype.decoder = Utf32LEDecoder; +Utf32LECodec.prototype.bomAware = true; + +// -- Encoding + +function Utf32LEEncoder(options) { + this.addBOM = options && options.addBOM; + this.highSurrogate = null; +} + +Utf32LEEncoder.prototype.write = function(str) { + if (this.addBOM) { + str = BOMChar + str; + this.addBOM = false; + } + + var src = Buffer.from(str, 'ucs2'); + var dst = Buffer.alloc(src.length * 2); + var offset = 0; + + for (var i = 0; i < src.length; i += 2) { + var lowByte = src[i]; + var highByte = src[i + 1]; + var isHighSurrogate = (0xD8 <= highByte && highByte < 0xDC); + var isLowSurrogate = (0xDC <= highByte && highByte < 0xE0); + + if (this.highSurrogate) { + if (isHighSurrogate || !isLowSurrogate) { + // There shouldn't be two high surrogates in a row, nor a high surrogate which isn't followed by a low + // surrogate. If this happens, keep the pending high surrogate as a stand-alone semi-invalid character + // (technically wrong, but expected by some applications, like Windows file names). + dst[offset++] = this.highSurrogate[0]; dst[offset++] = this.highSurrogate[1]; dst[offset++] = 0; dst[offset++] = 0; + } + else { + // Create 32-bit value from high and low surrogates; + var codepoint = (((this.highSurrogate[1] - 0xD8) << 18) | (this.highSurrogate[0] << 10) | + ((highByte - 0xDC) << 8) | lowByte) + 0x10000; + + dst[offset++] = codepoint & 0x000000FF; + dst[offset++] = (codepoint & 0x0000FF00) >> 8; + dst[offset++] = (codepoint & 0x00FF0000) >> 16; + dst[offset++] = (codepoint & 0xFF000000) >> 24; + this.highSurrogate = null; + + continue; + } + } + + if (isHighSurrogate) + this.highSurrogate = [lowByte, highByte]; + else { + // Even if the current character is a low surrogate, with no previous high surrogate, we'll + // encode it as a semi-invalid stand-alone character for the same reasons expressed above for + // unpaired high surrogates. + dst[offset++] = lowByte; dst[offset++] = highByte; dst[offset++] = 0; dst[offset++] = 0; + this.highSurrogate = null; + } + } + + if (offset < dst.length) + dst = dst.slice(0, offset); + + return dst; +}; + +Utf32LEEncoder.prototype.end = function() { + // Treat any leftover high surrogate as a semi-valid independent character. + if (!this.highSurrogate) + return; + + var buf = Buffer.alloc(4); + + buf[0] = this.highSurrogate[0]; buf[1] = this.highSurrogate[1]; buf[2] = 0; buf[3] = 0; + this.highSurrogate = null; + + return buf; +}; + +// -- Decoding + +function Utf32LEDecoder(options, codec) { + this.badChar = Buffer.from(codec.iconv.defaultCharUnicode, 'ucs2'); + this.overflow = null; +} + +Utf32LEDecoder.prototype.write = function(src) { + if (src.length === 0) + return ''; + else if (this.overflow) { + src = Buffer.concat([this.overflow, src]); + } + + var goodLength = src.length - src.length % 4; + + if (src.length !== goodLength) { + this.overflow = src.slice(goodLength); + src = src.slice(0, goodLength); + } + else + this.overflow = null; + + var dst = Buffer.alloc(goodLength); + var offset = 0; + + for (var i = 0; i < goodLength; i += 4) { + var b0 = src[i]; + var b1 = src[i + 1]; + var b2 = src[i + 2]; + var b3 = src[i + 3]; + + if (b3 === 0 && b2 === 0) { + // Simple 16-bit character + dst[offset++] = b0; dst[offset++] = b1; + } + else { + var codepoint = b3 * 0x1000000 + b2 * 0x10000 + b1 * 0x100 + b0; + + if (codepoint > 0x10FFFF) { + // Not a valid Unicode codepoint + dst[offset++] = this.badChar[0]; dst[offset++] = this.badChar[1]; + } + else { + codepoint -= 0x10000; + var high = 0xD800 | (codepoint >> 10); + var low = 0xDC00 + (codepoint & 0x3FF); + dst[offset++] = high & 0xFF; dst[offset++] = high >> 8; + dst[offset++] = low & 0xFF; dst[offset++] = low >> 8; + } + } + } + + return dst.slice(0, offset).toString('ucs2'); +}; + +Utf32LEDecoder.prototype.end = function() { + this.overflow = null; +}; + +// == UTF32-BE codec. ========================================================== + +exports.utf32be = Utf32BECodec; + +function Utf32BECodec(options, iconv) { + this.iconv = iconv; +} + +Utf32BECodec.prototype.encoder = Utf32BEEncoder; +Utf32BECodec.prototype.decoder = Utf32BEDecoder; +Utf32BECodec.prototype.bomAware = true; + +// -- Encoding + +function Utf32BEEncoder(options) { + this.addBOM = options && options.addBOM; + this.highSurrogate = null; +} + +Utf32BEEncoder.prototype.write = function(str) { + if (this.addBOM) { + str = BOMChar + str; + this.addBOM = false; + } + + var src = Buffer.from(str, 'ucs2'); + var dst = Buffer.alloc(src.length * 2); + var offset = 0; + + for (var i = 0; i < src.length; i += 2) { + var lowByte = src[i]; + var highByte = src[i + 1]; + var isHighSurrogate = (0xD8 <= highByte && highByte < 0xDC); + var isLowSurrogate = (0xDC <= highByte && highByte < 0xE0); + + if (this.highSurrogate) { + if (isHighSurrogate || !isLowSurrogate) { + // There shouldn't be two high surrogates in a row, nor a high surrogate which isn't followed by a low + // surrogate. If this happens, keep the pending high surrogate as a stand-alone semi-invalid character + // (technically wrong, but expected by some applications, like Windows file names). + dst[offset++] = 0; dst[offset++] = 0; dst[offset++] = this.highSurrogate[1]; dst[offset++] = this.highSurrogate[0]; + } + else { + // Create 32-bit value from high and low surrogates; + var codepoint = (((this.highSurrogate[1] - 0xD8) << 18) | (this.highSurrogate[0] << 10) | + ((highByte - 0xDC) << 8) | lowByte) + 0x10000; + + dst[offset++] = (codepoint & 0xFF000000) >> 24; + dst[offset++] = (codepoint & 0x00FF0000) >> 16; + dst[offset++] = (codepoint & 0x0000FF00) >> 8; + dst[offset++] = codepoint & 0x000000FF; + this.highSurrogate = null; + + continue; + } + } + + if (isHighSurrogate) + this.highSurrogate = [lowByte, highByte]; + else { + // Even if the current character is a low surrogate, with no previous high surrogate, we'll + // encode it as a semi-invalid stand-alone character for the same reasons expressed above for + // unpaired high surrogates. + dst[offset++] = 0; dst[offset++] = 0; dst[offset++] = highByte; dst[offset++] = lowByte; + this.highSurrogate = null; + } + } + + if (offset < dst.length) + dst = dst.slice(0, offset); + + return dst; +}; + +Utf32BEEncoder.prototype.end = function() { + // Treat any leftover high surrogate as a semi-invalid independent character. + if (!this.highSurrogate) + return; + + var buf = Buffer.alloc(4); + + buf[0] = 0; buf[1] = 0; buf[2] = this.highSurrogate[1]; buf[3] = this.highSurrogate[0]; + this.highSurrogate = null; + + return buf; +}; + + +// -- Decoding + +function Utf32BEDecoder(options, codec) { + this.badChar = Buffer.from(codec.iconv.defaultCharUnicode, 'ucs2'); + this.overflow = null; +} + +Utf32BEDecoder.prototype.write = function(src) { + if (src.length === 0) + return ''; + else if (this.overflow) { + src = Buffer.concat([this.overflow, src]); + } + + var goodLength = src.length - src.length % 4; + + if (src.length !== goodLength) { + this.overflow = src.slice(goodLength); + src = src.slice(0, goodLength); + } + else + this.overflow = null; + + var dst = Buffer.alloc(goodLength); + var offset = 0; + + for (var i = 0; i < goodLength; i += 4) { + var b3 = src[i]; + var b2 = src[i + 1]; + var b1 = src[i + 2]; + var b0 = src[i + 3]; + + if (b3 === 0 && b2 === 0) { + // Simple 16-bit character + dst[offset++] = b0; dst[offset++] = b1; + } + else { + var codepoint = b3 * 0x1000000 + b2 * 0x10000 + b1 * 0x100 + b0; + + if (codepoint > 0x10FFFF) { + // Not a valid Unicode codepoint + dst[offset++] = this.badChar[0]; dst[offset++] = this.badChar[1]; + } + else { + codepoint -= 0x10000; + var high = 0xD800 | (codepoint >> 10); + var low = 0xDC00 + (codepoint & 0x3FF); + dst[offset++] = high & 0xFF; dst[offset++] = high >> 8; + dst[offset++] = low & 0xFF; dst[offset++] = low >> 8; + } + } + } + + return dst.slice(0, offset).toString('ucs2'); +}; + +Utf32BEDecoder.prototype.end = function() { + this.overflow = null; +}; + +// == UTF-32 codec ============================================================= +// Decoder chooses automatically from UTF-32LE and UTF-32BE using BOM and space-based heuristic. +// Defaults to UTF-32LE. http://en.wikipedia.org/wiki/UTF-32 +// Decoder default can be changed: iconv.decode(buf, 'utf36', {defaultEncoding: 'utf-32be'}); + +// Encoder prepends BOM (which can be overridden with addBOM: false). + +exports.utf32 = Utf32Codec; + +function Utf32Codec(codecOptions, iconv) { + this.iconv = iconv; +} + +Utf32Codec.prototype.encoder = Utf32Encoder; +Utf32Codec.prototype.decoder = Utf32Decoder; + +// -- Encoding + +function Utf32Encoder(options, codec) { + options = options || {}; + + if (options.addBOM === undefined) + options.addBOM = true; + + this.encoder = codec.iconv.getEncoder('utf-32le', options); +} + +Utf32Encoder.prototype.write = function(str) { + return this.encoder.write(str); +}; + +Utf32Encoder.prototype.end = function() { + return this.encoder.end(); +}; + + +// -- Decoding + +function Utf32Decoder(options, codec) { + this.decoder = null; + this.initialBytes = []; + this.initialBytesLen = 0; + this.options = options || {}; + this.iconv = codec.iconv; +} + +Utf32Decoder.prototype.write = function(buf) { + if (!this.decoder) { + // Codec is not chosen yet. Accumulate initial bytes. + this.initialBytes.push(buf); + this.initialBytesLen += buf.length; + + if (this.initialBytesLen < 32) // We need more bytes to use space heuristic (see below) + return ''; + + // We have enough bytes -> detect endianness. + var buf2 = Buffer.concat(this.initialBytes), + encoding = detectEncoding(buf2, this.options.defaultEncoding); + this.decoder = this.iconv.getDecoder(encoding, this.options); + this.initialBytes.length = this.initialBytesLen = 0; + } + + return this.decoder.write(buf); +}; + + +Utf32Decoder.prototype.end = function() { + if (!this.decoder) { + var buf = Buffer.concat(this.initialBytes), + encoding = detectEncoding(buf, this.options.defaultEncoding); + this.decoder = this.iconv.getDecoder(encoding, this.options); + + var res = this.decoder.write(buf), + trail = this.decoder.end(); + + return trail ? (res + trail) : res; + } + return this.decoder.end(); +}; + +function detectEncoding(buf, defaultEncoding) { + var enc = defaultEncoding || 'utf-32le'; + + if (buf.length >= 4) { + // Check BOM. + if (buf[0] === 0 && buf[1] === 0 && buf[2] === 0xFE && buf[3] === 0xFF) // UTF-32BE BOM + enc = 'utf-32be'; + else if (buf[0] === 0xFF && buf[1] === 0xFE && buf[2] === 0 && buf[3] === 0) // UTF-32LE BOM + enc = 'utf-32le'; + else { + // No BOM found. Try to deduce encoding from initial content. + // Most of the time, the content has ASCII chars (U+00**), but the opposite (U+**00) is uncommon. + // So, we count ASCII as if it was LE or BE, and decide from that. + var asciiCharsLE = 0, asciiCharsBE = 0, // Counts of chars in both positions + _len = Math.min(buf.length - (buf.length % 4), 128); // Len is always even. + + for (var i = 0; i < _len; i += 4) { + if (buf[i] === 0 && buf[i + 1] === 0 && buf[i + 2] === 0 && buf[i + 3] !== 0) asciiCharsBE++; + if (buf[i] !== 0 && buf[i + 1] === 0 && buf[i + 2] === 0 && buf[i + 3] === 0) asciiCharsLE++; + } + + if (asciiCharsBE > asciiCharsLE) + enc = 'utf-32be'; + else if (asciiCharsBE < asciiCharsLE) + enc = 'utf-32le'; + } + } + + return enc; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6350265 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,994 @@ +{ + "name": "iconv-lite", + "version": "0.4.24", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true, + "optional": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "async": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", + "integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "errto": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/errto/-/errto-0.2.1.tgz", + "integrity": "sha1-1uI7NyYfhO2GlpX3e0+34GxkeEg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/iconv/-/iconv-2.3.4.tgz", + "integrity": "sha512-v2Rree7xRtrC9o3Bi9nTNOKXvApmwLFdX50k72+K1W4mJ93LBdpaLcHcInYo9gr/GcQk8MFoCetrYRcb/o3RLg==", + "dev": true, + "requires": { + "nan": "^2.13.1", + "safer-buffer": "^2.1.2" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } + } + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "^3.0.0", + "lodash._basecreate": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "he": "1.1.1", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "unorm": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", + "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json index a7c74fc..513e1fa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "iconv-lite", "description": "Convert character encodings in pure javascript.", - "version": "0.4.24", + "version": "0.5.0", "license": "MIT", "keywords": [ "iconv", diff --git a/test/utf32-test.js b/test/utf32-test.js new file mode 100644 index 0000000..c6f1c83 --- /dev/null +++ b/test/utf32-test.js @@ -0,0 +1,90 @@ +var assert = require('assert'), + iconv = require(__dirname+'/../'); + +var testStr = '1aя中文☃💩', + testStr2 = '❝Stray high \uD977😱 and low\uDDDD☔ surrogate values.❞'; + utf32leBuf = Buffer.from([0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x4F, 0x04, 0x00, 0x00, + 0x2D, 0x4E, 0x00, 0x00, 0x87, 0x65, 0x00, 0x00, 0x03, 0x26, 0x00, 0x00, 0xA9, 0xF4, 0x01, 0x00]), + utf32beBuf = Buffer.from([0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x04, 0x4F, + 0x00, 0x00, 0x4E, 0x2D, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x26, 0x03, 0x00, 0x01, 0xF4, 0xA9]), + utf32leBOM = Buffer.from([0xFF, 0xFE, 0x00, 0x00]), + utf32beBOM = Buffer.from([0x00, 0x00, 0xFE, 0xFF]), + sampleStr = '\n<俄语>данные'; + +describe('UTF-32LE codec', function() { + it('encodes basic strings correctly', function() { + assert.equal(iconv.encode(testStr, 'UTF32-LE').toString('hex'), utf32leBuf.toString('hex')); + }); + + it('decodes basic buffers correctly', function() { + assert.equal(iconv.decode(utf32leBuf, 'UTF32-LE'), testStr); + }); + + it('decodes uneven length buffers with no error', function() { + assert.equal(iconv.decode(new Buffer([0x61, 0, 0, 0, 0]), 'UTF32-LE'), 'a'); + }); + + it('handles invalid surrogates gracefully', function() { + var encoded = iconv.encode(testStr2, 'UTF32-LE'); + assert.equal(iconv.decode(encoded, 'UTF32-LE'), testStr2); + }); +}); + +describe('UTF-32LE encoder', function() { + it('Adds BOM when encoding', function() { + // assert.equal(iconv.encode(testStr, 'utf-32le').toString('hex'), utf32leBOM.toString('hex') + utf32leBuf.toString('hex')); + }); +}); + +describe('UTF-32BE codec', function() { + it('encodes basic strings correctly', function() { + assert.equal(iconv.encode(testStr, 'UTF32-BE').toString('hex'), utf32beBuf.toString('hex')); + }); + + it('decodes basic buffers correctly', function() { + assert.equal(iconv.decode(utf32beBuf, 'UTF32-BE'), testStr); + }); + + it('decodes uneven length buffers with no error', function() { + assert.equal(iconv.decode(new Buffer([0, 0, 0, 0x61, 0]), 'UTF32-BE'), 'a'); + }); + + it('handles invalid surrogates gracefully', function() { + var encoded = iconv.encode(testStr2, 'UTF32-BE'); + assert.equal(iconv.decode(encoded, 'UTF32-BE'), testStr2); + }); +}); + +describe('UTF-32BE encoder', function() { + it('Adds BOM when encoding', function() { + // assert.equal(iconv.encode(testStr, 'utf-32be').toString('hex'), utf32beBOM.toString('hex') + utf32beBuf.toString('hex')); + }); +}); + +// describe('UTF-32BE decoder', function() { +// }); + +// describe('UTF-32 decoder', function() { +// it('uses BOM to determine encoding', function() { +// assert.equal(iconv.decode(Buffer.concat([utf16leBOM, utf16leBuf]), 'utf-16'), testStr); +// assert.equal(iconv.decode(Buffer.concat([utf16beBOM, utf16beBuf]), 'utf-16'), testStr); +// }); +// +// it('handles very short buffers nicely', function() { +// assert.equal(iconv.decode(new Buffer([]), 'utf-16'), ''); +// assert.equal(iconv.decode(new Buffer([0x61]), 'utf-16'), ''); +// }); +// +// it('uses spaces when there is no BOM to determine encoding', function() { +// assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-16le'), 'utf-16'), sampleStr); +// assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-16be'), 'utf-16'), sampleStr); +// }); +// +// it('uses UTF-16LE if no BOM and heuristics failed', function() { +// assert.equal(iconv.decode(utf16leBuf, 'utf-16'), testStr); +// }); +// +// it('can be given a different default encoding', function() { +// assert.equal(iconv.decode(utf16leBuf, 'utf-16', {default: 'utf-16le'}), testStr); +// }); +// }); From ace36189a1bd2c043b2084ac058f38fbdf268c66 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 24 Jun 2019 13:46:43 -0400 Subject: [PATCH 02/24] Finish off general UTC-32 (auto LE or BE), and add UCS-4 aliases. --- encodings/utf32.js | 48 ++++++++++++++++-------------- test/utf32-test.js | 74 ++++++++++++++++++++++------------------------ 2 files changed, 62 insertions(+), 60 deletions(-) diff --git a/encodings/utf32.js b/encodings/utf32.js index 598522a..aae0131 100644 --- a/encodings/utf32.js +++ b/encodings/utf32.js @@ -1,11 +1,11 @@ 'use strict'; var Buffer = require('safer-buffer').Buffer; -var BOMChar = '\uFEFF'; // == UTF32-LE codec. ========================================================== exports.utf32le = Utf32LECodec; +exports.ucs4le = Utf32LECodec; function Utf32LECodec(options, iconv) { this.iconv = iconv; @@ -17,17 +17,11 @@ Utf32LECodec.prototype.bomAware = true; // -- Encoding -function Utf32LEEncoder(options) { - this.addBOM = options && options.addBOM; +function Utf32LEEncoder() { this.highSurrogate = null; } Utf32LEEncoder.prototype.write = function(str) { - if (this.addBOM) { - str = BOMChar + str; - this.addBOM = false; - } - var src = Buffer.from(str, 'ucs2'); var dst = Buffer.alloc(src.length * 2); var offset = 0; @@ -153,6 +147,7 @@ Utf32LEDecoder.prototype.end = function() { // == UTF32-BE codec. ========================================================== exports.utf32be = Utf32BECodec; +exports.ucs4be = Utf32BECodec; function Utf32BECodec(options, iconv) { this.iconv = iconv; @@ -164,17 +159,11 @@ Utf32BECodec.prototype.bomAware = true; // -- Encoding -function Utf32BEEncoder(options) { - this.addBOM = options && options.addBOM; +function Utf32BEEncoder() { this.highSurrogate = null; } Utf32BEEncoder.prototype.write = function(str) { - if (this.addBOM) { - str = BOMChar + str; - this.addBOM = false; - } - var src = Buffer.from(str, 'ucs2'); var dst = Buffer.alloc(src.length * 2); var offset = 0; @@ -301,13 +290,14 @@ Utf32BEDecoder.prototype.end = function() { // == UTF-32 codec ============================================================= // Decoder chooses automatically from UTF-32LE and UTF-32BE using BOM and space-based heuristic. // Defaults to UTF-32LE. http://en.wikipedia.org/wiki/UTF-32 -// Decoder default can be changed: iconv.decode(buf, 'utf36', {defaultEncoding: 'utf-32be'}); +// Encoder/decoder default can be changed: iconv.decode(buf, 'utf32', {defaultEncoding: 'utf-32be'}); -// Encoder prepends BOM (which can be overridden with addBOM: false). +// Encoder prepends BOM (which can be overridden with (addBOM: false}). exports.utf32 = Utf32Codec; +exports.ucs4 = Utf32Codec; -function Utf32Codec(codecOptions, iconv) { +function Utf32Codec(options, iconv) { this.iconv = iconv; } @@ -322,7 +312,7 @@ function Utf32Encoder(options, codec) { if (options.addBOM === undefined) options.addBOM = true; - this.encoder = codec.iconv.getEncoder('utf-32le', options); + this.encoder = codec.iconv.getEncoder(options.defaultEncoding || 'utf-32le', options); } Utf32Encoder.prototype.write = function(str) { @@ -375,6 +365,7 @@ Utf32Decoder.prototype.end = function() { return trail ? (res + trail) : res; } + return this.decoder.end(); }; @@ -389,16 +380,29 @@ function detectEncoding(buf, defaultEncoding) { enc = 'utf-32le'; else { // No BOM found. Try to deduce encoding from initial content. - // Most of the time, the content has ASCII chars (U+00**), but the opposite (U+**00) is uncommon. + // Using the wrong endian-ism for UTF-32 will very often result in codepoints that are beyond + // the valid Unicode limit of 0x10FFFF. That will be used as the primary determinant. + // + // Further, we can suppose the content is mostly plain ASCII chars (U+00**). // So, we count ASCII as if it was LE or BE, and decide from that. + var invalidLE = 0, invalidBE = 0; var asciiCharsLE = 0, asciiCharsBE = 0, // Counts of chars in both positions _len = Math.min(buf.length - (buf.length % 4), 128); // Len is always even. for (var i = 0; i < _len; i += 4) { - if (buf[i] === 0 && buf[i + 1] === 0 && buf[i + 2] === 0 && buf[i + 3] !== 0) asciiCharsBE++; - if (buf[i] !== 0 && buf[i + 1] === 0 && buf[i + 2] === 0 && buf[i + 3] === 0) asciiCharsLE++; + var b0 = buf[i], b1 = buf[i + 1], b2 = buf[i + 2], b3 = buf[i + 3]; + + if (b0 !== 0 || b1 > 0x10) ++invalidBE; + if (b3 !== 0 || b2 > 0x10) ++invalidLE; + + if (b0 === 0 && b1 === 0 && b2 === 0 && b3 !== 0) asciiCharsBE++; + if (b0 !== 0 && b1 === 0 && b2 === 0 && b3 === 0) asciiCharsLE++; } + if (invalidBE < invalidLE) + enc = 'utf-32be'; + else if (invalidLE < invalidBE) + enc = 'utf-32le'; if (asciiCharsBE > asciiCharsLE) enc = 'utf-32be'; else if (asciiCharsBE < asciiCharsLE) diff --git a/test/utf32-test.js b/test/utf32-test.js index c6f1c83..39a07db 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -2,13 +2,17 @@ var assert = require('assert'), iconv = require(__dirname+'/../'); var testStr = '1aя中文☃💩', - testStr2 = '❝Stray high \uD977😱 and low\uDDDD☔ surrogate values.❞'; + testStr2 = '❝Stray high \uD977😱 and low\uDDDD☔ surrogate values.❞', utf32leBuf = Buffer.from([0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x4F, 0x04, 0x00, 0x00, 0x2D, 0x4E, 0x00, 0x00, 0x87, 0x65, 0x00, 0x00, 0x03, 0x26, 0x00, 0x00, 0xA9, 0xF4, 0x01, 0x00]), utf32beBuf = Buffer.from([0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x04, 0x4F, 0x00, 0x00, 0x4E, 0x2D, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x26, 0x03, 0x00, 0x01, 0xF4, 0xA9]), utf32leBOM = Buffer.from([0xFF, 0xFE, 0x00, 0x00]), utf32beBOM = Buffer.from([0x00, 0x00, 0xFE, 0xFF]), + utf32leBufWithBOM = Buffer.concat([utf32leBOM, utf32leBuf]), + utf32beBufWithBOM = Buffer.concat([utf32beBOM, utf32beBuf]), + utf32leBufWithInvalidChar = Buffer.concat([utf32leBuf, Buffer.from([0x12, 0x34, 0x56, 0x78])]), + utf32beBufWithInvalidChar = Buffer.concat([utf32beBuf, Buffer.from([0x12, 0x34, 0x56, 0x78])]), sampleStr = '\n<俄语>данные'; describe('UTF-32LE codec', function() { @@ -17,7 +21,7 @@ describe('UTF-32LE codec', function() { }); it('decodes basic buffers correctly', function() { - assert.equal(iconv.decode(utf32leBuf, 'UTF32-LE'), testStr); + assert.equal(iconv.decode(utf32leBuf, 'ucs4le'), testStr); }); it('decodes uneven length buffers with no error', function() { @@ -28,11 +32,9 @@ describe('UTF-32LE codec', function() { var encoded = iconv.encode(testStr2, 'UTF32-LE'); assert.equal(iconv.decode(encoded, 'UTF32-LE'), testStr2); }); -}); -describe('UTF-32LE encoder', function() { - it('Adds BOM when encoding', function() { - // assert.equal(iconv.encode(testStr, 'utf-32le').toString('hex'), utf32leBOM.toString('hex') + utf32leBuf.toString('hex')); + it('handles invalid Unicode codepoints gracefully', function() { + assert.equal(iconv.decode(utf32leBufWithInvalidChar, 'utf-32'), testStr + '�'); }); }); @@ -42,7 +44,7 @@ describe('UTF-32BE codec', function() { }); it('decodes basic buffers correctly', function() { - assert.equal(iconv.decode(utf32beBuf, 'UTF32-BE'), testStr); + assert.equal(iconv.decode(utf32beBuf, 'ucs4be'), testStr); }); it('decodes uneven length buffers with no error', function() { @@ -53,38 +55,34 @@ describe('UTF-32BE codec', function() { var encoded = iconv.encode(testStr2, 'UTF32-BE'); assert.equal(iconv.decode(encoded, 'UTF32-BE'), testStr2); }); -}); -describe('UTF-32BE encoder', function() { - it('Adds BOM when encoding', function() { - // assert.equal(iconv.encode(testStr, 'utf-32be').toString('hex'), utf32beBOM.toString('hex') + utf32beBuf.toString('hex')); + it('handles invalid Unicode codepoints gracefully', function() { + assert.equal(iconv.decode(utf32beBufWithInvalidChar, 'utf-32'), testStr + '�'); }); }); -// describe('UTF-32BE decoder', function() { -// }); - -// describe('UTF-32 decoder', function() { -// it('uses BOM to determine encoding', function() { -// assert.equal(iconv.decode(Buffer.concat([utf16leBOM, utf16leBuf]), 'utf-16'), testStr); -// assert.equal(iconv.decode(Buffer.concat([utf16beBOM, utf16beBuf]), 'utf-16'), testStr); -// }); -// -// it('handles very short buffers nicely', function() { -// assert.equal(iconv.decode(new Buffer([]), 'utf-16'), ''); -// assert.equal(iconv.decode(new Buffer([0x61]), 'utf-16'), ''); -// }); -// -// it('uses spaces when there is no BOM to determine encoding', function() { -// assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-16le'), 'utf-16'), sampleStr); -// assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-16be'), 'utf-16'), sampleStr); -// }); -// -// it('uses UTF-16LE if no BOM and heuristics failed', function() { -// assert.equal(iconv.decode(utf16leBuf, 'utf-16'), testStr); -// }); -// -// it('can be given a different default encoding', function() { -// assert.equal(iconv.decode(utf16leBuf, 'utf-16', {default: 'utf-16le'}), testStr); -// }); -// }); +describe('UTF-32 general encoder', function() { + it('Adds BOM when encoding, defaults to UTF-32LE', function() { + assert.equal(iconv.encode(testStr, 'utf-32').toString('hex'), utf32leBOM.toString('hex') + utf32leBuf.toString('hex')); + }); + + it('Doesn\'t add BOM and uses UTF-32BE when specified', function() { + assert.equal(iconv.encode(testStr, 'ucs4', {addBOM: false, defaultEncoding: 'ucs4be'}).toString('hex'), utf32beBuf.toString('hex')); + }); + + it('Correctly decodes UTF-32LE using BOM', function() { + assert.equal(iconv.decode(utf32leBufWithBOM, 'utf-32'), testStr); + }); + + it('Correctly decodes UTF-32LE without BOM', function() { + assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-32-le'), 'utf-32'), sampleStr); + }); + + it('Correctly decodes UTF-32BE using BOM', function() { + assert.equal(iconv.decode(utf32beBufWithBOM, 'utf-32', { stripBOM: false }), '\uFEFF' + testStr); + }); + + it('Correctly decodes UTF-32BE without BOM', function() { + assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-32-be'), 'utf-32'), sampleStr); + }); +}); From f0f96247387527e61c02d9a99f36b3cf234f7887 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 24 Jun 2019 13:58:22 -0400 Subject: [PATCH 03/24] Fix typo in unit test. --- test/utf32-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utf32-test.js b/test/utf32-test.js index 39a07db..5e79a53 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -61,7 +61,7 @@ describe('UTF-32BE codec', function() { }); }); -describe('UTF-32 general encoder', function() { +describe('UTF-32 general codec', function() { it('Adds BOM when encoding, defaults to UTF-32LE', function() { assert.equal(iconv.encode(testStr, 'utf-32').toString('hex'), utf32leBOM.toString('hex') + utf32leBuf.toString('hex')); }); From 8d104ad14d681bc17723c714e2c0e3b13cfd5120 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 24 Jun 2019 14:52:00 -0400 Subject: [PATCH 04/24] Fix uses of Buffer.from() that caused compatibility problems with older versions of Node. --- test/utf32-test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/utf32-test.js b/test/utf32-test.js index 5e79a53..d4b6c7a 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -3,16 +3,16 @@ var assert = require('assert'), var testStr = '1aя中文☃💩', testStr2 = '❝Stray high \uD977😱 and low\uDDDD☔ surrogate values.❞', - utf32leBuf = Buffer.from([0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x4F, 0x04, 0x00, 0x00, + utf32leBuf = new Buffer([0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x4F, 0x04, 0x00, 0x00, 0x2D, 0x4E, 0x00, 0x00, 0x87, 0x65, 0x00, 0x00, 0x03, 0x26, 0x00, 0x00, 0xA9, 0xF4, 0x01, 0x00]), - utf32beBuf = Buffer.from([0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x04, 0x4F, + utf32beBuf = new Buffer([0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x04, 0x4F, 0x00, 0x00, 0x4E, 0x2D, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x26, 0x03, 0x00, 0x01, 0xF4, 0xA9]), - utf32leBOM = Buffer.from([0xFF, 0xFE, 0x00, 0x00]), - utf32beBOM = Buffer.from([0x00, 0x00, 0xFE, 0xFF]), + utf32leBOM = new Buffer([0xFF, 0xFE, 0x00, 0x00]), + utf32beBOM = new Buffer([0x00, 0x00, 0xFE, 0xFF]), utf32leBufWithBOM = Buffer.concat([utf32leBOM, utf32leBuf]), utf32beBufWithBOM = Buffer.concat([utf32beBOM, utf32beBuf]), - utf32leBufWithInvalidChar = Buffer.concat([utf32leBuf, Buffer.from([0x12, 0x34, 0x56, 0x78])]), - utf32beBufWithInvalidChar = Buffer.concat([utf32beBuf, Buffer.from([0x12, 0x34, 0x56, 0x78])]), + utf32leBufWithInvalidChar = Buffer.concat([utf32leBuf, new Buffer([0x12, 0x34, 0x56, 0x78])]), + utf32beBufWithInvalidChar = Buffer.concat([utf32beBuf, new Buffer([0x12, 0x34, 0x56, 0x78])]), sampleStr = '\n<俄语>данные'; describe('UTF-32LE codec', function() { From 25e0413c3f3aca3b46aae042105bd2d4658c8ddc Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 24 Jun 2019 18:26:32 -0400 Subject: [PATCH 05/24] Updated README.md to include UTF-32 options. --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c981c37..3c17ade 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ iconv.undoExtendNodeEncodings(); ## Supported encodings * All node.js native encodings: utf8, ucs2 / utf16-le, ascii, binary, base64, hex. - * Additional unicode encodings: utf16, utf16-be, utf-7, utf-7-imap. + * Additional unicode encodings: utf16, utf16-be, utf-7, utf-7-imap, utf32, utf32-le, and utf32-be. * All widespread singlebyte encodings: Windows 125x family, ISO-8859 family, IBM/DOS codepages, Macintosh family, KOI8 family, all others supported by iconv library. Aliases like 'latin1', 'us-ascii' also supported. @@ -133,6 +133,12 @@ smart about endianness in the following ways: overridden with `defaultEncoding: 'utf-16be'` option. Strips BOM unless `stripBOM: false`. * Encoding: uses UTF-16LE and writes BOM by default. Use `addBOM: false` to override. +## UTF-32 Encodings + +This library supports UTF-32LE, UTF-32BE and UTF-32 encodings. Like the UTF-16 encoding above, UTF-32 defaults to UTF-32LE, but uses BOM and 'spaces heuristics' to determine input endianness. + * The default of UTF-32LE can be overridden with the `defaultEncoding: 'utf-32be'` option. Strips BOM unless `stripBOM: false`. + * Encoding: uses UTF-32LE and writes BOM by default. Use `addBOM: false` to override. (`defaultEncoding: 'utf-32be'` can also be used here to change encoding.) + ## Other notes When decoding, be sure to supply a Buffer to decode() method, otherwise [bad things usually happen](https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding). From 9b84cb3497e3ab2f605acbb536667015486bbed4 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Tue, 25 Jun 2019 09:41:56 -0400 Subject: [PATCH 06/24] Get rid of package-lock.json. --- package-lock.json | 994 ---------------------------------------------- 1 file changed, 994 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 6350265..0000000 --- a/package-lock.json +++ /dev/null @@ -1,994 +0,0 @@ -{ - "name": "iconv-lite", - "version": "0.4.24", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true, - "optional": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "async": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", - "integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "diff": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", - "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "errto": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/errto/-/errto-0.2.1.tgz", - "integrity": "sha1-1uI7NyYfhO2GlpX3e0+34GxkeEg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, - "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", - "dev": true - }, - "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "iconv": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/iconv/-/iconv-2.3.4.tgz", - "integrity": "sha512-v2Rree7xRtrC9o3Bi9nTNOKXvApmwLFdX50k72+K1W4mJ93LBdpaLcHcInYo9gr/GcQk8MFoCetrYRcb/o3RLg==", - "dev": true, - "requires": { - "nan": "^2.13.1", - "safer-buffer": "^2.1.2" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - } - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basecreate": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash.create": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", - "dev": true, - "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._basecreate": "^3.0.0", - "lodash._isiterateecall": "^3.0.0" - } - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, - "requires": { - "mime-db": "1.40.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mocha": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", - "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.9.0", - "debug": "2.6.8", - "diff": "3.2.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.1", - "growl": "1.9.2", - "he": "1.1.1", - "json3": "3.3.2", - "lodash.create": "3.1.1", - "mkdirp": "0.5.1", - "supports-color": "3.1.2" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, - "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", - "dev": true - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "dev": true, - "requires": { - "punycode": "^1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "unorm": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", - "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } -} From 325c0fe1972b90da1e7c31048b6b3f3b4471c42d Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Tue, 25 Jun 2019 15:51:44 -0400 Subject: [PATCH 07/24] Merging Utf32-LE and-BE codec into a single set of classes with an isLE flag. Other small code tweaks. --- .gitignore | 1 + encodings/utf32.js | 270 ++++++++++++--------------------------------- package.json | 2 +- test/utf32-test.js | 34 ++++-- 4 files changed, 97 insertions(+), 210 deletions(-) diff --git a/.gitignore b/.gitignore index e849c9c..2794f15 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ wiki *sublime-* coverage /.idea +package-lock.json diff --git a/encodings/utf32.js b/encodings/utf32.js index aae0131..d188983 100644 --- a/encodings/utf32.js +++ b/encodings/utf32.js @@ -2,208 +2,69 @@ var Buffer = require('safer-buffer').Buffer; -// == UTF32-LE codec. ========================================================== +// == UTF32-LE/BE codec. ========================================================== -exports.utf32le = Utf32LECodec; -exports.ucs4le = Utf32LECodec; +exports._utf32 = Utf32Codec; -function Utf32LECodec(options, iconv) { +function Utf32Codec(codecOptions, iconv) { this.iconv = iconv; + this.bomAware = true; + this.isLE = codecOptions.isLE; } -Utf32LECodec.prototype.encoder = Utf32LEEncoder; -Utf32LECodec.prototype.decoder = Utf32LEDecoder; -Utf32LECodec.prototype.bomAware = true; +exports.utf32le = { type: '_utf32', isLE: true }; +exports.utf32be = { type: '_utf32', isLE: false }; -// -- Encoding - -function Utf32LEEncoder() { - this.highSurrogate = null; -} - -Utf32LEEncoder.prototype.write = function(str) { - var src = Buffer.from(str, 'ucs2'); - var dst = Buffer.alloc(src.length * 2); - var offset = 0; - - for (var i = 0; i < src.length; i += 2) { - var lowByte = src[i]; - var highByte = src[i + 1]; - var isHighSurrogate = (0xD8 <= highByte && highByte < 0xDC); - var isLowSurrogate = (0xDC <= highByte && highByte < 0xE0); - - if (this.highSurrogate) { - if (isHighSurrogate || !isLowSurrogate) { - // There shouldn't be two high surrogates in a row, nor a high surrogate which isn't followed by a low - // surrogate. If this happens, keep the pending high surrogate as a stand-alone semi-invalid character - // (technically wrong, but expected by some applications, like Windows file names). - dst[offset++] = this.highSurrogate[0]; dst[offset++] = this.highSurrogate[1]; dst[offset++] = 0; dst[offset++] = 0; - } - else { - // Create 32-bit value from high and low surrogates; - var codepoint = (((this.highSurrogate[1] - 0xD8) << 18) | (this.highSurrogate[0] << 10) | - ((highByte - 0xDC) << 8) | lowByte) + 0x10000; - - dst[offset++] = codepoint & 0x000000FF; - dst[offset++] = (codepoint & 0x0000FF00) >> 8; - dst[offset++] = (codepoint & 0x00FF0000) >> 16; - dst[offset++] = (codepoint & 0xFF000000) >> 24; - this.highSurrogate = null; - - continue; - } - } - - if (isHighSurrogate) - this.highSurrogate = [lowByte, highByte]; - else { - // Even if the current character is a low surrogate, with no previous high surrogate, we'll - // encode it as a semi-invalid stand-alone character for the same reasons expressed above for - // unpaired high surrogates. - dst[offset++] = lowByte; dst[offset++] = highByte; dst[offset++] = 0; dst[offset++] = 0; - this.highSurrogate = null; - } - } - - if (offset < dst.length) - dst = dst.slice(0, offset); - - return dst; -}; - -Utf32LEEncoder.prototype.end = function() { - // Treat any leftover high surrogate as a semi-valid independent character. - if (!this.highSurrogate) - return; - - var buf = Buffer.alloc(4); - - buf[0] = this.highSurrogate[0]; buf[1] = this.highSurrogate[1]; buf[2] = 0; buf[3] = 0; - this.highSurrogate = null; - - return buf; -}; - -// -- Decoding - -function Utf32LEDecoder(options, codec) { - this.badChar = Buffer.from(codec.iconv.defaultCharUnicode, 'ucs2'); - this.overflow = null; -} - -Utf32LEDecoder.prototype.write = function(src) { - if (src.length === 0) - return ''; - else if (this.overflow) { - src = Buffer.concat([this.overflow, src]); - } - - var goodLength = src.length - src.length % 4; - - if (src.length !== goodLength) { - this.overflow = src.slice(goodLength); - src = src.slice(0, goodLength); - } - else - this.overflow = null; - - var dst = Buffer.alloc(goodLength); - var offset = 0; - - for (var i = 0; i < goodLength; i += 4) { - var b0 = src[i]; - var b1 = src[i + 1]; - var b2 = src[i + 2]; - var b3 = src[i + 3]; - - if (b3 === 0 && b2 === 0) { - // Simple 16-bit character - dst[offset++] = b0; dst[offset++] = b1; - } - else { - var codepoint = b3 * 0x1000000 + b2 * 0x10000 + b1 * 0x100 + b0; - - if (codepoint > 0x10FFFF) { - // Not a valid Unicode codepoint - dst[offset++] = this.badChar[0]; dst[offset++] = this.badChar[1]; - } - else { - codepoint -= 0x10000; - var high = 0xD800 | (codepoint >> 10); - var low = 0xDC00 + (codepoint & 0x3FF); - dst[offset++] = high & 0xFF; dst[offset++] = high >> 8; - dst[offset++] = low & 0xFF; dst[offset++] = low >> 8; - } - } - } - - return dst.slice(0, offset).toString('ucs2'); -}; - -Utf32LEDecoder.prototype.end = function() { - this.overflow = null; -}; +// Aliases +exports.ucs4le = 'utf32le'; +exports.ucs4be = 'utf32be'; -// == UTF32-BE codec. ========================================================== - -exports.utf32be = Utf32BECodec; -exports.ucs4be = Utf32BECodec; - -function Utf32BECodec(options, iconv) { - this.iconv = iconv; -} - -Utf32BECodec.prototype.encoder = Utf32BEEncoder; -Utf32BECodec.prototype.decoder = Utf32BEDecoder; -Utf32BECodec.prototype.bomAware = true; +Utf32Codec.prototype.encoder = Utf32Encoder; +Utf32Codec.prototype.decoder = Utf32Decoder; // -- Encoding -function Utf32BEEncoder() { - this.highSurrogate = null; +function Utf32Encoder(options, codec) { + this.isLE = codec.isLE; + this.highSurrogate = 0; } -Utf32BEEncoder.prototype.write = function(str) { +Utf32Encoder.prototype.write = function(str) { var src = Buffer.from(str, 'ucs2'); var dst = Buffer.alloc(src.length * 2); var offset = 0; for (var i = 0; i < src.length; i += 2) { - var lowByte = src[i]; - var highByte = src[i + 1]; - var isHighSurrogate = (0xD8 <= highByte && highByte < 0xDC); - var isLowSurrogate = (0xDC <= highByte && highByte < 0xE0); + var code = src.readUInt16LE(i); + var isHighSurrogate = (0xD800 <= code && code < 0xDC00); + var isLowSurrogate = (0xDC00 <= code && code < 0xE000); if (this.highSurrogate) { if (isHighSurrogate || !isLowSurrogate) { // There shouldn't be two high surrogates in a row, nor a high surrogate which isn't followed by a low // surrogate. If this happens, keep the pending high surrogate as a stand-alone semi-invalid character // (technically wrong, but expected by some applications, like Windows file names). - dst[offset++] = 0; dst[offset++] = 0; dst[offset++] = this.highSurrogate[1]; dst[offset++] = this.highSurrogate[0]; + offset = write32(dst, this.isLE, this.highSurrogate, offset); } else { // Create 32-bit value from high and low surrogates; - var codepoint = (((this.highSurrogate[1] - 0xD8) << 18) | (this.highSurrogate[0] << 10) | - ((highByte - 0xDC) << 8) | lowByte) + 0x10000; + var codepoint = (((this.highSurrogate - 0xD800) << 10) | (code - 0xDC00)) + 0x10000; - dst[offset++] = (codepoint & 0xFF000000) >> 24; - dst[offset++] = (codepoint & 0x00FF0000) >> 16; - dst[offset++] = (codepoint & 0x0000FF00) >> 8; - dst[offset++] = codepoint & 0x000000FF; - this.highSurrogate = null; + offset = write32(dst, this.isLE, codepoint, offset); + this.highSurrogate = 0; continue; } } if (isHighSurrogate) - this.highSurrogate = [lowByte, highByte]; + this.highSurrogate = code; else { // Even if the current character is a low surrogate, with no previous high surrogate, we'll // encode it as a semi-invalid stand-alone character for the same reasons expressed above for // unpaired high surrogates. - dst[offset++] = 0; dst[offset++] = 0; dst[offset++] = highByte; dst[offset++] = lowByte; - this.highSurrogate = null; + offset = write32(dst, this.isLE, code, offset); + this.highSurrogate = 0; } } @@ -213,33 +74,33 @@ Utf32BEEncoder.prototype.write = function(str) { return dst; }; -Utf32BEEncoder.prototype.end = function() { - // Treat any leftover high surrogate as a semi-invalid independent character. +Utf32Encoder.prototype.end = function() { + // Treat any leftover high surrogate as a semi-valid independent character. if (!this.highSurrogate) return; var buf = Buffer.alloc(4); - buf[0] = 0; buf[1] = 0; buf[2] = this.highSurrogate[1]; buf[3] = this.highSurrogate[0]; - this.highSurrogate = null; + write32(buf, this.isLE, this.highSurrogate, 0); + this.highSurrogate = 0; return buf; }; - // -- Decoding -function Utf32BEDecoder(options, codec) { - this.badChar = Buffer.from(codec.iconv.defaultCharUnicode, 'ucs2'); +function Utf32Decoder(options, codec) { + this.isLE = codec.isLE; + this.badChar = codec.iconv.defaultCharUnicode.charCodeAt(0); this.overflow = null; } -Utf32BEDecoder.prototype.write = function(src) { +Utf32Decoder.prototype.write = function(src) { if (src.length === 0) return ''; - else if (this.overflow) { + + if (this.overflow) src = Buffer.concat([this.overflow, src]); - } var goodLength = src.length - src.length % 4; @@ -254,28 +115,28 @@ Utf32BEDecoder.prototype.write = function(src) { var offset = 0; for (var i = 0; i < goodLength; i += 4) { - var b3 = src[i]; - var b2 = src[i + 1]; - var b1 = src[i + 2]; - var b0 = src[i + 3]; + var codepoint = this.isLE ? src.readUInt32LE(i) : src.readUInt32BE(i); - if (b3 === 0 && b2 === 0) { + if (codepoint < 0x10000) { // Simple 16-bit character - dst[offset++] = b0; dst[offset++] = b1; + dst.writeUInt16LE(codepoint, offset); + offset += 2; } else { - var codepoint = b3 * 0x1000000 + b2 * 0x10000 + b1 * 0x100 + b0; - if (codepoint > 0x10FFFF) { // Not a valid Unicode codepoint - dst[offset++] = this.badChar[0]; dst[offset++] = this.badChar[1]; + dst.writeUInt16LE(this.badChar, offset); + offset += 2; } else { + // Create high and low surrogates. codepoint -= 0x10000; var high = 0xD800 | (codepoint >> 10); var low = 0xDC00 + (codepoint & 0x3FF); - dst[offset++] = high & 0xFF; dst[offset++] = high >> 8; - dst[offset++] = low & 0xFF; dst[offset++] = low >> 8; + dst.writeUInt16LE(high, offset); + offset += 2; + dst.writeUInt16LE(low, offset); + offset += 2; } } } @@ -283,30 +144,30 @@ Utf32BEDecoder.prototype.write = function(src) { return dst.slice(0, offset).toString('ucs2'); }; -Utf32BEDecoder.prototype.end = function() { +Utf32Decoder.prototype.end = function() { this.overflow = null; }; -// == UTF-32 codec ============================================================= +// == UTF-32 Auto codec ============================================================= // Decoder chooses automatically from UTF-32LE and UTF-32BE using BOM and space-based heuristic. // Defaults to UTF-32LE. http://en.wikipedia.org/wiki/UTF-32 // Encoder/decoder default can be changed: iconv.decode(buf, 'utf32', {defaultEncoding: 'utf-32be'}); // Encoder prepends BOM (which can be overridden with (addBOM: false}). -exports.utf32 = Utf32Codec; -exports.ucs4 = Utf32Codec; +exports.utf32 = Utf32AutoCodec; +exports.ucs4 = Utf32AutoCodec; -function Utf32Codec(options, iconv) { +function Utf32AutoCodec(options, iconv) { this.iconv = iconv; } -Utf32Codec.prototype.encoder = Utf32Encoder; -Utf32Codec.prototype.decoder = Utf32Decoder; +Utf32AutoCodec.prototype.encoder = Utf32AutoEncoder; +Utf32AutoCodec.prototype.decoder = Utf32AutoDecoder; // -- Encoding -function Utf32Encoder(options, codec) { +function Utf32AutoEncoder(options, codec) { options = options || {}; if (options.addBOM === undefined) @@ -315,18 +176,17 @@ function Utf32Encoder(options, codec) { this.encoder = codec.iconv.getEncoder(options.defaultEncoding || 'utf-32le', options); } -Utf32Encoder.prototype.write = function(str) { +Utf32AutoEncoder.prototype.write = function(str) { return this.encoder.write(str); }; -Utf32Encoder.prototype.end = function() { +Utf32AutoEncoder.prototype.end = function() { return this.encoder.end(); }; - // -- Decoding -function Utf32Decoder(options, codec) { +function Utf32AutoDecoder(options, codec) { this.decoder = null; this.initialBytes = []; this.initialBytesLen = 0; @@ -334,7 +194,7 @@ function Utf32Decoder(options, codec) { this.iconv = codec.iconv; } -Utf32Decoder.prototype.write = function(buf) { +Utf32AutoDecoder.prototype.write = function(buf) { if (!this.decoder) { // Codec is not chosen yet. Accumulate initial bytes. this.initialBytes.push(buf); @@ -353,8 +213,7 @@ Utf32Decoder.prototype.write = function(buf) { return this.decoder.write(buf); }; - -Utf32Decoder.prototype.end = function() { +Utf32AutoDecoder.prototype.end = function() { if (!this.decoder) { var buf = Buffer.concat(this.initialBytes), encoding = detectEncoding(buf, this.options.defaultEncoding); @@ -412,3 +271,12 @@ function detectEncoding(buf, defaultEncoding) { return enc; } + +function write32(buf, isLE, value, offset) { + if (isLE) + buf.writeUInt32LE(value, offset); + else + buf.writeUInt32BE(value, offset); + + return offset + 4; +} diff --git a/package.json b/package.json index 513e1fa..a7c74fc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "iconv-lite", "description": "Convert character encodings in pure javascript.", - "version": "0.5.0", + "version": "0.4.24", "license": "MIT", "keywords": [ "iconv", diff --git a/test/utf32-test.js b/test/utf32-test.js index d4b6c7a..6ea586c 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -30,7 +30,7 @@ describe('UTF-32LE codec', function() { it('handles invalid surrogates gracefully', function() { var encoded = iconv.encode(testStr2, 'UTF32-LE'); - assert.equal(iconv.decode(encoded, 'UTF32-LE'), testStr2); + assert.equal(escape(iconv.decode(encoded, 'UTF32-LE')), escape(testStr2)); }); it('handles invalid Unicode codepoints gracefully', function() { @@ -53,7 +53,7 @@ describe('UTF-32BE codec', function() { it('handles invalid surrogates gracefully', function() { var encoded = iconv.encode(testStr2, 'UTF32-BE'); - assert.equal(iconv.decode(encoded, 'UTF32-BE'), testStr2); + assert.equal(escape(iconv.decode(encoded, 'UTF32-BE')), escape(testStr2)); }); it('handles invalid Unicode codepoints gracefully', function() { @@ -62,27 +62,45 @@ describe('UTF-32BE codec', function() { }); describe('UTF-32 general codec', function() { - it('Adds BOM when encoding, defaults to UTF-32LE', function() { + it('adds BOM when encoding, defaults to UTF-32LE', function() { assert.equal(iconv.encode(testStr, 'utf-32').toString('hex'), utf32leBOM.toString('hex') + utf32leBuf.toString('hex')); }); - it('Doesn\'t add BOM and uses UTF-32BE when specified', function() { + it('doesn\'t add BOM and uses UTF-32BE when specified', function() { assert.equal(iconv.encode(testStr, 'ucs4', {addBOM: false, defaultEncoding: 'ucs4be'}).toString('hex'), utf32beBuf.toString('hex')); }); - it('Correctly decodes UTF-32LE using BOM', function() { + it('correctly decodes UTF-32LE using BOM', function() { assert.equal(iconv.decode(utf32leBufWithBOM, 'utf-32'), testStr); }); - it('Correctly decodes UTF-32LE without BOM', function() { + it('correctly decodes UTF-32LE without BOM', function() { assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-32-le'), 'utf-32'), sampleStr); }); - it('Correctly decodes UTF-32BE using BOM', function() { + it('correctly decodes UTF-32BE using BOM', function() { assert.equal(iconv.decode(utf32beBufWithBOM, 'utf-32', { stripBOM: false }), '\uFEFF' + testStr); }); - it('Correctly decodes UTF-32BE without BOM', function() { + it('correctly decodes UTF-32BE without BOM', function() { assert.equal(iconv.decode(iconv.encode(sampleStr, 'utf-32-be'), 'utf-32'), sampleStr); }); }); + +// Utility function to make bad matches easier to visualize. +function escape(s) { + var sb = []; + + for (var i = 0; i < s.length; ++i) { + var cc = s.charCodeAt(i); + + if (32 <= cc && cc < 127 && cc !== 0x5C) + sb.push(s.charAt(i)); + else { + var h = s.charCodeAt(i).toString(16).toUpperCase(); + sb.push('\\u' + '0'.repeat(4 - h.length) + h); + } + } + + return sb.join(''); +} From 56a4754a151800d8fef9273812dcce08b8ed03db Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Tue, 25 Jun 2019 16:43:10 -0400 Subject: [PATCH 08/24] Added all-codepoint unit tests for UTF-32. --- encodings/utf32.js | 4 ++-- test/utf32-test.js | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/encodings/utf32.js b/encodings/utf32.js index d188983..16b4492 100644 --- a/encodings/utf32.js +++ b/encodings/utf32.js @@ -233,9 +233,9 @@ function detectEncoding(buf, defaultEncoding) { if (buf.length >= 4) { // Check BOM. - if (buf[0] === 0 && buf[1] === 0 && buf[2] === 0xFE && buf[3] === 0xFF) // UTF-32BE BOM + if (buf.readUInt32BE() === 0xFEFF) // UTF-32LE BOM enc = 'utf-32be'; - else if (buf[0] === 0xFF && buf[1] === 0xFE && buf[2] === 0 && buf[3] === 0) // UTF-32LE BOM + else if (buf.readUInt32LE() === 0xFEFF) // UTF-32LE BOM enc = 'utf-32le'; else { // No BOM found. Try to deduce encoding from initial content. diff --git a/test/utf32-test.js b/test/utf32-test.js index 6ea586c..5ae305e 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -15,6 +15,20 @@ var testStr = '1aя中文☃💩', utf32beBufWithInvalidChar = Buffer.concat([utf32beBuf, new Buffer([0x12, 0x34, 0x56, 0x78])]), sampleStr = '\n<俄语>данные'; +var allCharsStr = ''; +var allCharsLEBuf = new Buffer(0x10F800 * 4); +var allCharsBEBuf = new Buffer(0x10F800 * 4); +var skip = 0; + +for (var i = 0; i <= 0x10F7FF; ++i) { + if (i === 0xD800) + skip = 0x800; + + allCharsStr += String.fromCodePoint(i + skip); + allCharsLEBuf.writeUInt32LE(i + skip, i * 4); + allCharsBEBuf.writeUInt32BE(i + skip, i * 4); +} + describe('UTF-32LE codec', function() { it('encodes basic strings correctly', function() { assert.equal(iconv.encode(testStr, 'UTF32-LE').toString('hex'), utf32leBuf.toString('hex')); @@ -34,7 +48,15 @@ describe('UTF-32LE codec', function() { }); it('handles invalid Unicode codepoints gracefully', function() { - assert.equal(iconv.decode(utf32leBufWithInvalidChar, 'utf-32'), testStr + '�'); + assert.equal(iconv.decode(utf32leBufWithInvalidChar, 'utf-32le'), testStr + '�'); + }); + + it('handles encoding all valid codepoints', function() { + assert.deepEqual(iconv.encode(allCharsStr, 'utf-32le'), allCharsLEBuf); + }); + + it('handles decoding all valid codepoints', function() { + assert.equal(iconv.decode(allCharsLEBuf, 'utf-32le'), allCharsStr); }); }); @@ -57,7 +79,15 @@ describe('UTF-32BE codec', function() { }); it('handles invalid Unicode codepoints gracefully', function() { - assert.equal(iconv.decode(utf32beBufWithInvalidChar, 'utf-32'), testStr + '�'); + assert.equal(iconv.decode(utf32beBufWithInvalidChar, 'utf-32be'), testStr + '�'); + }); + + it('handles encoding all valid codepoints', function() { + assert.deepEqual(iconv.encode(allCharsStr, 'utf-32be'), allCharsBEBuf); + }); + + it('handles decoding all valid codepoints', function() { + assert.equal(iconv.decode(allCharsBEBuf, 'utf-32be'), allCharsStr); }); }); From cfe04d6cc995503af033ef80bb5083e5b9c639aa Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Tue, 25 Jun 2019 16:52:11 -0400 Subject: [PATCH 09/24] Disable some unit tests on older versions of Node. --- test/utf32-test.js | 58 +++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/test/utf32-test.js b/test/utf32-test.js index 5ae305e..6c5829c 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -15,18 +15,24 @@ var testStr = '1aя中文☃💩', utf32beBufWithInvalidChar = Buffer.concat([utf32beBuf, new Buffer([0x12, 0x34, 0x56, 0x78])]), sampleStr = '\n<俄语>данные'; -var allCharsStr = ''; -var allCharsLEBuf = new Buffer(0x10F800 * 4); -var allCharsBEBuf = new Buffer(0x10F800 * 4); -var skip = 0; - -for (var i = 0; i <= 0x10F7FF; ++i) { - if (i === 0xD800) - skip = 0x800; - - allCharsStr += String.fromCodePoint(i + skip); - allCharsLEBuf.writeUInt32LE(i + skip, i * 4); - allCharsBEBuf.writeUInt32BE(i + skip, i * 4); +var doBigTest =false; + +if (String.fromCodePoint) { + var allCharsStr = ''; + var allCharsLEBuf = new Buffer(0x10F800 * 4); + var allCharsBEBuf = new Buffer(0x10F800 * 4); + var skip = 0; + + for (var i = 0; i <= 0x10F7FF; ++i) { + if (i === 0xD800) + skip = 0x800; + + allCharsStr += String.fromCodePoint(i + skip); + allCharsLEBuf.writeUInt32LE(i + skip, i * 4); + allCharsBEBuf.writeUInt32BE(i + skip, i * 4); + } + + doBigTest = true; } describe('UTF-32LE codec', function() { @@ -51,13 +57,15 @@ describe('UTF-32LE codec', function() { assert.equal(iconv.decode(utf32leBufWithInvalidChar, 'utf-32le'), testStr + '�'); }); - it('handles encoding all valid codepoints', function() { - assert.deepEqual(iconv.encode(allCharsStr, 'utf-32le'), allCharsLEBuf); - }); + if (doBigTest) { + it('handles encoding all valid codepoints', function() { + assert.deepEqual(iconv.encode(allCharsStr, 'utf-32le'), allCharsLEBuf); + }); - it('handles decoding all valid codepoints', function() { - assert.equal(iconv.decode(allCharsLEBuf, 'utf-32le'), allCharsStr); - }); + it('handles decoding all valid codepoints', function() { + assert.equal(iconv.decode(allCharsLEBuf, 'utf-32le'), allCharsStr); + }); + } }); describe('UTF-32BE codec', function() { @@ -82,13 +90,15 @@ describe('UTF-32BE codec', function() { assert.equal(iconv.decode(utf32beBufWithInvalidChar, 'utf-32be'), testStr + '�'); }); - it('handles encoding all valid codepoints', function() { - assert.deepEqual(iconv.encode(allCharsStr, 'utf-32be'), allCharsBEBuf); - }); + if (doBigTest) { + it('handles encoding all valid codepoints', function() { + assert.deepEqual(iconv.encode(allCharsStr, 'utf-32be'), allCharsBEBuf); + }); - it('handles decoding all valid codepoints', function() { - assert.equal(iconv.decode(allCharsBEBuf, 'utf-32be'), allCharsStr); - }); + it('handles decoding all valid codepoints', function() { + assert.equal(iconv.decode(allCharsBEBuf, 'utf-32be'), allCharsStr); + }); + } }); describe('UTF-32 general codec', function() { From 1f56e004c18daa61af9eced1b8fea0a8402bbd93 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Tue, 25 Jun 2019 19:13:44 -0400 Subject: [PATCH 10/24] Fixes for working correctly with older versions of Node. --- encodings/utf32.js | 4 +-- test/utf32-test.js | 68 ++++++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/encodings/utf32.js b/encodings/utf32.js index 16b4492..fcab73d 100644 --- a/encodings/utf32.js +++ b/encodings/utf32.js @@ -233,9 +233,9 @@ function detectEncoding(buf, defaultEncoding) { if (buf.length >= 4) { // Check BOM. - if (buf.readUInt32BE() === 0xFEFF) // UTF-32LE BOM + if (buf.readUInt32BE(0) === 0xFEFF) // UTF-32LE BOM enc = 'utf-32be'; - else if (buf.readUInt32LE() === 0xFEFF) // UTF-32LE BOM + else if (buf.readUInt32LE(0) === 0xFEFF) // UTF-32LE BOM enc = 'utf-32le'; else { // No BOM found. Try to deduce encoding from initial content. diff --git a/test/utf32-test.js b/test/utf32-test.js index 6c5829c..7232fcd 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -15,24 +15,33 @@ var testStr = '1aя中文☃💩', utf32beBufWithInvalidChar = Buffer.concat([utf32beBuf, new Buffer([0x12, 0x34, 0x56, 0x78])]), sampleStr = '\n<俄语>данные'; -var doBigTest =false; +var fromCodePoint = String.fromCodePoint; -if (String.fromCodePoint) { - var allCharsStr = ''; - var allCharsLEBuf = new Buffer(0x10F800 * 4); - var allCharsBEBuf = new Buffer(0x10F800 * 4); - var skip = 0; +if (!fromCodePoint) { + fromCodePoint = function(cp) { + if (cp < 0x10000) + return String.fromCharCode(cp); - for (var i = 0; i <= 0x10F7FF; ++i) { - if (i === 0xD800) - skip = 0x800; + cp -= 0x10000; - allCharsStr += String.fromCodePoint(i + skip); - allCharsLEBuf.writeUInt32LE(i + skip, i * 4); - allCharsBEBuf.writeUInt32BE(i + skip, i * 4); + return String.fromCharCode(0xD800 | (cp >> 10)) + + String.fromCharCode(0xDC00 + (cp & 0x3FF)); } +} + +var allCharsStr = ''; +var allCharsLEBuf = new Buffer(0x10F800 * 4); +var allCharsBEBuf = new Buffer(0x10F800 * 4); +var skip = 0; + +for (var i = 0; i <= 0x10F7FF; ++i) { + if (i === 0xD800) + skip = 0x800; - doBigTest = true; + var cp = i + skip; + allCharsStr += fromCodePoint(cp); + allCharsLEBuf.writeUInt32LE(cp, i * 4); + allCharsBEBuf.writeUInt32BE(cp, i * 4); } describe('UTF-32LE codec', function() { @@ -57,15 +66,13 @@ describe('UTF-32LE codec', function() { assert.equal(iconv.decode(utf32leBufWithInvalidChar, 'utf-32le'), testStr + '�'); }); - if (doBigTest) { - it('handles encoding all valid codepoints', function() { - assert.deepEqual(iconv.encode(allCharsStr, 'utf-32le'), allCharsLEBuf); - }); + it('handles encoding all valid codepoints', function() { + assert.deepEqual(iconv.encode(allCharsStr, 'utf-32le'), allCharsLEBuf); + }); - it('handles decoding all valid codepoints', function() { - assert.equal(iconv.decode(allCharsLEBuf, 'utf-32le'), allCharsStr); - }); - } + it('handles decoding all valid codepoints', function() { + assert.equal(iconv.decode(allCharsLEBuf, 'utf-32le'), allCharsStr); + }); }); describe('UTF-32BE codec', function() { @@ -90,15 +97,13 @@ describe('UTF-32BE codec', function() { assert.equal(iconv.decode(utf32beBufWithInvalidChar, 'utf-32be'), testStr + '�'); }); - if (doBigTest) { - it('handles encoding all valid codepoints', function() { - assert.deepEqual(iconv.encode(allCharsStr, 'utf-32be'), allCharsBEBuf); - }); + it('handles encoding all valid codepoints', function() { + assert.deepEqual(iconv.encode(allCharsStr, 'utf-32be'), allCharsBEBuf); + }); - it('handles decoding all valid codepoints', function() { - assert.equal(iconv.decode(allCharsBEBuf, 'utf-32be'), allCharsStr); - }); - } + it('handles decoding all valid codepoints', function() { + assert.equal(iconv.decode(allCharsBEBuf, 'utf-32be'), allCharsStr); + }); }); describe('UTF-32 general codec', function() { @@ -138,7 +143,10 @@ function escape(s) { sb.push(s.charAt(i)); else { var h = s.charCodeAt(i).toString(16).toUpperCase(); - sb.push('\\u' + '0'.repeat(4 - h.length) + h); + while (h.length < 4) // No String.repeat in old versions of Node! + h = '0' + h; + + sb.push('\\u' + h); } } From af9cff7bad683e4e933bb9a709bea49d69e83775 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Wed, 26 Jun 2019 09:28:36 -0400 Subject: [PATCH 11/24] Add comparison to node-iconv, and possible speed improvement. --- encodings/utf32.js | 25 ++++++++++++------------- test/utf32-test.js | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/encodings/utf32.js b/encodings/utf32.js index fcab73d..133900f 100644 --- a/encodings/utf32.js +++ b/encodings/utf32.js @@ -32,6 +32,7 @@ function Utf32Encoder(options, codec) { Utf32Encoder.prototype.write = function(str) { var src = Buffer.from(str, 'ucs2'); var dst = Buffer.alloc(src.length * 2); + var write32 = this.isLE ? dst.writeUInt32LE : dst.writeUInt32BE; var offset = 0; for (var i = 0; i < src.length; i += 2) { @@ -44,13 +45,15 @@ Utf32Encoder.prototype.write = function(str) { // There shouldn't be two high surrogates in a row, nor a high surrogate which isn't followed by a low // surrogate. If this happens, keep the pending high surrogate as a stand-alone semi-invalid character // (technically wrong, but expected by some applications, like Windows file names). - offset = write32(dst, this.isLE, this.highSurrogate, offset); + write32.call(dst, this.highSurrogate, offset); + offset += 4; } else { // Create 32-bit value from high and low surrogates; var codepoint = (((this.highSurrogate - 0xD800) << 10) | (code - 0xDC00)) + 0x10000; - offset = write32(dst, this.isLE, codepoint, offset); + write32.call(dst, codepoint, offset); + offset += 4; this.highSurrogate = 0; continue; @@ -63,7 +66,8 @@ Utf32Encoder.prototype.write = function(str) { // Even if the current character is a low surrogate, with no previous high surrogate, we'll // encode it as a semi-invalid stand-alone character for the same reasons expressed above for // unpaired high surrogates. - offset = write32(dst, this.isLE, code, offset); + write32.call(dst, code, offset); + offset += 4; this.highSurrogate = 0; } } @@ -81,7 +85,11 @@ Utf32Encoder.prototype.end = function() { var buf = Buffer.alloc(4); - write32(buf, this.isLE, this.highSurrogate, 0); + if (this.isLE) + buf.writeUInt32LE(this.highSurrogate, 0); + else + buf.writeUInt32BE(this.highSurrogate, 0); + this.highSurrogate = 0; return buf; @@ -271,12 +279,3 @@ function detectEncoding(buf, defaultEncoding) { return enc; } - -function write32(buf, isLE, value, offset) { - if (isLE) - buf.writeUInt32LE(value, offset); - else - buf.writeUInt32BE(value, offset); - - return offset + 4; -} diff --git a/test/utf32-test.js b/test/utf32-test.js index 7232fcd..23faab2 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -1,5 +1,6 @@ var assert = require('assert'), - iconv = require(__dirname+'/../'); + iconv = require(__dirname+'/../'), + Iconv = require('iconv').Iconv; var testStr = '1aя中文☃💩', testStr2 = '❝Stray high \uD977😱 and low\uDDDD☔ surrogate values.❞', @@ -68,10 +69,16 @@ describe('UTF-32LE codec', function() { it('handles encoding all valid codepoints', function() { assert.deepEqual(iconv.encode(allCharsStr, 'utf-32le'), allCharsLEBuf); + var nodeIconv = new Iconv('UTF-8', 'UTF-32LE'); + var nodeBuf = nodeIconv.convert(allCharsStr); + assert.deepEqual(nodeBuf, allCharsLEBuf); }); it('handles decoding all valid codepoints', function() { assert.equal(iconv.decode(allCharsLEBuf, 'utf-32le'), allCharsStr); + var nodeIconv = new Iconv('UTF-32LE', 'UTF-8'); + var nodeStr = nodeIconv.convert(allCharsLEBuf).toString('utf8'); + assert.equal(nodeStr, allCharsStr); }); }); @@ -99,10 +106,16 @@ describe('UTF-32BE codec', function() { it('handles encoding all valid codepoints', function() { assert.deepEqual(iconv.encode(allCharsStr, 'utf-32be'), allCharsBEBuf); + var nodeIconv = new Iconv('UTF-8', 'UTF-32BE'); + var nodeBuf = nodeIconv.convert(allCharsStr); + assert.deepEqual(nodeBuf, allCharsBEBuf); }); it('handles decoding all valid codepoints', function() { assert.equal(iconv.decode(allCharsBEBuf, 'utf-32be'), allCharsStr); + var nodeIconv = new Iconv('UTF-32BE', 'UTF-8'); + var nodeStr = nodeIconv.convert(allCharsBEBuf).toString('utf8'); + assert.equal(nodeStr, allCharsStr); }); }); From 7d6f9551fab389301eb490a65439a35dbb05fd3d Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Fri, 28 Jun 2019 09:00:56 -0400 Subject: [PATCH 12/24] Preliminary work for transliteration support. --- encodings/tables/transliteration-ranges.json | 640 +++++++++++++++++++ generation/gen-transliteration.js | 59 ++ package.json | 14 +- 3 files changed, 707 insertions(+), 6 deletions(-) create mode 100644 encodings/tables/transliteration-ranges.json create mode 100644 generation/gen-transliteration.js diff --git a/encodings/tables/transliteration-ranges.json b/encodings/tables/transliteration-ranges.json new file mode 100644 index 0000000..072707c --- /dev/null +++ b/encodings/tables/transliteration-ranges.json @@ -0,0 +1,640 @@ +{"armscii8":[[0,160],[171,171],[187,187],[1329,1366],[1370,1374],[1377,1415],[1417, +1418],[8212,8212],[8230,8230]],"ascii":[[0,127]],"base64":[[0,11]],"big5hkscs":[[0, +127],[167,168],[175,177],[183,183],[192,193],[200,202],[210,211],[215,215],[224,225], +[232,234],[236,237],[242,243],[247,250],[252,252],[256,257],[274,275],[282,283],[299, +299],[331,333],[339,339],[363,363],[461,462],[464,466],[468,468],[470,470],[472,472], +[474,474],[476,476],[592,593],[596,596],[603,603],[609,609],[618,618],[629,629],[643, +643],[650,650],[710,711],[713,715],[717,717],[729,729],[913,929],[931,937],[945,961], +[963,969],[1025,1025],[1040,1103],[1105,1105],[7870,7873],[8211,8212],[8216,8217], +[8220,8221],[8229,8231],[8242,8242],[8245,8245],[8251,8251],[8364,8364],[8451,8451], +[8453,8453],[8457,8457],[8470,8470],[8481,8481],[8544,8553],[8560,8569],[8592,8595], +[8598,8601],[8632,8633],[8679,8679],[8725,8725],[8730,8730],[8734,8736],[8739,8739], +[8741,8741],[8745,8747],[8750,8750],[8756,8757],[8786,8786],[8800,8801],[8806,8807], +[8853,8853],[8857,8857],[8869,8869],[8895,8895],[9178,9179],[9216,9247],[9249,9249], +[9312,9321],[9332,9341],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492], +[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9588], +[9601,9615],[9620,9621],[9632,9633],[9650,9651],[9660,9661],[9670,9671],[9675,9675], +[9678,9679],[9698,9701],[9733,9734],[9792,9792],[9794,9794],[10045,10045],[11904, +11904],[11908,11908],[11910,11912],[11914,11914],[11916,11917],[11925,11925],[11932, +11933],[11941,11941],[11943,11943],[11946,11946],[11948,11948],[11950,11950],[11958, +11958],[11964,11964],[11966,11966],[11974,11974],[11978,11978],[11980,11981],[11983, +11983],[11990,11991],[11998,11998],[12003,12003],[12083,12083],[12288,12291],[12293, +12306],[12308,12309],[12317,12318],[12321,12329],[12353,12435],[12443,12446],[12449, +12534],[12540,12542],[12549,12585],[12736,12751],[12849,12849],[12963,12963],[13198, +13199],[13212,13214],[13217,13217],[13252,13252],[13262,13262],[13265,13266],[13269, +13269],[13365,13365],[13376,13376],[13386,13386],[13388,13388],[13412,13412],[13427, +13427],[13434,13434],[13437,13438],[13459,13459],[13462,13462],[13477,13477],[13487, +13487],[13500,13500],[13505,13505],[13512,13512],[13535,13535],[13540,13540],[13542, +13542],[13563,13563],[13574,13574],[13630,13630],[13649,13649],[13651,13651],[13657, +13657],[13665,13665],[13677,13677],[13680,13680],[13682,13682],[13687,13688],[13700, +13700],[13719,13720],[13729,13729],[13733,13733],[13741,13741],[13759,13759],[13761, +13761],[13765,13765],[13767,13767],[13770,13770],[13774,13774],[13778,13778],[13782, +13782],[13787,13787],[13789,13789],[13809,13811],[13819,13819],[13822,13822],[13833, +13833],[13848,13848],[13850,13850],[13859,13859],[13861,13861],[13869,13869],[13877, +13877],[13881,13881],[13886,13886],[13895,13897],[13902,13902],[13919,13919],[13921, +13921],[13946,13946],[13953,13953],[13978,13978],[13989,13989],[13994,13994],[13996, +13996],[14000,14001],[14005,14005],[14009,14009],[14012,14012],[14017,14017],[14019, +14021],[14023,14024],[14035,14036],[14038,14038],[14045,14045],[14049,14050],[14053, +14054],[14069,14069],[14081,14081],[14083,14083],[14088,14088],[14090,14090],[14093, +14093],[14108,14108],[14114,14115],[14117,14117],[14124,14125],[14128,14128],[14130, +14131],[14138,14138],[14144,14144],[14147,14147],[14178,14178],[14191,14191],[14231, +14231],[14240,14240],[14265,14265],[14270,14270],[14294,14294],[14322,14322]],"binary":[[0, +255],[257,511],[513,767],[769,1023],[1025,1279],[1281,1535],[1537,1791],[1793,2047], +[2049,2303],[2305,2559],[2561,2815],[2817,3071],[3073,3327],[3329,3583],[3585,3839], +[3841,4095],[4097,4351],[4353,4607],[4609,4863],[4865,5119],[5121,5375],[5377,5631], +[5633,5887],[5889,6143],[6145,6399],[6401,6655],[6657,6911],[6913,7167],[7169,7423], +[7425,7679],[7681,7935],[7937,8191],[8193,8447],[8449,8703],[8705,8959],[8961,9215], +[9217,9471],[9473,9727],[9729,9983],[9985,10239],[10241,10495],[10497,10751],[10753, +11007],[11009,11263],[11265,11519],[11521,11775],[11777,12031],[12033,12287],[12289, +12543],[12545,12799],[12801,13055],[13057,13311],[13313,13567],[13569,13823],[13825, +14079],[14081,14335],[14337,14591],[14593,14847],[14849,15103],[15105,15359],[15361, +15615],[15617,15871],[15873,16127],[16129,16383],[16385,16639],[16641,16895],[16897, +17151],[17153,17407],[17409,17663],[17665,17919],[17921,18175],[18177,18431],[18433, +18687],[18689,18943],[18945,19199],[19201,19455],[19457,19711],[19713,19967],[19969, +20223],[20225,20479],[20481,20735],[20737,20991],[20993,21247],[21249,21503],[21505, +21759],[21761,22015],[22017,22271],[22273,22527],[22529,22783],[22785,23039],[23041, +23295],[23297,23551],[23553,23807],[23809,24063],[24065,24319],[24321,24575],[24577, +24831],[24833,25087],[25089,25343],[25345,25599],[25601,25855],[25857,26111],[26113, +26367],[26369,26623],[26625,26879],[26881,27135],[27137,27391],[27393,27647],[27649, +27903],[27905,28159],[28161,28415],[28417,28671],[28673,28927],[28929,29183],[29185, +29439],[29441,29695],[29697,29951],[29953,30207],[30209,30463],[30465,30719],[30721, +30975],[30977,31231],[31233,31487],[31489,31743],[31745,31999],[32001,32255],[32257, +32511],[32513,32767],[32769,33023],[33025,33279],[33281,33535],[33537,33791],[33793, +34047],[34049,34303],[34305,34559],[34561,34815],[34817,35071],[35073,35327],[35329, +35583],[35585,35839],[35841,36095],[36097,36351],[36353,36607],[36609,36863],[36865, +37119],[37121,37375],[37377,37631],[37633,37887],[37889,38143],[38145,38399],[38401, +38655],[38657,38911],[38913,39167],[39169,39423],[39425,39679],[39681,39935],[39937, +40191],[40193,40447],[40449,40703],[40705,40959],[40961,41215],[41217,41471],[41473, +41727],[41729,41983],[41985,42239],[42241,42495],[42497,42751],[42753,43007],[43009, +43263],[43265,43519],[43521,43775],[43777,44031],[44033,44287],[44289,44543],[44545, +44799],[44801,45055],[45057,45311],[45313,45567],[45569,45823],[45825,46079],[46081, +46335],[46337,46591],[46593,46847],[46849,47103],[47105,47359],[47361,47615],[47617, +47871],[47873,48127],[48129,48383],[48385,48639],[48641,48895],[48897,49151],[49153, +49407],[49409,49663],[49665,49919],[49921,50175],[50177,50431],[50433,50687],[50689, +50943],[50945,51199],[51201,51455],[51457,51711],[51713,51967],[51969,52223],[52225, +52479],[52481,52735],[52737,52991],[52993,53247],[53249,53503],[53505,53759],[53761, +54015],[54017,54271],[54273,54527],[54529,54783],[54785,55039],[55041,55295],[55297, +55551],[55553,55807],[55809,56063],[56065,56319],[56321,56575],[56577,56831],[56833, +57087],[57089,57343],[57345,57599],[57601,57855],[57857,58111],[58113,58367],[58369, +58623],[58625,58879],[58881,59135],[59137,59391],[59393,59647],[59649,59903],[59905, +60159],[60161,60415],[60417,60671],[60673,60927],[60929,61183],[61185,61439],[61441, +61695],[61697,61951],[61953,62207],[62209,62463],[62465,62719],[62721,62975],[62977, +63231],[63233,63487],[63491,63491],[63493,63493],[63495,63495],[63497,63497],[63499, +63499],[63501,63501],[63503,63503],[63505,63505]],"cesu8":[[0,2160639]],"cp1046":[[0, +127],[136,136],[160,160],[164,164],[173,173],[215,215],[247,247],[1548,1548],[1563, +1563],[1567,1567],[1569,1591],[1593,1594],[1600,1618],[1632,1641],[9472,9472],[9474, +9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9632,9632],[61684,61692],[63089, +63089],[63095,63095],[63097,63097],[63099,63099],[63101,63101],[63103,63103],[63106, +63106],[63108,63108],[63112,63112],[63114,63115],[63118,63118],[63121,63121],[63127, +63127],[63131,63131],[63135,63135],[63139,63139],[63143,63143],[63155,63155],[63159, +63159],[63163,63163],[63167,63167],[63175,63175],[63178,63180],[63182,63184],[63187, +63187],[63191,63191],[63195,63195],[63199,63199],[63203,63203],[63207,63207],[63209, +63209],[63212,63212],[63216,63216],[63218,63219],[63221,63228]],"cp1124":[[0,160], +[167,167],[173,173],[1025,1026],[1028,1036],[1038,1103],[1105,1106],[1108,1116],[1118, +1119],[1168,1169],[8470,8470]],"cp1125":[[0,127],[160,160],[164,164],[183,183],[1025, +1025],[1028,1028],[1030,1031],[1040,1103],[1105,1105],[1108,1108],[1110,1111],[1168, +1169],[8470,8470],[8730,8730],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492, +9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552, +9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"cp1129":[[0, +167],[169,179],[181,183],[185,194],[196,203],[205,207],[209,209],[211,212],[214,220], +[223,226],[228,235],[237,239],[241,241],[243,244],[246,252],[255,255],[258,259],[272, +273],[338,339],[376,376],[416,417],[431,432],[768,769],[771,771],[777,777],[803,803], +[8363,8363]],"cp1133":[[0,160],[162,162],[166,166],[172,172],[3713,3714],[3716,3716], +[3719,3720],[3722,3722],[3725,3725],[3732,3735],[3737,3743],[3745,3747],[3749,3749], +[3751,3751],[3754,3755],[3757,3769],[3771,3773],[3776,3780],[3782,3782],[3784,3789], +[3792,3801],[3804,3805],[8365,8365]],"cp1161":[[0,127],[160,160],[162,162],[166,166], +[172,172],[3585,3642],[3647,3675],[8364,8364]],"cp1162":[[0,127],[129,132],[134,144], +[152,160],[3585,3642],[3647,3675],[8211,8212],[8216,8217],[8220,8221],[8226,8226], +[8230,8230],[8364,8364]],"cp1163":[[0,163],[165,167],[169,179],[181,183],[185,194], +[196,203],[205,207],[209,209],[211,212],[214,220],[223,226],[228,235],[237,239],[241, +241],[243,244],[246,252],[255,255],[258,259],[272,273],[338,339],[376,376],[416,417], +[431,432],[768,769],[771,771],[777,777],[803,803],[8363,8364]],"cp437":[[0,127],[160, +163],[165,165],[170,172],[176,178],[181,181],[183,183],[186,189],[191,191],[196,199], +[201,201],[209,209],[214,214],[220,220],[223,226],[228,239],[241,244],[246,247],[249, +252],[255,255],[402,402],[915,915],[920,920],[931,931],[934,934],[937,937],[945,945], +[948,949],[960,960],[963,964],[966,966],[8319,8319],[8359,8359],[8729,8730],[8734, +8734],[8745,8745],[8776,8776],[8801,8801],[8804,8805],[8976,8976],[8992,8993],[9472, +9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508, +9508],[9516,9516],[9524,9524],[9532,9532],[9552,9580],[9600,9600],[9604,9604],[9608, +9608],[9612,9612],[9616,9619],[9632,9632]],"cp737":[[0,127],[160,160],[176,178],[183, +183],[247,247],[902,902],[904,906],[908,908],[910,911],[913,929],[931,943],[945,974], +[8319,8319],[8729,8730],[8776,8776],[8804,8805],[9472,9472],[9474,9474],[9484,9484], +[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524], +[9532,9532],[9552,9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619], +[9632,9632]],"cp775":[[0,127],[160,160],[162,164],[166,167],[169,169],[171,174],[176, +179],[181,183],[185,185],[187,190],[196,198],[201,201],[211,211],[213,216],[220,220], +[223,223],[228,230],[233,233],[243,243],[245,248],[252,252],[256,257],[260,263],[268, +269],[274,275],[278,281],[290,291],[298,299],[302,303],[310,311],[315,316],[321,326], +[332,333],[342,343],[346,347],[352,353],[362,363],[370,371],[377,382],[8217,8217], +[8220,8222],[8729,8729],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492], +[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9553], +[9556,9556],[9559,9559],[9562,9562],[9565,9565],[9568,9568],[9571,9571],[9574,9574], +[9577,9577],[9580,9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619], +[9632,9632]],"cp808":[[0,127],[160,160],[176,176],[183,183],[1025,1025],[1028,1028], +[1031,1031],[1038,1038],[1040,1103],[1105,1105],[1108,1108],[1111,1111],[1118,1118], +[8364,8364],[8470,8470],[8729,8730],[9472,9472],[9474,9474],[9484,9484],[9488,9488], +[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532], +[9552,9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]], +"cp850":[[0,127],[160,255],[305,305],[402,402],[8215,8215],[9472,9472],[9474,9474], +[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516], +[9524,9524],[9532,9532],[9552,9553],[9556,9556],[9559,9559],[9562,9562],[9565,9565], +[9568,9568],[9571,9571],[9574,9574],[9577,9577],[9580,9580],[9600,9600],[9604,9604], +[9608,9608],[9617,9619],[9632,9632]],"cp852":[[0,127],[160,160],[164,164],[167,168], +[171,173],[176,176],[180,180],[184,184],[187,187],[193,194],[196,196],[199,199],[201, +201],[203,203],[205,206],[211,212],[214,215],[218,218],[220,221],[223,223],[225,226], +[228,228],[231,231],[233,233],[235,235],[237,238],[243,244],[246,247],[250,250],[252, +253],[258,263],[268,273],[280,283],[313,314],[317,318],[321,324],[327,328],[336,337], +[340,341],[344,347],[350,357],[366,369],[377,382],[711,711],[728,729],[731,731],[733, +733],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9500, +9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9553],[9556,9556],[9559, +9559],[9562,9562],[9565,9565],[9568,9568],[9571,9571],[9574,9574],[9577,9577],[9580, +9580],[9600,9600],[9604,9604],[9608,9608],[9617,9619],[9632,9632]],"cp855":[[0,127], +[160,160],[164,164],[167,167],[171,171],[173,173],[187,187],[1025,1036],[1038,1103], +[1105,1116],[1118,1119],[8470,8470],[9472,9472],[9474,9474],[9484,9484],[9488,9488], +[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532], +[9552,9553],[9556,9556],[9559,9559],[9562,9562],[9565,9565],[9568,9568],[9571,9571], +[9574,9574],[9577,9577],[9580,9580],[9600,9600],[9604,9604],[9608,9608],[9617,9619], +[9632,9632]],"cp856":[[0,127],[160,160],[162,169],[171,185],[187,190],[215,215],[247, +247],[1488,1514],[8215,8215],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492, +9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552, +9553],[9556,9556],[9559,9559],[9562,9562],[9565,9565],[9568,9568],[9571,9571],[9574, +9574],[9577,9577],[9580,9580],[9600,9600],[9604,9604],[9608,9608],[9617,9619],[9632, +9632]],"cp857":[[0,127],[160,207],[209,220],[223,239],[241,252],[255,255],[286,287], +[304,305],[350,351],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496, +9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9553],[9556, +9556],[9559,9559],[9562,9562],[9565,9565],[9568,9568],[9571,9571],[9574,9574],[9577, +9577],[9580,9580],[9600,9600],[9604,9604],[9608,9608],[9617,9619],[9632,9632]],"cp858":[[0, +127],[160,255],[402,402],[8215,8215],[8364,8364],[9472,9472],[9474,9474],[9484,9484], +[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524], +[9532,9532],[9552,9553],[9556,9556],[9559,9559],[9562,9562],[9565,9565],[9568,9568], +[9571,9571],[9574,9574],[9577,9577],[9580,9580],[9600,9600],[9604,9604],[9608,9608], +[9617,9619],[9632,9632]],"cp860":[[0,127],[160,163],[170,172],[176,178],[181,181], +[183,183],[186,189],[191,195],[199,202],[204,205],[209,213],[217,218],[220,220],[223, +227],[231,234],[236,237],[241,245],[247,247],[249,250],[252,252],[915,915],[920,920], +[931,931],[934,934],[937,937],[945,945],[948,949],[960,960],[963,964],[966,966],[8319, +8319],[8359,8359],[8729,8730],[8734,8734],[8745,8745],[8776,8776],[8801,8801],[8804, +8805],[8992,8993],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496, +9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9580],[9600, +9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"cp861":[[0,127], +[160,161],[163,163],[171,172],[176,178],[181,181],[183,183],[187,189],[191,191],[193, +193],[196,199],[201,201],[205,205],[208,208],[211,211],[214,214],[216,216],[218,218], +[220,226],[228,235],[237,237],[240,240],[243,244],[246,248],[250,254],[402,402],[915, +915],[920,920],[931,931],[934,934],[937,937],[945,945],[948,949],[960,960],[963,964], +[966,966],[8319,8319],[8359,8359],[8729,8730],[8734,8734],[8745,8745],[8776,8776], +[8801,8801],[8804,8805],[8976,8976],[8992,8993],[9472,9472],[9474,9474],[9484,9484], +[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524], +[9532,9532],[9552,9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619], +[9632,9632]],"cp862":[[0,127],[160,163],[165,165],[170,172],[176,178],[181,181],[183, +183],[186,189],[191,191],[209,209],[223,223],[225,225],[237,237],[241,241],[243,243], +[247,247],[250,250],[402,402],[915,915],[920,920],[931,931],[934,934],[937,937],[945, +945],[948,949],[960,960],[963,964],[966,966],[1488,1514],[8319,8319],[8359,8359], +[8729,8730],[8734,8734],[8745,8745],[8776,8776],[8801,8801],[8804,8805],[8976,8976], +[8992,8993],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496], +[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9580],[9600,9600], +[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"cp863":[[0,127],[160, +160],[162,164],[166,168],[171,172],[175,184],[187,190],[192,192],[194,194],[199,203], +[206,207],[212,212],[217,217],[219,220],[223,224],[226,226],[231,235],[238,239],[243, +244],[247,247],[249,252],[402,402],[915,915],[920,920],[931,931],[934,934],[937,937], +[945,945],[948,949],[960,960],[963,964],[966,966],[8215,8215],[8319,8319],[8729,8730], +[8734,8734],[8745,8745],[8776,8776],[8801,8801],[8804,8805],[8976,8976],[8992,8993], +[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9500,9500], +[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9580],[9600,9600],[9604,9604], +[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"cp864":[[0,36],[38,127],[160,160], +[162,164],[166,166],[171,173],[176,177],[183,183],[187,189],[215,215],[247,247],[946, +946],[966,966],[1548,1548],[1563,1563],[1567,1567],[1600,1600],[1617,1617],[1632, +1642],[8729,8730],[8734,8734],[8776,8776],[9472,9472],[9474,9474],[9484,9484],[9488, +9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532, +9532],[9618,9618],[9632,9632],[63101,63101],[63104,63109],[63115,63115],[63117,63119], +[63121,63121],[63123,63123],[63125,63125],[63127,63127],[63129,63129],[63131,63131], +[63133,63133],[63135,63135],[63137,63137],[63139,63139],[63141,63141],[63143,63143], +[63145,63145],[63147,63147],[63149,63149],[63151,63151],[63153,63153],[63155,63155], +[63157,63157],[63159,63159],[63161,63161],[63163,63163],[63165,63165],[63167,63167], +[63169,63169],[63173,63173],[63177,63185],[63187,63187],[63189,63189],[63191,63191], +[63193,63193],[63195,63195],[63197,63197],[63199,63199],[63201,63201],[63203,63203], +[63205,63205],[63207,63207],[63209,63209],[63211,63213],[63215,63219],[63221,63224], +[63227,63228]],"cp865":[[0,127],[160,161],[163,164],[170,172],[176,178],[181,181], +[183,183],[186,186],[188,189],[191,191],[196,199],[201,201],[209,209],[214,214],[216, +216],[220,220],[223,226],[228,239],[241,244],[246,252],[255,255],[402,402],[915,915], +[920,920],[931,931],[934,934],[937,937],[945,945],[948,949],[960,960],[963,964],[966, +966],[8319,8319],[8359,8359],[8729,8730],[8734,8734],[8745,8745],[8776,8776],[8801, +8801],[8804,8805],[8976,8976],[8992,8993],[9472,9472],[9474,9474],[9484,9484],[9488, +9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532, +9532],[9552,9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632, +9632]],"cp866":[[0,127],[160,160],[164,164],[176,176],[183,183],[1025,1025],[1028, +1028],[1031,1031],[1038,1038],[1040,1103],[1105,1105],[1108,1108],[1111,1111],[1118, +1118],[8470,8470],[8729,8730],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492, +9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552, +9580],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"cp869":[[0, +127],[160,160],[163,163],[166,169],[171,173],[176,179],[183,183],[187,187],[189,189], +[900,902],[904,906],[908,908],[910,929],[931,974],[8213,8213],[8216,8217],[9472,9472], +[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508], +[9516,9516],[9524,9524],[9532,9532],[9552,9553],[9556,9556],[9559,9559],[9562,9562], +[9565,9565],[9568,9568],[9571,9571],[9574,9574],[9577,9577],[9580,9580],[9600,9600], +[9604,9604],[9608,9608],[9617,9619],[9632,9632]],"cp922":[[0,174],[176,207],[209, +221],[223,239],[241,253],[255,255],[352,353],[381,382],[8254,8254]],"cp936":[[0,127], +[164,164],[167,168],[176,177],[183,183],[215,215],[224,225],[232,234],[236,237],[242, +243],[247,247],[249,250],[252,252],[257,257],[275,275],[283,283],[299,299],[324,324], +[328,328],[333,333],[363,363],[462,462],[464,464],[466,466],[468,468],[470,470],[472, +472],[474,474],[476,476],[593,593],[609,609],[711,711],[713,715],[729,729],[913,929], +[931,937],[945,961],[963,969],[1025,1025],[1040,1103],[1105,1105],[8208,8208],[8211, +8214],[8216,8217],[8220,8221],[8229,8230],[8240,8240],[8242,8243],[8245,8245],[8251, +8251],[8364,8364],[8451,8451],[8453,8453],[8457,8457],[8470,8470],[8481,8481],[8544, +8555],[8560,8569],[8592,8595],[8598,8601],[8712,8712],[8719,8719],[8721,8721],[8725, +8725],[8730,8730],[8733,8736],[8739,8739],[8741,8741],[8743,8747],[8750,8750],[8756, +8759],[8765,8765],[8776,8776],[8780,8780],[8786,8786],[8800,8801],[8804,8807],[8814, +8815],[8853,8853],[8857,8857],[8869,8869],[8895,8895],[8978,8978],[9312,9321],[9332, +9371],[9472,9547],[9552,9587],[9601,9615],[9619,9621],[9632,9633],[9650,9651],[9660, +9661],[9670,9671],[9675,9675],[9678,9679],[9698,9701],[9733,9734],[9737,9737],[9792, +9792],[9794,9794],[12288,12291],[12293,12311],[12317,12318],[12321,12329],[12353, +12435],[12443,12446],[12449,12534],[12540,12542],[12549,12585],[12832,12841],[12849, +12849],[12963,12963],[13198,13199],[13212,13214],[13217,13217],[13252,13252],[13262, +13262],[13265,13266],[13269,13269],[19968,40869],[61740,61740],[61817,61817],[61845, +61845],[61927,61927],[61937,61937],[61964,61967],[61969,61969],[61971,61972],[61976, +61976],[61983,61985],[61987,61988],[61991,61993],[63024,63025],[63027,63044],[63049, +63058],[63060,63063],[63065,63078],[63080,63083],[63233,63326],[63456,63461]],"cp949":[[0, +127],[161,161],[164,164],[167,168],[170,170],[173,174],[176,180],[182,186],[188,191], +[198,198],[208,208],[215,216],[222,223],[230,230],[240,240],[247,248],[254,254],[273, +273],[294,295],[305,307],[312,312],[319,322],[329,331],[338,339],[358,359],[711,711], +[720,720],[728,731],[733,733],[913,929],[931,937],[945,961],[963,969],[1025,1025], +[1040,1103],[1105,1105],[8213,8213],[8216,8217],[8220,8221],[8224,8225],[8229,8230], +[8240,8240],[8242,8243],[8251,8251],[8308,8308],[8319,8319],[8321,8324],[8364,8364], +[8451,8451],[8457,8457],[8467,8467],[8470,8470],[8481,8482],[8486,8486],[8491,8491], +[8531,8532],[8539,8542],[8544,8553],[8560,8569],[8592,8601],[8658,8658],[8660,8660], +[8704,8704],[8706,8707],[8711,8712],[8715,8715],[8719,8719],[8721,8721],[8730,8730], +[8733,8734],[8736,8736],[8741,8741],[8743,8748],[8750,8750],[8756,8757],[8764,8765], +[8786,8786],[8800,8801],[8804,8805],[8810,8811],[8834,8835],[8838,8839],[8857,8857], +[8869,8869],[8978,8978],[9312,9326],[9332,9346],[9372,9397],[9424,9449],[9472,9475], +[9484,9547],[9618,9618],[9632,9633],[9635,9641],[9650,9651],[9654,9655],[9660,9661], +[9664,9665],[9670,9672],[9675,9675],[9678,9681],[9733,9734],[9742,9743],[9756,9756], +[9758,9758],[9792,9792],[9794,9794],[9824,9825],[9827,9829],[9831,9834],[9836,9837], +[12288,12291],[12296,12305],[12307,12309],[12353,12435],[12449,12534],[12593,12686], +[12800,12828],[12896,12923],[12927,12927],[13184,13188],[13192,13258],[13263,13264], +[13267,13267],[13270,13270],[13272,13272],[13275,13277],[19968,19969],[19971,19971], +[19975,19979],[19981,19981],[19985,19985],[19988,19990],[19992,19993],[19998,19998], +[20013,20013],[20018,20018],[20024,20025],[20027,20027],[20034,20035],[20037,20037], +[20043,20043],[20045,20047],[20054,20054],[20056,20057],[20061,20063],[20075,20075], +[20077,20077],[20083,20083],[20086,20087],[20094,20094],[20098,20098],[20102,20102], +[20104,20104],[20107,20108],[20110,20110],[20112,20114],[20116,20117],[20120,20120], +[20123,20123],[20126,20126],[20129,20130],[20132,20134],[20136,20136],[20139,20142], +[20150,20150],[20154,20154],[20160,20161],[20164,20164],[20167,20167],[20170,20171], +[20173,20173],[20180,20185],[20189,20189],[20191,20191],[20195,20197],[20208,20208], +[20210,20210],[20214,20215],[20219,20219],[20225,20225],[20233,20235],[20237,20241], +[20271,20271],[20276,20276],[20278,20278],[20280,20280],[20282,20282],[20284,20285], +[20291,20291],[20294,20296],[20301,20305],[20309,20309],[20313,20316],[20329,20329], +[20335,20336],[20339,20339],[20342,20342],[20346,20346],[20350,20351],[20353,20353], +[20355,20356],[20358,20358],[20360,20360],[20362,20363],[20365,20365],[20367,20367], +[20369,20369],[20374,20374],[20376,20376],[20379,20379],[20381,20381],[20398,20399], +[20405,20406],[20415,20415],[20418,20420],[20425,20426],[20430,20430],[20433,20433], +[20435,20436],[20439,20439],[20442,20442],[20445,20445],[20447,20449],[20462,20463], +[20465,20465],[20467,20467],[20469,20469],[20472,20472],[20474,20474],[20482,20482], +[20486,20486],[20489,20489],[20491,20491],[20493,20493],[20497,20498],[20502,20502], +[20505,20506],[20508,20508],[20510,20511],[20513,20513],[20515,20516],[20518,20520], +[20522,20525],[20539,20539],[20547,20547],[20551,20553],[20559,20559],[20565,20565], +[20570,20570],[20572,20572],[20581,20581],[20596,20598],[20600,20600],[20608,20608], +[20613,20613]],"cp950":[[0,127],[167,167],[175,177],[183,183],[215,215],[247,247], +[711,711],[713,715],[717,717],[729,729],[913,929],[931,937],[945,961],[963,969],[8211, +8212],[8216,8217],[8220,8221],[8229,8231],[8242,8242],[8245,8245],[8251,8251],[8364, +8364],[8451,8451],[8453,8453],[8457,8457],[8544,8553],[8592,8595],[8598,8601],[8725, +8725],[8730,8730],[8734,8736],[8739,8739],[8741,8741],[8745,8747],[8750,8750],[8756, +8757],[8786,8786],[8800,8801],[8806,8807],[8853,8853],[8857,8857],[8869,8869],[8895, +8895],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496],[9500, +9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9588],[9601,9615],[9619, +9621],[9632,9633],[9650,9651],[9660,9661],[9670,9671],[9675,9675],[9678,9679],[9698, +9701],[9733,9734],[9792,9792],[9794,9794],[12288,12291],[12296,12306],[12308,12309], +[12317,12318],[12321,12329],[12549,12585],[12963,12963],[13198,13199],[13212,13214], +[13217,13217],[13252,13252],[13262,13262],[13265,13266],[13269,13269],[19968,19969], +[19971,19971],[19975,19985],[19988,19990],[19992,19993],[19998,19999],[20006,20006], +[20011,20011],[20013,20014],[20016,20019],[20024,20025],[20027,20028],[20034,20035], +[20037,20037],[20039,20040],[20043,20043],[20045,20047],[20050,20051],[20054,20054], +[20056,20057],[20060,20063],[20073,20073],[20083,20083],[20094,20095],[20098,20100], +[20102,20102],[20104,20104],[20107,20110],[20113,20117],[20121,20121],[20123,20123], +[20126,20127],[20129,20130],[20132,20134],[20136,20136],[20139,20142],[20147,20147], +[20150,20150],[20153,20154],[20160,20164],[20166,20171],[20173,20173],[20180,20186], +[20188,20191],[20193,20193],[20195,20197],[20200,20201],[20208,20215],[20219,20219], +[20221,20221],[20223,20226],[20228,20229],[20232,20235],[20237,20245],[20248,20249], +[20253,20253],[20258,20258],[20268,20269],[20271,20272],[20275,20276],[20278,20278], +[20280,20280],[20282,20287],[20289,20289],[20291,20291],[20294,20297],[20300,20324], +[20327,20327],[20329,20332],[20334,20336],[20339,20361],[20363,20363],[20365,20365], +[20367,20370],[20372,20376],[20378,20382],[20398,20399],[20402,20403],[20405,20407], +[20409,20411],[20415,20421],[20423,20423],[20425,20427],[20429,20433],[20435,20436], +[20438,20449],[20460,20460],[20462,20463],[20465,20465],[20467,20472],[20474,20474], +[20478,20478],[20480,20480],[20485,20487],[20489,20489],[20491,20495],[20497,20508], +[20510,20515],[20517,20525],[20527,20529],[20531,20531],[20533,20533],[20535,20535], +[20540,20540],[20544,20545],[20547,20547],[20549,20559],[20561,20561],[20563,20563], +[20565,20565],[20567,20567],[20570,20581],[20584,20587],[20589,20592],[20594,20599], +[20602,20602],[20605,20605],[20608,20608],[20610,20611],[20613,20613],[20615,20615], +[20619,20622],[20625,20626],[20628,20630],[20632,20638],[20642,20643],[20652,20664], +[20666,20667],[20669,20671],[20673,20674],[20676,20683],[20686,20687],[20689,20689], +[20691,20695],[20698,20699],[20701,20701],[20704,20704],[20707,20714],[20716,20721], +[20723,20723],[20725,20726],[20728,20729],[20731,20731],[20733,20736],[20738,20748], +[20752,20757],[20759,20760],[20762,20762],[20764,20764],[20767,20770],[20772,20774], +[20777,20778],[20781,20782],[20784,20789],[20791,20797],[20799,20801],[20803,20809], +[20811,20813],[20818,20818],[20820,20821],[20823,20823],[20825,20831],[20833,20835], +[20837,20837],[20839,20841],[20843,20846],[20849,20849],[20853,20856],[20860,20860], +[20864,20864],[20871,20871],[20873,20874],[20877,20877],[20879,20879],[20881,20885]], +"eucjp":[[0,127],[161,161],[164,170],[174,177],[180,180],[182,182],[184,184],[186, +186],[191,207],[209,275],[278,290],[292,299],[302,333],[336,382],[461,476],[501,501], +[711,711],[728,731],[733,733],[900,902],[904,906],[908,908],[910,929],[931,974],[1025, +1036],[1038,1103],[1105,1116],[1118,1119],[8208,8208],[8213,8213],[8216,8217],[8220, +8221],[8224,8225],[8229,8230],[8240,8240],[8242,8243],[8251,8251],[8254,8254],[8451, +8451],[8470,8470],[8481,8482],[8491,8491],[8544,8553],[8560,8569],[8592,8595],[8658, +8658],[8660,8660],[8704,8704],[8706,8707],[8711,8712],[8715,8715],[8721,8721],[8730, +8730],[8733,8736],[8741,8741],[8743,8748],[8750,8750],[8756,8757],[8765,8765],[8786, +8786],[8800,8801],[8806,8807],[8810,8811],[8834,8835],[8838,8839],[8869,8869],[8895, +8895],[8978,8978],[9312,9331],[9472,9475],[9484,9484],[9487,9488],[9491,9492],[9495, +9496],[9499,9501],[9504,9504],[9507,9509],[9512,9512],[9515,9516],[9519,9520],[9523, +9524],[9527,9528],[9531,9532],[9535,9535],[9538,9538],[9547,9547],[9632,9633],[9650, +9651],[9660,9661],[9670,9671],[9675,9675],[9678,9679],[9711,9711],[9733,9734],[9792, +9792],[9794,9794],[9834,9834],[9837,9837],[9839,9839],[12288,12291],[12293,12309], +[12317,12317],[12319,12319],[12353,12435],[12443,12446],[12449,12534],[12539,12542], +[12849,12850],[12857,12857],[12964,12968],[13059,13059],[13069,13069],[13076,13076], +[13080,13080],[13090,13091],[13094,13095],[13099,13099],[13110,13110],[13115,13115], +[13129,13130],[13133,13133],[13137,13137],[13143,13143],[13179,13182],[13198,13199], +[13212,13214],[13217,13217],[13252,13252],[13261,13261],[19968,19973],[19975,19982], +[19984,19986],[19988,19993],[19998,19999],[20001,20001],[20003,20004],[20006,20006], +[20008,20008],[20010,20011],[20013,20018],[20021,20022],[20024,20025],[20027,20028], +[20031,20037],[20039,20039],[20043,20043],[20045,20047],[20049,20049],[20053,20058], +[20060,20063],[20066,20067],[20072,20073],[20081,20081],[20083,20085],[20089,20089], +[20094,20096],[20098,20098],[20101,20102],[20104,20110],[20113,20114],[20116,20121], +[20123,20130],[20132,20134],[20136,20136],[20139,20144],[20147,20147],[20150,20150], +[20153,20154],[20160,20164],[20166,20167],[20170,20171],[20173,20176],[20180,20187], +[20189,20197],[20200,20200],[20205,20211],[20213,20215],[20219,20227],[20232,20242], +[20245,20247],[20249,20250],[20252,20253],[20270,20273],[20275,20286],[20288,20288], +[20290,20291],[20294,20297],[20299,20320],[20323,20323],[20329,20330],[20332,20332], +[20334,20337],[20339,20339],[20341,20351],[20353,20358],[20360,20372],[20374,20379], +[20381,20385],[20395,20395],[20397,20399],[20402,20402],[20405,20407],[20409,20409], +[20411,20422],[20424,20434],[20436,20436],[20439,20440],[20442,20445],[20447,20453], +[20462,20464],[20466,20467],[20469,20470],[20472,20472],[20474,20474],[20476,20481], +[20484,20487],[20489,20500],[20502,20511],[20513,20526],[20528,20528],[20530,20531], +[20533,20534],[20537,20537],[20539,20539],[20544,20547],[20549,20554],[20556,20556], +[20558,20563],[20565,20567],[20569,20570],[20572,20572],[20575,20576],[20578,20579], +[20581,20583],[20586,20586],[20588,20589],[20592,20594],[20596,20598],[20600,20600], +[20605,20605],[20608,20609],[20611,20614],[20618,20618],[20621,20628],[20630,20630], +[20632,20636],[20638,20642],[20650,20650],[20652,20653],[20655,20656],[20658,20661], +[20663,20663],[20665,20666],[20669,20670],[20672,20672],[20674,20677]],"gb18030":[[0, +2160639]],"gbk":[[0,127],[164,164],[167,168],[176,177],[183,183],[215,215],[224,225], +[232,234],[236,237],[242,243],[247,247],[249,250],[252,252],[257,257],[275,275],[283, +283],[299,299],[324,324],[328,328],[333,333],[363,363],[462,462],[464,464],[466,466], +[468,468],[470,470],[472,472],[474,474],[476,476],[505,505],[593,593],[609,609],[711, +711],[713,715],[729,729],[913,929],[931,937],[945,961],[963,969],[1025,1025],[1040, +1103],[1105,1105],[8208,8208],[8211,8214],[8216,8217],[8220,8221],[8229,8230],[8240, +8240],[8242,8243],[8245,8245],[8251,8251],[8364,8364],[8451,8451],[8453,8453],[8457, +8457],[8470,8470],[8481,8481],[8544,8555],[8560,8569],[8592,8595],[8598,8601],[8712, +8712],[8719,8719],[8721,8721],[8725,8725],[8730,8730],[8733,8736],[8739,8739],[8741, +8741],[8743,8747],[8750,8750],[8756,8759],[8765,8765],[8776,8776],[8780,8780],[8786, +8786],[8800,8801],[8804,8807],[8814,8815],[8853,8853],[8857,8857],[8869,8869],[8895, +8895],[8978,8978],[9312,9321],[9332,9371],[9472,9547],[9552,9587],[9601,9615],[9619, +9621],[9632,9633],[9650,9651],[9660,9661],[9670,9671],[9675,9675],[9678,9679],[9698, +9701],[9733,9734],[9737,9737],[9792,9792],[9794,9794],[11905,11905],[11908,11908], +[11912,11912],[11915,11916],[11927,11927],[11943,11943],[11946,11946],[11950,11950], +[11955,11955],[11958,11959],[11963,11963],[11978,11978],[12272,12283],[12288,12291], +[12293,12311],[12317,12318],[12321,12329],[12350,12350],[12353,12435],[12443,12446], +[12449,12534],[12540,12542],[12549,12585],[12832,12841],[12849,12849],[12963,12963], +[13198,13199],[13212,13214],[13217,13217],[13252,13252],[13262,13262],[13265,13266], +[13269,13269],[13383,13383],[13427,13427],[13726,13726],[13838,13838],[13850,13850], +[14616,14616],[14702,14702],[14799,14800],[14815,14815],[14963,14963],[15182,15182], +[15470,15470],[15584,15584],[16470,16470],[16735,16735],[17207,17207],[17324,17324], +[17329,17329],[17373,17373],[17622,17622],[17996,17996],[18017,18017],[18211,18211], +[18217,18217],[18300,18300],[18317,18317],[18759,18759],[18810,18810],[18813,18813], +[18818,18819],[18821,18822],[18843,18843],[18847,18847],[18870,18871],[19575,19575], +[19615,19619],[19731,19737],[19886,19886],[19968,40869],[55296,56804],[56806,57195], +[57197,57287],[57289,57318],[57332,57364],[57366,57368],[57374,57374],[57382,57382], +[57387,57388],[57393,57394],[57403,57403],[57411,57411],[57428,57429],[57444,57444], +[61740,61740],[61817,61817],[61845,61845],[61927,61927],[61937,61937],[61964,61967], +[61969,61969],[61971,61972],[61976,61976],[61983,61985],[61987,61988],[61991,61993], +[63024,63025],[63027,63044],[63049,63058],[63060,63063],[63065,63078],[63080,63083], +[63233,63326],[63456,63461]],"georgianacademy":[[0,129],[141,144],[157,158],[160, +191],[231,255],[338,339],[352,353],[376,376],[402,402],[710,710],[732,732],[4304, +4342],[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240],[8249, +8250],[8482,8482]],"georgianps":[[0,129],[141,144],[157,158],[160,191],[230,255], +[338,339],[352,353],[376,376],[402,402],[710,710],[732,732],[4304,4341],[8211,8212], +[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240],[8249,8250],[8482,8482]], +"hex":[],"hproman8":[[0,165],[167,168],[170,171],[175,177],[180,183],[186,214],[216, +246],[248,255],[352,353],[376,376],[402,402],[710,710],[715,715],[732,732],[8212, +8212],[8356,8356],[9632,9632]],"iso646cn":[[0,35],[37,125],[127,127],[165,165],[8254, +8254]],"iso646jp":[[0,91],[93,125],[127,127],[165,165],[8254,8254]],"iso88591":[[0, +255]],"iso885910":[[0,160],[167,167],[173,173],[176,176],[183,183],[193,198],[201, +201],[203,203],[205,208],[211,214],[216,216],[218,223],[225,230],[233,233],[235,235], +[237,240],[243,246],[248,248],[250,254],[256,257],[260,261],[268,269],[272,275],[278, +281],[290,291],[296,299],[302,303],[310,312],[315,316],[325,326],[330,333],[352,353], +[358,363],[370,371],[381,382],[8213,8213]],"iso885911":[[0,160],[3585,3642],[3647, +3675]],"iso885913":[[0,160],[162,164],[166,167],[169,169],[171,174],[176,179],[181, +183],[185,185],[187,190],[196,198],[201,201],[211,211],[213,216],[220,220],[223,223], +[228,230],[233,233],[243,243],[245,248],[252,252],[256,257],[260,263],[268,269],[274, +275],[278,281],[290,291],[298,299],[302,303],[310,311],[315,316],[321,326],[332,333], +[342,343],[346,347],[352,353],[362,363],[370,371],[377,382],[8217,8217],[8220,8222]], +"iso885914":[[0,160],[163,163],[167,167],[169,169],[173,174],[182,182],[192,207], +[209,214],[216,221],[223,239],[241,246],[248,253],[255,255],[266,267],[288,289],[372, +376],[7682,7683],[7690,7691],[7710,7711],[7744,7745],[7766,7767],[7776,7777],[7786, +7787],[7808,7813],[7922,7923]],"iso885915":[[0,163],[165,165],[167,167],[169,179], +[181,183],[185,187],[191,255],[338,339],[352,353],[376,376],[381,382],[8364,8364]], +"iso885916":[[0,160],[167,167],[169,169],[171,171],[173,173],[176,177],[182,183], +[187,187],[192,194],[196,196],[198,207],[210,212],[214,214],[217,220],[223,226],[228, +228],[230,239],[242,244],[246,246],[249,252],[255,255],[258,263],[268,269],[272,273], +[280,281],[321,324],[336,339],[346,347],[352,353],[368,369],[376,382],[536,539],[8221, +8222],[8364,8364]],"iso88592":[[0,160],[164,164],[167,168],[173,173],[176,176],[180, +180],[184,184],[193,194],[196,196],[199,199],[201,201],[203,203],[205,206],[211,212], +[214,215],[218,218],[220,221],[223,223],[225,226],[228,228],[231,231],[233,233],[235, +235],[237,238],[243,244],[246,247],[250,250],[252,253],[258,263],[268,273],[280,283], +[313,314],[317,318],[321,324],[327,328],[336,337],[340,341],[344,347],[350,357],[366, +369],[377,382],[711,711],[728,729],[731,731],[733,733]],"iso88593":[[0,160],[163, +164],[167,168],[173,173],[176,176],[178,181],[183,184],[189,189],[192,194],[196,196], +[199,207],[209,212],[214,215],[217,220],[223,226],[228,228],[231,239],[241,244],[246, +247],[249,252],[264,267],[284,289],[292,295],[304,305],[308,309],[348,351],[364,365], +[379,380],[728,729]],"iso88594":[[0,160],[164,164],[167,168],[173,173],[175,176], +[180,180],[184,184],[193,198],[201,201],[203,203],[205,206],[212,216],[218,220],[223, +223],[225,230],[233,233],[235,235],[237,238],[244,248],[250,252],[256,257],[260,261], +[268,269],[272,275],[278,281],[290,291],[296,299],[302,303],[310,312],[315,316],[325, +326],[330,333],[342,343],[352,353],[358,363],[370,371],[381,382],[711,711],[729,729], +[731,731]],"iso88595":[[0,160],[167,167],[173,173],[1025,1036],[1038,1103],[1105, +1116],[1118,1119],[8470,8470]],"iso88596":[[0,160],[164,164],[173,173],[1548,1548], +[1563,1563],[1567,1567],[1569,1594],[1600,1618]],"iso88597":[[0,160],[163,163],[166, +169],[171,173],[176,179],[183,183],[187,187],[189,189],[890,890],[900,902],[904,906], +[908,908],[910,929],[931,974],[8213,8213],[8216,8217],[8364,8364],[8367,8367]],"iso88598":[[0, +160],[162,169],[171,185],[187,190],[215,215],[247,247],[1488,1514],[8206,8207],[8215, +8215]],"iso88599":[[0,207],[209,220],[223,239],[241,252],[255,255],[286,287],[304, +305],[350,351]],"koi8r":[[0,127],[160,160],[169,169],[176,176],[178,178],[183,183], +[247,247],[1025,1025],[1040,1103],[1105,1105],[8729,8730],[8776,8776],[8804,8805], +[8992,8993],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496], +[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9580],[9600,9600], +[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"koi8ru":[[0,127],[160, +160],[169,169],[176,176],[178,178],[183,183],[247,247],[1025,1025],[1028,1028],[1030, +1031],[1038,1038],[1040,1103],[1105,1105],[1108,1108],[1110,1111],[1118,1118],[1168, +1169],[8729,8730],[8776,8776],[8804,8805],[8992,8993],[9472,9472],[9474,9474],[9484, +9484],[9488,9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524, +9524],[9532,9532],[9552,9554],[9556,9556],[9559,9563],[9566,9569],[9571,9571],[9574, +9578],[9600,9600],[9604,9604],[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"koi8t":[[0, +127],[164,164],[166,167],[169,169],[171,174],[176,178],[182,183],[187,187],[1025, +1025],[1040,1103],[1105,1105],[1170,1171],[1178,1179],[1202,1203],[1206,1207],[1250, +1251],[1262,1263],[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240, +8240],[8249,8250],[8470,8470],[8482,8482]],"koi8u":[[0,127],[160,160],[169,169],[176, +176],[178,178],[183,183],[247,247],[1025,1025],[1028,1028],[1030,1031],[1040,1103], +[1105,1105],[1108,1108],[1110,1111],[1168,1169],[8729,8730],[8776,8776],[8804,8805], +[8992,8993],[9472,9472],[9474,9474],[9484,9484],[9488,9488],[9492,9492],[9496,9496], +[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532,9532],[9552,9554],[9556,9556], +[9559,9563],[9565,9569],[9571,9571],[9574,9578],[9580,9580],[9600,9600],[9604,9604], +[9608,9608],[9612,9612],[9616,9619],[9632,9632]],"maccenteuro":[[0,127],[160,160], +[163,163],[167,169],[171,172],[174,174],[176,176],[182,182],[187,187],[193,193],[196, +196],[201,201],[205,205],[211,214],[218,218],[220,221],[223,223],[225,225],[228,228], +[233,233],[237,237],[243,247],[250,250],[252,253],[256,257],[260,263],[268,271],[274, +275],[278,283],[290,291],[298,299],[302,303],[310,311],[313,318],[321,328],[332,333], +[336,337],[340,347],[352,353],[356,357],[362,363],[366,371],[377,382],[711,711],[8211, +8212],[8216,8218],[8220,8222],[8224,8224],[8226,8226],[8230,8230],[8249,8250],[8482, +8482],[8706,8706],[8710,8710],[8721,8721],[8730,8730],[8800,8800],[8804,8805],[9674, +9674]],"maccroatian":[[0,127],[160,164],[167,172],[174,177],[180,184],[186,187],[191, +207],[209,214],[216,220],[223,239],[241,252],[262,263],[268,269],[272,273],[305,305], +[338,339],[352,353],[381,382],[402,402],[710,711],[730,730],[732,732],[960,960],[8211, +8212],[8216,8218],[8220,8222],[8224,8224],[8226,8226],[8230,8230],[8240,8240],[8249, +8250],[8260,8260],[8482,8482],[8486,8486],[8706,8706],[8710,8710],[8719,8719],[8721, +8721],[8730,8730],[8734,8734],[8747,8747],[8776,8776],[8800,8800],[8804,8805],[9674, +9674]],"maccyrillic":[[0,127],[160,160],[162,164],[167,167],[169,169],[171,172],[174, +174],[176,177],[181,182],[187,187],[247,247],[402,402],[1025,1036],[1038,1103],[1105, +1116],[1118,1119],[8211,8212],[8216,8217],[8220,8222],[8224,8224],[8226,8226],[8230, +8230],[8470,8470],[8482,8482],[8706,8706],[8710,8710],[8730,8730],[8734,8734],[8776, +8776],[8800,8800],[8804,8805]],"macgreek":[[0,127],[160,160],[163,163],[165,169], +[171,174],[176,179],[185,185],[187,187],[189,189],[196,196],[201,201],[214,214],[220, +220],[223,224],[226,226],[228,228],[231,235],[238,239],[244,244],[246,247],[249,249], +[251,252],[339,339],[900,906],[908,908],[910,929],[931,974],[8211,8211],[8213,8213], +[8216,8217],[8220,8221],[8224,8224],[8226,8226],[8230,8230],[8240,8240],[8482,8482], +[8776,8776],[8800,8800],[8804,8805]],"maciceland":[[0,127],[160,165],[167,172],[174, +177],[180,184],[186,187],[191,214],[216,255],[305,305],[338,339],[376,376],[402,402], +[710,711],[728,733],[960,960],[8211,8212],[8216,8218],[8220,8222],[8226,8226],[8230, +8230],[8240,8240],[8260,8260],[8482,8482],[8486,8486],[8706,8706],[8710,8710],[8719, +8719],[8721,8721],[8730,8730],[8734,8734],[8747,8747],[8776,8776],[8800,8800],[8804, +8805],[9674,9674]],"macintosh":[[0,127],[160,165],[167,172],[174,177],[180,184],[186, +187],[191,207],[209,214],[216,220],[223,239],[241,252],[255,255],[305,305],[338,339], +[376,376],[402,402],[710,711],[728,733],[960,960],[8211,8212],[8216,8218],[8220,8222], +[8224,8226],[8230,8230],[8240,8240],[8249,8250],[8260,8260],[8482,8482],[8486,8486], +[8706,8706],[8710,8710],[8719,8719],[8721,8721],[8730,8730],[8734,8734],[8747,8747], +[8776,8776],[8800,8800],[8804,8805],[9674,9674],[62209,62210]],"macroman":[[0,127], +[160,165],[167,172],[174,177],[180,184],[186,187],[191,207],[209,214],[216,220],[223, +239],[241,252],[255,255],[305,305],[338,339],[376,376],[402,402],[710,711],[728,733], +[960,960],[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240], +[8249,8250],[8260,8260],[8482,8482],[8486,8486],[8706,8706],[8710,8710],[8719,8719], +[8721,8721],[8730,8730],[8734,8734],[8747,8747],[8776,8776],[8800,8800],[8804,8805], +[9674,9674],[62209,62210]],"macromania":[[0,127],[160,165],[167,172],[174,177],[180, +184],[186,187],[191,197],[199,207],[209,214],[217,220],[223,229],[231,239],[241,247], +[249,252],[255,255],[258,259],[305,305],[338,339],[350,351],[354,355],[376,376],[402, +402],[710,711],[728,733],[960,960],[8211,8212],[8216,8218],[8220,8222],[8224,8226], +[8230,8230],[8240,8240],[8249,8250],[8260,8260],[8482,8482],[8486,8486],[8706,8706], +[8710,8710],[8719,8719],[8721,8721],[8730,8730],[8734,8734],[8747,8747],[8776,8776], +[8800,8800],[8804,8805],[9674,9674]],"macthai":[[0,127],[160,160],[169,169],[171, +171],[174,174],[187,187],[3585,3642],[3647,3661],[3663,3673],[8203,8203],[8211,8212], +[8216,8217],[8220,8221],[8226,8226],[8230,8230],[8482,8482],[61572,61593],[63231, +63231]],"macturkish":[[0,127],[160,163],[165,165],[167,172],[174,177],[180,184],[186, +187],[191,207],[209,214],[216,220],[223,239],[241,252],[255,255],[286,287],[304,305], +[338,339],[350,351],[376,376],[402,402],[710,711],[728,733],[960,960],[8211,8212], +[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240],[8482,8482],[8486,8486], +[8706,8706],[8710,8710],[8719,8719],[8721,8721],[8730,8730],[8734,8734],[8747,8747], +[8776,8776],[8800,8800],[8804,8805],[9674,9674]],"macukraine":[[0,127],[160,160], +[163,164],[167,167],[169,169],[171,172],[174,174],[176,177],[181,182],[187,187],[247, +247],[402,402],[1025,1036],[1038,1103],[1105,1116],[1118,1119],[1168,1169],[8211, +8212],[8216,8217],[8220,8222],[8224,8224],[8226,8226],[8230,8230],[8470,8470],[8482, +8482],[8710,8710],[8730,8730],[8734,8734],[8776,8776],[8800,8800],[8804,8805]],"mik":[[0, +127],[160,160],[167,167],[176,178],[181,181],[183,183],[223,223],[247,247],[915,915], +[920,920],[931,931],[934,934],[937,937],[945,945],[948,949],[960,960],[963,964],[966, +966],[1040,1103],[8319,8319],[8470,8470],[8729,8730],[8734,8734],[8745,8745],[8776, +8776],[8801,8801],[8804,8805],[8992,8993],[9472,9472],[9474,9474],[9484,9484],[9488, +9488],[9492,9492],[9496,9496],[9500,9500],[9508,9508],[9516,9516],[9524,9524],[9532, +9532],[9552,9553],[9556,9556],[9559,9559],[9562,9562],[9565,9565],[9568,9568],[9571, +9571],[9574,9574],[9577,9577],[9580,9580],[9600,9600],[9604,9604],[9608,9608],[9612, +9612],[9616,9619],[9632,9632]],"pt154":[[0,127],[160,160],[167,167],[169,169],[171, +172],[174,174],[176,176],[182,183],[187,187],[1025,1025],[1030,1030],[1032,1032], +[1038,1038],[1040,1103],[1105,1105],[1110,1110],[1112,1112],[1118,1118],[1170,1171], +[1174,1181],[1184,1187],[1194,1195],[1198,1203],[1206,1211],[1240,1241],[1250,1251], +[1256,1257],[1262,1263],[8211,8212],[8216,8217],[8220,8222],[8226,8226],[8230,8230], +[8470,8470]],"rk1048":[[0,127],[160,160],[164,164],[166,167],[169,169],[171,174], +[176,177],[181,183],[187,187],[1025,1027],[1030,1030],[1033,1034],[1039,1103],[1105, +1107],[1110,1110],[1113,1114],[1119,1119],[1170,1171],[1178,1179],[1186,1187],[1198, +1201],[1210,1211],[1240,1241],[1256,1257],[8211,8212],[8216,8218],[8220,8222],[8224, +8226],[8230,8230],[8240,8240],[8249,8250],[8364,8364],[8470,8470],[8482,8482]],"shiftjis":[[0, +128],[165,165],[167,168],[176,177],[180,180],[182,182],[215,215],[247,247],[913,929], +[931,937],[945,961],[963,969],[1025,1025],[1040,1103],[1105,1105],[8208,8208],[8213, +8213],[8216,8217],[8220,8221],[8224,8225],[8229,8230],[8240,8240],[8242,8243],[8251, +8251],[8254,8254],[8451,8451],[8470,8470],[8481,8481],[8491,8491],[8544,8553],[8560, +8569],[8592,8595],[8658,8658],[8660,8660],[8704,8704],[8706,8707],[8711,8712],[8715, +8715],[8721,8721],[8730,8730],[8733,8736],[8741,8741],[8743,8748],[8750,8750],[8756, +8757],[8765,8765],[8786,8786],[8800,8801],[8806,8807],[8810,8811],[8834,8835],[8838, +8839],[8869,8869],[8895,8895],[8978,8978],[9312,9331],[9472,9475],[9484,9484],[9487, +9488],[9491,9492],[9495,9496],[9499,9501],[9504,9504],[9507,9509],[9512,9512],[9515, +9516],[9519,9520],[9523,9524],[9527,9528],[9531,9532],[9535,9535],[9538,9538],[9547, +9547],[9632,9633],[9650,9651],[9660,9661],[9670,9671],[9675,9675],[9678,9679],[9711, +9711],[9733,9734],[9792,9792],[9794,9794],[9834,9834],[9837,9837],[9839,9839],[12288, +12291],[12293,12309],[12317,12317],[12319,12319],[12353,12435],[12443,12446],[12449, +12534],[12539,12542],[12849,12850],[12857,12857],[12964,12968],[13059,13059],[13069, +13069],[13076,13076],[13080,13080],[13090,13091],[13094,13095],[13099,13099],[13110, +13110],[13115,13115],[13129,13130],[13133,13133],[13137,13137],[13143,13143],[13179, +13182],[13198,13199],[13212,13214],[13217,13217],[13252,13252],[13261,13261],[19968, +19969],[19971,19971],[19975,19979],[19981,19982],[19984,19985],[19988,19993],[19998, +19998],[20001,20001],[20006,20006],[20008,20008],[20010,20010],[20013,20013],[20017, +20018],[20022,20022],[20024,20025],[20027,20028],[20031,20031],[20034,20035],[20037, +20037],[20043,20043],[20045,20047],[20053,20057],[20061,20063],[20066,20066],[20081, +20081],[20083,20083],[20094,20094],[20096,20096],[20098,20098],[20101,20102],[20104, +20108],[20110,20110],[20113,20114],[20116,20117],[20120,20121],[20123,20124],[20126, +20130],[20132,20134],[20136,20136],[20139,20142],[20144,20144],[20147,20147],[20150, +20150],[20154,20154],[20160,20162],[20164,20164],[20166,20167],[20170,20171],[20173, +20175],[20180,20185],[20189,20191],[20193,20193],[20195,20197],[20205,20206],[20208, +20208],[20210,20210],[20214,20215],[20219,20220],[20224,20225],[20227,20227],[20233, +20234],[20237,20241],[20250,20250],[20252,20253],[20271,20272],[20276,20276],[20278, +20278],[20280,20282],[20284,20285],[20291,20291],[20294,20295],[20301,20305],[20307, +20307],[20309,20311],[20313,20318],[20329,20329],[20335,20336],[20339,20339],[20341, +20342],[20347,20348],[20351,20351],[20355,20355],[20358,20358],[20360,20360],[20362, +20363],[20365,20365],[20367,20367],[20369,20370],[20372,20372],[20374,20374],[20376, +20376],[20378,20379],[20381,20381],[20384,20385],[20395,20395],[20397,20399],[20405, +20406],[20415,20415],[20418,20420],[20425,20426],[20429,20430],[20432,20433],[20436, +20436],[20439,20440],[20442,20443],[20445,20445],[20447,20447],[20449,20449],[20451, +20453],[20462,20463],[20467,20467],[20469,20470],[20472,20472],[20474,20474],[20478, +20479],[20485,20486],[20489,20489],[20491,20491],[20493,20493],[20495,20495],[20497, +20498],[20500,20500],[20502,20502],[20505,20506],[20510,20511],[20513,20518],[20520, +20525],[20534,20534],[20537,20537],[20544,20544],[20546,20547],[20550,20553],[20559, +20560],[20565,20566],[20570,20570],[20572,20572],[20581,20581],[20588,20588],[20592, +20592],[20594,20594]],"tcvn":[[3,3],[7,16],[24,127],[160,160],[192,195],[200,202], +[204,205],[210,213],[217,218],[221,221],[224,227],[232,234],[236,237],[242,245],[249, +250],[253,253],[258,259],[272,273],[296,297],[360,361],[416,417],[431,432],[768,769], +[771,771],[777,777],[803,803],[7840,7929]],"tis620":[[0,127],[3585,3642],[3647,3675]], +"ucs2":[[0,2160639]],"ucs4":[[0,2160639]],"utf16":[[0,2160639]],"utf16be":[[0,2160639]], +"utf32":[[0,2160639]],"utf32be":[[0,2160639]],"utf32le":[[0,2160639]],"utf7":[[0, +2160639]],"utf7imap":[[0,2160639]],"utf8":[[0,2160639]],"viscii":[[0,1],[3,4],[7, +19],[21,24],[26,29],[31,127],[192,195],[200,202],[204,205],[210,213],[217,218],[221, +221],[224,227],[232,234],[236,237],[242,245],[249,250],[253,253],[258,259],[272,273], +[296,297],[360,361],[416,417],[431,432],[7840,7929]],"windows1250":[[0,127],[160, +160],[164,164],[166,169],[171,174],[176,177],[180,184],[187,187],[193,194],[196,196], +[199,199],[201,201],[203,203],[205,206],[211,212],[214,215],[218,218],[220,221],[223, +223],[225,226],[228,228],[231,231],[233,233],[235,235],[237,238],[243,244],[246,247], +[250,250],[252,253],[258,263],[268,273],[280,283],[313,314],[317,318],[321,324],[327, +328],[336,337],[340,341],[344,347],[350,357],[366,369],[377,382],[711,711],[728,729], +[731,731],[733,733],[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240, +8240],[8249,8250],[8364,8364],[8482,8482]],"windows1251":[[0,127],[160,160],[164, +164],[166,167],[169,169],[171,174],[176,177],[181,183],[187,187],[1025,1036],[1038, +1103],[1105,1116],[1118,1119],[1168,1169],[8211,8212],[8216,8218],[8220,8222],[8224, +8226],[8230,8230],[8240,8240],[8249,8250],[8364,8364],[8470,8470],[8482,8482]],"windows1252":[[0, +127],[160,255],[338,339],[352,353],[376,376],[381,382],[402,402],[710,710],[732,732], +[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240],[8249,8250], +[8364,8364],[8482,8482]],"windows1253":[[0,127],[160,160],[163,169],[171,174],[176, +179],[181,183],[187,187],[189,189],[402,402],[900,902],[904,906],[908,908],[910,929], +[931,974],[8211,8213],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240], +[8249,8250],[8364,8364],[8482,8482]],"windows1254":[[0,127],[160,207],[209,220],[223, +239],[241,252],[255,255],[286,287],[304,305],[338,339],[350,353],[376,376],[402,402], +[710,710],[732,732],[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240, +8240],[8249,8250],[8364,8364],[8482,8482]],"windows1255":[[0,127],[160,163],[165, +169],[171,185],[187,191],[215,215],[247,247],[402,402],[710,710],[732,732],[1456, +1475],[1488,1514],[1520,1524],[8206,8207],[8211,8212],[8216,8218],[8220,8222],[8224, +8226],[8230,8230],[8240,8240],[8249,8250],[8362,8362],[8364,8364],[8482,8482]],"windows1256":[[0, +127],[160,160],[162,169],[171,185],[187,190],[215,215],[224,224],[226,226],[231,235], +[238,239],[244,244],[247,247],[249,249],[251,252],[338,339],[402,402],[710,710],[1548, +1548],[1563,1563],[1567,1567],[1569,1594],[1600,1618],[1657,1657],[1662,1662],[1670, +1670],[1672,1672],[1681,1681],[1688,1688],[1705,1705],[1711,1711],[1722,1722],[1726, +1726],[1729,1729],[1746,1746],[8204,8207],[8211,8212],[8216,8218],[8220,8222],[8224, +8226],[8230,8230],[8240,8240],[8249,8250],[8364,8364],[8482,8482]],"windows1257":[[0, +127],[160,160],[162,164],[166,169],[171,185],[187,190],[196,198],[201,201],[211,211], +[213,216],[220,220],[223,223],[228,230],[233,233],[243,243],[245,248],[252,252],[256, +257],[260,263],[268,269],[274,275],[278,281],[290,291],[298,299],[302,303],[310,311], +[315,316],[321,326],[332,333],[342,343],[346,347],[352,353],[362,363],[370,371],[377, +382],[711,711],[729,729],[731,731],[8211,8212],[8216,8218],[8220,8222],[8224,8226], +[8230,8230],[8240,8240],[8249,8250],[8364,8364],[8482,8482]],"windows1258":[[0,127], +[160,194],[196,203],[205,207],[209,209],[211,212],[214,220],[223,226],[228,235],[237, +239],[241,241],[243,244],[246,252],[255,255],[258,259],[272,273],[338,339],[376,376], +[402,402],[416,417],[431,432],[710,710],[732,732],[768,769],[771,771],[777,777],[803, +803],[8211,8212],[8216,8218],[8220,8222],[8224,8226],[8230,8230],[8240,8240],[8249, +8250],[8363,8364],[8482,8482]],"windows874":[[0,127],[160,160],[3585,3642],[3647, +3675],[8211,8212],[8216,8217],[8220,8221],[8226,8226],[8230,8230],[8364,8364]]} diff --git a/generation/gen-transliteration.js b/generation/gen-transliteration.js new file mode 100644 index 0000000..064219e --- /dev/null +++ b/generation/gen-transliteration.js @@ -0,0 +1,59 @@ +var iconv = require('../lib/index'); +var fs = require('fs'); + +// Make sure we can tell missing characters apart from question marks. +iconv.defaultCharSingleByte = '\u0000'; + +// Trigger loading of encodings +iconv.getCodec('ascii'); + +var encodings = Object.keys(iconv.encodings).filter(function(enc) { + return enc.charAt(0) !== '_' && typeof iconv.encodings[enc] !== 'string'; +}).sort(); + +var allCharsStr = ''; +var skip = 0; + +for (var i = 0; i <= 0x10F7FF; ++i) { + if (i === 0xD800) + skip = 0x800; + + allCharsStr += String.fromCodePoint(i + skip); +} + +var encodingRanges = {}; + +encodings.forEach(function(enc) { + var chars = iconv.decode(iconv.encode(allCharsStr, enc), enc); + var ranges = []; + var start = -1; + + // Yes, less-than-or-equal, deliberately going one extra character beyond the end of the string. + for (var i = 0; i <= chars.length; ++i) { + var cp = chars.codePointAt(i); + + var badChar = (isNaN(cp) || cp === 0 || cp === 0xFFFD); + + if (i !== 0 && i !== 0xF7FD && i !== 0xFFFD) { + if (start < 0 && !badChar) + start = (i === 1 ? 0 : i); + else if (start >= 0 && badChar) { + ranges.push([start, i - 1]); + start = -1; + + if (ranges.length > 255) + break; + } + } + + if (cp > 0xFFFF) + ++i; + } + + encodingRanges[enc] = ranges; +}); + +var json = JSON.stringify(encodingRanges); + +json = json.replace(/(.{80}.*?(,|$))/g, '$1\n') + '\n'; +fs.writeFileSync(__dirname + '/../encodings/tables/transliteration-ranges.json', json); diff --git a/package.json b/package.json index 513e1fa..b133670 100644 --- a/package.json +++ b/package.json @@ -24,21 +24,23 @@ "scripts": { "coverage": "istanbul cover _mocha -- --grep .", "coverage-open": "open coverage/lcov-report/index.html", - "test": "mocha --reporter spec --grep ." + "test": "mocha --reporter spec --grep .", + "translit": "node generation/gen-transliteration.js" }, "browser": { "./lib/extend-node": false, "./lib/streams": false }, "devDependencies": { - "mocha": "^3.1.0", - "request": "~2.87.0", - "unorm": "*", - "errto": "*", "async": "*", + "errto": "*", + "iconv": "*", "istanbul": "*", + "mocha": "^3.1.0", + "request": "~2.87.0", "semver": "*", - "iconv": "*" + "unidecode": "^0.1.8", + "unorm": "*" }, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" From a6f91b4869bcc066da1d7a7743495ef12d352a2c Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Fri, 28 Jun 2019 17:04:12 -0400 Subject: [PATCH 13/24] Changing computers check-in. --- .gitignore | 4 +++- .npmignore | 1 + generation/test-translit.js | 3 +++ lib/index.js | 12 +++++++++++- package.json | 3 ++- 5 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 generation/test-translit.js diff --git a/.gitignore b/.gitignore index 2794f15..d7f0fcb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ wiki *sublime-* coverage /.idea -package-lock.json +/package-lock.json +/.npmrc +/.vscode/settings.json diff --git a/.npmignore b/.npmignore index e1bafe0..e9ea0c1 100644 --- a/.npmignore +++ b/.npmignore @@ -5,3 +5,4 @@ test wiki coverage .travis.yml + diff --git a/generation/test-translit.js b/generation/test-translit.js new file mode 100644 index 0000000..32dd0f8 --- /dev/null +++ b/generation/test-translit.js @@ -0,0 +1,3 @@ +var iconv = require('../lib/index'); + +iconv.transliterate('hello', 'latin1'); diff --git a/lib/index.js b/lib/index.js index 5391919..eef3313 100644 --- a/lib/index.js +++ b/lib/index.js @@ -66,6 +66,7 @@ iconv.getCodec = function getCodec(encoding) { // Canonicalize encoding name: strip all non-alphanumeric chars and appended year. var enc = iconv._canonicalizeEncoding(encoding); + var rootName = enc; // Traverse iconv.encodings to find actual codec. var codecOptions = {}; @@ -78,7 +79,7 @@ iconv.getCodec = function getCodec(encoding) { switch (typeof codecDef) { case "string": // Direct alias to other encoding. - enc = codecDef; + enc = rootName = codecDef; break; case "object": // Alias with options. Can be layered. @@ -99,6 +100,9 @@ iconv.getCodec = function getCodec(encoding) { // It'll be called only once (for each different options object). codec = new codecDef(codecOptions, iconv); + if (!codec.encodingName) + codec.encodingName = rootName; + iconv._codecDataCache[codecOptions.encodingName] = codec; // Save it to be reused later. return codec; @@ -133,6 +137,12 @@ iconv.getDecoder = function getDecoder(encoding, options) { return decoder; } +iconv.transliterate = function transliterate(str, encoding) { + var codec = iconv.getCodec(encoding); + + console.log(codec); +} + // Load extensions in Node. All of them are omitted in Browserify build via 'browser' field in package.json. var nodeVer = typeof process !== 'undefined' && process.versions && process.versions.node; diff --git a/package.json b/package.json index b133670..b915c11 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "coverage": "istanbul cover _mocha -- --grep .", "coverage-open": "open coverage/lcov-report/index.html", "test": "mocha --reporter spec --grep .", - "translit": "node generation/gen-transliteration.js" + "translit": "node generation/gen-transliteration.js", + "test-translit": "node generation/test-translit.js" }, "browser": { "./lib/extend-node": false, From a550b78cc667ee09326c1d1d98711b4471c40350 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Sat, 29 Jun 2019 15:52:15 -0400 Subject: [PATCH 14/24] First version of transliteration support. --- generation/test-translit.js | 3 - lib/index.js | 119 +++++++++++++++++++++++++++++++++--- package.json | 3 +- test/translit-test.js | 66 ++++++++++++++++++++ test/utf32-test.js | 2 +- 5 files changed, 179 insertions(+), 14 deletions(-) delete mode 100644 generation/test-translit.js create mode 100644 test/translit-test.js diff --git a/generation/test-translit.js b/generation/test-translit.js deleted file mode 100644 index 32dd0f8..0000000 --- a/generation/test-translit.js +++ /dev/null @@ -1,3 +0,0 @@ -var iconv = require('../lib/index'); - -iconv.transliterate('hello', 'latin1'); diff --git a/lib/index.js b/lib/index.js index eef3313..3425aa4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,12 +18,25 @@ iconv.defaultCharSingleByte = '?'; // Public API. iconv.encode = function encode(str, encoding, options) { str = "" + (str || ""); // Ensure string. + var parts = (typeof encoding === 'string' ? encoding.toLowerCase().split('//') : null); + + if (parts && parts.length > 1) { + encoding = parts[0]; + + if (parts[1] === 'translit') { + options = options || {}; + options.transliterate = true; + } + } var encoder = iconv.getEncoder(encoding, options); + if (options && options.transliterate) + encoder = new TransliterationWrapper(encoder, options); + var res = encoder.write(str); var trail = encoder.end(); - + return (trail && trail.length > 0) ? Buffer.concat([res, trail]) : res; } @@ -63,7 +76,7 @@ iconv._codecDataCache = {}; iconv.getCodec = function getCodec(encoding) { if (!iconv.encodings) iconv.encodings = require("../encodings"); // Lazy load all encoding definitions. - + // Canonicalize encoding name: strip all non-alphanumeric chars and appended year. var enc = iconv._canonicalizeEncoding(encoding); var rootName = enc; @@ -88,7 +101,7 @@ iconv.getCodec = function getCodec(encoding) { if (!codecOptions.encodingName) codecOptions.encodingName = enc; - + enc = codecDef.type; break; @@ -121,6 +134,8 @@ iconv.getEncoder = function getEncoder(encoding, options) { var codec = iconv.getCodec(encoding), encoder = new codec.encoder(options, codec); + encoder.encodingName = codec.encodingName; + if (codec.bomAware && options && options.addBOM) encoder = new bomHandling.PrependBOM(encoder, options); @@ -137,12 +152,100 @@ iconv.getDecoder = function getDecoder(encoding, options) { return decoder; } -iconv.transliterate = function transliterate(str, encoding) { - var codec = iconv.getCodec(encoding); - - console.log(codec); +String.prototype.codePointAt || (String.prototype.codePointAt = function(index) { + var code = this.charCodeAt(index); + + if (0xD800 <= code && code <= 0xDBFF) { + var surr = this.charCodeAt(index + 1); + + if (!isNaN(surr) && 0xDC00 <= surr && surr <= 0xDFFF) + code = 0x10000 + ((code - 0xD800) << 10) + (surr - 0xDC00); + } + + return code; +}); + +function TransliterationWrapper(encoder, options) { + this.encoder = encoder; + this.encodingName = encoder.encodingName + '-translit'; } - + +TransliterationWrapper.prototype.write = function(str) { + return this.encoder.write(iconv.transliterate(str, this.encoder.encodingName)); +}; + +TransliterationWrapper.prototype.end = function() { + return this.encoder.end(); +}; + +var unidecode; +var encodingRanges; +var translitWarnings = {}; + +iconv.transliterate = function transliterate(str, targetEncoding) { + var codec = iconv.getCodec(targetEncoding); + // Should throw an error before reaching here for an invalid encoding + var encoding = codec.encodingName; + + if (/^(utf.*|cesu8|gb18030|ucs2|ucs4)$/.test(encoding)) + return str; + + if (!encodingRanges) { + try { + unidecode = require('unidecode'); + } + catch { + throw new Error('Transliteration requires unidecode package'); + } + } + + // For plain ASCII, we can do this quickly. + if (encoding === 'ascii') + return unidecode(str); + + if (!encodingRanges) + encodingRanges = require('../encodings/tables/transliteration-ranges.json'); + + var ranges = encodingRanges[encoding]; + + if (!ranges) { + if (!translitWarnings[targetEncoding]) { + translitWarnings[targetEncoding] = true; + console.warning('Transliteration not available for "' + targetEncoding + '".'); + } + + return str; + } + + for (var i = 0; i < str.length; ++i) { + var start = i; + var cp = str.codePointAt(i); + + if (cp < 128) + continue; + else if (cp > 0xFFFF) + ++i; + + var missing = true; + + for (var j = 0; j < ranges.length; ++j) { + if (ranges[j][0] <= cp && cp <= ranges[j][1]) { + missing = false; + break; + } + } + + if (missing) { + var len = i - start + 1; + var substitute = unidecode(str.substr(start, len)); + str = str.substr(0, start) + substitute + str.substr(i + 1); + i += substitute.length - len; + } + } + + return str; +}; + // Load extensions in Node. All of them are omitted in Browserify build via 'browser' field in package.json. var nodeVer = typeof process !== 'undefined' && process.versions && process.versions.node; diff --git a/package.json b/package.json index b915c11..b133670 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,7 @@ "coverage": "istanbul cover _mocha -- --grep .", "coverage-open": "open coverage/lcov-report/index.html", "test": "mocha --reporter spec --grep .", - "translit": "node generation/gen-transliteration.js", - "test-translit": "node generation/test-translit.js" + "translit": "node generation/gen-transliteration.js" }, "browser": { "./lib/extend-node": false, diff --git a/test/translit-test.js b/test/translit-test.js new file mode 100644 index 0000000..6cdc9ea --- /dev/null +++ b/test/translit-test.js @@ -0,0 +1,66 @@ +var assert = require('assert'), + iconv = require(__dirname + '/../'), + Iconv = require('iconv').Iconv; + +var sample1 = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +字符编码 文字コード Κωδικοποίηση χαρακτήρων Кодировка символов'; + +describe('Direct transliteration', function() { + it('should transliterate for ISO-8859-1 (as latin1)', function() { + assert.equal(iconv.transliterate(sample1, 'latin1'), '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); + }); + + it('should transliterate for ASCII', function() { + assert.equal(iconv.transliterate(sample1, 'ascii'), '!C/PS$?Y=|SS"(c)a<>1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ +Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); + }); + + it('should transliterate for EUC-JP', function() { + assert.equal(iconv.transliterate(sample1, 'EUC-JP'), '¡C/PS¤¥¦§¨©ª<>1/41/23/4¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏDÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +Zi Fu Bian Ma Wen Zi コード Κωδικοποίηση χαρακτήρων Кодировка символов'); + }); + + it('should transliterate for Windows-1251', function() { + assert.equal(iconv.transliterate(sample1, 'windows1251'), '!C/PS¤Y=¦§"©a«¬­®-°±23\'µ¶·,1o»1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ +Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Кодировка символов'); + }); + + it('should transliterate for MacGreek', function() { + assert.equal(iconv.transliterate(sample1, 'MacGreek'), '!C/£$?¥¦§¨©a«¬­®-°±²³\'uP*,¹o»1/4½3/4?AAAAÄAAECEÉEEIIIIDNOOOOÖxOUUUÜUThßàaâaäaaeçèéêëiiîïdnooôoö÷oùuûüythy\n\ +Zi Fu Bian Ma Wen Zi kodo Κωδικοποίηση χαρακτήρων Kodirovka simvolov'); + }); +}); + +describe('Transliteration via encoder', function() { + it('should transliterate for ISO-8859-1 (as latin1)', function() { + var buf = iconv.encode(sample1, 'latin1//translit'); + assert.equal(iconv.decode(buf, 'latin1'), '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); + }); + + it('should transliterate for ASCII', function() { + var buf = iconv.encode(sample1, 'ascii', { transliterate: true }); + assert.equal(iconv.decode(buf, 'ascii'), '!C/PS$?Y=|SS"(c)a<>1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ +Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); + }); + + // TODO: Put this test back when `¥` and `‾` are handled correctly. +// it('should transliterate for EUC-JP', function() { +// var buf = iconv.encode(sample1, 'EUC-JP', { transliterate: true }); +// assert.equal(iconv.decode(buf, 'EUC-JP'), '¡C/PS¤¥¦§¨©ª<>1/41/23/4¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏDÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +// Zi Fu Bian Ma Wen Zi コード Κωδικοποίηση χαρακτήρων Кодировка символов'); +// }); + + it('should transliterate for Windows-1251', function() { + var buf = iconv.encode(sample1, 'windows1251', { transliterate: true }); + assert.equal(iconv.decode(buf, 'windows1251'), '!C/PS¤Y=¦§"©a«¬­®-°±23\'µ¶·,1o»1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ +Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Кодировка символов'); + }); + + it('should transliterate for MacGreek', function() { + var buf = iconv.encode(sample1, 'MacGreek', { transliterate: true }); + assert.equal(iconv.decode(buf, 'MacGreek'), '!C/£$?¥¦§¨©a«¬­®-°±²³\'uP*,¹o»1/4½3/4?AAAAÄAAECEÉEEIIIIDNOOOOÖxOUUUÜUThßàaâaäaaeçèéêëiiîïdnooôoö÷oùuûüythy\n\ +Zi Fu Bian Ma Wen Zi kodo Κωδικοποίηση χαρακτήρων Kodirovka simvolov'); + }); +}); diff --git a/test/utf32-test.js b/test/utf32-test.js index 23faab2..57c6e7d 100644 --- a/test/utf32-test.js +++ b/test/utf32-test.js @@ -1,5 +1,5 @@ var assert = require('assert'), - iconv = require(__dirname+'/../'), + iconv = require(__dirname + '/../'), Iconv = require('iconv').Iconv; var testStr = '1aя中文☃💩', From f5742823455fc32bfdb09ee131ff75ae98a8ac59 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Sat, 29 Jun 2019 15:58:06 -0400 Subject: [PATCH 15/24] Odd... my Node.js environment was fine with a "catch" without a variable, but the automated testing on travis-ci.com didn't like it. --- lib/index.js | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 3425aa4..87e2d0c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -194,7 +194,7 @@ iconv.transliterate = function transliterate(str, targetEncoding) { try { unidecode = require('unidecode'); } - catch { + catch (err) { throw new Error('Transliteration requires unidecode package'); } } diff --git a/package.json b/package.json index b133670..233c022 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "iconv", "convert", "charset", - "icu" + "icu", + "transliterate" ], "author": "Alexander Shtuchkin ", "main": "./lib/index.js", From 7b13689ca269a7a2d60c1ee4dfd009424318d465 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Sat, 29 Jun 2019 16:59:30 -0400 Subject: [PATCH 16/24] Update documentation for transliteration. --- README.md | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f013795..3d2b09e 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ * In-browser usage via [Browserify](https://github.com/substack/node-browserify) (~180k gzip compressed with Buffer shim included). * Typescript [type definition file](https://github.com/ashtuchkin/iconv-lite/blob/master/lib/index.d.ts) included. * React Native is supported (need to explicitly `npm install` two more modules: `buffer` and `stream`). + * Transliteration option is available when the [unidecode](https://www.npmjs.com/package/unidecode) package is added to your project * License: MIT. [![NPM Stats](https://nodei.co/npm/iconv-lite.png)](https://npmjs.org/package/iconv-lite/) @@ -31,6 +32,9 @@ buf = iconv.encode("Sample input string", 'win1251'); // Check if encoding is supported iconv.encodingExists("us-ascii") + +// Convert from js string to an encoded buffer, keeping accented characters like "é", but transliterating Chinese. +buf2 = iconv.encode("Café 北京", 'iso-8859-1', { transliterate: true }); ``` ### Streaming API (Node v0.10+) @@ -143,11 +147,32 @@ This library supports UTF-32LE, UTF-32BE and UTF-32 encodings. Like the UTF-16 e * The default of UTF-32LE can be overridden with the `defaultEncoding: 'utf-32be'` option. Strips BOM unless `stripBOM: false`. * Encoding: uses UTF-32LE and writes BOM by default. Use `addBOM: false` to override. (`defaultEncoding: 'utf-32be'` can also be used here to change encoding.) +## Transliteration + +If the [unidecode](https://www.npmjs.com/package/unidecode) package is added to your project (`npm install unidecode`), the option will be available to transliterate characters which are not available in a particular encoding. The transliterations are always plain ASCII characters, but unlike using unidecode directly (which will convert *all* non-ASCII characters into transliterations), transliterations done using iconv will only transliterate characters which are not available in the target encoding. + +In this example: +``` +buf = iconv.encode("Café 北京", 'iso-8859-1', { transliterate: true }); +``` +The output is ``. Converted back into ISO-8859-1 text, this is "Café Bei Jing ", preserving the accented "é", and only transliterating the Chinese characters. + +Transliteration to a string instead of a buffer can also be done directly, like this: +``` +str = iconv.transliterate("Café 北京", 'iso-8859-1'); +``` +When encoding to create a buffer, the node-iconv style of requesting transliteration can also be used: +``` +buf = iconv.encode("Café 北京", 'iso-8859-1//translit'); +``` + +Please take note that transliteration only affects encoding, not decoding. + ## Other notes -When decoding, be sure to supply a Buffer to decode() method, otherwise [bad things usually happen](https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding). -Untranslatable characters are set to � or ?. No transliteration is currently supported. -Node versions 0.10.31 and 0.11.13 are buggy, don't use them (see #65, #77). +* When decoding, be sure to supply a Buffer to decode() method, otherwise [bad things usually happen](https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding). +* Untranslatable characters are set to � or ? unless using transliteration. +* Node versions 0.10.31 and 0.11.13 are buggy, don't use them (see #65, #77). ## Testing From 198d7e26ccd7bc83130c3066e4ad875b12bdca31 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Sun, 30 Jun 2019 05:32:07 -0400 Subject: [PATCH 17/24] Much to my surprise, a regex global replace turns out to be much faster than build a string in a for loop. --- lib/index.js | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/index.js b/lib/index.js index 87e2d0c..4c2370d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -181,6 +181,17 @@ TransliterationWrapper.prototype.end = function() { var unidecode; var encodingRanges; var translitWarnings = {}; +var ranges; +var codepoints; + +try { + // Can we use Unicode-aware regexes? + codepoints = /[^\x00-\x7F]/gu; +} +catch (err) { + // Nope! This mess will have to do. + codepoints = /(?:[\x80-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g; +} iconv.transliterate = function transliterate(str, targetEncoding) { var codec = iconv.getCodec(targetEncoding); @@ -206,7 +217,7 @@ iconv.transliterate = function transliterate(str, targetEncoding) { if (!encodingRanges) encodingRanges = require('../encodings/tables/transliteration-ranges.json'); - var ranges = encodingRanges[encoding]; + ranges = encodingRanges[encoding]; if (!ranges) { if (!translitWarnings[targetEncoding]) { @@ -217,34 +228,20 @@ iconv.transliterate = function transliterate(str, targetEncoding) { return str; } - for (var i = 0; i < str.length; ++i) { - var start = i; - var cp = str.codePointAt(i); - - if (cp < 128) - continue; - else if (cp > 0xFFFF) - ++i; + // Much to my surprise, using a regex like this is much faster than building a string in a for loop. + return str.replace(codepoints, getTransliteration); +}; - var missing = true; +function getTransliteration(ch) { + var cp = ch.codePointAt(0); - for (var j = 0; j < ranges.length; ++j) { - if (ranges[j][0] <= cp && cp <= ranges[j][1]) { - missing = false; - break; - } - } - - if (missing) { - var len = i - start + 1; - var substitute = unidecode(str.substr(start, len)); - str = str.substr(0, start) + substitute + str.substr(i + 1); - i += substitute.length - len; - } + for (var j = 0; j < ranges.length; ++j) { + if (ranges[j][0] <= cp && cp <= ranges[j][1]) + return ch; } - return str; -}; + return unidecode(ch); +} // Load extensions in Node. All of them are omitted in Browserify build via 'browser' field in package.json. From 5797fb52d43392139db15958f4476b4a0175cb5b Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 1 Jul 2019 00:53:16 -0400 Subject: [PATCH 18/24] Add typings for transliteration. Add ability to deal with smart spacing and German options. Remove conflicts in unit testing where unidecode and unidecode-plus transliterate differently. --- lib/index.d.ts | 12 +++++- lib/index.js | 88 ++++++++++++++++++++++++++++++++++++++----- package.json | 1 + test/translit-test.js | 39 +++++++++++++------ 4 files changed, 118 insertions(+), 22 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index 0547eb3..c51dcb3 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -15,10 +15,18 @@ declare module 'iconv-lite' { export function decodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream; export function encodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream; + + export function transliterate(str: string, targetEncoding: string, options?: TransliterationOptions) } -export interface Options { - stripBOM?: boolean; +export interface TransliterationOptions { + german?: boolean; + smartSpacing?: boolean; +} + +export interface Options extends TransliterationOptions { addBOM?: boolean; defaultEncoding?: string; + stripBOM?: boolean; + transliterate?: boolean; } diff --git a/lib/index.js b/lib/index.js index 4c2370d..2804c0b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -155,7 +155,7 @@ iconv.getDecoder = function getDecoder(encoding, options) { String.prototype.codePointAt || (String.prototype.codePointAt = function(index) { var code = this.charCodeAt(index); - if (0xD800 <= code && code <= 0xDBFF) { + if (!isNaN(code) && 0xD800 <= code && code <= 0xDBFF) { var surr = this.charCodeAt(index + 1); if (!isNaN(surr) && 0xDC00 <= surr && surr <= 0xDFFF) @@ -168,20 +168,65 @@ String.prototype.codePointAt || (String.prototype.codePointAt = function(index) function TransliterationWrapper(encoder, options) { this.encoder = encoder; this.encodingName = encoder.encodingName + '-translit'; + this.smartSpacing = (options && options.smartSpacing); + this.options = { deferredSmartSpacing: this.smartSpacing, german: options && options.german }; + this.pending = ''; } +var MAX_PENDING = 16384; + TransliterationWrapper.prototype.write = function(str) { - return this.encoder.write(iconv.transliterate(str, this.encoder.encodingName)); + str = iconv.transliterate(str, this.encoder.encodingName, this.options); + + if (this.smartSpacing) { + str = this.pending + str; + this.pending = ''; + + // Split the text being written out at a safe place where smart spacing won't + // need to make any changes. + var $ = /^(.*)([^\s\x80\x81][\s\x80\x81].*)$/.exec(str); + + if ($) { + str = unidecode.resolveSpacing($[1]); + this.pending = $[2]; + } + else if (str.length > MAX_PENDING) + str = unidecode.resolveSpacing(str); + else { + this.pending = str; + str = ''; + } + } + + return this.encoder.write(str); }; TransliterationWrapper.prototype.end = function() { - return this.encoder.end(); + var buf = null; + var bufs = []; + + if (this.pending) { + buf = this.encoder.write(unidecode.resolveSpacing(this.pending)); + this.pending = ''; + } + + if (buf) + bufs.push(buf); + + buf = this.encoder.end(); + + if (buf) + bufs.push(buf); + + return Buffer.concat(bufs); }; var unidecode; +var hasUnidecodePlus = false; var encodingRanges; var translitWarnings = {}; var ranges; +var plusOptions; var codepoints; try { @@ -193,7 +238,7 @@ catch (err) { codepoints = /(?:[\x80-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g; } -iconv.transliterate = function transliterate(str, targetEncoding) { +iconv.transliterate = function transliterate(str, targetEncoding, options) { var codec = iconv.getCodec(targetEncoding); // Should throw an error before reaching here for an invalid encoding var encoding = codec.encodingName; @@ -203,16 +248,33 @@ iconv.transliterate = function transliterate(str, targetEncoding) { if (!encodingRanges) { try { - unidecode = require('unidecode'); + unidecode = require('unidecode-plus'); + hasUnidecodePlus = true; } catch (err) { - throw new Error('Transliteration requires unidecode package'); + try { + unidecode = require('unidecode'); + } + catch (err) { + throw new Error('Transliteration requires unidecode package'); + } } } + var deferredSmartSpacing = options && options.deferredSmartSpacing; + var smartSpacing = deferredSmartSpacing || (options && options.smartSpacing); + var german = options && options.german; + + if (!hasUnidecodePlus && (smartSpacing || german)) + throw new Error('Options "smartSpacing" and "german" are only available when using unidecode-plus'); + else if (hasUnidecodePlus) + plusOptions = { deferredSmartSpacing: deferredSmartSpacing, german: german, smartSpacing: smartSpacing }; + else + plusOptions = undefined; + // For plain ASCII, we can do this quickly. if (encoding === 'ascii') - return unidecode(str); + return unidecode(str, plusOptions); if (!encodingRanges) encodingRanges = require('../encodings/tables/transliteration-ranges.json'); @@ -228,8 +290,13 @@ iconv.transliterate = function transliterate(str, targetEncoding) { return str; } - // Much to my surprise, using a regex like this is much faster than building a string in a for loop. - return str.replace(codepoints, getTransliteration); + if (smartSpacing) { + options.skipRanges = ranges; + return unidecode(str, options); + } + else + // Much to my surprise, using a regex like this is much faster than building a string in a for loop. + return str.replace(codepoints, getTransliteration); }; function getTransliteration(ch) { @@ -243,6 +310,9 @@ function getTransliteration(ch) { return unidecode(ch); } +iconv.hasUnidecodePlus = function hasUnidecodePlus() { + return hasUnidecodePlus; +}; // Load extensions in Node. All of them are omitted in Browserify build via 'browser' field in package.json. var nodeVer = typeof process !== 'undefined' && process.versions && process.versions.node; diff --git a/package.json b/package.json index 233c022..a54f824 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "./lib/streams": false }, "devDependencies": { + "@types/node": "^12.0.10", "async": "*", "errto": "*", "iconv": "*", diff --git a/test/translit-test.js b/test/translit-test.js index 6cdc9ea..3f596d6 100644 --- a/test/translit-test.js +++ b/test/translit-test.js @@ -2,32 +2,32 @@ var assert = require('assert'), iconv = require(__dirname + '/../'), Iconv = require('iconv').Iconv; -var sample1 = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +var sample1 = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ_ØÙÚÛÜ_Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ 字符编码 文字コード Κωδικοποίηση χαρακτήρων Кодировка символов'; describe('Direct transliteration', function() { it('should transliterate for ISO-8859-1 (as latin1)', function() { - assert.equal(iconv.transliterate(sample1, 'latin1'), '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ + assert.equal(iconv.transliterate(sample1, 'latin1'), '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ_ØÙÚÛÜ_Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); }); it('should transliterate for ASCII', function() { - assert.equal(iconv.transliterate(sample1, 'ascii'), '!C/PS$?Y=|SS"(c)a<>1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ + assert.equal(iconv.transliterate(sample1, 'ascii'), '!C/PS$?Y=|SS"(c)a<>1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOO_OUUUU_Thssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); }); it('should transliterate for EUC-JP', function() { - assert.equal(iconv.transliterate(sample1, 'EUC-JP'), '¡C/PS¤¥¦§¨©ª<>1/41/23/4¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏDÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ + assert.equal(iconv.transliterate(sample1, 'EUC-JP'), '¡C/PS¤¥¦§¨©ª<>1/41/23/4¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏDÑÒÓÔÕÖ_ØÙÚÛÜ_Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ Zi Fu Bian Ma Wen Zi コード Κωδικοποίηση χαρακτήρων Кодировка символов'); }); it('should transliterate for Windows-1251', function() { - assert.equal(iconv.transliterate(sample1, 'windows1251'), '!C/PS¤Y=¦§"©a«¬­®-°±23\'µ¶·,1o»1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ + assert.equal(iconv.transliterate(sample1, 'windows1251'), '!C/PS¤Y=¦§"©a«¬­®-°±23\'µ¶·,1o»1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOO_OUUUU_Thssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Кодировка символов'); }); it('should transliterate for MacGreek', function() { - assert.equal(iconv.transliterate(sample1, 'MacGreek'), '!C/£$?¥¦§¨©a«¬­®-°±²³\'uP*,¹o»1/4½3/4?AAAAÄAAECEÉEEIIIIDNOOOOÖxOUUUÜUThßàaâaäaaeçèéêëiiîïdnooôoö÷oùuûüythy\n\ + assert.equal(iconv.transliterate(sample1, 'MacGreek'), '!C/£$?¥¦§¨©a«¬­®-°±²³\'uP*,¹o»1/4½3/4?AAAAÄAAECEÉEEIIIIDNOOOOÖ_OUUUÜ_Thßàaâaäaaeçèéêëiiîïdnooôoö÷oùuûüythy\n\ Zi Fu Bian Ma Wen Zi kodo Κωδικοποίηση χαρακτήρων Kodirovka simvolov'); }); }); @@ -35,32 +35,49 @@ Zi Fu Bian Ma Wen Zi kodo Κωδικοποίηση χαρακτήρων Kodirov describe('Transliteration via encoder', function() { it('should transliterate for ISO-8859-1 (as latin1)', function() { var buf = iconv.encode(sample1, 'latin1//translit'); - assert.equal(iconv.decode(buf, 'latin1'), '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ + assert.equal(iconv.decode(buf, 'latin1'), '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ_ØÙÚÛÜ_Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); }); it('should transliterate for ASCII', function() { var buf = iconv.encode(sample1, 'ascii', { transliterate: true }); - assert.equal(iconv.decode(buf, 'ascii'), '!C/PS$?Y=|SS"(c)a<>1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ + assert.equal(iconv.decode(buf, 'ascii'), '!C/PS$?Y=|SS"(c)a<>1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOO_OUUUU_Thssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Kodirovka simvolov'); }); // TODO: Put this test back when `¥` and `‾` are handled correctly. // it('should transliterate for EUC-JP', function() { // var buf = iconv.encode(sample1, 'EUC-JP', { transliterate: true }); -// assert.equal(iconv.decode(buf, 'EUC-JP'), '¡C/PS¤¥¦§¨©ª<>1/41/23/4¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏDÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ +// assert.equal(iconv.decode(buf, 'EUC-JP'), '¡C/PS¤¥¦§¨©ª<>1/41/23/4¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏDÑÒÓÔÕÖ_ØÙÚÛÜ_Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n\ // Zi Fu Bian Ma Wen Zi コード Κωδικοποίηση χαρακτήρων Кодировка символов'); // }); it('should transliterate for Windows-1251', function() { var buf = iconv.encode(sample1, 'windows1251', { transliterate: true }); - assert.equal(iconv.decode(buf, 'windows1251'), '!C/PS¤Y=¦§"©a«¬­®-°±23\'µ¶·,1o»1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOOxOUUUUUThssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ + assert.equal(iconv.decode(buf, 'windows1251'), '!C/PS¤Y=¦§"©a«¬­®-°±23\'µ¶·,1o»1/41/23/4?AAAAAAAECEEEEIIIIDNOOOOO_OUUUU_Thssaaaaaaaeceeeeiiiidnooooo/ouuuuythy\n\ Zi Fu Bian Ma Wen Zi kodo Kodikopoiese kharakteron Кодировка символов'); }); it('should transliterate for MacGreek', function() { var buf = iconv.encode(sample1, 'MacGreek', { transliterate: true }); - assert.equal(iconv.decode(buf, 'MacGreek'), '!C/£$?¥¦§¨©a«¬­®-°±²³\'uP*,¹o»1/4½3/4?AAAAÄAAECEÉEEIIIIDNOOOOÖxOUUUÜUThßàaâaäaaeçèéêëiiîïdnooôoö÷oùuûüythy\n\ + assert.equal(iconv.decode(buf, 'MacGreek'), '!C/£$?¥¦§¨©a«¬­®-°±²³\'uP*,¹o»1/4½3/4?AAAAÄAAECEÉEEIIIIDNOOOOÖ_OUUUÜ_Thßàaâaäaaeçèéêëiiîïdnooôoö÷oùuûüythy\n\ Zi Fu Bian Ma Wen Zi kodo Κωδικοποίηση χαρακτήρων Kodirovka simvolov'); }); + + if (iconv.hasUnidecodePlus()) { + it('should transliterate for ASCII with smart spacing', function() { + var buf = iconv.encode('Café 北京, 鞋 size 10½, 33⅓ RPM', 'ascii', { transliterate: true, smartSpacing: true }); + assert.equal(iconv.decode(buf, 'ascii'), 'Cafe Bei Jing, Xie size 10 1/2, 33 1/3 RPM'); + }); + + it('should transliterate for ISO-8859-1 with smart spacing', function() { + var buf = iconv.encode('Café 北京, 鞋 size 10½, 33⅓ RPM', 'ISO-8859-1', { transliterate: true, smartSpacing: true }); + assert.equal(iconv.decode(buf, 'ISO-8859-1'), 'Café Bei Jing, Xie size 10½, 33 1/3 RPM'); + }); + + it('should transliterate for ASCII with German option', function() { + var buf = iconv.encode('ÄäÖöÜü, Schrödinger', 'ascii', { transliterate: true, german: true }); + assert.equal(iconv.decode(buf, 'ascii'), 'AEaeOEoeUEue, Schroedinger'); + }); + } }); From bb0a2ea536b30f637e3d8d53c2c1774bb2a4c95c Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 1 Jul 2019 01:05:31 -0400 Subject: [PATCH 19/24] Make extra unit testing for smart spacing and German dependent on whether unidecode-plus in available or not. --- lib/index.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/index.js b/lib/index.js index 2804c0b..dd710bb 100644 --- a/lib/index.js +++ b/lib/index.js @@ -221,6 +221,7 @@ TransliterationWrapper.prototype.end = function() { return Buffer.concat(bufs); }; +var checkedForUnidecode = false; var unidecode; var hasUnidecodePlus = false; var encodingRanges; @@ -246,20 +247,7 @@ iconv.transliterate = function transliterate(str, targetEncoding, options) { if (/^(utf.*|cesu8|gb18030|ucs2|ucs4)$/.test(encoding)) return str; - if (!encodingRanges) { - try { - unidecode = require('unidecode-plus'); - hasUnidecodePlus = true; - } - catch (err) { - try { - unidecode = require('unidecode'); - } - catch (err) { - throw new Error('Transliteration requires unidecode package'); - } - } - } + iconv.hasUnidecodePlus(); var deferredSmartSpacing = options && options.deferredSmartSpacing; var smartSpacing = deferredSmartSpacing || (options && options.smartSpacing); @@ -310,7 +298,24 @@ function getTransliteration(ch) { return unidecode(ch); } -iconv.hasUnidecodePlus = function hasUnidecodePlus() { +iconv.hasUnidecodePlus = function () { + if (!checkedForUnidecode) { + try { + unidecode = require('unidecode-plus'); + hasUnidecodePlus = true; + } + catch (err) { + try { + unidecode = require('unidecode'); + } + catch (err) { + throw new Error('Transliteration requires unidecode package'); + } + } + + checkedForUnidecode = true; + } + return hasUnidecodePlus; }; From 84f01e45884ade0125005a1f72b5ccd4ae4ae8a7 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 1 Jul 2019 01:44:45 -0400 Subject: [PATCH 20/24] Update with unidecode-plus 0.0.0-alpha.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a54f824..ea45f38 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "mocha": "^3.1.0", "request": "~2.87.0", "semver": "*", - "unidecode": "^0.1.8", + "unidecode-plus": "0.0.0-alpha.1", "unorm": "*" }, "dependencies": { From cac5bc8af1a9009a69302c9360cfbaf6805ae2bb Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Mon, 1 Jul 2019 17:08:22 -0400 Subject: [PATCH 21/24] Update with new unidecode plus and related documentation. --- .npmignore | 2 +- README.md | 10 ++++++++-- lib/index.d.ts | 12 ++++++------ package.json | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.npmignore b/.npmignore index e9ea0c1..d345f14 100644 --- a/.npmignore +++ b/.npmignore @@ -5,4 +5,4 @@ test wiki coverage .travis.yml - +/.npmrc diff --git a/README.md b/README.md index 3d2b09e..e92b9c5 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ * In-browser usage via [Browserify](https://github.com/substack/node-browserify) (~180k gzip compressed with Buffer shim included). * Typescript [type definition file](https://github.com/ashtuchkin/iconv-lite/blob/master/lib/index.d.ts) included. * React Native is supported (need to explicitly `npm install` two more modules: `buffer` and `stream`). - * Transliteration option is available when the [unidecode](https://www.npmjs.com/package/unidecode) package is added to your project + * Transliteration option is available when either [unidecode-plus](https://www.npmjs.com/package/unidecode-plus) or [unidecode](https://www.npmjs.com/package/unidecode) are added to your project * License: MIT. [![NPM Stats](https://nodei.co/npm/iconv-lite.png)](https://npmjs.org/package/iconv-lite/) @@ -149,7 +149,7 @@ This library supports UTF-32LE, UTF-32BE and UTF-32 encodings. Like the UTF-16 e ## Transliteration -If the [unidecode](https://www.npmjs.com/package/unidecode) package is added to your project (`npm install unidecode`), the option will be available to transliterate characters which are not available in a particular encoding. The transliterations are always plain ASCII characters, but unlike using unidecode directly (which will convert *all* non-ASCII characters into transliterations), transliterations done using iconv will only transliterate characters which are not available in the target encoding. +If either [unidecode-plus](https://www.npmjs.com/package/unidecode-plus) or [unidecode](https://www.npmjs.com/package/unidecode) are added to your project ("`npm install unidecode-plus`" or "`npm install unidecode`"), the option will be available to transliterate characters which are not available in a particular encoding. The transliterations are always plain ASCII characters, but unlike using unidecode directly (which will convert *all* non-ASCII characters into transliterations), transliterations done using iconv will only transliterate characters which are not available in the target character encoding. In this example: ``` @@ -166,6 +166,12 @@ When encoding to create a buffer, the node-iconv style of requesting translitera buf = iconv.encode("Café 北京", 'iso-8859-1//translit'); ``` +If you use `unidecode-plus` instead of `unidecode`, you get two additional transliteration options: `german`, and `smartSpacing`. + +The `german` option transliterates `Ä`, `ä`, `Ö`, `ö`, `Ü`, and `ü` to `AE`, `ae`, `OE`, `oe`, `UE`, and `ue`, respectively, instead of just removing the umlauts. + +The `smartSpacing` options improves the formatting of transliterated text, removing some unnecessary spaces, and adding others for clarity. For example, "Café 北京, 鞋 size 10½" becomes "Cafe Bei Jing, Xie size 10 1/2" using `smartSpacing`. Without it, you get "Cafe Bei Jing , Xie size 101/2". (See the [unidecode-plus site](https://github.com/kshetline/unidecode-plus/blob/master/README.md) for more detail.) + Please take note that transliteration only affects encoding, not decoding. ## Other notes diff --git a/lib/index.d.ts b/lib/index.d.ts index c51dcb3..e6c2c0f 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -6,17 +6,17 @@ *--------------------------------------------------------------------------------------------*/ declare module 'iconv-lite' { - export function decode(buffer: Buffer, encoding: string, options?: Options): string; + export function decode(buffer: Buffer, encoding: string, options?: Options): string; - export function encode(content: string, encoding: string, options?: Options): Buffer; + export function encode(content: string, encoding: string, options?: Options): Buffer; - export function encodingExists(encoding: string): boolean; + export function encodingExists(encoding: string): boolean; - export function decodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream; + export function decodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream; - export function encodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream; + export function encodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream; - export function transliterate(str: string, targetEncoding: string, options?: TransliterationOptions) + export function transliterate(str: string, targetEncoding: string, options?: TransliterationOptions): string; } export interface TransliterationOptions { diff --git a/package.json b/package.json index ea45f38..160e5f8 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "mocha": "^3.1.0", "request": "~2.87.0", "semver": "*", - "unidecode-plus": "0.0.0-alpha.1", + "unidecode-plus": "1.0.0", "unorm": "*" }, "dependencies": { From c69bdb52acb3fd2fb0c1ec0098fc05d597cc427a Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Tue, 2 Jul 2019 01:48:03 -0400 Subject: [PATCH 22/24] Update with unidecode-1.0.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 160e5f8..391f330 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "mocha": "^3.1.0", "request": "~2.87.0", "semver": "*", - "unidecode-plus": "1.0.0", + "unidecode-plus": "^1.0.1", "unorm": "*" }, "dependencies": { From 6d2d25a8f5cd7ddbbe9f4ffe7f3c636e31f9459d Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Sat, 13 Jul 2019 16:16:11 -0400 Subject: [PATCH 23/24] Transliteration in German mode now works with combining diaeresis. Add unit testing for streaming transliteration. --- README.md | 2 +- lib/index.js | 29 ++++++++++++++++++++++++++--- package.json | 2 +- test/translit-test.js | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e92b9c5..0bfaf34 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ str = iconv.decode(Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]), 'win1251'); buf = iconv.encode("Sample input string", 'win1251'); // Check if encoding is supported -iconv.encodingExists("us-ascii") +iconv.encodingExists("us-ascii"); // Convert from js string to an encoded buffer, keeping accented characters like "é", but transliterating Chinese. buf2 = iconv.encode("Café 北京", 'iso-8859-1', { transliterate: true }); diff --git a/lib/index.js b/lib/index.js index dd710bb..bebb2f5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -139,6 +139,9 @@ iconv.getEncoder = function getEncoder(encoding, options) { if (codec.bomAware && options && options.addBOM) encoder = new bomHandling.PrependBOM(encoder, options); + if (options && options.transliterate) + encoder = new TransliterationWrapper(encoder, options); + return encoder; } @@ -169,13 +172,21 @@ function TransliterationWrapper(encoder, options) { this.encoder = encoder; this.encodingName = encoder.encodingName + '-translit'; this.smartSpacing = (options && options.smartSpacing); - this.options = { deferredSmartSpacing: this.smartSpacing, german: options && options.german }; + this.german = (options && options.german); + this.options = { deferredSmartSpacing: this.smartSpacing, german: this.german }; this.pending = ''; } var MAX_PENDING = 16384; TransliterationWrapper.prototype.write = function(str) { + var $; + + if (this.german && ($ = /^(.*)([aou])$/i.exec(this.pending))) { + str = $[2] + str; + this.pending = $[1]; + } + str = iconv.transliterate(str, this.encoder.encodingName, this.options); if (this.smartSpacing) { @@ -184,7 +195,7 @@ TransliterationWrapper.prototype.write = function(str) { // Split the text being written out at a safe place where smart spacing won't // need to make any changes. - var $ = /^(.*)([^\s\x80\x81][\s\x80\x81].*)$/.exec(str); + $ = /^(.*)([^\s\x80\x81][\s\x80\x81].*)$/.exec(str); if ($) { str = unidecode.resolveSpacing($[1]); @@ -192,12 +203,24 @@ TransliterationWrapper.prototype.write = function(str) { } else if (str.length > MAX_PENDING) str = unidecode.resolveSpacing(str); + else if (str.length > 0 && /[^\x80\x81]/.test(str)) { + // Save one character from the end for the next spacing context + this.pending = str.charAt(str.length - 1); + str = str.substr(0, str.length - 1); + } else { this.pending = str; str = ''; } } + // If in German mode, and the last character currently available is an A, O, or U, make it a pending + // character on the chance the character that comes along next is a combining diaeresis. + if (this.german && ($ = /^(.*)([aou])$/i.exec(str))) { + str = $[1]; + this.pending = $[2] + this.pending; + } + return this.encoder.write(str); }; @@ -206,7 +229,7 @@ TransliterationWrapper.prototype.end = function() { var bufs = []; if (this.pending) { - buf = this.encoder.write(unidecode.resolveSpacing(this.pending)); + buf = this.encoder.write(this.smartSpacing ? unidecode.resolveSpacing(this.pending) : this.pending); this.pending = ''; } diff --git a/package.json b/package.json index 391f330..ea0ca5e 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "mocha": "^3.1.0", "request": "~2.87.0", "semver": "*", - "unidecode-plus": "^1.0.1", + "unidecode-plus": "^1.0.2", "unorm": "*" }, "dependencies": { diff --git a/test/translit-test.js b/test/translit-test.js index 3f596d6..0d4e422 100644 --- a/test/translit-test.js +++ b/test/translit-test.js @@ -81,3 +81,21 @@ Zi Fu Bian Ma Wen Zi kodo Κωδικοποίηση χαρακτήρων Kodirov }); } }); + +describe('Transliteration via stream', function() { + it('should handle inconvenient breaks in spacing and accented characters', function() { + var encoder = iconv.getEncoder('ascii', { transliterate: true, smartSpacing: true, german: true }); + var buf = new Buffer([]); + + buf = Buffer.concat([buf, encoder.write('😁北Schro')]); + buf = Buffer.concat([buf, encoder.write('\u0308dinger❜s cat 10')]); + buf = Buffer.concat([buf, encoder.write('½')]); + + var end = encoder.end(); + + if (end) + buf = Buffer.concat([buf, end]); + + assert.equal(iconv.decode(buf, 'ascii'), ':-D Bei Schroedinger\'s cat 10 1/2'); + }); +}); From 4e4f4da6e59c790735942f4d95c8d5588b30cfa3 Mon Sep 17 00:00:00 2001 From: Kerry Shetline Date: Sat, 13 Jul 2019 16:27:12 -0400 Subject: [PATCH 24/24] Transliteration in German mode now works with combining diaeresis. Add unit testing for streaming transliteration. --- lib/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/index.js b/lib/index.js index bebb2f5..f05dba1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -31,9 +31,6 @@ iconv.encode = function encode(str, encoding, options) { var encoder = iconv.getEncoder(encoding, options); - if (options && options.transliterate) - encoder = new TransliterationWrapper(encoder, options); - var res = encoder.write(str); var trail = encoder.end();