utf16.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. "use strict"
  2. // Note: UTF16-LE (or UCS2) codec is Node.js native. See encodings/internal.js
  3. // == UTF16-BE codec. ==========================================================
  4. exports.utf16be = Utf16BECodec;
  5. function Utf16BECodec() {
  6. }
  7. Utf16BECodec.prototype.encoder = Utf16BEEncoder;
  8. Utf16BECodec.prototype.decoder = Utf16BEDecoder;
  9. Utf16BECodec.prototype.bomAware = true;
  10. // -- Encoding
  11. function Utf16BEEncoder() {
  12. }
  13. Utf16BEEncoder.prototype.write = function(str) {
  14. var buf = new Buffer(str, 'ucs2');
  15. for (var i = 0; i < buf.length; i += 2) {
  16. var tmp = buf[i]; buf[i] = buf[i+1]; buf[i+1] = tmp;
  17. }
  18. return buf;
  19. }
  20. Utf16BEEncoder.prototype.end = function() {
  21. }
  22. // -- Decoding
  23. function Utf16BEDecoder() {
  24. this.overflowByte = -1;
  25. }
  26. Utf16BEDecoder.prototype.write = function(buf) {
  27. if (buf.length == 0)
  28. return '';
  29. var buf2 = new Buffer(buf.length + 1),
  30. i = 0, j = 0;
  31. if (this.overflowByte !== -1) {
  32. buf2[0] = buf[0];
  33. buf2[1] = this.overflowByte;
  34. i = 1; j = 2;
  35. }
  36. for (; i < buf.length-1; i += 2, j+= 2) {
  37. buf2[j] = buf[i+1];
  38. buf2[j+1] = buf[i];
  39. }
  40. this.overflowByte = (i == buf.length-1) ? buf[buf.length-1] : -1;
  41. return buf2.slice(0, j).toString('ucs2');
  42. }
  43. Utf16BEDecoder.prototype.end = function() {
  44. }
  45. // == UTF-16 codec =============================================================
  46. // Decoder chooses automatically from UTF-16LE and UTF-16BE using BOM and space-based heuristic.
  47. // Defaults to UTF-16LE, as it's prevalent and default in Node.
  48. // http://en.wikipedia.org/wiki/UTF-16 and http://encoding.spec.whatwg.org/#utf-16le
  49. // Decoder default can be changed: iconv.decode(buf, 'utf16', {defaultEncoding: 'utf-16be'});
  50. // Encoder uses UTF-16LE and prepends BOM (which can be overridden with addBOM: false).
  51. exports.utf16 = Utf16Codec;
  52. function Utf16Codec(codecOptions, iconv) {
  53. this.iconv = iconv;
  54. }
  55. Utf16Codec.prototype.encoder = Utf16Encoder;
  56. Utf16Codec.prototype.decoder = Utf16Decoder;
  57. // -- Encoding (pass-through)
  58. function Utf16Encoder(options, codec) {
  59. options = options || {};
  60. if (options.addBOM === undefined)
  61. options.addBOM = true;
  62. this.encoder = codec.iconv.getEncoder('utf-16le', options);
  63. }
  64. Utf16Encoder.prototype.write = function(str) {
  65. return this.encoder.write(str);
  66. }
  67. Utf16Encoder.prototype.end = function() {
  68. return this.encoder.end();
  69. }
  70. // -- Decoding
  71. function Utf16Decoder(options, codec) {
  72. this.decoder = null;
  73. this.initialBytes = [];
  74. this.initialBytesLen = 0;
  75. this.options = options || {};
  76. this.iconv = codec.iconv;
  77. }
  78. Utf16Decoder.prototype.write = function(buf) {
  79. if (!this.decoder) {
  80. // Codec is not chosen yet. Accumulate initial bytes.
  81. this.initialBytes.push(buf);
  82. this.initialBytesLen += buf.length;
  83. if (this.initialBytesLen < 16) // We need more bytes to use space heuristic (see below)
  84. return '';
  85. // We have enough bytes -> detect endianness.
  86. var buf = Buffer.concat(this.initialBytes),
  87. encoding = detectEncoding(buf, this.options.defaultEncoding);
  88. this.decoder = this.iconv.getDecoder(encoding, this.options);
  89. this.initialBytes.length = this.initialBytesLen = 0;
  90. }
  91. return this.decoder.write(buf);
  92. }
  93. Utf16Decoder.prototype.end = function() {
  94. if (!this.decoder) {
  95. var buf = Buffer.concat(this.initialBytes),
  96. encoding = detectEncoding(buf, this.options.defaultEncoding);
  97. this.decoder = this.iconv.getDecoder(encoding, this.options);
  98. var res = this.decoder.write(buf),
  99. trail = this.decoder.end();
  100. return trail ? (res + trail) : res;
  101. }
  102. return this.decoder.end();
  103. }
  104. function detectEncoding(buf, defaultEncoding) {
  105. var enc = defaultEncoding || 'utf-16le';
  106. if (buf.length >= 2) {
  107. // Check BOM.
  108. if (buf[0] == 0xFE && buf[1] == 0xFF) // UTF-16BE BOM
  109. enc = 'utf-16be';
  110. else if (buf[0] == 0xFF && buf[1] == 0xFE) // UTF-16LE BOM
  111. enc = 'utf-16le';
  112. else {
  113. // No BOM found. Try to deduce encoding from initial content.
  114. // Most of the time, the content has ASCII chars (U+00**), but the opposite (U+**00) is uncommon.
  115. // So, we count ASCII as if it was LE or BE, and decide from that.
  116. var asciiCharsLE = 0, asciiCharsBE = 0, // Counts of chars in both positions
  117. _len = Math.min(buf.length - (buf.length % 2), 64); // Len is always even.
  118. for (var i = 0; i < _len; i += 2) {
  119. if (buf[i] === 0 && buf[i+1] !== 0) asciiCharsBE++;
  120. if (buf[i] !== 0 && buf[i+1] === 0) asciiCharsLE++;
  121. }
  122. if (asciiCharsBE > asciiCharsLE)
  123. enc = 'utf-16be';
  124. else if (asciiCharsBE < asciiCharsLE)
  125. enc = 'utf-16le';
  126. }
  127. }
  128. return enc;
  129. }