extend-node.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // == Extend Node primitives to use iconv-lite =================================
  2. module.exports = function (iconv) {
  3. var original = undefined; // Place to keep original methods.
  4. iconv.extendNodeEncodings = function extendNodeEncodings() {
  5. if (original) return;
  6. original = {};
  7. var nodeNativeEncodings = {
  8. 'hex': true, 'utf8': true, 'utf-8': true, 'ascii': true, 'binary': true,
  9. 'base64': true, 'ucs2': true, 'ucs-2': true, 'utf16le': true, 'utf-16le': true,
  10. };
  11. Buffer.isNativeEncoding = function(enc) {
  12. return nodeNativeEncodings[enc && enc.toLowerCase()];
  13. }
  14. // -- SlowBuffer -----------------------------------------------------------
  15. var SlowBuffer = require('buffer').SlowBuffer;
  16. original.SlowBufferToString = SlowBuffer.prototype.toString;
  17. SlowBuffer.prototype.toString = function(encoding, start, end) {
  18. encoding = String(encoding || 'utf8').toLowerCase();
  19. start = +start || 0;
  20. if (typeof end !== 'number') end = this.length;
  21. // Fastpath empty strings
  22. if (+end == start)
  23. return '';
  24. // Use native conversion when possible
  25. if (Buffer.isNativeEncoding(encoding))
  26. return original.SlowBufferToString.call(this, encoding, start, end);
  27. // Otherwise, use our decoding method.
  28. if (typeof start == 'undefined') start = 0;
  29. if (typeof end == 'undefined') end = this.length;
  30. return iconv.decode(this.slice(start, end), encoding);
  31. }
  32. original.SlowBufferWrite = SlowBuffer.prototype.write;
  33. SlowBuffer.prototype.write = function(string, offset, length, encoding) {
  34. // Support both (string, offset, length, encoding)
  35. // and the legacy (string, encoding, offset, length)
  36. if (isFinite(offset)) {
  37. if (!isFinite(length)) {
  38. encoding = length;
  39. length = undefined;
  40. }
  41. } else { // legacy
  42. var swap = encoding;
  43. encoding = offset;
  44. offset = length;
  45. length = swap;
  46. }
  47. offset = +offset || 0;
  48. var remaining = this.length - offset;
  49. if (!length) {
  50. length = remaining;
  51. } else {
  52. length = +length;
  53. if (length > remaining) {
  54. length = remaining;
  55. }
  56. }
  57. encoding = String(encoding || 'utf8').toLowerCase();
  58. // Use native conversion when possible
  59. if (Buffer.isNativeEncoding(encoding))
  60. return original.SlowBufferWrite.call(this, string, offset, length, encoding);
  61. if (string.length > 0 && (length < 0 || offset < 0))
  62. throw new RangeError('attempt to write beyond buffer bounds');
  63. // Otherwise, use our encoding method.
  64. var buf = iconv.encode(string, encoding);
  65. if (buf.length < length) length = buf.length;
  66. buf.copy(this, offset, 0, length);
  67. return length;
  68. }
  69. // -- Buffer ---------------------------------------------------------------
  70. original.BufferIsEncoding = Buffer.isEncoding;
  71. Buffer.isEncoding = function(encoding) {
  72. return Buffer.isNativeEncoding(encoding) || iconv.encodingExists(encoding);
  73. }
  74. original.BufferByteLength = Buffer.byteLength;
  75. Buffer.byteLength = SlowBuffer.byteLength = function(str, encoding) {
  76. encoding = String(encoding || 'utf8').toLowerCase();
  77. // Use native conversion when possible
  78. if (Buffer.isNativeEncoding(encoding))
  79. return original.BufferByteLength.call(this, str, encoding);
  80. // Slow, I know, but we don't have a better way yet.
  81. return iconv.encode(str, encoding).length;
  82. }
  83. original.BufferToString = Buffer.prototype.toString;
  84. Buffer.prototype.toString = function(encoding, start, end) {
  85. encoding = String(encoding || 'utf8').toLowerCase();
  86. // Use native conversion when possible
  87. if (Buffer.isNativeEncoding(encoding))
  88. return original.BufferToString.call(this, encoding, start, end);
  89. // Otherwise, use our decoding method.
  90. if (typeof start == 'undefined') start = 0;
  91. if (typeof end == 'undefined') end = this.length;
  92. return iconv.decode(this.slice(start, end), encoding);
  93. }
  94. original.BufferWrite = Buffer.prototype.write;
  95. Buffer.prototype.write = function(string, offset, length, encoding) {
  96. var _offset = offset, _length = length, _encoding = encoding;
  97. // Support both (string, offset, length, encoding)
  98. // and the legacy (string, encoding, offset, length)
  99. if (isFinite(offset)) {
  100. if (!isFinite(length)) {
  101. encoding = length;
  102. length = undefined;
  103. }
  104. } else { // legacy
  105. var swap = encoding;
  106. encoding = offset;
  107. offset = length;
  108. length = swap;
  109. }
  110. encoding = String(encoding || 'utf8').toLowerCase();
  111. // Use native conversion when possible
  112. if (Buffer.isNativeEncoding(encoding))
  113. return original.BufferWrite.call(this, string, _offset, _length, _encoding);
  114. offset = +offset || 0;
  115. var remaining = this.length - offset;
  116. if (!length) {
  117. length = remaining;
  118. } else {
  119. length = +length;
  120. if (length > remaining) {
  121. length = remaining;
  122. }
  123. }
  124. if (string.length > 0 && (length < 0 || offset < 0))
  125. throw new RangeError('attempt to write beyond buffer bounds');
  126. // Otherwise, use our encoding method.
  127. var buf = iconv.encode(string, encoding);
  128. if (buf.length < length) length = buf.length;
  129. buf.copy(this, offset, 0, length);
  130. return length;
  131. // TODO: Set _charsWritten.
  132. }
  133. // -- Readable -------------------------------------------------------------
  134. if (iconv.supportsStreams) {
  135. var Readable = require('stream').Readable;
  136. original.ReadableSetEncoding = Readable.prototype.setEncoding;
  137. Readable.prototype.setEncoding = function setEncoding(enc, options) {
  138. // Try to use original function when possible.
  139. if (Buffer.isNativeEncoding(enc))
  140. return original.ReadableSetEncoding.call(this, enc);
  141. // Try to use our own decoder, it has the same interface.
  142. this._readableState.decoder = iconv.getCodec(enc).decoder(options);
  143. this._readableState.encoding = enc;
  144. }
  145. Readable.prototype.collect = iconv._collect;
  146. }
  147. }
  148. // Remove iconv-lite Node primitive extensions.
  149. iconv.undoExtendNodeEncodings = function undoExtendNodeEncodings() {
  150. if (!original)
  151. throw new Error("require('iconv-lite').undoExtendNodeEncodings(): Nothing to undo; extendNodeEncodings() is not called.")
  152. delete Buffer.isNativeEncoding;
  153. var SlowBuffer = require('buffer').SlowBuffer;
  154. SlowBuffer.prototype.toString = original.SlowBufferToString;
  155. SlowBuffer.prototype.write = original.SlowBufferWrite;
  156. Buffer.isEncoding = original.BufferIsEncoding;
  157. Buffer.byteLength = original.BufferByteLength;
  158. Buffer.prototype.toString = original.BufferToString;
  159. Buffer.prototype.write = original.BufferWrite;
  160. if (iconv.supportsStreams) {
  161. var Readable = require('stream').Readable;
  162. Readable.prototype.setEncoding = original.ReadableSetEncoding;
  163. delete Readable.prototype.collect;
  164. }
  165. original = undefined;
  166. }
  167. }