-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
buffer: add swap16() and swap32() methods #5724
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
'use strict'; | ||
|
||
const common = require('../common.js'); | ||
|
||
const bench = common.createBenchmark(main, { | ||
method: ['swap16', 'swap32', 'htons', 'htonl'], | ||
len: [4, 64, 512, 768, 1024, 1536, 2056, 4096, 8192], | ||
n: [1e6] | ||
}); | ||
|
||
// The htons and htonl methods below are used to benchmark the | ||
// performance difference between doing the byteswap in pure | ||
// javascript regardless of Buffer size as opposed to dropping | ||
// down to the native layer for larger Buffer sizes. | ||
|
||
Buffer.prototype.htons = function htons() { | ||
if (this.length % 2 !== 0) | ||
throw new RangeError(); | ||
for (var i = 0, n = 0; i < this.length; i += 2) { | ||
n = this[i]; | ||
this[i] = this[i + 1]; | ||
this[i + 1] = n; | ||
} | ||
return this; | ||
}; | ||
|
||
Buffer.prototype.htonl = function htonl() { | ||
if (this.length % 2 !== 0) | ||
throw new RangeError(); | ||
for (var i = 0, n = 0; i < this.length; i += 4) { | ||
n = this[i]; | ||
this[i] = this[i + 3]; | ||
this[i + 3] = n; | ||
n = this[i + 1]; | ||
this[i + 1] = this[i + 2]; | ||
this[i + 2] = n; | ||
} | ||
return this; | ||
}; | ||
|
||
function createBuffer(len) { | ||
const buf = Buffer.allocUnsafe(len); | ||
for (var i = 1; i <= len; i++) | ||
buf[i - 1] = i; | ||
return buf; | ||
} | ||
|
||
function bufferSwap(n, buf, method) { | ||
for (var i = 1; i <= n; i++) | ||
buf[method](); | ||
} | ||
|
||
function main(conf) { | ||
const method = conf.method; | ||
const len = conf.len | 0; | ||
const n = conf.n | 0; | ||
const buf = createBuffer(len); | ||
bench.start(); | ||
bufferSwap(n, buf, method); | ||
bench.end(n); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,48 @@ var poolSize, poolOffset, allocPool; | |
|
||
|
||
binding.setupBufferJS(Buffer.prototype, bindingObj); | ||
|
||
const swap16n = binding.swap16; | ||
const swap32n = binding.swap32; | ||
|
||
function swap(b, n, m) { | ||
const i = b[n]; | ||
b[n] = b[m]; | ||
b[m] = i; | ||
} | ||
|
||
Buffer.prototype.swap16 = function swap16() { | ||
// For Buffer.length < 512, it's generally faster to | ||
// do the swap in javascript. For larger buffers, | ||
// dropping down to the native code is faster. | ||
const len = this.length; | ||
if (len % 2 !== 0) | ||
throw new RangeError('Buffer length must be a multiple of 16-bits'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incredible nitpicking, but nonetheless I would opt for one of these:
or:
I hope you understand the nuance difference I'm getting at. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will change to |
||
if (len < 512) { | ||
for (var i = 0; i < len; i += 2) | ||
swap(this, i, i + 1); | ||
return this; | ||
} | ||
return swap16n.apply(this); | ||
}; | ||
|
||
Buffer.prototype.swap32 = function swap32() { | ||
// For Buffer.length < 1024, it's generally faster to | ||
// do the swap in javascript. For larger buffers, | ||
// dropping down to the native code is faster. | ||
const len = this.length; | ||
if (len % 4 !== 0) | ||
throw new RangeError('Buffer length must be a multiple of 32-bits'); | ||
if (len < 1024) { | ||
for (var i = 0; i < len; i += 4) { | ||
swap(this, i, i + 3); | ||
swap(this, i + 1, i + 2); | ||
} | ||
return this; | ||
} | ||
return swap32n.apply(this); | ||
}; | ||
|
||
const flags = bindingObj.flags; | ||
const kNoZeroFill = 0; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
'use strict'; | ||
|
||
require('../common'); | ||
const assert = require('assert'); | ||
|
||
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4]); | ||
|
||
assert.strictEqual(buf, buf.swap16()); | ||
assert.deepStrictEqual(buf, Buffer.from([0x2, 0x1, 0x4, 0x3])); | ||
|
||
assert.strictEqual(buf, buf.swap32()); | ||
assert.deepStrictEqual(buf, Buffer.from([0x3, 0x4, 0x1, 0x2])); | ||
|
||
const buf_array = []; | ||
for (var i = 1; i < 33; i++) | ||
buf_array.push(i); | ||
const buf2 = Buffer.from(buf_array); | ||
buf2.swap32(); | ||
assert.deepStrictEqual(buf2, | ||
Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x0c, | ||
0x0b, 0x0a, 0x09, 0x10, 0x0f, 0x0e, 0x0d, 0x14, 0x13, | ||
0x12, 0x11, 0x18, 0x17, 0x16, 0x15, 0x1c, 0x1b, 0x1a, | ||
0x19, 0x20, 0x1f, 0x1e, 0x1d])); | ||
buf2.swap16(); | ||
assert.deepStrictEqual(buf2, | ||
Buffer.from([0x03, 0x04, 0x01, 0x02, 0x07, 0x08, 0x05, 0x06, 0x0b, | ||
0x0c, 0x09, 0x0a, 0x0f, 0x10, 0x0d, 0x0e, 0x13, 0x14, | ||
0x11, 0x12, 0x17, 0x18, 0x15, 0x16, 0x1b, 0x1c, 0x19, | ||
0x1a, 0x1f, 0x20, 0x1d, 0x1e])); | ||
|
||
const buf3 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); | ||
buf3.slice(1, 5).swap32(); | ||
assert.deepStrictEqual(buf3, Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); | ||
|
||
buf3.slice(1, 5).swap16(); | ||
assert.deepStrictEqual(buf3, Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); | ||
|
||
// Force use of native code (Buffer size above threshold limit for js impl) | ||
const buf4 = Buffer.allocUnsafe(1024).fill([0x1, 0x2, 0x3, 0x4]); | ||
const buf5 = Buffer.allocUnsafe(1024).fill([0x2, 0x1, 0x4, 0x3]); | ||
const buf6 = Buffer.allocUnsafe(1024) | ||
.fill([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); | ||
const buf7 = Buffer.allocUnsafe(1024) | ||
.fill([0x4, 0x3, 0x2, 0x1, 0x8, 0x7, 0x6, 0x5]); | ||
|
||
buf4.swap16(); | ||
assert.deepStrictEqual(buf4, buf5); | ||
|
||
buf6.swap32(); | ||
assert.deepStrictEqual(buf6, buf7); | ||
|
||
|
||
const re16 = /Buffer length must be a multiple of 16-bits/; | ||
const re32 = /Buffer length must be a multiple of 32-bits/; | ||
|
||
assert.throws(() => Buffer.from(buf3).swap16(), re16); | ||
assert.throws(() => Buffer.alloc(1025).swap16(), re16); | ||
assert.throws(() => Buffer.from(buf3).swap32(), re32); | ||
assert.throws(() => buf3.slice(1, 3).swap32(), re32); | ||
assert.throws(() => Buffer.alloc(1025).swap32(), re32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is kinda confusing. htons is for converting between byte orders over the wire
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the term is used only in the benchmark and was selected because it's common for the operation. It's not actually used anywhere in the regular code or API.