[major] Simplify the C++ code and how the functions are exported

This commit is contained in:
Luigi Pinca
2017-02-03 18:14:02 +01:00
parent bb83e9a1b6
commit 2342a6c2fc
3 changed files with 211 additions and 185 deletions
+62 -26
View File
@@ -4,59 +4,95 @@
[![Linux/macOS Build](https://travis-ci.org/websockets/bufferutil.svg?branch=master)](https://travis-ci.org/websockets/bufferutil)
[![Windows Build](https://ci.appveyor.com/api/projects/status/github/websockets/bufferutil?branch=master&svg=true)](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
View File
@@ -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
View File
@@ -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)