-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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 buf.mask for ws #1202
Changes from all commits
914dc46
3e425fc
7bf9a2d
e92f4b7
7ef29ac
fb4ac8a
0f7b918
8c72550
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 |
---|---|---|
|
@@ -54,6 +54,12 @@ using v8::Uint32; | |
using v8::Value; | ||
|
||
|
||
union CopyDWord { | ||
char as_bytes[sizeof(uint32_t)]; | ||
uint32_t as_dword; | ||
}; | ||
|
||
|
||
bool HasInstance(Handle<Value> val) { | ||
return val->IsObject() && HasInstance(val.As<Object>()); | ||
} | ||
|
@@ -310,6 +316,71 @@ void Base64Slice(const FunctionCallbackInfo<Value>& args) { | |
} | ||
|
||
|
||
// bytesCopied = buffer.mask(mask, target, targetOffset, sourceStart, sourceEnd) | ||
void Mask(const FunctionCallbackInfo<Value> &args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
|
||
CopyDWord mask; | ||
mask.as_dword = args[0]->Uint32Value(); | ||
|
||
CHECK(args[1]->IsObject()); | ||
Local<Object> target = args[1].As<Object>(); | ||
|
||
if (!HasInstance(target)) | ||
return env->ThrowTypeError("second arg should be a Buffer"); | ||
|
||
ARGS_THIS(args.This()) | ||
size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); | ||
char* target_data = static_cast<char*>( | ||
target->GetIndexedPropertiesExternalArrayData()); | ||
size_t target_start; | ||
size_t source_start; | ||
size_t source_end; | ||
|
||
CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &target_start)); | ||
CHECK_NOT_OOB(ParseArrayIndex(args[3], 0, &source_start)); | ||
CHECK_NOT_OOB(ParseArrayIndex(args[4], obj_length, &source_end)); | ||
|
||
// Copy 0 bytes; we're done | ||
if (target_start >= target_length || source_start >= source_end) | ||
return args.GetReturnValue().Set(0); | ||
|
||
if (source_start > obj_length) | ||
return env->ThrowRangeError("out of range index"); | ||
|
||
if (source_end - source_start > target_length - target_start) | ||
source_end = source_start + target_length - target_start; | ||
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. Ignore my previous comment. This calculation can in theory overflow (although not yet in practice due to size restrictions of buffers) assuming 32 bits size_t: I'd probably add a 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. Should I add 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. Right, same issue. Yes, a check would be good. Medium term, we should look into removing the duplication in that file. |
||
CHECK_LE(source_start, source_end); | ||
|
||
uint32_t to_copy = MIN(MIN(source_end - source_start, | ||
target_length - target_start), | ||
obj_length - source_start); | ||
size_t written = (to_copy / sizeof(uint32_t)) * sizeof(uint32_t); | ||
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. Doesn't the linter complain about the 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. Also, |
||
obj_data += source_start; | ||
target_data += target_start; | ||
|
||
CopyDWord tmp; | ||
for (size_t i = 0; i < written; i += sizeof(tmp.as_bytes)) { | ||
memcpy(tmp.as_bytes, obj_data + i, sizeof(tmp.as_bytes)); | ||
tmp.as_dword ^= mask.as_dword; | ||
memcpy(target_data + i, tmp.as_bytes, sizeof(tmp.as_bytes)); | ||
} | ||
target_data += written; | ||
obj_data += written; | ||
|
||
switch (to_copy % 4) { | ||
case 3: | ||
target_data[2] = obj_data[2] ^ mask.as_bytes[2]; | ||
case 2: | ||
target_data[1] = obj_data[1] ^ mask.as_bytes[1]; | ||
case 1: | ||
target_data[0] = obj_data[0] ^ mask.as_bytes[0]; | ||
case 0: {} | ||
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. You can just omit the |
||
} | ||
return args.GetReturnValue().Set(to_copy); | ||
} | ||
|
||
|
||
// bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]); | ||
void Copy(const FunctionCallbackInfo<Value> &args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
|
@@ -741,6 +812,7 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { | |
env->SetMethod(proto, "utf8Write", Utf8Write); | ||
|
||
env->SetMethod(proto, "copy", Copy); | ||
env->SetMethod(proto, "mask", Mask); | ||
|
||
// for backwards compatibility | ||
proto->ForceSet(env->offset_string(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
var common = require('../common'); | ||
var assert = require('assert'); | ||
|
||
var tests = [ | ||
testBasic, | ||
testBounds, | ||
testOverflow, | ||
testNonAligned, | ||
testReturnValue | ||
] | ||
|
||
tests.forEach(function(fn) { | ||
fn(); | ||
}); | ||
|
||
function referenceImplementation(source, maskNum, output, offset, start, end) { | ||
var i = 0; | ||
var mask = new Buffer(4); | ||
var length = end - start; | ||
mask.writeUInt32LE(maskNum, 0); | ||
var toCopy = Math.min(length, output.length - offset); | ||
var maskIdx = 0; | ||
for (var i = 0; i < toCopy; ++i) { | ||
output[i + offset] = source[i + start] ^ mask[maskIdx]; | ||
maskIdx = (maskIdx + 1) & 3; | ||
} | ||
} | ||
|
||
function testBasic() { | ||
var input = new Buffer(256); | ||
var output = new Buffer(256); | ||
var refoutput = new Buffer(256); | ||
var mask = 0xF0F0F0F0; | ||
|
||
for (var i = 0; i < input.length; ++i) | ||
input[i] = i; | ||
|
||
output[0] = refoutput[0] = 0; | ||
referenceImplementation(input, mask, refoutput, 1, 0, 255); | ||
input.mask(mask, output, 1, 0, 255); | ||
for (var i = 0; i < input.length; ++i) { | ||
assert.equal(output[i], refoutput[i]); | ||
} | ||
} | ||
|
||
function testBounds() { | ||
var input = new Buffer(16); | ||
var output = new Buffer(32); | ||
try { | ||
input.mask(120120, output, 0, 0, 17); | ||
assert.fail('expected error'); | ||
} catch(err) { | ||
assert.ok(err); | ||
} | ||
} | ||
|
||
function testOverflow() { | ||
var input = new Buffer(16); | ||
var output = new Buffer(15); | ||
try { | ||
input.mask(120120, output); | ||
assert.fail('expected error'); | ||
} catch(err) { | ||
assert.ok(err); | ||
} | ||
} | ||
|
||
function testNonAligned() { | ||
var input = new Buffer(16); | ||
var output = new Buffer(16); | ||
var refoutput = new Buffer(16); | ||
var mask = 0xF0F0F0F0; | ||
|
||
for (var i = 0; i < input.length; ++i) | ||
input[i] = i; | ||
|
||
for (var end = 3; end > 0; --end) { | ||
referenceImplementation(input, mask, refoutput, 0, 0, end); | ||
input.mask(mask, output, 0, 0, end); | ||
|
||
for (var i = 0; i < end; ++i) | ||
assert.equal(output[i], refoutput[i]); | ||
} | ||
} | ||
|
||
function testReturnValue() { | ||
var input = new Buffer(16); | ||
var output = new Buffer(16); | ||
assert.equal(input.mask(0, output), 16) | ||
assert.equal(input.mask(0, output, 4), 12) | ||
assert.equal(input.mask(0, output, 4, 6), 10) | ||
assert.equal(input.mask(0, output, 4, 6, 4), 0) | ||
assert.equal(input.mask(0, output, 4, 6, 8), 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.
It doesn't hurt but you don't have to store the mask in a union, just a uint32_t is alright.