mirror of
https://github.com/hanzoai/bufferutil.git
synced 2026-09-24 16:57:30 +00:00
[major] Simplify the C++ code and how the functions are exported
This commit is contained in:
@@ -4,59 +4,95 @@
|
||||
[](https://travis-ci.org/websockets/bufferutil)
|
||||
[](https://ci.appveyor.com/project/lpinca/bufferutil)
|
||||
|
||||
Buffer utils is one of the modules that makes `ws` fast. It's optimized for
|
||||
certain buffer based operations such as merging buffers, generating WebSocket
|
||||
masks and unmasking.
|
||||
|
||||
As the module consists of binary components, it should be used an
|
||||
`optionalDependency` so when installation fails, it doesn't halt the
|
||||
installation of your module. There are fallback files available in this
|
||||
repository. See `fallback.js` for the suggest fallback implementation if
|
||||
installation fails.
|
||||
`bufferutil` is what makes `ws` fast. It provides some utilities to efficiently
|
||||
perform some operations such as masking and unmasking the data payload of
|
||||
WebSocket frames.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install bufferutil
|
||||
npm install bufferutil --save-optional
|
||||
```
|
||||
|
||||
The `--save-optional` flag tells npm to save the package in your package.json
|
||||
under the [`optionalDependencies`](https://docs.npmjs.com/files/package.json#optionaldependencies)
|
||||
key.
|
||||
|
||||
## API
|
||||
|
||||
In all examples we assume that you've already required the BufferUtil as
|
||||
followed:
|
||||
The module exports 3 different functions.
|
||||
|
||||
### `bufferUtil.merge(target, list)`
|
||||
|
||||
Merges an array of buffers into a target buffer.
|
||||
|
||||
#### Arguments
|
||||
|
||||
- `target` - The target buffer.
|
||||
- `list` - An array containing the `Buffer` instances to merge.
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
'use strict';
|
||||
|
||||
var bu = require('bufferutil').BufferUtil;
|
||||
const bufferUtil = require('bufferutil');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const target = Buffer.allocUnsafe(10);
|
||||
|
||||
bufferUtil.merge(target, [crypto.randomBytes(5), crypto.randomBytes(5)]);
|
||||
```
|
||||
|
||||
The module exposes 3 different functions:
|
||||
#### `bufferUtil.mask(source, mask, output, offset, length)`
|
||||
|
||||
#### merge
|
||||
Masks a buffer using the given masking-key as specified by the WebSocket
|
||||
protocol.
|
||||
|
||||
Merge multiple buffers in the first supplied buffer argument:
|
||||
#### Arguments
|
||||
|
||||
- `source` - The buffer to mask.
|
||||
- `mask` - A buffer representing the masking-key.
|
||||
- `output` - The buffer where to store the result.
|
||||
- `offset` - The offset at which to start writing.
|
||||
- `length` - The number of bytes to mask.
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
bu.merge(buffer, [buffer1, buffer2]);
|
||||
'use strict';
|
||||
|
||||
const bufferUtil = require('bufferutil');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const source = crypto.randomBytes(10);
|
||||
const mask = crypto.randomBytes(4);
|
||||
|
||||
bufferUtil.mask(source, mask, source, 0, source.length);
|
||||
```
|
||||
|
||||
This merges buffer1 and buffer2 which are in an array into buffer.
|
||||
#### `bufferUtil.unmask(buffer, mask)`
|
||||
|
||||
#### mask
|
||||
Unmasks a buffer using the given masking-key as specified by the WebSocket
|
||||
protocol.
|
||||
|
||||
Apply a WebSocket mask on the given data.
|
||||
#### Arguments
|
||||
|
||||
- `buffer` - The buffer to unmask.
|
||||
- `mask` - A buffer representing the masking-key.
|
||||
|
||||
#### Example
|
||||
|
||||
```js
|
||||
bu.mask(buffer, mask);
|
||||
```
|
||||
'use strict';
|
||||
|
||||
#### unmask
|
||||
const bufferUtil = require('bufferutil');
|
||||
const crypto = require('crypto');
|
||||
|
||||
Remove a WebSocket mask on the given data.;w
|
||||
const buffer = crypto.randomBytes(10);
|
||||
const mask = crypto.randomBytes(4);
|
||||
|
||||
```js
|
||||
bu.unmask(buffer, mask);
|
||||
bufferUtil.unmask(buffer, mask);
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
+46
-20
@@ -6,25 +6,51 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.BufferUtil = {
|
||||
merge: function (mergedBuffer, buffers) {
|
||||
var offset = 0;
|
||||
for (var i = 0, l = buffers.length; i < l; ++i) {
|
||||
var buf = buffers[i];
|
||||
buf.copy(mergedBuffer, offset);
|
||||
offset += buf.length;
|
||||
}
|
||||
},
|
||||
mask: function (source, mask, output, offset, length) {
|
||||
for (var i = 0; i < length; i++) {
|
||||
output[offset + i] = source[i] ^ mask[i & 3];
|
||||
}
|
||||
},
|
||||
unmask: function (data, mask) {
|
||||
// required until https://github.com/nodejs/node/issues/9006 is resolved
|
||||
var length = data.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
data[i] ^= mask[i & 3];
|
||||
}
|
||||
/**
|
||||
* Merges an array of buffers into a target buffer.
|
||||
*
|
||||
* @param {Buffer} target The target buffer
|
||||
* @param {Buffer[]} buffers The array of buffers to merge
|
||||
* @public
|
||||
*/
|
||||
const merge = (target, buffers) => {
|
||||
var offset = 0;
|
||||
for (var i = 0; i < buffers.length; i++) {
|
||||
const buf = buffers[i];
|
||||
buf.copy(target, offset);
|
||||
offset += buf.length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Masks a buffer using the given mask.
|
||||
*
|
||||
* @param {Buffer} source The buffer to mask
|
||||
* @param {Buffer} mask The mask to use
|
||||
* @param {Buffer} output The buffer where to store the result
|
||||
* @param {Number} offset The offset at which to start writing
|
||||
* @param {Number} length The number of bytes to mask.
|
||||
* @public
|
||||
*/
|
||||
const mask = (source, mask, output, offset, length) => {
|
||||
for (var i = 0; i < length; i++) {
|
||||
output[offset + i] = source[i] ^ mask[i & 3];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unmasks a buffer using the given mask.
|
||||
*
|
||||
* @param {Buffer} buffer The buffer to unmask
|
||||
* @param {Buffer} mask The mask to use
|
||||
* @public
|
||||
*/
|
||||
const unmask = (buffer, mask) => {
|
||||
// Required until https://github.com/nodejs/node/issues/9006 is resolved.
|
||||
const length = buffer.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
buffer[i] ^= mask[i & 3];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { merge, mask, unmask };
|
||||
|
||||
+103
-139
@@ -4,161 +4,125 @@
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
#include <v8.h>
|
||||
#include <node.h>
|
||||
#include <node_version.h>
|
||||
#include <node_buffer.h>
|
||||
#include <node_object_wrap.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
#include "nan.h"
|
||||
#include <nan.h>
|
||||
|
||||
using namespace v8;
|
||||
using namespace node;
|
||||
NAN_METHOD(merge) {
|
||||
char* target = node::Buffer::Data(info[0]);
|
||||
v8::Local<v8::Array> list = info[1].As<v8::Array>();
|
||||
uint32_t size = list->Length();
|
||||
size_t offset = 0;
|
||||
|
||||
class BufferUtil : public ObjectWrap
|
||||
{
|
||||
public:
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
v8::Local<v8::Value> source = list->Get(i);
|
||||
size_t length = node::Buffer::Length(source);
|
||||
memcpy(target + offset, node::Buffer::Data(source), length);
|
||||
offset += length;
|
||||
}
|
||||
}
|
||||
|
||||
static void Initialize(v8::Handle<v8::Object> target)
|
||||
{
|
||||
Nan::HandleScope scope;
|
||||
Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
Nan::SetMethod(t, "unmask", BufferUtil::Unmask);
|
||||
Nan::SetMethod(t, "mask", BufferUtil::Mask);
|
||||
Nan::SetMethod(t, "merge", BufferUtil::Merge);
|
||||
Nan::Set(target, Nan::New<String>("BufferUtil").ToLocalChecked(), t->GetFunction());
|
||||
NAN_METHOD(mask) {
|
||||
char* from = node::Buffer::Data(info[0]);
|
||||
char* mask = node::Buffer::Data(info[1]);
|
||||
char* to = node::Buffer::Data(info[2]) + info[3]->Int32Value();
|
||||
size_t length = info[4]->Int32Value();
|
||||
size_t index = 0;
|
||||
|
||||
//
|
||||
// Alignment preamble.
|
||||
//
|
||||
while (index < length && (reinterpret_cast<size_t>(from) & 0x07)) {
|
||||
*to++ = *from++ ^ mask[index % 4];
|
||||
index++;
|
||||
}
|
||||
length -= index;
|
||||
if (!length) return;
|
||||
|
||||
//
|
||||
// Realign mask and convert to 64 bit.
|
||||
//
|
||||
char maskAlignedArray[8];
|
||||
|
||||
for (size_t i = 0; i < 8; i++, index++) {
|
||||
maskAlignedArray[i] = mask[index % 4];
|
||||
}
|
||||
|
||||
protected:
|
||||
//
|
||||
// Apply 64 bit mask in 8 byte chunks.
|
||||
//
|
||||
size_t loop = length / 8;
|
||||
uint64_t* pMask8 = reinterpret_cast<uint64_t*>(maskAlignedArray);
|
||||
|
||||
static NAN_METHOD(New)
|
||||
{
|
||||
Nan::HandleScope scope;
|
||||
BufferUtil* bufferUtil = new BufferUtil();
|
||||
bufferUtil->Wrap(info.This());
|
||||
info.GetReturnValue().Set(info.This());
|
||||
while (loop--) {
|
||||
uint64_t* pFrom8 = reinterpret_cast<uint64_t*>(from);
|
||||
uint64_t* pTo8 = reinterpret_cast<uint64_t*>(to);
|
||||
*pTo8 = *pFrom8 ^ *pMask8;
|
||||
from += 8;
|
||||
to += 8;
|
||||
}
|
||||
|
||||
static NAN_METHOD(Merge)
|
||||
{
|
||||
Nan::HandleScope scope;
|
||||
Local<Object> bufferObj = info[0]->ToObject();
|
||||
char* buffer = Buffer::Data(bufferObj);
|
||||
Local<Array> array = Local<Array>::Cast(info[1]);
|
||||
unsigned int arrayLength = array->Length();
|
||||
size_t offset = 0;
|
||||
unsigned int i;
|
||||
for (i = 0; i < arrayLength; ++i) {
|
||||
Local<Object> src = array->Get(i)->ToObject();
|
||||
size_t length = Buffer::Length(src);
|
||||
memcpy(buffer + offset, Buffer::Data(src), length);
|
||||
offset += length;
|
||||
}
|
||||
info.GetReturnValue().Set(Nan::True());
|
||||
//
|
||||
// Apply mask to remaining data.
|
||||
//
|
||||
char* pmaskAlignedArray = maskAlignedArray;
|
||||
|
||||
length &= 0x7;
|
||||
while (length--) {
|
||||
*to++ = *from++ ^ *pmaskAlignedArray++;
|
||||
}
|
||||
}
|
||||
|
||||
NAN_METHOD(unmask) {
|
||||
char* from = node::Buffer::Data(info[0]);
|
||||
size_t length = node::Buffer::Length(info[0]);
|
||||
char* mask = node::Buffer::Data(info[1]);
|
||||
size_t index = 0;
|
||||
|
||||
//
|
||||
// Alignment preamble.
|
||||
//
|
||||
while (index < length && (reinterpret_cast<size_t>(from) & 0x07)) {
|
||||
*from++ ^= mask[index % 4];
|
||||
index++;
|
||||
}
|
||||
length -= index;
|
||||
if (!length) return;
|
||||
|
||||
//
|
||||
// Realign mask and convert to 64 bit.
|
||||
//
|
||||
char maskAlignedArray[8];
|
||||
|
||||
for (size_t i = 0; i < 8; i++, index++) {
|
||||
maskAlignedArray[i] = mask[index % 4];
|
||||
}
|
||||
|
||||
static NAN_METHOD(Unmask)
|
||||
{
|
||||
Nan::HandleScope scope;
|
||||
Local<Object> buffer_obj = info[0]->ToObject();
|
||||
Local<Object> mask_obj = info[1]->ToObject();
|
||||
//
|
||||
// Apply 64 bit mask in 8 byte chunks.
|
||||
//
|
||||
size_t loop = length / 8;
|
||||
uint64_t* pMask8 = reinterpret_cast<uint64_t*>(maskAlignedArray);
|
||||
|
||||
char* mask = Buffer::Data(mask_obj);
|
||||
char* from = Buffer::Data(buffer_obj);
|
||||
size_t length = Buffer::Length(buffer_obj);
|
||||
|
||||
info.GetReturnValue().Set(Nan::True());
|
||||
|
||||
/* Alignment preamble */
|
||||
size_t index;
|
||||
for (index = 0; index < length && (reinterpret_cast<size_t>(from) & 0x07); index++)
|
||||
*from++ ^= mask[index % 4];
|
||||
length -= index;
|
||||
if (!length)
|
||||
return;
|
||||
|
||||
/* Realign mask and convert to 64 bit */
|
||||
char maskAlignedArray[8];
|
||||
for (size_t i = 0; i < 8; i++, index++)
|
||||
maskAlignedArray[i] = mask[index % 4];
|
||||
|
||||
/* Apply 64 bit mask in 8 byte chunks */
|
||||
size_t loop = length / 8;
|
||||
uint64_t* pSource8;
|
||||
uint64_t* pMask8 = reinterpret_cast<uint64_t*>(maskAlignedArray);
|
||||
while (loop--) {
|
||||
pSource8 = reinterpret_cast<uint64_t*>(from);
|
||||
*pSource8 ^= *pMask8;
|
||||
from += 8;
|
||||
}
|
||||
|
||||
/* Apply mask to remaining data */
|
||||
char* pmaskAlignedArray = maskAlignedArray;
|
||||
length &= 0x7;
|
||||
while (length--)
|
||||
*from++ ^= *pmaskAlignedArray++;
|
||||
while (loop--) {
|
||||
uint64_t* pSource8 = reinterpret_cast<uint64_t*>(from);
|
||||
*pSource8 ^= *pMask8;
|
||||
from += 8;
|
||||
}
|
||||
|
||||
static NAN_METHOD(Mask)
|
||||
{
|
||||
Nan::HandleScope scope;
|
||||
Local<Object> buffer_obj = info[0]->ToObject();
|
||||
Local<Object> mask_obj = info[1]->ToObject();
|
||||
Local<Object> output_obj = info[2]->ToObject();
|
||||
size_t dataOffset = info[3]->Int32Value();
|
||||
size_t length = info[4]->Int32Value();
|
||||
//
|
||||
// Apply mask to remaining data.
|
||||
//
|
||||
char* pmaskAlignedArray = maskAlignedArray;
|
||||
|
||||
char* mask = Buffer::Data(mask_obj);
|
||||
char* from = Buffer::Data(buffer_obj);
|
||||
char* to = Buffer::Data(output_obj) + dataOffset;
|
||||
|
||||
info.GetReturnValue().Set(Nan::True());
|
||||
|
||||
/* Alignment preamble */
|
||||
size_t index;
|
||||
for (index = 0; index < length && (reinterpret_cast<size_t>(from) & 0x07); index++)
|
||||
*to++ = *from++ ^ mask[index % 4];
|
||||
length -= index;
|
||||
if (!length)
|
||||
return;
|
||||
|
||||
/* Realign mask and convert to 64 bit */
|
||||
char maskAlignedArray[8];
|
||||
for (size_t i = 0; i < 8; i++, index++)
|
||||
maskAlignedArray[i] = mask[index % 4];
|
||||
|
||||
/* Apply 64 bit mask in 8 byte chunks */
|
||||
size_t loop = length / 8;
|
||||
uint64_t* pFrom8;
|
||||
uint64_t* pTo8;
|
||||
uint64_t* pMask8 = reinterpret_cast<uint64_t*>(maskAlignedArray);
|
||||
while (loop--) {
|
||||
pFrom8 = reinterpret_cast<uint64_t*>(from);
|
||||
pTo8 = reinterpret_cast<uint64_t*>(to);
|
||||
*pTo8 = *pFrom8 ^ *pMask8;
|
||||
from += 8;
|
||||
to += 8;
|
||||
}
|
||||
|
||||
/* Apply mask to remaining data */
|
||||
char* pmaskAlignedArray = maskAlignedArray;
|
||||
length &= 0x7;
|
||||
while (length--)
|
||||
*to++ = *from++ ^ *pmaskAlignedArray++;
|
||||
length &= 0x7;
|
||||
while (length--) {
|
||||
*from++ ^= *pmaskAlignedArray++;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if !NODE_VERSION_AT_LEAST(0,10,0)
|
||||
extern "C"
|
||||
#endif
|
||||
void init (Handle<Object> target)
|
||||
{
|
||||
Nan::HandleScope scope;
|
||||
BufferUtil::Initialize(target);
|
||||
NAN_MODULE_INIT(init) {
|
||||
NAN_EXPORT(target, merge);
|
||||
NAN_EXPORT(target, mask);
|
||||
NAN_EXPORT(target, unmask);
|
||||
}
|
||||
|
||||
NODE_MODULE(bufferutil, init)
|
||||
|
||||
Reference in New Issue
Block a user