-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
Conversation
I think Just how common are these anyway? |
I can live with The |
Methods renamed to swap16/swap32 |
@@ -1094,6 +1130,8 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { | |||
env->SetMethod(proto, "hexWrite", HexWrite); | |||
env->SetMethod(proto, "ucs2Write", Ucs2Write); | |||
env->SetMethod(proto, "utf8Write", Utf8Write); | |||
env->SetMethod(proto, "swap16", SwapShort); | |||
env->SetMethod(proto, "swap32", SwapLong); |
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.
While case differs, generally the names are the same between c++ and js. Would Swap{16,32}
work?
@trevnorris ... ok, updated! PTAL |
Does this belong in core? |
|
||
if (ts_obj_length % sizeof(uint16_t) != 0) | ||
return env->ThrowRangeError( | ||
"Buffer length must be a multiple of 16-bits"); |
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.
nit: wrap multi-line statements in conditionals with {}
, and double indent with multi-line statements. another thing lint should be catching.
@Fishrock123 Asked myself the same thing. I believe it may just fit within the parameters of what functionality belongs in core. For example: // Read in utf16be file
const file = fs.readFileSync(path).swap16().toString('utf16le');
// Write it back to disk
fs.writeFileSync(path, Buffer(file, 'utf16le').swap16()); Though additional input is welcome. @jasnell have a nit, but LGTM otherwise. Let's leave this open though for at least a couple days for others to respond. |
@Fishrock123 ... it is certainly possible to do in userland but there's a bit of a performance penalty if it's done in pure javascript... I'll post some benchmark numbers in a couple of minutes that compares the performance of swap16/swap32 to equivalent pure javascript code. I went back and forth on this one also but I landed in the same @trevnorris did... that it likely fits just inside that boundary. |
Benchmarks ... the
|
buf[i - 1] = i; | ||
bench.start(); | ||
for (i = 0; i < n; i += 1) | ||
method.apply(buf); |
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.
Maybe avoid using apply
inside the bench start/end?
In buffer-iterate.js it just calls off the instance...
const name = conf.method;
//...
for (i = 0; i < n; i += 1)
method[name](buf);
@trevnorris @williamkapke ... nits addressed. I also updated the implementation such that if Buffer length is below a certain threshold, a pure javascript version of the swap will be used rather than dropping down to the native layer. Based on the benchmarks, when the Buffer is below a certain size, it's far more efficient to swap in js. We may be able to further tweak the threshold but the current limits appear to be within the margin of error. |
if (len < 512) { | ||
if (len % 2 !== 0) | ||
throw new RangeError('Buffer length must be a multiple of 16-bits'); | ||
for (var i = 0, n = 0; i < this.length; i += 2) { |
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.
you already have len
above... you might as well use it here too! :D
(Same for swap32
)
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.
doh! missed that one. good catch :-)
If we're doing this, what about (also) including a more generic I think it makes the JS side cleaner... Buffer.prototype.swap = function swap(a, b) {
var x = this[a];
this[a] = this[b];
this[b] = x;
return this;
};
Buffer.prototype.swap32 = function swap32() {
for (var i = 0; i < buf.length; i += 2) {
this.swap(i++, i + 1);
this.swap(i++, i + 1);
}
return this;
};
Buffer.prototype.swap16 = function swap16() {
for (var i = 0; i < buf.length; i++) {
this.swap(i, ++i);
}
return this;
};
const buf = Buffer([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]);
console.log(buf.swap(1,2));
//or
console.log(buf.swap16());
//or
console.log(buf.swap32()); |
Having a swap function makes sense but I wouldn't expose it as part of the Buffer API. Nits addressed! |
heh- that's how I originally wrote it and then decided to put swap on the prototype ;) LGTM! |
bench.start(); | ||
for (i = 0; i < n; i += 1) | ||
buf[method](); | ||
bench.end(n); |
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.
mind throwing these (the for loop) in their own function? when profiling it's easier to look for execution of a named function that only contains the code actually being profiled.
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.
will do
74769f0
to
26b4ffd
Compare
@trevnorris ... done! also squashed the commits and rebased to pick up the buffer api changes also |
Interprets the `Buffer` as an array of unsigned 32-bit integers and swaps | ||
the byte-order *in-place*. Throws a `RangeError` if the `Buffer` length is | ||
not a multiple of 32 bits. The method returns a reference to the Buffer, so | ||
calls can be chained |
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.
Missing closing period.
What are the semantics when the size of the buffer is not a multiple of 2 / 4? EDIT: found the answer by looking at the test-case. |
It's also covered in the documentation addition. A |
@@ -17,6 +17,48 @@ var poolSize, poolOffset, allocPool; | |||
|
|||
|
|||
binding.setupBufferJS(Buffer.prototype, bindingObj); | |||
|
|||
const swap16n = Buffer.prototype.swap16; | |||
const swap32n = Buffer.prototype.swap32; |
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.
Why are we doing this instead of attaching the native implementation to binding.swap*()
?
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.
Just an artifact of how it was originally written. You're right, attaching directly to binding.swap*()
is better.
Nice tests. Left two comments. Other than that LGTM. |
@trevnorris .. thanks .. updated to address those nits! |
@jasnell Thanks much. LGTM |
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Incredible nitpicking, but nonetheless I would opt for one of these:
Buffer size must be a multiple of 16 bits
or:
Buffer length must be a multiple of 2
I hope you understand the nuance difference I'm getting at.
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.
Will change to Buffer size must be a multiple of 16 bits
when I land.
Adds Buffer.prototype.swap16() and Buffer.prototype.swap32() methods that mutate the Buffer instance in-place by swapping the 16-bit and 32-bit byte-order. Example: ```js const buf = Buffer([0x1, 0x2, 0x3, 0x4]); buf.swap16(); console.log(buf); // prints Buffer(0x2, 0x1, 0x4, 0x3); buf.swap32(); console.log(buf); // prints Buffer(0x3, 0x4, 0x1, 0x2); ``` PR-URL: #5724 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Landed in 7d73e60. Thanks all! |
Adds Buffer.prototype.swap16() and Buffer.prototype.swap32() methods that mutate the Buffer instance in-place by swapping the 16-bit and 32-bit byte-order. Example: ```js const buf = Buffer([0x1, 0x2, 0x3, 0x4]); buf.swap16(); console.log(buf); // prints Buffer(0x2, 0x1, 0x4, 0x3); buf.swap32(); console.log(buf); // prints Buffer(0x3, 0x4, 0x1, 0x2); ``` PR-URL: #5724 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Notable changes: * buffer: * make byteLength work with ArrayBuffer & DataView (Jackson Tian) [#5255](#5255) * backport --zero-fill-buffers command line option (James M Snell) [#5744](#5744) * backport new buffer constructor APIs (James M Snell) [#5763](#5763) * add swap16() and swap32() methods (James M Snell) [#5724](#5724) * fs: add the fs.mkdtemp() function. (Florian MARGAINE) [#5333](#5333) * net: emit host in lookup event (HUANG Wei) [#5598](#5598) * node: --no-browser-globals configure flag (Fedor Indutny) [#5853](#5853) * npm: Upgrade to v3.8.3. (Forrest L Norvell) * repl: support standalone blocks (Prince J Wesley) [#5581](#5581) * src: override v8 thread defaults using cli options (Tom Gallacher) [#4344](#4344)
Notable changes: * buffer: * make byteLength work with ArrayBuffer & DataView (Jackson Tian) [#5255](#5255) * backport --zero-fill-buffers command line option (James M Snell) [#5744](#5744) * backport new buffer constructor APIs (James M Snell) [#5763](#5763) * add swap16() and swap32() methods (James M Snell) [#5724](#5724) * fs: add the fs.mkdtemp() function. (Florian MARGAINE) [#5333](#5333) * net: emit host in lookup event (HUANG Wei) [#5598](#5598) * node: --no-browser-globals configure flag (Fedor Indutny) [#5853](#5853) * npm: Upgrade to v3.8.3. Fixes a security flaw in the use of authentication tokens in HTTP requests that would allow an attacker to set up a server that could collect tokens from users of the command-line interface. Authentication tokens have previously been sent with every request made by the CLI for logged-in users, regardless of the destination of the request. This update fixes this by only including those tokens for requests made against the registry or registries used for the current install. (Forrest L Norvell) [npm#6](npm#6) * repl: support standalone blocks (Prince J Wesley) [#5581](#5581) * src: override v8 thread defaults using cli options (Tom Gallacher) [#4344](#4344)
Notable changes: * buffer: * make byteLength work with ArrayBuffer & DataView (Jackson Tian) [#5255](#5255) * backport --zero-fill-buffers command line option (James M Snell) [#5744](#5744) * backport new buffer constructor APIs (James M Snell) [#5763](#5763) * add swap16() and swap32() methods (James M Snell) [#5724](#5724) * fs: add the fs.mkdtemp() function. (Florian MARGAINE) [#5333](#5333) * net: emit host in lookup event (HUANG Wei) [#5598](#5598) * node: --no-browser-globals configure flag (Fedor Indutny) [#5853](#5853) * npm: Upgrade to v3.8.3. Fixes a security flaw in the use of authentication tokens in HTTP requests that would allow an attacker to set up a server that could collect tokens from users of the command-line interface. Authentication tokens have previously been sent with every request made by the CLI for logged-in users, regardless of the destination of the request. This update fixes this by only including those tokens for requests made against the registry or registries used for the current install. (Forrest L Norvell) [npm#6](npm#6) * repl: support standalone blocks (Prince J Wesley) [#5581](#5581) * src: override v8 thread defaults using cli options (Tom Gallacher) [#4344](#4344)
Notable changes: * buffer: * make byteLength work with ArrayBuffer & DataView (Jackson Tian) [#5255](#5255) * backport --zero-fill-buffers command line option (James M Snell) [#5744](#5744) * backport new buffer constructor APIs (James M Snell) [#5763](#5763) * add swap16() and swap32() methods (James M Snell) [#5724](#5724) * fs: add the fs.mkdtemp() function. (Florian MARGAINE) [#5333](#5333) * net: emit host in lookup event (HUANG Wei) [#5598](#5598) * node: --no-browser-globals configure flag (Fedor Indutny) [#5853](#5853) * npm: Upgrade to v3.8.3. Fixes a security flaw in the use of authentication tokens in HTTP requests that would allow an attacker to set up a server that could collect tokens from users of the command-line interface. Authentication tokens have previously been sent with every request made by the CLI for logged-in users, regardless of the destination of the request. This update fixes this by only including those tokens for requests made against the registry or registries used for the current install. (Forrest L Norvell) [npm#6](npm#6) * repl: support standalone blocks (Prince J Wesley) [#5581](#5581) * src: override v8 thread defaults using cli options (Tom Gallacher) [#4344](#4344) PR-URL: #5970
Pull Request check-list
make -j8 test
(UNIX) orvcbuild test nosign
(Windows) pass withthis change (including linting)?
test (or a benchmark) included?
existing APIs, or introduces new ones)?
Affected core subsystem(s)
buffer
Description of change
Adds
Buffer.prototype.swapShort()Buffer.prototype.swap16() andBuffer.prototype.swapLong()Buffer.prototype.swap32() methods thatmutate the Buffer instance in-place by swapping the 16-bit and 32-bit
byte-order.
Example:
Was looking at a number of use cases recently around reading in UTF16BE data and converting that to UTF8 and realized that we really didn't expose an efficient mechanism for doing a byte-swap in Buffer. This seemed like a useful API addition.
/cc @trevnorris @srl295