Skip to content

Commit

Permalink
buffer: implement iterable interface
Browse files Browse the repository at this point in the history
This makes possible to use `for..of` loop with
buffers. Also related `keys`, `values` and `entries`
methods are added for feature parity with `Uint8Array`.

PR-URL: #525
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
vkurchatkin committed Jan 28, 2015
1 parent 3cbb5cd commit 45d8d9f
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
63 changes: 63 additions & 0 deletions benchmark/buffers/buffer-iterate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
var SlowBuffer = require('buffer').SlowBuffer;
var common = require('../common.js');
var assert = require('assert');

var bench = common.createBenchmark(main, {
size: [16, 512, 1024, 4096, 16386],
type: ['fast', 'slow'],
method: ['for', 'forOf', 'iterator'],
n: [1e3]
});

var methods = {
'for': benchFor,
'forOf': benchForOf,
'iterator': benchIterator
};

function main(conf) {
var len = +conf.size;
var clazz = conf.type === 'fast' ? Buffer : SlowBuffer;
var buffer = new clazz(len);
buffer.fill(0);

methods[conf.method](buffer, conf.n);
}


function benchFor(buffer, n) {
bench.start();

for (var k = 0; k < n; k++)
for (var i = 0; i < buffer.length; i++)
assert(buffer[i] === 0);

bench.end(n);
}

function benchForOf(buffer, n) {
bench.start();

for (var k = 0; k < n; k++)
for (var b of buffer)
assert(b === 0);

bench.end(n);
}

function benchIterator(buffer, n) {
bench.start();

for (var k = 0; k < n; k++) {
var iter = buffer[Symbol.iterator]();
var cur = iter.next();

while (!cur.done) {
assert(cur.value === 0);
cur = iter.next();
}

}

bench.end(n);
}
29 changes: 29 additions & 0 deletions doc/api/buffer.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,19 @@ buffer.
var b = new Buffer(50);
b.fill("h");

### buffer.values()

Creates iterator for buffer values (bytes). This function is called automatically
when `buffer` is used in a `for..of` statement.

### buffer.keys()

Creates iterator for buffer keys (indices).

### buffer.entries()

Creates iterator for `[index, byte]` arrays.

## buffer.INSPECT_MAX_BYTES

* Number, Default: 50
Expand All @@ -807,6 +820,22 @@ be overridden by user modules.
Note that this is a property on the buffer module returned by
`require('buffer')`, not on the Buffer global, or a buffer instance.

## ES6 iteration

Buffers can be iterated over using `for..of` syntax:

var buf = new Buffer([1, 2, 3]);

for (var b of buf)
console.log(b)

// 1
// 2
// 3

Additionally, `buffer.values()`, `buffer.keys()` and `buffer.entries()`
methods can be used to create iterators.

## Class: SlowBuffer

Returns an un-pooled `Buffer`.
Expand Down
77 changes: 77 additions & 0 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -930,3 +930,80 @@ Buffer.prototype.writeDoubleBE = function writeDoubleBE(val, offset, noAssert) {
internal.writeDoubleBE(this, val, offset);
return offset + 8;
};

// ES6 iterator

var ITERATOR_KIND_KEYS = 1;
var ITERATOR_KIND_ENTRIES = 3;

function BufferIteratorResult(value, done) {
this.value = value;
this.done = done;
}

var resultCache = new Array(256);

for (var i = 0; i < 256; i++)
resultCache[i] = Object.freeze(new BufferIteratorResult(i, false));

var finalResult = Object.freeze(new BufferIteratorResult(undefined, true));

function BufferIterator(buffer, kind) {
this._buffer = buffer;
this._kind = kind;
this._index = 0;
}

BufferIterator.prototype.next = function() {
var buffer = this._buffer;
var kind = this._kind;
var index = this._index;

if (index >= buffer.length)
return finalResult;

this._index++;

if (kind === ITERATOR_KIND_ENTRIES)
return new BufferIteratorResult([index, buffer[index]], false);

return new BufferIteratorResult(index, false);
};

function BufferValueIterator(buffer) {
BufferIterator.call(this, buffer, null);
}

BufferValueIterator.prototype.next = function() {
var buffer = this._buffer;
var index = this._index;

if (index >= buffer.length)
return finalResult;

this._index++;

return resultCache[buffer[index]];
};


BufferIterator.prototype[Symbol.iterator] = function() {
return this;
};

BufferValueIterator.prototype[Symbol.iterator] =
BufferIterator.prototype[Symbol.iterator];

Buffer.prototype.keys = function() {
return new BufferIterator(this, ITERATOR_KIND_KEYS);
};

Buffer.prototype.entries = function() {
return new BufferIterator(this, ITERATOR_KIND_ENTRIES);
};

Buffer.prototype.values = function() {
return new BufferValueIterator(this);
};

Buffer.prototype[Symbol.iterator] = Buffer.prototype.values;
61 changes: 61 additions & 0 deletions test/parallel/test-buffer-iterator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
var common = require('../common');
var assert = require('assert');

var buffer = new Buffer([1, 2, 3, 4, 5]);
var arr;
var b;

// buffers should be iterable

arr = [];

for (b of buffer)
arr.push(b);

assert.deepEqual(arr, [1, 2, 3, 4, 5]);


// buffer iterators should be iterable

arr = [];

for (b of buffer[Symbol.iterator]())
arr.push(b);

assert.deepEqual(arr, [1, 2, 3, 4, 5]);


// buffer#values() should return iterator for values

arr = [];

for (b of buffer.values())
arr.push(b);

assert.deepEqual(arr, [1, 2, 3, 4, 5]);


// buffer#keys() should return iterator for keys

arr = [];

for (b of buffer.keys())
arr.push(b);

assert.deepEqual(arr, [0, 1, 2, 3, 4]);


// buffer#entries() should return iterator for entries

arr = [];

for (var b of buffer.entries())
arr.push(b);

assert.deepEqual(arr, [
[0, 1],
[1, 2],
[2, 3],
[3, 4],
[4, 5]
]);

0 comments on commit 45d8d9f

Please sign in to comment.