package/index.js0000644000175000017500000001562111645421706014351 0ustar substacksubstackmodule.exports = Buffers; function Buffers (bufs) { if (!(this instanceof Buffers)) return new Buffers(bufs); this.buffers = bufs || []; this.length = this.buffers.reduce(function (size, buf) { return size + buf.length }, 0); } Buffers.prototype.push = function () { for (var i = 0; i < arguments.length; i++) { if (!Buffer.isBuffer(arguments[i])) { throw new TypeError('Tried to push a non-buffer'); } } for (var i = 0; i < arguments.length; i++) { var buf = arguments[i]; this.buffers.push(buf); this.length += buf.length; } return this.length; }; Buffers.prototype.unshift = function () { for (var i = 0; i < arguments.length; i++) { if (!Buffer.isBuffer(arguments[i])) { throw new TypeError('Tried to unshift a non-buffer'); } } for (var i = 0; i < arguments.length; i++) { var buf = arguments[i]; this.buffers.unshift(buf); this.length += buf.length; } return this.length; }; Buffers.prototype.copy = function (dst, dStart, start, end) { return this.slice(start, end).copy(dst, dStart, 0, end - start); }; Buffers.prototype.splice = function (i, howMany) { var buffers = this.buffers; var index = i >= 0 ? i : this.length - i; var reps = [].slice.call(arguments, 2); if (howMany === undefined) { howMany = this.length - index; } else if (howMany > this.length - index) { howMany = this.length - index; } for (var i = 0; i < reps.length; i++) { this.length += reps[i].length; } var removed = new Buffers(); var bytes = 0; var startBytes = 0; for ( var ii = 0; ii < buffers.length && startBytes + buffers[ii].length < index; ii ++ ) { startBytes += buffers[ii].length } if (index - startBytes > 0) { var start = index - startBytes; if (start + howMany < buffers[ii].length) { removed.push(buffers[ii].slice(start, start + howMany)); var orig = buffers[ii]; //var buf = new Buffer(orig.length - howMany); var buf0 = new Buffer(start); for (var i = 0; i < start; i++) { buf0[i] = orig[i]; } var buf1 = new Buffer(orig.length - start - howMany); for (var i = start + howMany; i < orig.length; i++) { buf1[ i - howMany - start ] = orig[i] } if (reps.length > 0) { var reps_ = reps.slice(); reps_.unshift(buf0); reps_.push(buf1); buffers.splice.apply(buffers, [ ii, 1 ].concat(reps_)); ii += reps_.length; reps = []; } else { buffers.splice(ii, 1, buf0, buf1); //buffers[ii] = buf; ii += 2; } } else { removed.push(buffers[ii].slice(start)); buffers[ii] = buffers[ii].slice(0, start); ii ++; } } if (reps.length > 0) { buffers.splice.apply(buffers, [ ii, 0 ].concat(reps)); ii += reps.length; } while (removed.length < howMany) { var buf = buffers[ii]; var len = buf.length; var take = Math.min(len, howMany - removed.length); if (take === len) { removed.push(buf); buffers.splice(ii, 1); } else { removed.push(buf.slice(0, take)); buffers[ii] = buffers[ii].slice(take); } } this.length -= removed.length; return removed; }; Buffers.prototype.slice = function (i, j) { var buffers = this.buffers; if (j === undefined) j = this.length; if (i === undefined) i = 0; if (j > this.length) j = this.length; var startBytes = 0; for ( var si = 0; si < buffers.length && startBytes + buffers[si].length <= i; si ++ ) { startBytes += buffers[si].length } var target = new Buffer(j - i); var ti = 0; for (var ii = si; ti < j - i && ii < buffers.length; ii++) { var len = buffers[ii].length; var start = ti === 0 ? i - startBytes : 0; var end = ti + len >= j - i ? Math.min(start + (j - i) - ti, len) : len ; buffers[ii].copy(target, ti, start, end); ti += end - start; } return target; }; Buffers.prototype.pos = function (i) { if (i < 0 || i >= this.length) throw new Error('oob'); var l = i, bi = 0, bu = null; for (;;) { bu = this.buffers[bi]; if (l < bu.length) { return {buf: bi, offset: l}; } else { l -= bu.length; } bi++; } }; Buffers.prototype.get = function get (i) { var pos = this.pos(i); return this.buffers[pos.buf].get(pos.offset); }; Buffers.prototype.set = function set (i, b) { var pos = this.pos(i); return this.buffers[pos.buf].set(pos.offset, b); }; Buffers.prototype.indexOf = function (needle, offset) { if ("string" === typeof needle) { needle = new Buffer(needle); } else if (needle instanceof Buffer) { // already a buffer } else { throw new Error('Invalid type for a search string'); } if (!needle.length) { return 0; } if (!this.length) { return -1; } var i = 0, j = 0, match = 0, mstart, pos = 0; // start search from a particular point in the virtual buffer if (offset) { var p = this.pos(offset); i = p.buf; j = p.offset; pos = offset; } // for each character in virtual buffer for (;;) { while (j >= this.buffers[i].length) { j = 0; i++; if (i >= this.buffers.length) { // search string not found return -1; } } var char = this.buffers[i][j]; if (char == needle[match]) { // keep track where match started if (match == 0) { mstart = { i: i, j: j, pos: pos }; } match++; if (match == needle.length) { // full match return mstart.pos; } } else if (match != 0) { // a partial match ended, go back to match starting position // this will continue the search at the next character i = mstart.i; j = mstart.j; pos = mstart.pos; match = 0; } j++; pos++; } }; Buffers.prototype.toBuffer = function() { return this.slice(); } Buffers.prototype.toString = function(encoding, start, end) { return this.slice(start, end).toString(encoding); } package/package.json0000644000175000017500000000065011645421706015166 0ustar substacksubstack{ "name" : "buffers", "description" : "Treat a collection of Buffers as a single contiguous partially mutable Buffer.", "version" : "0.1.1", "repository" : "http://github.com/substack/node-buffers.git", "author" : "James Halliday (http://substack.net)", "main" : "./index", "scripts" : { "test" : "expresso" }, "engines" : { "node" : ">=0.2.0" } } package/README.markdown0000644000175000017500000000530511645421706015403 0ustar substacksubstackbuffers ======= Treat a collection of Buffers as a single contiguous partially mutable Buffer. Where possible, operations execute without creating a new Buffer and copying everything over. This is a cleaner more Buffery rehash of [bufferlist](http://github.com/substack/node-bufferlist). example ======= slice ----- var Buffers = require('buffers'); var bufs = Buffers(); bufs.push(new Buffer([1,2,3])); bufs.push(new Buffer([4,5,6,7])); bufs.push(new Buffer([8,9,10])); console.dir(bufs.slice(2,8)) output: $ node examples/slice.js splice ------ var Buffers = require('buffers'); var bufs = Buffers([ new Buffer([1,2,3]), new Buffer([4,5,6,7]), new Buffer([8,9,10]), ]); var removed = bufs.splice(2, 4); console.dir({ removed : removed.slice(), bufs : bufs.slice(), }); output: $ node examples/splice.js { removed: , bufs: } methods ======= Buffers(buffers) ---------------- Create a Buffers with an array of `Buffer`s if specified, else `[]`. .push(buf1, buf2...) -------------------- Push buffers onto the end. Just like `Array.prototype.push`. .unshift(buf1, buf2...) ----------------------- Unshift buffers onto the head. Just like `Array.prototype.unshift`. .slice(i, j) ------------ Slice a range out of the buffer collection as if it were contiguous. Works just like the `Array.prototype.slice` version. .splice(i, howMany, replacements) --------------------------------- Splice the buffer collection as if it were contiguous. Works just like `Array.prototype.splice`, even the replacement part! .copy(dst, dstStart, start, end) -------------------------------- Copy the buffer collection as if it were contiguous to the `dst` Buffer with the specified bounds. Works just like `Buffer.prototype.copy`. .get(i) ------- Get a single element at index `i`. .set(i, x) ---------- Set a single element's value at index `i`. .indexOf(needle, offset) ---------- Find a string or buffer `needle` inside the buffer collection. Returns the position of the search string or -1 if the search string was not found. Provide an `offset` to skip that number of characters at the beginning of the search. This can be used to find additional matches. This function will return the correct result even if the search string is spread out over multiple internal buffers. .toBuffer() ----------- Convert the buffer collection to a single buffer, equivalent with `.slice(0, buffers.length)`; .toString(encoding, start, end) ----------- Decodes and returns a string from the buffer collection. Works just like `Buffer.prototype.toString` package/examples/slice.js0000644000175000017500000000034011645421706016147 0ustar substacksubstackvar Buffers = require('buffers'); var bufs = Buffers(); bufs.push(new Buffer([1,2,3])); bufs.push(new Buffer([4,5,6,7])); bufs.push(new Buffer([8,9,10])); console.dir(bufs.slice(2,8)) // Output: package/examples/splice.js0000644000175000017500000000055611645421706016340 0ustar substacksubstackvar Buffers = require('buffers'); var bufs = Buffers([ new Buffer([1,2,3]), new Buffer([4,5,6,7]), new Buffer([8,9,10]), ]); var removed = bufs.splice(2, 4, new Buffer('ab'), new Buffer('cd')); console.dir({ removed : removed.slice(), bufs : bufs.slice(), }); /* Output: { removed: , bufs: } */ package/test/buffers.js0000644000175000017500000001462411645421706015657 0ustar substacksubstackvar assert = require('assert'); var Buffers = require('../'); function create (xs, split) { var bufs = Buffers(); var offset = 0; split.forEach(function (i) { bufs.push(new Buffer(xs.slice(offset, offset + i))); offset += i; }); return bufs; } exports.slice = function () { var xs = [0,1,2,3,4,5,6,7,8,9]; var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ]; splits.forEach(function (split) { var bufs = create(xs, split); assert.eql(new Buffer(xs), bufs.slice(), '[' + xs.join(',') + ']' + ' != ' + '[' + [].join.call(bufs.slice(), ',') + ']' ); for (var i = 0; i < xs.length; i++) { for (var j = i; j < xs.length; j++) { var a = bufs.slice(i,j); var b = new Buffer(xs.slice(i,j)); assert.eql(a, b, '[' + [].join.call(a, ',') + ']' + ' != ' + '[' + [].join.call(b, ',') + ']' ); } } }); }; exports.splice = function () { var xs = [0,1,2,3,4,5,6,7,8,9]; var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ]; splits.forEach(function (split) { for (var i = 0; i < xs.length; i++) { for (var j = i; j < xs.length; j++) { var bufs = create(xs, split); var xs_ = xs.slice(); var a_ = bufs.splice(i,j); var a = [].slice.call(a_.slice()); var b = xs_.splice(i,j); assert.eql(a, b, '[' + a.join(',') + ']' + ' != ' + '[' + b.join(',') + ']' ); assert.eql(bufs.slice(), new Buffer(xs_), '[' + [].join.call(bufs.slice(), ',') + ']' + ' != ' + '[' + [].join.call(xs_, ',') + ']' ); } } }); }; exports.spliceRep = function () { var xs = [0,1,2,3,4,5,6,7,8,9]; var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ]; var reps = [ [], [1], [5,6], [3,1,3,3,7], [9,8,7,6,5,4,3,2,1,2,3,4,5] ]; splits.forEach(function (split) { reps.forEach(function (rep) { for (var i = 0; i < xs.length; i++) { for (var j = i; j < xs.length; j++) { var bufs = create(xs, split); var xs_ = xs.slice(); var a_ = bufs.splice.apply( bufs, [ i, j ].concat(new Buffer(rep)) ); var a = [].slice.call(a_.slice()); var b = xs_.splice.apply(xs_, [ i, j ].concat(rep)); assert.eql(a, b, '[' + a.join(',') + ']' + ' != ' + '[' + b.join(',') + ']' ); assert.eql(bufs.slice(), new Buffer(xs_), '[' + [].join.call(bufs.slice(), ',') + ']' + ' != ' + '[' + [].join.call(xs_, ',') + ']' ); } } }); }); }; exports.copy = function () { var xs = [0,1,2,3,4,5,6,7,8,9]; var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ]; splits.forEach(function (split) { var bufs = create(xs, split); var buf = new Buffer(xs); for (var i = 0; i < xs.length; i++) { for (var j = i; j < xs.length; j++) { var t0 = new Buffer(j - i); var t1 = new Buffer(j - i); assert.eql( bufs.copy(t0, 0, i, j), buf.copy(t1, 0, i, j) ); assert.eql( [].slice.call(t0), [].slice.call(t1) ); } } }); }; exports.push = function () { var bufs = Buffers(); bufs.push(new Buffer([0])); bufs.push(new Buffer([1,2,3])); bufs.push(new Buffer([4,5])); bufs.push(new Buffer([6,7,8,9])); assert.eql( [].slice.call(bufs.slice()), [0,1,2,3,4,5,6,7,8,9] ); assert.throws(function () { bufs.push(new Buffer([11,12]), 'moo'); }); assert.eql(bufs.buffers.length, 4); }; exports.unshift = function () { var bufs = Buffers(); bufs.unshift(new Buffer([6,7,8,9])); bufs.unshift(new Buffer([4,5])); bufs.unshift(new Buffer([1,2,3])); bufs.unshift(new Buffer([0])); assert.eql( [].slice.call(bufs.slice()), [0,1,2,3,4,5,6,7,8,9] ); assert.throws(function () { bufs.unshift(new Buffer([-2,-1]), 'moo'); }); assert.eql(bufs.buffers.length, 4); }; exports.get = function () { var bufs = Buffers(); bufs.unshift(new Buffer([6,7,8,9])); bufs.unshift(new Buffer([4,5])); bufs.unshift(new Buffer([1,2,3])); bufs.unshift(new Buffer([0])); assert.eql( bufs.get(0), 0 ); assert.eql( bufs.get(1), 1 ); assert.eql( bufs.get(2), 2 ); assert.eql( bufs.get(3), 3 ); assert.eql( bufs.get(4), 4 ); assert.eql( bufs.get(5), 5 ); assert.eql( bufs.get(6), 6 ); assert.eql( bufs.get(7), 7 ); assert.eql( bufs.get(8), 8 ); assert.eql( bufs.get(9), 9 ); }; exports.set = function () { var bufs = Buffers(); bufs.push(new Buffer("Hel")); bufs.push(new Buffer("lo")); bufs.push(new Buffer("!")); bufs.set(0, 'h'.charCodeAt(0) ); bufs.set(3, 'L'.charCodeAt(0) ); bufs.set(5, '.'.charCodeAt(0) ); assert.eql( bufs.slice(0).toString(), 'helLo.' ); }; exports.indexOf = function () { var bufs = Buffers(); bufs.push(new Buffer("Hel")); bufs.push(new Buffer("lo,")); bufs.push(new Buffer(" how are ")); bufs.push(new Buffer("you")); bufs.push(new Buffer("?")); assert.eql( bufs.indexOf("Hello"), 0 ); assert.eql( bufs.indexOf("Hello", 1), -1 ); assert.eql( bufs.indexOf("ello"), 1 ); assert.eql( bufs.indexOf("ello", 1), 1 ); assert.eql( bufs.indexOf("ello", 2), -1 ); assert.eql( bufs.indexOf("e"), 1 ); assert.eql( bufs.indexOf("e", 2), 13 ); assert.eql( bufs.indexOf(new Buffer([0x65]), 2), 13 ); };