Skip to content

Commit

Permalink
Merge pull request #5 from SalvatorePreviti/refactoring
Browse files Browse the repository at this point in the history
Refactoring
  • Loading branch information
SalvatorePreviti authored Jun 16, 2018
2 parents 4a487e2 + 8e166ec commit 6076a73
Show file tree
Hide file tree
Showing 18 changed files with 221 additions and 82 deletions.
9 changes: 2 additions & 7 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,12 @@
"node": true
},
"overrides": [
{
"files": ["**/*.mjs"],
"rules": {
"node/no-unsupported-features": ["error", { "ignores": ["modules"] }]
}
},
{
"files": ["scripts/**/*.js"],
"plugins": [],
"rules": {
"node/no-unpublished-require": 0
"node/no-unpublished-require": 0,
"no-console": 0
}
},
{
Expand Down
27 changes: 22 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,14 @@ declare class RoaringBitmap32 implements Iterable<number> {
*/
public toArray(): number[]

/**
* Creates a new plain JS Set<number> and fills it with all the values in the bitmap.
* The returned set may be very big, use this function only when you know what you are doing.
*
* @returns A new plain JS array that contains all the items in the set in order.
*/
public toSet(): Set<number>

/**
* Returns a plain JS array with all the values in the bitmap.
* Used by JSON.stringify to serialize this bitmap.
Expand Down Expand Up @@ -613,20 +621,29 @@ declare class RoaringModule {
public readonly RoaringBitmap32Iterator: typeof RoaringBitmap32Iterator

/**
* True if SSE4.2 instruction set is supported and currently used by CRoaring library.
* Property: The version of the CRoaring libary as a string.
* Example: "0.2.42"
*/
public readonly CRoaringVersion: string

/**
* Property: Indicates wether Streaming SIMD Extensions 4.2 instruction set is supported and currently used by the underlying CRoaring library.
*/
public readonly SSE42: boolean

/**
* True if AVX2 instruction set is supported and currently used by CRoaring library.
* Property: Indicates wether Advanced Vector Extensions 2 instruction set is supported and currently used by the underlying CRoaring library.
*/
public readonly AVX2: boolean

/**
* Property: The version of the CRoaring libary as a string.
* Example: "0.2.42"
* Property: The instruction set supported and currently used by the underlying CRoraring library.
* Possible values are:
* - 'AVX2' - Advanced Vector Extensions 2
* - 'SSE42' - Streaming SIMD Extensions 4.2
* - 'PLAIN' - no special instruction set
*/
public readonly CRoaringVersion: string
public readonly instructionSet: 'AVX2' | 'SSE42' | 'PLAIN'

/**
* Property: The version of the roaring npm package as a string.
Expand Down
32 changes: 2 additions & 30 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,2 @@
const cpuInfo = require('./lib/cpuinfo')

let roaringNodePath
if (cpuInfo.AVX2) {
roaringNodePath = `./build/Release/roaring-avx2.node`
} else if (cpuInfo.SSE42) {
roaringNodePath = './build/Release/roaring-sse42.node'
} else {
roaringNodePath = './build/Release/roaring.node'
}

const roaringNode = require(roaringNodePath)

roaringNode._initTypes({
Array,
Buffer,
Uint32Array
})

if (!roaringNode.hasOwnProperty('PackageVersion')) {
Object.defineProperty(roaringNode, 'PackageVersion', {
get() {
return require('./package.json').version
},
enumerable: true,
configurable: false
})
}

module.exports = roaringNode
const getRoaring = require('./lib/getRoaring')
module.exports = getRoaring()
26 changes: 13 additions & 13 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const cpuinfo = require('./lib/cpuinfo')
const instructionSet = require('./lib/instructionSet')

function makeTestProject() {
function testProject() {
return {
testEnvironment: 'node',
verbose: true,
Expand All @@ -18,30 +18,30 @@ function makeTestProject() {
}
}

const defaultProject = makeTestProject()
const defaultProject = testProject()

defaultProject.setupFiles = ['./scripts/config/jest-init-plain.js']
defaultProject.setupFiles = ['./test/config/jest-init-plain.js']

const projects = [defaultProject]

if (cpuinfo.AVX2) {
const avx2Project = makeTestProject()
avx2Project.displayName = 'AVX'
avx2Project.setupFiles = ['./scripts/config/jest-init-avx2.js']
if (instructionSet === 'AVX2') {
const avx2Project = testProject()
avx2Project.displayName = 'AVX2 '
avx2Project.setupFiles = ['./test/config/jest-init-avx2.js']
projects.push(avx2Project)
}

if (cpuinfo.SSE42) {
const sse42Project = makeTestProject()
sse42Project.displayName = 'SSE'
sse42Project.setupFiles = ['./scripts/config/jest-init-sse4.js']
if (instructionSet === 'AVX2' || instructionSet === 'SSE42') {
const sse42Project = testProject()
sse42Project.displayName = 'SSE42'
sse42Project.setupFiles = ['./test/config/jest-init-sse4.js']
projects.push(sse42Project)
}

if (projects.length === 1) {
module.exports = defaultProject
} else {
defaultProject.displayName = ' C '
defaultProject.displayName = 'PLAIN'
module.exports = {
projects
}
Expand Down
87 changes: 87 additions & 0 deletions lib/getRoaring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const instructionSet = require('./instructionSet')
const moduleExists = require('./moduleExists')

const roaringNodePaths = {
AVX2: '../build/Release/roaring-avx2.node',
SSE42: '../build/Release/roaring-sse42.node',
PLAIN: '../build/Release/roaring.node'
}

const cache = {
AVX2: undefined,
SSE42: undefined,
PLAIN: undefined
}

let packageVersion

function getPackageVersion() {
if (packageVersion === undefined) {
packageVersion = require('../package.json').version
}
return packageVersion
}

function loadRoaring(roaringNodePath) {
const roaringNode = require(roaringNodePath)

roaringNode._initTypes({
Set,
Array,
Buffer,
Uint32Array
})

if (!roaringNode.hasOwnProperty('PackageVersion')) {
Object.defineProperty(roaringNode, 'PackageVersion', {
get: getPackageVersion,
enumerable: true,
configurable: false
})
}
return roaringNode
}

function getByInstructionSet(set) {
const cached = cache[set]
if (typeof cached === 'object') {
return cached === null ? undefined : cached
}

const roaringNodePath = roaringNodePaths[set]
if (typeof roaringNodePath !== 'string') {
throw new TypeError(`Unknown instruction set: ${set}`)
}

if (
(set === 'AVX2' && instructionSet !== 'AVX2') ||
(set === 'SSE42' && instructionSet === 'PLAIN') ||
(instructionSet !== 'PLAIN' && !moduleExists(roaringNodePath))
) {
cache[set] = null
return undefined
}

const roaring = loadRoaring(roaringNodePath)
cache[set] = roaring
return roaring
}

function getRoaring(instructionSetToUse) {
if (instructionSetToUse !== undefined) {
return getByInstructionSet(instructionSetToUse)
}
const cached = cache[instructionSet]
if (typeof cached === 'object') {
return cached === null ? undefined : cached
}
if (instructionSet === 'AVX2') {
return getByInstructionSet('AVX2') || getByInstructionSet('SSE42') || getByInstructionSet('PLAIN')
}
if (instructionSet === 'SSE42') {
return getByInstructionSet('SSE42') || getByInstructionSet('PLAIN')
}
return getByInstructionSet('PLAIN')
}

module.exports = getRoaring
23 changes: 12 additions & 11 deletions lib/cpuinfo.js → lib/instructionSet.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const path = require('path')
const fs = require('fs')
const os = require('os')
const moduleExists = require('./moduleExists')

const trueValues = {
true: true,
Expand All @@ -9,11 +8,6 @@ const trueValues = {
1: true
}

const cpuinfo = {
SSE42: false,
AVX2: false
}

function parseBool(value) {
if (value === null || value === undefined) {
return false
Expand All @@ -40,17 +34,24 @@ function parseBool(value) {
const disableSSE42 = parseBool(process.env.ROARING_DISABLE_SSE42)
const disableAVX2 = parseBool(process.env.ROARING_DISABLE_AVX2)

let instructionSet = 'PLAIN'

if (os.arch() === 'x64' && (!disableSSE42 || !disableAVX2)) {
try {
const cpuinfoNodePath = '../build/Release/cpuinfo.node'
if (fs.existsSync(path.join(__dirname, cpuinfoNodePath))) {
if (moduleExists(cpuinfoNodePath)) {
const cpuinfoNode = require(cpuinfoNodePath)
cpuinfo.SSE42 = !!cpuinfoNode.SSE42 && !disableSSE42
cpuinfo.AVX2 = !!cpuinfoNode.AVX2 && !disableAVX2
if (cpuinfoNode.SSE42) {
if (cpuinfoNode.AVX2 && !parseBool(process.env.ROARING_DISABLE_AVX2)) {
instructionSet = 'AVX2'
} else if (!parseBool(process.env.ROARING_DISABLE_SSE42)) {
instructionSet = 'SSE42'
}
}
}
} catch (error) {
// ignore error
}
}

module.exports = cpuinfo
module.exports = instructionSet
9 changes: 9 additions & 0 deletions lib/moduleExists.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function moduleExists(modulePath) {
try {
return !!require.resolve(modulePath)
} catch (e) {
return false
}
}

module.exports = moduleExists
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "roaring",
"version": "0.2.5",
"version": "0.2.6",
"description": "CRoaring port for NodeJS",
"main": "index.js",
"types": "index.d.ts",
Expand Down
9 changes: 9 additions & 0 deletions src/cpp/RoaringBitmap32/RoaringBitmap32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void RoaringBitmap32::Init(v8::Local<v8::Object> exports) {
Nan::SetPrototypeMethod(ctor, "select", select);
Nan::SetPrototypeMethod(ctor, "toUint32Array", toUint32Array);
Nan::SetPrototypeMethod(ctor, "toArray", toArray);
Nan::SetPrototypeMethod(ctor, "toSet", toSet);
Nan::SetPrototypeMethod(ctor, "toJSON", toArray);
Nan::SetPrototypeMethod(ctor, "getSerializationSizeInBytes", getSerializationSizeInBytes);
Nan::SetPrototypeMethod(ctor, "serialize", serialize);
Expand Down Expand Up @@ -298,6 +299,14 @@ void RoaringBitmap32::toArray(const Nan::FunctionCallbackInfo<v8::Value> & info)
info.GetReturnValue().Set(TypedArrays::Array_from.Get(info.GetIsolate())->Call(TypedArrays::Array.Get(info.GetIsolate()), 1, argv));
}

void RoaringBitmap32::toSet(const Nan::FunctionCallbackInfo<v8::Value> & info) {
v8::Local<v8::Value> argv[1] = {info.This()};
auto v = Nan::NewInstance(TypedArrays::Set_ctor.Get(info.GetIsolate()), 1, argv);
if (!v.IsEmpty()) {
info.GetReturnValue().Set(v.ToLocalChecked());
}
}

void RoaringBitmap32::toString(const Nan::FunctionCallbackInfo<v8::Value> & info) {
RoaringBitmap32 * self = Nan::ObjectWrap::Unwrap<RoaringBitmap32>(info.Holder());
std::string result("RoaringBitmap32:");
Expand Down
1 change: 1 addition & 0 deletions src/cpp/RoaringBitmap32/RoaringBitmap32.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class RoaringBitmap32 : public Nan::ObjectWrap {

static void toUint32Array(const Nan::FunctionCallbackInfo<v8::Value> & info);
static void toArray(const Nan::FunctionCallbackInfo<v8::Value> & info);
static void toSet(const Nan::FunctionCallbackInfo<v8::Value> & info);
static void getSerializationSizeInBytes(const Nan::FunctionCallbackInfo<v8::Value> & info);
static void serialize(const Nan::FunctionCallbackInfo<v8::Value> & info);

Expand Down
7 changes: 7 additions & 0 deletions src/cpp/TypedArrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Nan::Persistent<v8::Function> TypedArrays::Buffer_allocUnsafe;
Nan::Persistent<v8::Object> TypedArrays::Array;
Nan::Persistent<v8::Function> TypedArrays::Array_from;

Nan::Persistent<v8::Object> TypedArrays::Set;
Nan::Persistent<v8::Function> TypedArrays::Set_ctor;

void TypedArrays::initTypedArrays(const v8::Local<v8::Object> & global) {
auto uint32Array = Nan::Get(global, Nan::New("Uint32Array").ToLocalChecked()).ToLocalChecked()->ToObject();
TypedArrays::Uint32Array.Reset(uint32Array);
Expand All @@ -23,6 +26,10 @@ void TypedArrays::initTypedArrays(const v8::Local<v8::Object> & global) {
auto array = Nan::Get(global, Nan::New("Array").ToLocalChecked()).ToLocalChecked()->ToObject();
TypedArrays::Array.Reset(array);
TypedArrays::Array_from.Reset(v8::Local<v8::Function>::Cast(Nan::Get(array, Nan::New("from").ToLocalChecked()).ToLocalChecked()));

auto set = Nan::Get(global, Nan::New("Set").ToLocalChecked()).ToLocalChecked()->ToObject();
TypedArrays::Set.Reset(set);
TypedArrays::Set_ctor.Reset(v8::Local<v8::Function>::Cast(set));
}

v8::Local<v8::Value> TypedArrays::bufferAllocUnsafe(v8::Isolate * isolate, size_t size) {
Expand Down
3 changes: 3 additions & 0 deletions src/cpp/TypedArrays.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class TypedArrays {
static Nan::Persistent<v8::Function> Uint32Array_ctor;
static Nan::Persistent<v8::Function> Uint32Array_from;

static Nan::Persistent<v8::Object> Set;
static Nan::Persistent<v8::Function> Set_ctor;

static void initTypedArrays(const v8::Local<v8::Object> & global);

static v8::Local<v8::Value> bufferAllocUnsafe(v8::Isolate * isolate, size_t size);
Expand Down
10 changes: 7 additions & 3 deletions src/cpp/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ void InitModule(v8::Local<v8::Object> exports) {

#ifdef USESSE4
v8utils::defineReadonlyField(exports, "SSE42", Nan::New(true));
#ifdef USEAVX
v8utils::defineReadonlyField(exports, "AVX2", Nan::New(true));
v8utils::defineReadonlyField(exports, "instructionSet", Nan::New("AVX2").ToLocalChecked());
#else
v8utils::defineReadonlyField(exports, "SSE42", Nan::New(false));
v8utils::defineReadonlyField(exports, "AVX2", Nan::New(false));
v8utils::defineReadonlyField(exports, "instructionSet", Nan::New("SSE42").ToLocalChecked());
#endif

#ifdef USEAVX
v8utils::defineReadonlyField(exports, "AVX2", Nan::New(true));
#else
v8utils::defineReadonlyField(exports, "SSE42", Nan::New(false));
v8utils::defineReadonlyField(exports, "AVX2", Nan::New(false));
v8utils::defineReadonlyField(exports, "instructionSet", Nan::New("PLAIN").ToLocalChecked());
#endif

RoaringBitmap32::Init(exports);
Expand Down
Loading

0 comments on commit 6076a73

Please sign in to comment.