1"use strict"; 2var Buffer = require("buffer").Buffer; 3// Note: not polyfilled with safer-buffer on a purpose, as overrides Buffer 4 5// == Extend Node primitives to use iconv-lite ================================= 6 7module.exports = function (iconv) { 8 var original = undefined; // Place to keep original methods. 9 10 // Node authors rewrote Buffer internals to make it compatible with 11 // Uint8Array and we cannot patch key functions since then. 12 // Note: this does use older Buffer API on a purpose 13 iconv.supportsNodeEncodingsExtension = !(Buffer.from || new Buffer(0) instanceof Uint8Array); 14 15 iconv.extendNodeEncodings = function extendNodeEncodings() { 16 if (original) return; 17 original = {}; 18 19 if (!iconv.supportsNodeEncodingsExtension) { 20 console.error("ACTION NEEDED: require('iconv-lite').extendNodeEncodings() is not supported in your version of Node"); 21 console.error("See more info at https://github.com/ashtuchkin/iconv-lite/wiki/Node-v4-compatibility"); 22 return; 23 } 24 25 var nodeNativeEncodings = { 26 'hex': true, 'utf8': true, 'utf-8': true, 'ascii': true, 'binary': true, 27 'base64': true, 'ucs2': true, 'ucs-2': true, 'utf16le': true, 'utf-16le': true, 28 }; 29 30 Buffer.isNativeEncoding = function(enc) { 31 return enc && nodeNativeEncodings[enc.toLowerCase()]; 32 } 33 34 // -- SlowBuffer ----------------------------------------------------------- 35 var SlowBuffer = require('buffer').SlowBuffer; 36 37 original.SlowBufferToString = SlowBuffer.prototype.toString; 38 SlowBuffer.prototype.toString = function(encoding, start, end) { 39 encoding = String(encoding || 'utf8').toLowerCase(); 40 41 // Use native conversion when possible 42 if (Buffer.isNativeEncoding(encoding)) 43 return original.SlowBufferToString.call(this, encoding, start, end); 44 45 // Otherwise, use our decoding method. 46 if (typeof start == 'undefined') start = 0; 47 if (typeof end == 'undefined') end = this.length; 48 return iconv.decode(this.slice(start, end), encoding); 49 } 50 51 original.SlowBufferWrite = SlowBuffer.prototype.write; 52 SlowBuffer.prototype.write = function(string, offset, length, encoding) { 53 // Support both (string, offset, length, encoding) 54 // and the legacy (string, encoding, offset, length) 55 if (isFinite(offset)) { 56 if (!isFinite(length)) { 57 encoding = length; 58 length = undefined; 59 } 60 } else { // legacy 61 var swap = encoding; 62 encoding = offset; 63 offset = length; 64 length = swap; 65 } 66 67 offset = +offset || 0; 68 var remaining = this.length - offset; 69 if (!length) { 70 length = remaining; 71 } else { 72 length = +length; 73 if (length > remaining) { 74 length = remaining; 75 } 76 } 77 encoding = String(encoding || 'utf8').toLowerCase(); 78 79 // Use native conversion when possible 80 if (Buffer.isNativeEncoding(encoding)) 81 return original.SlowBufferWrite.call(this, string, offset, length, encoding); 82 83 if (string.length > 0 && (length < 0 || offset < 0)) 84 throw new RangeError('attempt to write beyond buffer bounds'); 85 86 // Otherwise, use our encoding method. 87 var buf = iconv.encode(string, encoding); 88 if (buf.length < length) length = buf.length; 89 buf.copy(this, offset, 0, length); 90 return length; 91 } 92 93 // -- Buffer --------------------------------------------------------------- 94 95 original.BufferIsEncoding = Buffer.isEncoding; 96 Buffer.isEncoding = function(encoding) { 97 return Buffer.isNativeEncoding(encoding) || iconv.encodingExists(encoding); 98 } 99 100 original.BufferByteLength = Buffer.byteLength; 101 Buffer.byteLength = SlowBuffer.byteLength = function(str, encoding) { 102 encoding = String(encoding || 'utf8').toLowerCase(); 103 104 // Use native conversion when possible 105 if (Buffer.isNativeEncoding(encoding)) 106 return original.BufferByteLength.call(this, str, encoding); 107 108 // Slow, I know, but we don't have a better way yet. 109 return iconv.encode(str, encoding).length; 110 } 111 112 original.BufferToString = Buffer.prototype.toString; 113 Buffer.prototype.toString = function(encoding, start, end) { 114 encoding = String(encoding || 'utf8').toLowerCase(); 115 116 // Use native conversion when possible 117 if (Buffer.isNativeEncoding(encoding)) 118 return original.BufferToString.call(this, encoding, start, end); 119 120 // Otherwise, use our decoding method. 121 if (typeof start == 'undefined') start = 0; 122 if (typeof end == 'undefined') end = this.length; 123 return iconv.decode(this.slice(start, end), encoding); 124 } 125 126 original.BufferWrite = Buffer.prototype.write; 127 Buffer.prototype.write = function(string, offset, length, encoding) { 128 var _offset = offset, _length = length, _encoding = encoding; 129 // Support both (string, offset, length, encoding) 130 // and the legacy (string, encoding, offset, length) 131 if (isFinite(offset)) { 132 if (!isFinite(length)) { 133 encoding = length; 134 length = undefined; 135 } 136 } else { // legacy 137 var swap = encoding; 138 encoding = offset; 139 offset = length; 140 length = swap; 141 } 142 143 encoding = String(encoding || 'utf8').toLowerCase(); 144 145 // Use native conversion when possible 146 if (Buffer.isNativeEncoding(encoding)) 147 return original.BufferWrite.call(this, string, _offset, _length, _encoding); 148 149 offset = +offset || 0; 150 var remaining = this.length - offset; 151 if (!length) { 152 length = remaining; 153 } else { 154 length = +length; 155 if (length > remaining) { 156 length = remaining; 157 } 158 } 159 160 if (string.length > 0 && (length < 0 || offset < 0)) 161 throw new RangeError('attempt to write beyond buffer bounds'); 162 163 // Otherwise, use our encoding method. 164 var buf = iconv.encode(string, encoding); 165 if (buf.length < length) length = buf.length; 166 buf.copy(this, offset, 0, length); 167 return length; 168 169 // TODO: Set _charsWritten. 170 } 171 172 173 // -- Readable ------------------------------------------------------------- 174 if (iconv.supportsStreams) { 175 var Readable = require('stream').Readable; 176 177 original.ReadableSetEncoding = Readable.prototype.setEncoding; 178 Readable.prototype.setEncoding = function setEncoding(enc, options) { 179 // Use our own decoder, it has the same interface. 180 // We cannot use original function as it doesn't handle BOM-s. 181 this._readableState.decoder = iconv.getDecoder(enc, options); 182 this._readableState.encoding = enc; 183 } 184 185 Readable.prototype.collect = iconv._collect; 186 } 187 } 188 189 // Remove iconv-lite Node primitive extensions. 190 iconv.undoExtendNodeEncodings = function undoExtendNodeEncodings() { 191 if (!iconv.supportsNodeEncodingsExtension) 192 return; 193 if (!original) 194 throw new Error("require('iconv-lite').undoExtendNodeEncodings(): Nothing to undo; extendNodeEncodings() is not called.") 195 196 delete Buffer.isNativeEncoding; 197 198 var SlowBuffer = require('buffer').SlowBuffer; 199 200 SlowBuffer.prototype.toString = original.SlowBufferToString; 201 SlowBuffer.prototype.write = original.SlowBufferWrite; 202 203 Buffer.isEncoding = original.BufferIsEncoding; 204 Buffer.byteLength = original.BufferByteLength; 205 Buffer.prototype.toString = original.BufferToString; 206 Buffer.prototype.write = original.BufferWrite; 207 208 if (iconv.supportsStreams) { 209 var Readable = require('stream').Readable; 210 211 Readable.prototype.setEncoding = original.ReadableSetEncoding; 212 delete Readable.prototype.collect; 213 } 214 215 original = undefined; 216 } 217} 218