From 45d8d9f8262983d7d6434f4500b4e88b63052cd5 Mon Sep 17 00:00:00 2001 From: Vladimir Kurchatkin Date: Tue, 23 Dec 2014 21:03:54 +0300 Subject: [PATCH] buffer: implement `iterable` interface 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: https://github.com/iojs/io.js/pull/525 Reviewed-By: Ben Noordhuis --- benchmark/buffers/buffer-iterate.js | 63 ++++++++++++++++++++++++++++ doc/api/buffer.markdown | 29 +++++++++++++ lib/buffer.js | 77 +++++++++++++++++++++++++++++++++++ test/parallel/test-buffer-iterator.js | 61 +++++++++++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 benchmark/buffers/buffer-iterate.js create mode 100644 test/parallel/test-buffer-iterator.js diff --git a/benchmark/buffers/buffer-iterate.js b/benchmark/buffers/buffer-iterate.js new file mode 100644 index 0000000..77a0b59 --- /dev/null +++ b/benchmark/buffers/buffer-iterate.js @@ -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); +} diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index 2269345..f683618 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -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 @@ -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`. diff --git a/lib/buffer.js b/lib/buffer.js index dbc5d5f..4c87726 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -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; diff --git a/test/parallel/test-buffer-iterator.js b/test/parallel/test-buffer-iterator.js new file mode 100644 index 0000000..ab02934 --- /dev/null +++ b/test/parallel/test-buffer-iterator.js @@ -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] +]); -- 2.7.4