diff --git a/.changeset/clever-pumas-drum.md b/.changeset/clever-pumas-drum.md new file mode 100644 index 00000000..3979839d --- /dev/null +++ b/.changeset/clever-pumas-drum.md @@ -0,0 +1,5 @@ +--- +"just-bash": patch +--- + +js-exec: fix Buffer shim correctness — ascii encode now uses & 0xff (not & 0x7f), consolidate latin1/ascii into shared \_rawEncode, fix Buffer.from(ArrayBuffer, offset, length), throw on invalid byteLength input, clamp negative toString start, throw RangeError for out-of-range write offset diff --git a/packages/just-bash/src/commands/js-exec/js-exec.buffer-encoding.test.ts b/packages/just-bash/src/commands/js-exec/js-exec.buffer-encoding.test.ts new file mode 100644 index 00000000..f153f9f8 --- /dev/null +++ b/packages/just-bash/src/commands/js-exec/js-exec.buffer-encoding.test.ts @@ -0,0 +1,331 @@ +import { describe, expect, it } from "vitest"; +import { Bash } from "../../Bash.js"; + +describe("buffer encoding", () => { + // ─── Encode side ───────────────────────────────────────────────── + + it("Buffer.from(str).toString('base64')", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('hello').toString('base64'))"`, + ); + expect(result.stdout).toBe("aGVsbG8=\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(str).toString('base64url')", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('hello').toString('base64url'))"`, + ); + expect(result.stdout).toBe("aGVsbG8\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('base64') with binary bytes", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0xff,0xfe,0x00]).toString('base64'))"`, + ); + expect(result.stdout).toBe("//4A\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('base64url') with binary bytes", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0xff,0xfe,0x00]).toString('base64url'))"`, + ); + expect(result.stdout).toBe("__4A\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('hex')", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0xff,0xfe,0x00]).toString('hex'))"`, + ); + expect(result.stdout).toBe("fffe00\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('latin1') preserves high bytes", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0xff]).toString('latin1'))"`, + ); + expect(result.stdout).toBe("ÿ\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('binary') aliases latin1", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0x41,0x42]).toString('binary'))"`, + ); + expect(result.stdout).toBe("AB\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('ascii') masks high bit", async () => { + const env = new Bash({ javascript: true }); + // 193 & 0x7F = 65 = 'A'; same output as 65 = 'A' + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([65,193]).toString('ascii'))"`, + ); + expect(result.stdout).toBe("AA\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from(bytes).toString('utf16le') decodes little-endian pairs", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0x68,0x00,0x69,0x00]).toString('utf16le'))"`, + ); + expect(result.stdout).toBe("hi\n"); + expect(result.exitCode).toBe(0); + }); + + // ─── Decode side ───────────────────────────────────────────────── + + it("Buffer.from('aGVsbG8=', 'base64')", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('aGVsbG8=', 'base64').toString())"`, + ); + expect(result.stdout).toBe("hello\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('aGVsbG8', 'base64') ignores missing padding", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('aGVsbG8', 'base64').toString())"`, + ); + expect(result.stdout).toBe("hello\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from base64 strips whitespace in the middle", async () => { + const env = new Bash({ javascript: true }); + // space between 'aGVs' and 'bG8=' is skipped; result is "hello" + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('aGVs bG8=', 'base64').toString())"`, + ); + expect(result.stdout).toBe("hello\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('__4A', 'base64url')", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('__4A', 'base64url').toString('hex'))"`, + ); + expect(result.stdout).toBe("fffe00\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('ff00ab', 'hex')", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('ff00ab', 'hex').toString('hex'))"`, + ); + expect(result.stdout).toBe("ff00ab\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('ff00ZZab', 'hex') stops at first invalid character", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('ff00ZZab', 'hex').toString('hex'))"`, + ); + expect(result.stdout).toBe("ff00\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('abc', 'hex') truncates trailing odd nibble", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('abc', 'hex').toString('hex'))"`, + ); + expect(result.stdout).toBe("ab\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('Hello', 'latin1') round-trips", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('Hello', 'latin1').toString('latin1'))"`, + ); + expect(result.stdout).toBe("Hello\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from high-byte latin1 string encodes low byte of each char", async () => { + const env = new Bash({ javascript: true }); + // 'ÿþ' — JS unicode escapes for U+00FF, U+00FE + // latin1 encoding takes low byte of each: [0xFF, 0xFE] + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('\\u00ff\\u00fe', 'latin1').toString('hex'))"`, + ); + expect(result.stdout).toBe("fffe\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.from('hi', 'utf16le') round-trips", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('hi', 'utf16le').toString('utf16le'))"`, + ); + expect(result.stdout).toBe("hi\n"); + expect(result.exitCode).toBe(0); + }); + + // ─── Cross-API ─────────────────────────────────────────────────── + + it("Buffer.byteLength('aGVsbG8=', 'base64') === 5", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.byteLength('aGVsbG8=', 'base64'))"`, + ); + expect(result.stdout).toBe("5\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.byteLength('ff00', 'hex') === 2", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.byteLength('ff00', 'hex'))"`, + ); + expect(result.stdout).toBe("2\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.alloc.write with base64 encoding writes decoded bytes", async () => { + const env = new Bash({ javascript: true }); + // 'aGVs' decodes to "hel" (3 bytes); length=4 allows up to 4 bytes but only 3 decoded + const result = await env.exec( + `js-exec -c "var b = Buffer.alloc(8); var n = b.write('aGVs', 0, 4, 'base64'); console.log(n, b.slice(0, n).toString())"`, + ); + expect(result.stdout).toBe("3 hel\n"); + expect(result.exitCode).toBe(0); + }); + + // ─── Round-trip regression (assert encoded constant, not just decode(encode(x))) ── + + it("base64 round-trip via asserted constant for 'hello world'", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('hello world').toString('base64'))"`, + ); + expect(result.stdout).toBe("aGVsbG8gd29ybGQ=\n"); + expect(result.exitCode).toBe(0); + }); + + it("hex round-trip preserves binary bytes via asserted constant", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0xde,0xad,0xbe,0xef]).toString('hex'))"`, + ); + expect(result.stdout).toBe("deadbeef\n"); + expect(result.exitCode).toBe(0); + }); + + // ─── Unknown encoding ───────────────────────────────────────────── + + it("Buffer.from(str, 'utf-7') throws TypeError", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "try { Buffer.from('hi', 'utf-7'); } catch(e) { console.log(e instanceof TypeError, e.message) }"`, + ); + expect(result.stdout).toBe("true Unknown encoding: utf-7\n"); + expect(result.exitCode).toBe(0); + }); + + it("buf.toString('utf-7') throws TypeError", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "try { Buffer.from('hi').toString('utf-7'); } catch(e) { console.log(e instanceof TypeError, e.message) }"`, + ); + expect(result.stdout).toBe("true Unknown encoding: utf-7\n"); + expect(result.exitCode).toBe(0); + }); + + // ─── Aliases ───────────────────────────────────────────────────── + + it("'binary' is alias for 'latin1'", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from([0x41,0xff]).toString('binary') === Buffer.from([0x41,0xff]).toString('latin1'))"`, + ); + expect(result.stdout).toBe("true\n"); + expect(result.exitCode).toBe(0); + }); + + it("'ucs2', 'ucs-2', 'utf-16le' all alias 'utf16le'", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "var b = Buffer.from([0x68,0x00,0x69,0x00]); console.log(b.toString('ucs2'), b.toString('ucs-2'), b.toString('utf-16le'), b.toString('utf16le'))"`, + ); + expect(result.stdout).toBe("hi hi hi hi\n"); + expect(result.exitCode).toBe(0); + }); + + it("'utf-8' is alias for 'utf8'", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(Buffer.from('hello', 'utf-8').toString('utf-8'))"`, + ); + expect(result.stdout).toBe("hello\n"); + expect(result.exitCode).toBe(0); + }); + + // ─── Node-compat edge cases ────────────────────────────────────── + + it("Buffer.from(ArrayBuffer) shares the backing store", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "var ab = new ArrayBuffer(4); var u8 = new Uint8Array(ab); u8[0]=1; var b = Buffer.from(ab); u8[0]=99; console.log(b._data[0], b._data.buffer === ab)"`, + ); + expect(result.stdout).toBe("99 true\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.prototype.toString clamps negative end to 0", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "console.log(JSON.stringify(Buffer.from('abc').toString('utf8', 0, -1)))"`, + ); + expect(result.stdout).toBe('""\n'); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.prototype.write throws RangeError when length exceeds remaining", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "try { Buffer.alloc(2).write('abc', 0, 3); console.log('no-throw'); } catch (e) { console.log(e.name, e.message); }"`, + ); + expect(result.stdout).toBe( + 'RangeError The value of "length" is out of range. It must be >= 0 && <= 2. Received 3\n', + ); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.prototype.write clamps to remaining when length fits buf but not remaining", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "var b = Buffer.alloc(5); var n = b.write('abcde', 3, 5); console.log(n, b.toString('hex'))"`, + ); + expect(result.stdout).toBe("2 0000006162\n"); + expect(result.exitCode).toBe(0); + }); + + it("Buffer.prototype.write throws RangeError for negative length", async () => { + const env = new Bash({ javascript: true }); + const result = await env.exec( + `js-exec -c "try { Buffer.alloc(2).write('abc', 0, -1); console.log('no-throw'); } catch (e) { console.log(e.name); }"`, + ); + expect(result.stdout).toBe("RangeError\n"); + expect(result.exitCode).toBe(0); + }); +}); diff --git a/packages/just-bash/src/commands/js-exec/module-shims.ts b/packages/just-bash/src/commands/js-exec/module-shims.ts index 7ce4c381..f729ac92 100644 --- a/packages/just-bash/src/commands/js-exec/module-shims.ts +++ b/packages/just-bash/src/commands/js-exec/module-shims.ts @@ -287,6 +287,135 @@ function _utf8Decode(bytes) { return str; } // _utf8Encode/_utf8Decode are IIFE-local vars, available to all module shims +// IMPORTANT: round-trip tests (encode then decode) mask broken encodings +// because both sides become no-ops. Encoding tests MUST assert the encoded +// constant directly. +function _normEnc(enc) { + if (enc === undefined || enc === null) return 'utf8'; + var e = String(enc).toLowerCase(); + if (e === 'utf8' || e === 'utf-8') return 'utf8'; + if (e === 'utf16le' || e === 'utf-16le' || e === 'ucs2' || e === 'ucs-2') return 'utf16le'; + if (e === 'latin1' || e === 'binary') return 'latin1'; + if (e === 'ascii') return 'ascii'; + if (e === 'base64') return 'base64'; + if (e === 'base64url') return 'base64url'; + if (e === 'hex') return 'hex'; + return undefined; +} +function _badEnc(enc) { + throw new TypeError("Unknown encoding: " + enc); +} +var _HEX = '0123456789abcdef'; +function _hexEncode(bytes) { + var s = ''; + for (var i = 0; i < bytes.length; i++) { + var b = bytes[i] & 0xff; + s += _HEX.charAt(b >> 4) + _HEX.charAt(b & 0x0f); + } + return s; +} +function _hexDecode(str) { + var out = []; + for (var i = 0; i + 1 < str.length; i += 2) { + var hi = _hexVal(str.charCodeAt(i)); + var lo = _hexVal(str.charCodeAt(i + 1)); + if (hi < 0 || lo < 0) break; + out.push((hi << 4) | lo); + } + return out; +} +function _hexVal(c) { + if (c >= 48 && c <= 57) return c - 48; + if (c >= 97 && c <= 102) return c - 87; + if (c >= 65 && c <= 70) return c - 55; + return -1; +} +function _rawEncode(str) { + var out = new Array(str.length); + for (var i = 0; i < str.length; i++) out[i] = str.charCodeAt(i) & 0xff; + return out; +} +function _latin1Decode(bytes) { + var s = ''; + for (var i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i] & 0xff); + return s; +} +function _asciiDecode(bytes) { + var s = ''; + for (var i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i] & 0x7f); + return s; +} +function _utf16leEncode(str) { + var out = new Array(str.length * 2); + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + out[i * 2] = c & 0xff; + out[i * 2 + 1] = (c >> 8) & 0xff; + } + return out; +} +function _utf16leDecode(bytes) { + var s = ''; + var end = bytes.length & ~1; + for (var i = 0; i < end; i += 2) { + s += String.fromCharCode(bytes[i] | (bytes[i + 1] << 8)); + } + return s; +} +var _B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var _B64DEC = null; +function _b64DecTable() { + if (_B64DEC) return _B64DEC; + var t = new Array(256); + for (var i = 0; i < 256; i++) t[i] = -1; + for (var j = 0; j < 64; j++) t[_B64.charCodeAt(j)] = j; + t[45] = 62; + t[95] = 63; + _B64DEC = t; + return t; +} +function _b64Encode(bytes) { + var s = ''; + var i = 0, len = bytes.length; + while (i + 2 < len) { + var n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + s += _B64.charAt((n >> 18) & 63) + _B64.charAt((n >> 12) & 63) + + _B64.charAt((n >> 6) & 63) + _B64.charAt(n & 63); + i += 3; + } + var rem = len - i; + if (rem === 1) { + var n1 = bytes[i] << 16; + s += _B64.charAt((n1 >> 18) & 63) + _B64.charAt((n1 >> 12) & 63) + '=='; + } else if (rem === 2) { + var n2 = (bytes[i] << 16) | (bytes[i + 1] << 8); + s += _B64.charAt((n2 >> 18) & 63) + _B64.charAt((n2 >> 12) & 63) + + _B64.charAt((n2 >> 6) & 63) + '='; + } + return s; +} +function _b64UrlEncode(bytes) { + var s = _b64Encode(bytes); + return s.replace(/=+$/, '').replace(/\\+/g, '-').replace(/\\//g, '_'); +} +function _b64Decode(str) { + var t = _b64DecTable(); + var out = []; + var buf = 0, bits = 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c === 61) break; + var v = c < 256 ? t[c] : -1; + if (v < 0) continue; + buf = (buf << 6) | v; + bits += 6; + if (bits >= 8) { + bits -= 8; + out.push((buf >> bits) & 0xff); + } + } + return out; +} function Buffer(arg) { if (typeof arg === 'number') { @@ -304,12 +433,27 @@ function Buffer(arg) { } Buffer.from = function(data, encoding) { if (typeof data === 'string') { - return new Buffer(_utf8Encode(data)); + var enc = _normEnc(encoding); + if (enc === undefined) _badEnc(encoding); + if (enc === 'utf8') return new Buffer(_utf8Encode(data)); + if (enc === 'utf16le') return new Buffer(_utf16leEncode(data)); + if (enc === 'latin1') return new Buffer(_rawEncode(data)); + if (enc === 'ascii') return new Buffer(_rawEncode(data)); + if (enc === 'hex') return new Buffer(_hexDecode(data)); + if (enc === 'base64' || enc === 'base64url') return new Buffer(_b64Decode(data)); } - if (data instanceof ArrayBuffer) return new Buffer(data); - if (data instanceof Uint8Array) return new Buffer(data); - if (Array.isArray(data)) return new Buffer(data); - if (data && data._data) return new Buffer(data._data.slice()); + if (data instanceof ArrayBuffer) { + var off = typeof encoding === 'number' ? encoding : 0; + var len = arguments.length > 2 ? arguments[2] : undefined; + var view = len === undefined ? new Uint8Array(data, off) : new Uint8Array(data, off, len); + var buf = Object.create(Buffer.prototype); + buf._data = view; + buf.length = view.length; + return buf; + } + if (data instanceof Uint8Array) return new Buffer(data); + if (Array.isArray(data)) return new Buffer(data); + if (data && data._data) return new Buffer(data._data.slice()); return new Buffer(0); }; Buffer.alloc = function(size, fill) { @@ -335,11 +479,42 @@ Buffer.concat = function(list, totalLength) { } return new Buffer(result); }; -Buffer.byteLength = function(str) { - return _utf8Encode(str).length; +Buffer.byteLength = function(value, encoding) { + if (typeof value !== 'string') { + if (value && value._data) return value._data.length; + if (value instanceof Uint8Array || value instanceof ArrayBuffer) { + return value.byteLength; + } + throw new TypeError('The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received type ' + typeof value + ' (' + value + ')'); + } + var enc = _normEnc(encoding); + if (enc === undefined) _badEnc(encoding); + if (enc === 'utf8') return _utf8Encode(value).length; + if (enc === 'utf16le') return value.length * 2; + if (enc === 'latin1' || enc === 'ascii') return value.length; + if (enc === 'hex') return (value.length / 2) | 0; + if (enc === 'base64' || enc === 'base64url') { + var clean = value.replace(/[^A-Za-z0-9+/=_-]/g, '').replace(/=+$/, ''); + return Math.floor(clean.length * 3 / 4); + } }; -Buffer.prototype.toString = function(encoding) { - return _utf8Decode(this._data); +Buffer.prototype.toString = function(encoding, start, end) { + var bytes = this._data; + if (start !== undefined || end !== undefined) { + var len = bytes.length; + var s = start === undefined ? 0 : (start < 0 ? 0 : (start > len ? len : start | 0)); + var e = end === undefined ? len : (end < 0 ? 0 : (end > len ? len : end | 0)); + bytes = e <= s ? bytes.subarray(0, 0) : bytes.subarray(s, e); + } + var enc = _normEnc(encoding); + if (enc === undefined) _badEnc(encoding); + if (enc === 'utf8') return _utf8Decode(bytes); + if (enc === 'utf16le') return _utf16leDecode(bytes); + if (enc === 'latin1') return _latin1Decode(bytes); + if (enc === 'ascii') return _asciiDecode(bytes); + if (enc === 'hex') return _hexEncode(bytes); + if (enc === 'base64') return _b64Encode(bytes); + if (enc === 'base64url') return _b64UrlEncode(bytes); }; Buffer.prototype.toJSON = function() { return { type: 'Buffer', data: Array.from(this._data) }; @@ -355,11 +530,32 @@ Buffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) { target._data.set(sub, targetStart); return sub.length; }; -Buffer.prototype.write = function(str, offset) { - var bytes = _utf8Encode(str); - offset = offset || 0; - this._data.set(bytes, offset); - return bytes.length; +Buffer.prototype.write = function(str, offset, length, encoding) { + if (typeof offset === 'string') { encoding = offset; offset = 0; length = undefined; } + else if (typeof length === 'string') { encoding = length; length = undefined; } + offset = offset | 0; + if (offset < 0 || offset > this._data.length) { + throw new RangeError('The value of "offset" is out of range. It must be >= 0 && <= ' + this._data.length + '. Received ' + offset); + } + var enc = _normEnc(encoding); + if (enc === undefined) _badEnc(encoding); + var bytes; + if (enc === 'utf8') bytes = _utf8Encode(str); + else if (enc === 'utf16le') bytes = _utf16leEncode(str); + else if (enc === 'latin1') bytes = _rawEncode(str); + else if (enc === 'ascii') bytes = _rawEncode(str); + else if (enc === 'hex') bytes = _hexDecode(str); + else bytes = _b64Decode(str); + var max = this._data.length - offset; + if (length !== undefined) { + length = length | 0; + if (length < 0 || length > this._data.length) { + throw new RangeError('The value of "length" is out of range. It must be >= 0 && <= ' + this._data.length + '. Received ' + length); + } + } + var write = Math.min(length === undefined ? max : length, bytes.length, max); + for (var i = 0; i < write; i++) this._data[offset + i] = bytes[i]; + return write; }; Buffer.prototype.fill = function(val, offset, end) { this._data.fill(typeof val === 'number' ? val : 0, offset, end);