diff --git a/.travis.yml b/.travis.yml index afd3576..acd87e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,12 +8,12 @@ addons: language: node_js node_js: - "12" + - "11" - "10" - "9" - "8" - - "6" before_install: - - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export CXX=g++-4.8; fi +- if [[ $TRAVIS_OS_NAME == "linux" ]]; then export CXX=g++-4.8; fi notifications: email: - - anandsuresh@gmail.com + - anandsuresh@gmail.com diff --git a/LICENSE b/LICENSE index 5fcd9b1..4d4c6e2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2015 Anand Suresh +Copyright (c) 2018-present Anand Suresh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/Makefile b/Makefile index dec451a..6c21f30 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ benchmark: build for f in $(BENCHMARK_FILES); do node $$f; done; clean: - -$(NODE_GYP) clean + $(NODE_GYP) clean .PHONY: clean build test diff --git a/README.md b/README.md index 5a5b88a..26a418f 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,91 @@ -# SSE4-CRC32 [![Build Status](https://travis-ci.org/anandsuresh/sse4_crc32.svg?branch=master)](https://travis-ci.org/anandsuresh/sse4_crc32) [![npm version](https://badge.fury.io/js/sse4_crc32.svg)](http://badge.fury.io/js/sse4_crc32) +# sse4-crc32 -Starting with the Nehalam series, Intel processors feature the Streaming SIMD Extensions instruction set which -provide a hardware-accelerated version of the CRC32 algorithm (Castagnoli variant). This library uses the -Intel SSE 4.2 instruction set to provide a fast CRC-32 algorithm. +[![node (scoped)](https://img.shields.io/node/v/sse4_crc32.svg?style=plastic)](https://nodejs.org/en/download/) +[![npm (scoped)](https://img.shields.io/npm/v/sse4_crc32.svg?style=plastic)](https://www.npmjs.com/package/sse4_crc32) +[![npm](https://img.shields.io/npm/dt/sse4_crc32.svg?style=plastic)](https://www.npmjs.com/package/sse4_crc32) +[![Travis](https://img.shields.io/travis/anandsuresh/sse4_crc32.svg?style=plastic)](https://travis-ci.org/anandsuresh/sse4_crc32) +[![license](https://img.shields.io/github/license/anandsuresh/sse4_crc32.svg?style=plastic)](LICENSE) +[![GitHub followers](https://img.shields.io/github/followers/anandsuresh.svg?style=social&label=Follow)](https://github.com/anandsuresh) +[![Twitter Follow](https://img.shields.io/twitter/follow/anandsuresh.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=anandsuresh) +Starting with the Nehalam series, Intel processors feature the Streaming SIMD Extensions instruction set which provide a hardware-accelerated version of the CRC-32 algorithm (Castagnoli variant). This library uses the Intel SSE 4.2 instruction set to provide a fast CRC-32C implementation. -## Features +## features -- Intel Streaming SIMD Extensions 4.2 based hardware accelerated CRC-32C calculation -- Graceful fallback to software-based CRC (table-based CRC calculation) -- Progressive CRC-32C calculation -- Supports Node.js buffers +- Intel Streaming SIMD Extensions 4.2 based hardware-accelerated CRC-32C calculation +- Graceful fallback to software-based CRC-32C (table-based CRC calculation) +- Supports streams, strings and buffers +## performance -## Performance - -The tests were run on a Macbook Air running an Intel Core i7 processor, with 8GB of RAM and used buffers -instead of strings to prevent having items on the V8 heap that might cause the garbage collector to fire -frequently and interfere with the test run-times. +The tests were run on a Macbook Air running an Intel Core i7 processor, with 16GB of RAM and used buffers instead of strings to prevent having items on the V8 heap that might cause the garbage collector to fire frequently and interfere with the test run-times. Below are the results from the 2 test cases: - > node benchmark/1.single_1kb_length_buffer.benchmark.js - 100000 calls to calculate CRC on a 1024 byte buffer... - Hardware-based CRC-32C: 27.388368 ms - Software-based CRC-32C: 87.832587 ms - Pure JS CRC-32C (table-based): 726.95698 ms - Pure JS CRC-32C (direct): 4332.993692 ms - - > node benchmark/2.multi_random_length_buffer.benchmark.js - 100000 calls to calculate CRC on random-length buffers upto 4096 bytes long... - Avg. buffer length: 2049 bytes - Hardware-based CRC-32C: 88.440507 ms - Software-based CRC-32C: 173.136638 ms - Pure JS CRC-32C (table-based): 2131.861339 ms - Pure JS CRC-32C (direct): 9006.901272 ms - - -## Installation +``` bash +> node benchmark -Use the following command to install the library from npm: - - npm install sse4_crc32 +single fixed-size buffer, 10000 iterations: +- Native SSE 4.2 CRC-32C: 4.319142 ms +- Native Table-based CRC-32C: 10.726345 ms +- JavaScript (table-based) CRC-32C: 60.704765 ms +- JavaScript (direct) CRC-32C: 336.376904 ms -## Usage +multiple random-length buffers, 10000 iterations: +- Native SSE 4.2 CRC-32C: 60.194043 ms +- Native Table-based CRC-32C: 225.459861 ms +- JavaScript (table-based) CRC-32C: 1786.079168 ms +- JavaScript (direct) CRC-32C: 12307.033387 ms +``` -Using the library is quite simple. Start out by including the library in your code: +## installation - var Sse4Crc32 = require("sse4_crc32"); +Use the following command to install the library from npm: -To calculate the 32-bit CRC for any string, simply use the following code: +``` bash +npm install --save sse4_crc32 +``` - var crc = Sse4Crc32.calculate("my string"); +## usage -Instead of passing in a string, a buffer can be passed to the `calculate()` function. +Import the module: -Furthermore, the `calculate()` function takes an optional `initialCrc` value as the second argument, allowing -for progressive CRC calculation. +``` javascript +const Sse4Crc32 = require("sse4_crc32") +``` - var crc = Sse4Crc32.calculate("my string"); - var newCrc = Sse4Crc32.calculate("my new string", crc); +Calculate the 32-bit CRC for any string: -Another way to calculate CRC in a progressive manner is as follows: +``` javascript +const crc = Sse4Crc32.calculate("my string") +``` - var sse4crc32 = new Sse4Crc32.CRC32(); - var myInputs = [ "some string", new Buffer("a buffer"), "yet another string", new Buffer("yet another buffer") ]; - var crc; +Instead of passing in a string, a buffer can be passed to the `calculate()` function. Furthermore, the `calculate()` function takes an optional `initialCrc` value as the second argument, allowing for progressive CRC calculation. - myInputs.forEach(function (string) { - sse4crc32.update(string); - }); +``` javascript +const crc = Sse4Crc32.calculate("my string") +const newCrc = Sse4Crc32.calculate("my new string", crc) +``` - crc = sse4crc32.crc(); +You can also calculate the CRC for streams, as follows: -Also see the example code in the [examples](https://github.com/anandsuresh/sse4_crc32/tree/master/examples) -directory. +``` javascript +const stream = Sse4Crc32 + .fromStream(fs.createReadStream('/path/to/file')) + .on('finish', () => console.log(`CRC-32C: ${stream.crc}`)) +``` -## How to compile +## how to compile Once the repository has been cloned, use one of the following commands to build the library: - make all // Builds the release version of the library and runs all tests - make debug // Builds the debug version of the library - make clean // Removes all files generated by builds - +``` bash +make all // Builds the release version of the library and runs all tests +make debug // Builds the debug version of the library +make clean // Removes all files generated by builds +``` -## Contact +## contact All feedback/suggestions/criticisms can be directed to [Anand Suresh](http://www.github.com/anandsuresh) diff --git a/appveyor.yml b/appveyor.yml index 274af68..3a9e22f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,18 +1,30 @@ version: 0.0.{build} environment: matrix: - - nodejs_version: "8" -image: - - Visual Studio 2017 - - Visual Studio 2015 - - Visual Studio 2013 + - nodejs_version: "8" + platform: x64 + - nodejs_version: "8" + platform: x86 + - nodejs_version: "9" + platform: x64 + - nodejs_version: "9" + platform: x86 + - nodejs_version: "10" + platform: x64 + - nodejs_version: "10" + platform: x86 + - nodejs_version: "11" + platform: x64 + - nodejs_version: "11" + platform: x86 + - nodejs_version: "12" + platform: x64 + - nodejs_version: "12" + platform: x86 install: - - ps: Install-Product node $env:nodejs_version x64 - # Prepend 2017, 2015 and 2013 tools. Harmless when the path for the other versions don't exist. - - set "PATH=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin;%ProgramFiles(x86)%\MSBuild\14.0\Bin;%ProgramFiles(x86)%\MSBuild\12.0\Bin;%PATH%" + - ps: Install-Product node $env:nodejs_version $env:platform # Upgrade npm to latest - - npm install --loglevel error -g npm - - set "PATH=%APPDATA%\npm;%PATH%" + - 'if "%nodejs_version%" LEQ 5 (npm install -g npm@5) else (npm install -g npm@latest)' - node -v - npm -v - npm install -g --loglevel error node-gyp diff --git a/benchmark/1.single_1kb_length_buffer.benchmark.js b/benchmark/1.single_1kb_length_buffer.benchmark.js deleted file mode 100644 index a7ec7c8..0000000 --- a/benchmark/1.single_1kb_length_buffer.benchmark.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file Benchmarks using a single 1kB buffer - * - * @author Anand Suresh - * @copyright Copyright (C) 2007-2015 Voxer LLC. All rights reserved. - * @license MIT - */ - -var Sse4Crc32 = require('../sse4_crc32'); -var jsCrc32 = require('crc32'); -var crc, startTime, endTime, i; - - - -/** - * The input buffer for the CRC-benchmarks - * @type {Buffer} - */ -var TEST_BUFFER = new Buffer('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mollis cursus metus vel tristique. Proin congue massa massa, a malesuada dolor ullamcorper a. Nulla eget leo vel orci venenatis placerat. Donec semper condimentum justo, vel sollicitudin dolor consequat id. Nunc sed aliquet felis, eget congue nisi. Mauris eu justo suscipit, elementum turpis ut, molestie tellus. Mauris ornare rutrum fringilla. Nulla dignissim luctus pretium. Nullam nec eros hendrerit sapien pellentesque sollicitudin. Integer eget ligula dui. Mauris nec cursus nibh. Nunc interdum elementum leo, eu sagittis eros sodales nec. Duis dictum nulla sed tincidunt malesuada. Quisque in vulputate sapien. Sed sit amet tellus a est porta rhoncus sed eu metus. Mauris non pulvinar nisl, volutpat luctus enim. Suspendisse est nisi, sagittis at risus quis, ultricies rhoncus sem. Donec ullamcorper purus eget sapien facilisis, eu eleifend felis viverra. Suspendisse elit neque, semper aliquet neque sed, egestas tempus leo. Duis condimentum turpis duis.'); - - -/** - * The number of times to invoke the CRC function for the benchmarks - * @type {Number} - */ -var INVOCATIONS = 100000; - - - -console.log(INVOCATIONS + ' calls to calculate CRC on a ' + TEST_BUFFER.length + ' byte buffer...'); - - - -// Hardware-based CRC-32C -startTime = process.hrtime(); -for (i = 0; i < INVOCATIONS; i++) { - crc = Sse4Crc32.calculateOnHardware(TEST_BUFFER); -} -endTime = process.hrtime(startTime); -console.log('\tHardware-based CRC-32C: %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); - - -// Software-based CRC-32C -startTime = process.hrtime(); -for (i = 0; i < INVOCATIONS; i++) { - crc = Sse4Crc32.calculateInSoftware(TEST_BUFFER); -} -endTime = process.hrtime(startTime); -console.log('\tSoftware-based CRC-32C: %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); - - -// Pure JS CRC-32C (table-based) -startTime = process.hrtime(); -for (i = 0; i < INVOCATIONS; i++) { - crc = jsCrc32(TEST_BUFFER, false); -} -endTime = process.hrtime(startTime); -console.log('\tPure JS CRC-32C (table-based): %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); - - -// Pure JS CRC-32C (direct) -startTime = process.hrtime(); -for (i = 0; i < INVOCATIONS; i++) { - crc = jsCrc32(TEST_BUFFER, true); -} -endTime = process.hrtime(startTime); -console.log('\tPure JS CRC-32C (direct): %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); diff --git a/benchmark/2.multi_random_length_buffer.benchmark.js b/benchmark/2.multi_random_length_buffer.benchmark.js deleted file mode 100644 index c5c9609..0000000 --- a/benchmark/2.multi_random_length_buffer.benchmark.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file Benchmarks using multiple random-length buffers - * - * @author Anand Suresh - * @copyright Copyright (C) 2007-2015 Voxer LLC. All rights reserved. - * @license MIT - */ - -var Sse4Crc32 = require('../sse4_crc32'); -var jsCrc32 = require('crc32'); -var crc, startTime, endTime, i; - - - -/** - * Maximum length of a test buffer - * @type {Number} - */ -var MAX_STRING_LENGTH = 4096; - - -/** - * A list of valid characters used to compose the test buffers - * @type {String} - */ -var VALID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+_=-[]{}\\|;\':",./<>?`~ '; - - -/** - * The number of times to invoke the CRC function for the benchmarks - * @type {Number} - */ -var INVOCATIONS = 100000; - - - -// Generate the test buffers -var testBuffers = []; -var avgBufferLength = 0; - -console.log(INVOCATIONS + ' calls to calculate CRC on random-length buffers upto ' + MAX_STRING_LENGTH + ' bytes long...'); -for (i = 0; i < INVOCATIONS; i++) { - var len = Math.random() * MAX_STRING_LENGTH; - var str = ''; - - for (var j = 0; j < len; j++) { - str += VALID_CHARS.charAt(Math.random() * VALID_CHARS.length); - } - testBuffers.push(new Buffer(str)); - avgBufferLength += len; -} -avgBufferLength /= INVOCATIONS; -console.log('\tAvg. buffer length: %d bytes', parseInt(avgBufferLength)); - - - -// Hardware-based CRC-32C -startTime = process.hrtime(); -testBuffers.forEach(function(str) { - crc = Sse4Crc32.calculateOnHardware(str); -}); -endTime = process.hrtime(startTime); -console.log('\tHardware-based CRC-32C: %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); - - -// Software-based CRC-32C -startTime = process.hrtime(); -testBuffers.forEach(function(str) { - crc = Sse4Crc32.calculateInSoftware(str); -}); -endTime = process.hrtime(startTime); -console.log('\tSoftware-based CRC-32C: %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); - - -// Pure JS CRC-32C (table-based) -startTime = process.hrtime(); -testBuffers.forEach(function(str) { - crc = jsCrc32(str, false); -}); -endTime = process.hrtime(startTime); -console.log('\tPure JS CRC-32C (table-based): %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); - - -// Pure JS CRC-32C (direct) -startTime = process.hrtime(); -testBuffers.forEach(function(str) { - crc = jsCrc32(str, true); -}); -endTime = process.hrtime(startTime); -console.log('\tPure JS CRC-32C (direct): %d ms', (endTime[0] * 1e9 + endTime[1]) / (1000 * 1000)); diff --git a/benchmark/index.js b/benchmark/index.js new file mode 100644 index 0000000..ad41e77 --- /dev/null +++ b/benchmark/index.js @@ -0,0 +1,42 @@ +/** + * @file Runs the benchmarks and reports results + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + * @license MIT + */ + +/** + * Records and returns the run-time of the specified function + * + * @param {Function} fn The function to execute + * @param {Number} count The number of iterations to run the function + * @returns {Number} The run-time of the function (in ms) + */ +function run (fn, count) { + const startTime = process.hrtime() + for (let i = 0; i < count; i++) fn(i) + const endTime = process.hrtime(startTime) + + return (endTime[0] * 1e9 + endTime[1]) / 1000000 +} + +/** + * Runs the benchmarks + */ +[ + require('./single_fixed_size_buffer.benchmark'), + require('./multiple_random_size_buffers.benchmark') +].forEach(benchmark => { + // init the benchmark, if needed + if (typeof benchmark.init === 'function') { + benchmark.init(benchmark) + } + + // run the tests + console.log(`${benchmark.title}, ${benchmark.iterations} iterations:`) + benchmark.tests + .map(test => Object.assign(test, {time: run(test.fn, benchmark.iterations)})) + .forEach(test => console.log(`- ${test.title}: %d ms`, test.time)) + console.log('\n') +}) diff --git a/benchmark/multiple_random_size_buffers.benchmark.js b/benchmark/multiple_random_size_buffers.benchmark.js new file mode 100644 index 0000000..59750b5 --- /dev/null +++ b/benchmark/multiple_random_size_buffers.benchmark.js @@ -0,0 +1,64 @@ +/** + * @file Benchmarks using multiple random-length buffers + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + * @license MIT + */ + +const Crc32C = require('..') +const jsCrc32 = require('crc32') + +/** + * A list of pre-generated buffers for the benchmark + * + * The use of pre-generated buffers as opposed to strings is to eliminate the + * possibility of the interference from the garbage-collector during the test. + * + * @type {Buffer[]} + */ +const BUFFERS = [] + +/** + * Initializes a list of randomly-sized buffers + * @param {Object} benchmark Reference to the benchmark being executed + */ +function initBuffers (benchmark) { + for (i = 0; i < benchmark.iterations; i++) { + const len = Math.random() * 65536 + const buf = Buffer.alloc(len) + + let j = 0 + do { + let data = Math.random().toString(36).slice(2) + let size = j + data.length > len ? len - j : data.length + buf.write(data, j, size) + j += size + } while (j < len) + + BUFFERS.push(buf) + } +} + +/** + * The set of tests to run + * @type {Object} + */ +module.exports = { + title: 'multiple random-length buffers', + iterations: 10000, + init: initBuffers, + tests: [{ + title: 'Native SSE 4.2 CRC-32C', + fn: i => Crc32C.sse42_crc(BUFFERS[i]) + }, { + title: 'Native Table-based CRC-32C', + fn: i => Crc32C.table_crc(BUFFERS[i]) + }, { + title: 'JavaScript (table-based) CRC-32C', + fn: i => jsCrc32(BUFFERS[i], false) + }, { + title: 'JavaScript (direct) CRC-32C', + fn: i => jsCrc32(BUFFERS[i], true) + }] +} diff --git a/benchmark/single_fixed_size_buffer.benchmark.js b/benchmark/single_fixed_size_buffer.benchmark.js new file mode 100644 index 0000000..83357ea --- /dev/null +++ b/benchmark/single_fixed_size_buffer.benchmark.js @@ -0,0 +1,51 @@ +/** + * @file Benchmarks using a single fixed-size buffer + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + * @license MIT + */ + +const Crc32C = require('..') +const jsCrc32 = require('crc32') + +/** + * The input buffer for the CRC-benchmarks + * @type {Buffer} + */ +const TEST_BUFFER = Buffer.from(`Lorem ipsum dolor sit amet, consectetur +adipiscing elit. Morbi mollis cursus metus vel tristique. Proin congue massa +massa, a malesuada dolor ullamcorper a. Nulla eget leo vel orci venenatis +placerat. Donec semper condimentum justo, vel sollicitudin dolor consequat id. +Nunc sed aliquet felis, eget congue nisi. Mauris eu justo suscipit, elementum +turpis ut, molestie tellus. Mauris ornare rutrum fringilla. Nulla dignissim +luctus pretium. Nullam nec eros hendrerit sapien pellentesque sollicitudin. +Integer eget ligula dui. Mauris nec cursus nibh. Nunc interdum elementum leo, eu +sagittis eros sodales nec. Duis dictum nulla sed tincidunt malesuada. Quisque in +vulputate sapien. Sed sit amet tellus a est porta rhoncus sed eu metus. Mauris +non pulvinar nisl, volutpat luctus enim. Suspendisse est nisi, sagittis at risus +quis, ultricies rhoncus sem. Donec ullamcorper purus eget sapien facilisis, eu +eleifend felis viverra. Suspendisse elit neque, semper aliquet neque sed, +egestas tempus leo. Duis condimentum turpis duis.`) + +/** + * The set of tests to run + * @type {Array} + */ +module.exports = { + title: 'single fixed-size buffer', + iterations: 10000, + tests: [{ + title: 'Native SSE 4.2 CRC-32C', + fn: () => Crc32C.sse42_crc(TEST_BUFFER) + }, { + title: 'Native Table-based CRC-32C', + fn: () => Crc32C.table_crc(TEST_BUFFER) + }, { + title: 'JavaScript (table-based) CRC-32C', + fn: () => jsCrc32(TEST_BUFFER, false) + }, { + title: 'JavaScript (direct) CRC-32C', + fn: () => jsCrc32(TEST_BUFFER, true) + }] +} diff --git a/binding.gyp b/binding.gyp index 523fff1..d70b70f 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,35 +2,44 @@ "includes": [ "./common.gypi" ], "conditions":[ ['target_arch in "ia32 x32 x64 x86 x86_64"', { - "targets": [ - { - "target_name": "crc32c_sse42", - "type": "static_library", - "sources": [ - "src/crc32c_sse42.cpp" - ], - "xcode_settings": { - "GCC_ENABLE_SSE42_EXTENSIONS": "YES" - }, - "cflags": [ "-msse4.2" ] - } - ] + "targets": [{ + "target_name": "sse42", + "type": "static_library", + "sources": [ "src/sse42.cpp" ], + "xcode_settings": { + "GCC_ENABLE_SSE42_EXTENSIONS": "YES" + }, + "cflags": [ "-msse4.2" ] + }] }] ], "targets": [ { - "target_name": "sse4_crc32", - "sources": [ - "src/crc32c.cpp" - ], + "target_name": "crc32c", + "sources": [ "src/crc32c.cpp", "src/table.cpp" ], + 'cflags!' : ['-fno-exceptions'], + 'cflags_cc!' : ['-fno-exceptions'], "include_dirs": [ - " - * @copyright Copyright (C) 2007-2015 Voxer LLC. All rights reserved. - * @license MIT - */ - -var Sse4Crc32 = require('../sse4_crc32'); - - -// Usage for calculating CRC32 for strings -var str = 'SSE4-CRC32: A hardware accelerated CRC32 implementation for node.js'; -console.log('crc("' + str + '") = ' + Sse4Crc32.calculate(str)); - - -// Usage for calculating CRC32 for buffers -var buf = new Buffer('SSE4-CRC32: A hardware accelerated CRC32 implementation for node.js'); -console.log('crc("' + buf + '") = ' + Sse4Crc32.calculate(buf)); - - -// Usage for calculating progressive CRC32 -var crc32 = new Sse4Crc32.CRC32(), - array = [ 'SSE4-CRC32: ', 'A hardware accelerated CRC32 implementation ', 'for node.js' ]; -array.forEach(function (str) { - crc32.update(str); -}); -console.log('crc([' + array + ']) = ' + crc32.crc()); diff --git a/index.js b/index.js new file mode 100644 index 0000000..cd54586 --- /dev/null +++ b/index.js @@ -0,0 +1,62 @@ +/** + * @file Provides hardware-based CRC-32C calculation with software fallback + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + * @license MIT + */ + +const Crc32C = require('bindings')('crc32c') +const stream = require('stream') +const util = require('util') + +/** + * Calculates the CRC32C for a stream + * + * @param {Number} [crc] The initial CRC, if any + * @constructor + */ +function Crc32CStream (crc) { + if (!(this instanceof Crc32CStream)) { + return new Crc32CStream(crc) + } + + if (crc == null) { + this.crc = 0 + } else if (typeof crc !== 'number') { + throw new TypeError(`crc MUST be a number; got ${typeof crc}`) + } else { + this.crc = crc + } + + Crc32CStream.super_.call(this, { + write: function (chunk, encoding, next) { + var err + + try { + this.crc = module.exports.calculate(chunk, this.crc) + } catch (e) { + err = e + } finally { + next(err, chunk) + } + } + }) +} +util.inherits(Crc32CStream, stream.Writable) + +/** + * Export the interface + * @type {Object} + */ +module.exports = { + fromStream: function (stream, crc) { return stream.pipe(new Crc32CStream(crc)) }, + calculate: Crc32C.hardware_support ? Crc32C.sse42_crc : Crc32C.table_crc +} + +// for debugging/benchmarks +if (process.NODE_ENV !== 'production') { + module.exports.hardware_support = Crc32C.hardware_support + module.exports.sse42_crc = Crc32C.sse42_crc + module.exports.table_crc = Crc32C.table_crc +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c6b61cb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1720 @@ +{ + "name": "sse4_crc32", + "version": "6.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "crc32": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/crc32/-/crc32-0.2.2.tgz", + "integrity": "sha1-etIg1v/c0Rn5/BJ6d3LKzqOQpLo=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", + "dev": true + }, + "es-abstract": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", + "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.8.6.tgz", + "integrity": "sha512-lFG7d6g3+/UaFDCOtqPiKAC9zngWWsQZl1g5q6gaONqrjq61SX2xFqXMleQiFVyDpYwa018E9hmlAFY22PCb+A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.2.tgz", + "integrity": "sha512-hR3At21uSrsjjDTWrbu0IMLTpnkpv8IIMFDFaoz43Tmu4LkmAXfH44vNNzpTnf+OAQQCHrb91y/wc2J4x5XgSQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", + "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-addon-api": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.3.0.tgz", + "integrity": "sha512-yagD4yKkZLeG4EJkh+8Qbqhqw+owDQ/PowqD8vb5a5rfNXS/PRC21SGyIbUVXfPp/jl4s+jyeZj6xnLnDPLazw==" + }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node-gyp": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.3.tgz", + "integrity": "sha512-z/JdtkFGUm0QaQUusvloyYuGDub3nUbOo5de1Fz57cM++osBTvQatBUSTlF1k/w8vFHPxxXW6zxGvkxXSpaBkQ==", + "dev": true, + "requires": { + "env-paths": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^4.4.8", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + }, + "yargs": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + } + } +} diff --git a/package.json b/package.json index 8c37fa4..6d88975 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "sse4.2", "error detection" ], - "version": "5.4.0", + "version": "6.0.0", "author": "Anand Suresh (https://github.com/anandsuresh)", "license": "MIT", "repository": { @@ -21,22 +21,22 @@ "url": "https://github.com/anandsuresh/sse4_crc32/issues" }, "engines": { - "node": ">=5.10.0" + "node": ">=4" }, - "main": "./sse4_crc32", "dependencies": { - "bindings": "~1.5.0", - "nan": "2.14.0" + "bindings": "^1.3.0", + "node-addon-api": "^1.3.0" }, "devDependencies": { - "chai": "^2.0.0", - "crc32": "^0.2.1", - "mocha": "^2.1.0", + "chai": "^4.2.0", + "crc32": "^0.2.2", + "mocha": "^6.2.0", "node-gyp": "^5.0.3" }, "scripts": { + "benchmark": "node benchmark", "test": "mocha test/*test.js", - "debug": "echo \"platform: $(uname -a)\"; echo \"node.js: $(node -v)\"; echo \"npm: $(npm -v)\"; echo \"node-gyp: $(./node_modules/.bin/node-gyp -v)\"; node -e \"console.log('H/w CRC Support: ' + require('./sse4_crc32').isHardwareCrcSupported());\"; node -pe \"require('./sse4_crc32');\"", - "hwcrc": "node -e \"console.log('H/w CRC Support: ' + require('./sse4_crc32').isHardwareCrcSupported());\"" + "debug": "echo \"platform: $(uname -a)\"; echo \"node.js: $(node -v)\"; echo \"npm: $(npm -v)\"; echo \"node-gyp: $(./node_modules/.bin/node-gyp -v)\"; node -e \"console.log('H/w CRC Support: ' + require('.').hardware_support);\"; node -pe \"require('.');\"", + "hwcrc": "node -e \"console.log('H/w CRC Support: ' + require('.').hardware_support);\"" } } diff --git a/src/crc32c.cpp b/src/crc32c.cpp index 18e7cd2..321449b 100644 --- a/src/crc32c.cpp +++ b/src/crc32c.cpp @@ -1,247 +1,123 @@ /** * @file crc32c.cpp - * @brief Node.js bindings for CRC-32C calculation using hardware-acceleration, when available. + * @brief Node.js bindings for CRC-32C calculation * - * The code below provides the bindings for the node-addon allowing for interfacing of C/C++ code with - * JavaScript. It chooses between two versions of the CRC-32C calculator: - * - The hardware-accelerated version that uses Intel's SSE 4.2 instructions, implemented in crc32c_sse42.cpp - * - A table-lookup based CRC calculated implemented in software for non-Nehalam-based architectures + * The code below provides the bindings for the node-addon allowing for + * interfacing of C/C++ code with JavaScript. It chooses between two versions of + * the CRC-32C calculator: + * - The hardware-accelerated version that uses Intel's SSE 4.2 instructions + * implemented in sse42.cpp + * - A table-lookup based CRC calculated implemented in software for non-Nehalam + * architectures implemented in table.cpp * * NOTES: - * - This code, though originally designed for little-endian hardware, should work for all platforms. - * - Table-based CRC-32C implementation based on code by Mark Adler at http://stackoverflow.com/a/17646775. + * - This code, though originally designed for little-endian hardware, should + * work for all platforms. + * - Table-based CRC-32C implementation based on code by Mark Adler at + * http://stackoverflow.com/a/17646775. * * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. */ -#include -#include - -#include "crc32c.h" - - - -using namespace v8; -using namespace node; - - - -// Bit-mask for the SSE 4.2 flag in the CPU ID -#define SSE4_2_FLAG 0x100000 - -// The CRC-32C polynomial in reversed bit order -#define CRC32C_POLYNOMIAL 0x82f63b78 - - - -// Stores the CRC-32 lookup table for the software-fallback implementation -static uint32_t crc32cTable[8][256]; - - - -/** - * Cross-platform CPU feature set detection to check for availability of hardware-based CRC-32C - */ -#ifdef _MSC_VER -#include // __cpuid -#endif -void cpuid(uint32_t op, uint32_t reg[4]) { -#if defined(_MSC_VER) - __cpuid((int *)reg, 1); -#elif defined(__x86_64__) - __asm__ volatile( - "pushq %%rbx \n\t" - "cpuid \n\t" - "movl %%ebx, %1 \n\t" - "popq %%rbx \n\t" - : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) - : "a"(op) - : "cc"); -#elif defined(__i386__) - __asm__ volatile( - "pushl %%ebx \n\t" - "cpuid \n\t" - "movl %%ebx, %1 \n\t" - "popl %%ebx \n\t" - : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) - : "a"(op) - : "cc"); -#else - reg[0] = reg[1] = reg[2] = reg[3] = 0; -#endif -} - - -/** - * Returns whether or not Intel's Streaming SIMD Extensions 4.2 is available on the hardware - * - * @return true if Intel's Streaming SIMD Extensions 4.2 are present; otherwise false - */ -bool isSSE42Available() { - uint32_t reg[4]; - - cpuid(1, reg); - return ((reg[2] >> 20) & 1) == 1; -} - +#include "sse42.h" +#include "table.h" +#include +#include /** - * Initializes the CRC-32C lookup table for software-based CRC calculation + * Calculates CRC-32C for the specified string/buffer using SSE 4.2 extensions */ -void initCrcTable() { - uint32_t i, j, crc; - - for (i = 0; i < 256; i++) { - crc = i; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; - crc32cTable[0][i] = crc; - } - - for (i = 0; i < 256; i++) { - crc = crc32cTable[0][i]; - for (j = 1; j < 8; j++) { - crc = crc32cTable[0][crc & 0xff] ^ (crc >> 8); - crc32cTable[j][i] = crc; - } +Napi::Value sse42_crc(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + uint32_t crc = 0; + const char *buf; + size_t len; + + // validate arguments + if (info.Length() < 1) { + return Napi::Number::New(env, 0); + } else if (info.Length() > 2) { + throw Napi::TypeError::New(env, "Invalid number of arguments!"); + } + + // use the specified initial CRC, if any + if (info.Length() > 1) { + if (!(info[1].IsNumber())) { + throw Napi::TypeError::New(env, "Initial CRC-32C is not an integer!"); } + crc = info[1].As().Uint32Value(); + } + + // Ensure the argument is a buffer or a string + if (info[0].IsBuffer()) { + Napi::Buffer data = info[0].As>(); + buf = (const char *)data.Data(); + len = (size_t)data.Length(); + } else if (info[0].IsString()) { + std::string strInput = info[0].As().Utf8Value(); + buf = (const char *)strInput.c_str(); + len = (size_t)strInput.length(); + } else { + throw Napi::TypeError::New(env, "input is not a string/buffer!"); + } + + return Napi::Number::New(env, sse42_calculate(crc, buf, len)); } - /** - * Calculates CRC-32C using the lookup table - * - * @param initialCrc The initial CRC to use for the operation - * @param buf The buffer that stores the data whose CRC is to be calculated - * @param len The size of the buffer - * @return The CRC-32C of the data in the buffer + * Calculates CRC-32C for the specified string/buffer using table-lookups */ -uint32_t swCrc32c(uint32_t initialCrc, const char *buf, size_t len) { - const char *next = buf; - uint64_t crc = initialCrc; - - - // If the string is empty, return 0 - if (len == 0) return (uint32_t)crc; - - // XOR the initial CRC with INT_MAX - crc ^= 0xFFFFFFFF; - - // Process byte-by-byte until aligned to 8-byte boundary - while (len && ((uintptr_t) next & 7) != 0) { - crc = crc32cTable[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); - len--; - } - - // Process 8 bytes at a time - while (len >= 8) { - crc ^= *(uint64_t *) next; - crc = crc32cTable[7][(crc >> 0) & 0xff] ^ crc32cTable[6][(crc >> 8) & 0xff] - ^ crc32cTable[5][(crc >> 16) & 0xff] ^ crc32cTable[4][(crc >> 24) & 0xff] - ^ crc32cTable[3][(crc >> 32) & 0xff] ^ crc32cTable[2][(crc >> 40) & 0xff] - ^ crc32cTable[1][(crc >> 48) & 0xff] ^ crc32cTable[0][(crc >> 56)]; - next += 8; - len -= 8; +Napi::Value table_crc(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + uint32_t crc = 0; + const char *buf; + size_t len; + + // validate arguments + if (info.Length() < 1) { + return Napi::Number::New(env, 0); + } else if (info.Length() > 2) { + throw Napi::TypeError::New(env, "Invalid number of arguments!"); + } + + // use the specified initial CRC, if any + if (info.Length() > 1) { + if (!(info[1].IsNumber())) { + throw Napi::TypeError::New(env, "Initial CRC-32C is not an integer!"); } - - // Process any remaining bytes - while (len) { - crc = crc32cTable[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); - len--; - } - - // XOR again with INT_MAX - return (uint32_t)(crc ^= 0xFFFFFFFF); -} - - -/** - * Returns whether or not hardware support is available for CRC calculation - */ -NAN_METHOD(isHardwareCrcSupported) { - Nan::HandleScope scope; - info.GetReturnValue().Set(Nan::New(isSSE42Available())); + crc = info[1].As().Uint32Value(); + } + + // Ensure the argument is a buffer or a string + if (info[0].IsBuffer()) { + Napi::Buffer data = info[0].As>(); + buf = (const char *)data.Data(); + len = (size_t)data.Length(); + } else if (info[0].IsString()) { + std::string strInput = info[0].As().Utf8Value(); + buf = (const char *)strInput.c_str(); + len = (size_t)strInput.length(); + } else { + throw Napi::TypeError::New(env, "input is not a string/buffer!"); + } + + return Napi::Number::New(env, table_calculate(crc, buf, len)); } - -/** - * Calculates CRC-32C for the specified string/buffer - */ -NAN_METHOD(calculateCrc) { - Nan::HandleScope scope; - uint32_t initCrc; - uint32_t crc; - bool useHardwareCrc; - - // Ensure an argument is passed - if (info.Length() < 1) { - info.GetReturnValue().Set(Nan::New(0)); - } else if (info.Length() > 3) { - Nan::ThrowTypeError("Invalid number of arguments!"); - return; - } - - // Check if the table-lookup is required - if (!info[0]->IsBoolean()) { - Nan::ThrowTypeError("useHardwareCrc isn't a boolean value as expected!"); - return; - } - useHardwareCrc = Nan::To(info[0]).FromMaybe(false); - - // Check for any initial CRC passed to the function - if (info.Length() > 2) { - if (!(info[2]->IsUint32())) { - Nan::ThrowTypeError("Initial CRC-32C is not an integer value as expected!"); - return; - } - initCrc = Nan::To(info[2]).FromMaybe(0); - } else { - initCrc = 0; - } - - // Ensure the argument is a buffer or a string - if (node::Buffer::HasInstance(info[1])) { - Local buf = Nan::To(info[1]).ToLocalChecked(); - - if (useHardwareCrc) { - crc = hwCrc32c(initCrc, (const char *)Buffer::Data(buf), (size_t)Buffer::Length(buf)); - } else { - crc = swCrc32c(initCrc, (const char *)Buffer::Data(buf), (size_t)Buffer::Length(buf)); - } - } else if (info[1]->IsObject()) { - Nan::ThrowTypeError("Cannot compute CRC-32C for objects!"); - return; - } else { - Local strInput = Nan::To(info[1]).ToLocalChecked(); - - if (useHardwareCrc) { - crc = hwCrc32c(initCrc, (const char *)(*Nan::Utf8String(strInput)), (size_t)Nan::Utf8String(strInput).length()); - } else { - crc = swCrc32c(initCrc, (const char *)(*Nan::Utf8String(strInput)), (size_t)Nan::Utf8String(strInput).length()); - } - } - - // Calculate the 32-bit CRC - info.GetReturnValue().Set(Nan::New(crc)); -} - - - /** * Initialize the module */ -void init(Local exports) { - initCrcTable(); +Napi::Object init(Napi::Env env, Napi::Object exports) { + // initialize the required pieces + table_init(); - Nan::SetMethod(exports, "isHardwareCrcSupported", isHardwareCrcSupported); - Nan::SetMethod(exports, "calculateCrc", calculateCrc); -} + // export the binding interface + exports["hardware_support"] = Napi::Boolean::New(env, sse42_is_available()); + exports["sse42_crc"] = Napi::Function::New(env, sse42_crc); + exports["table_crc"] = Napi::Function::New(env, table_crc); + return exports; +}; -NODE_MODULE(sse4_crc32, init) +NODE_API_MODULE(crc32c, init); diff --git a/src/crc32c.h b/src/crc32c.h deleted file mode 100644 index 4ab2379..0000000 --- a/src/crc32c.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @file crc32c.h - * @brief Function prototypes for hardware-based CRC-32C calculation - * - * @author Anand Suresh - */ - -#include - - -uint32_t hwCrc32c(uint32_t initialCrc, const char *buf, size_t len); diff --git a/src/crc32c_sse42.cpp b/src/crc32c_sse42.cpp deleted file mode 100644 index 1b41c74..0000000 --- a/src/crc32c_sse42.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @file crc32c_sse42.cpp - * @brief CRC32 calculator based on Intel's Streaming SIMD Extensions 4.2 - * - * The code below uses Intel's Streaming SIMD Extensions 4.2 (available from the Nehalam series) to perform - * high-speed CRC-32C calculation on hardware. This code is compiled with the -msse4.2 flag as a static - * library, and then declared as a dependency for the node-addon module, allowing for compilation on - * non-Nehalam processors. - * - * @author Anand Suresh - */ - -#include - -#include "crc32c.h" - - - -// Byte-boundary alignment issues -#define ALIGN_SIZE 0x08UL // Align at an 8-byte boundary -#define ALIGN_MASK (ALIGN_SIZE - 1) // Bitmask for 8-byte bound addresses - -// Performs H/W CRC operations -#define CALC_CRC(op, crc, type, buf, len) \ - do { \ - for (; (len) >= sizeof(type); (len) -= sizeof(type), buf += sizeof(type)) { \ - (crc) = op((crc), *(type *) (buf)); \ - } \ - } while(0) - - - -/** - * Calculates CRC-32C using hardware support - * - * @param initialCrc The initial CRC to use for the operation - * @param buf The buffer that stores the data whose CRC is to be calculated - * @param len The size of the buffer - * @return The CRC-32C of the data in the buffer - */ -uint32_t hwCrc32c(uint32_t initialCrc, const char *buf, size_t len) { - uint32_t crc = initialCrc; - - // If the string is empty, return the initial crc - if (len == 0) return crc; - - // XOR the initial CRC with INT_MAX - crc ^= 0xFFFFFFFF; - - // Align the input to the word boundary - for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) { - crc = _mm_crc32_u8(crc, *buf); - } - - // Blast off the CRC32 calculation on hardware -#if defined(__x86_64__) || defined(_M_X64) - CALC_CRC(_mm_crc32_u64, crc, uint64_t, buf, len); -#endif - CALC_CRC(_mm_crc32_u32, crc, uint32_t, buf, len); - CALC_CRC(_mm_crc32_u16, crc, uint16_t, buf, len); - CALC_CRC(_mm_crc32_u8, crc, uint8_t, buf, len); - - // XOR again with INT_MAX - return (crc ^= 0xFFFFFFFF); -} diff --git a/src/sse42.cpp b/src/sse42.cpp new file mode 100644 index 0000000..23bb268 --- /dev/null +++ b/src/sse42.cpp @@ -0,0 +1,106 @@ +/** + * @file sse42.cpp + * @brief CRC32 calculator based on Intel's Streaming SIMD Extensions 4.2 + * + * The code below uses Intel's Streaming SIMD Extensions 4.2 (available from the + * Nehalam series) to perform high-speed CRC-32C calculation on hardware. This + * code is compiled with the -msse4.2 flag as a static library, and then + * declared as a dependency for the node-addon module, allowing for compilation + * on non-Nehalam processors. + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + */ + +#include "sse42.h" +#include + +// Cross-platform CPU feature set detection to check for availability of Intel's +// Streaming SIMD Extensions 4.2 +#ifdef _MSC_VER +#include // __cpuid +#endif + +// Byte-boundary alignment issues +#define ALIGN_SIZE 0x08UL // Align at an 8-byte boundary +#define ALIGN_MASK (ALIGN_SIZE - 1) // Bitmask for 8-byte bound addresses + +// Performs H/W CRC operations +#define CALC_CRC(op, crc, type, buf, len) \ + do { \ + for (; (len) >= sizeof(type); \ + (len) -= sizeof(type), buf += sizeof(type)) { \ + (crc) = op((crc), *(type *)(buf)); \ + } \ + } while (0) + +/** + * Returns whether Intel's Streaming SIMD Extensions 4.2 is available + * + * @return true if present; otherwise false + */ +bool sse42_is_available() { + uint32_t reg[4]; + +#if defined(_MSC_VER) + __cpuid((int *)reg, 1); +#elif defined(__x86_64__) + __asm__ volatile("pushq %%rbx \n\t" + "cpuid \n\t" + "movl %%ebx, %1 \n\t" + "popq %%rbx \n\t" + : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) + : "a"(1) + : "cc"); +#elif defined(__i386__) + __asm__ volatile("pushl %%ebx \n\t" + "cpuid \n\t" + "movl %%ebx, %1 \n\t" + "popl %%ebx \n\t" + : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) + : "a"(1) + : "cc"); +#else + reg[0] = reg[1] = reg[2] = reg[3] = 0; +#endif + + return ((reg[2] >> 20) & 1) == 1; +} + +/** + * No additional initialization required for SSE 4.2 + */ +void sse42_init() {} + +/** + * Calculates CRC-32C using Intel's SSE 4.2 instruction set + * + * @param crc The initial CRC to use for the operation + * @param buf The buffer storing the data whose CRC is to be calculated + * @param len The size of the buffer + * @return The CRC-32C of the data in the buffer + */ +uint32_t sse42_calculate(uint32_t crc, const char *buf, size_t len) { + // If the string is empty, return the initial crc + if (len == 0) + return crc; + + // XOR the initial CRC with INT_MAX + crc ^= 0xFFFFFFFF; + + // Align the input to the word boundary + for (; (len > 0) && ((size_t)buf & ALIGN_MASK); len--, buf++) { + crc = _mm_crc32_u8(crc, *buf); + } + + // Blast off the CRC32 calculation on hardware +#if defined(__x86_64__) || defined(_M_X64) + CALC_CRC(_mm_crc32_u64, crc, uint64_t, buf, len); +#endif + CALC_CRC(_mm_crc32_u32, crc, uint32_t, buf, len); + CALC_CRC(_mm_crc32_u16, crc, uint16_t, buf, len); + CALC_CRC(_mm_crc32_u8, crc, uint8_t, buf, len); + + // XOR again with INT_MAX + return (crc ^= 0xFFFFFFFF); +} diff --git a/src/sse42.h b/src/sse42.h new file mode 100644 index 0000000..6752b19 --- /dev/null +++ b/src/sse42.h @@ -0,0 +1,19 @@ +/** + * @file sse42.h + * @brief CRC32 calculator based on Intel's Streaming SIMD Extensions 4.2 + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + */ + +#ifndef __CRC32C_SSE42__ +#define __CRC32C_SSE42__ + +#include +#include + +bool sse42_is_available(); +void sse42_init(); +uint32_t sse42_calculate(uint32_t, const char *, size_t); + +#endif // __CRC32C_SSE42__ diff --git a/src/table.cpp b/src/table.cpp new file mode 100644 index 0000000..3f41754 --- /dev/null +++ b/src/table.cpp @@ -0,0 +1,106 @@ +/** + * @file table.cpp + * @brief CRC32C calculator based on table-lookups + * + * Table-based CRC-32C implementation based on code from Mark Adler at + * http://stackoverflow.com/a/17646775. + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + */ + +#include "table.h" + +// The CRC-32C polynomial in reversed bit order +#define CRC32C_POLYNOMIAL 0x82f63b78 + +// Stores the CRC-32 lookup table for the software-fallback implementation +uint32_t table[8][256]; + +/** + * Returns whether or not this mode is available + * + * NOTE: Table-based CRC-32C calculation is available on all platforms! + * + * @return true + */ +bool table_is_available() { return true; } + +/** + * Initializes the CRC-32C lookup table for software-based CRC calculation + */ +void table_init() { + uint32_t i, j, crc; + + for (i = 0; i < 256; i++) { + crc = i; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + crc = crc & 1 ? (crc >> 1) ^ CRC32C_POLYNOMIAL : crc >> 1; + table[0][i] = crc; + } + + for (i = 0; i < 256; i++) { + crc = table[0][i]; + for (j = 1; j < 8; j++) { + crc = table[0][crc & 0xff] ^ (crc >> 8); + table[j][i] = crc; + } + } +} + +/** + * Calculates CRC-32C using the lookup table + * + * @param initCrc The initial CRC to use for the operation + * @param buf The buffer storing the data whose CRC is to be calculated + * @param len The size of the buffer + * @return The CRC-32C of the data in the buffer + */ +uint32_t table_calculate(uint32_t initCrc, const char *buf, size_t len) { + const char *next = buf; + uint64_t crc = initCrc; + + // If the string is empty, return 0 + if (len == 0) { + return (uint32_t)crc; + } + + // XOR the initial CRC with INT_MAX + crc ^= 0xFFFFFFFF; + + // Process byte-by-byte until aligned to 8-byte boundary + while (len && ((uintptr_t)next & 7) != 0) { + crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); + len--; + } + + // Process 8 bytes at a time + while (len >= 8) { + crc ^= *(uint64_t *)next; + crc = table[7][(crc >> 0) & 0xff] ^ + table[6][(crc >> 8) & 0xff] ^ + table[5][(crc >> 16) & 0xff] ^ + table[4][(crc >> 24) & 0xff] ^ + table[3][(crc >> 32) & 0xff] ^ + table[2][(crc >> 40) & 0xff] ^ + table[1][(crc >> 48) & 0xff] ^ + table[0][(crc >> 56)]; + next += 8; + len -= 8; + } + + // Process any remaining bytes + while (len) { + crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8); + len--; + } + + // XOR again with INT_MAX + return (uint32_t)(crc ^= 0xFFFFFFFF); +} diff --git a/src/table.h b/src/table.h new file mode 100644 index 0000000..caf24bd --- /dev/null +++ b/src/table.h @@ -0,0 +1,19 @@ +/** + * @file table.h + * @brief CRC32C calculator based on table-lookups + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + */ + +#ifndef __CRC32C_TABLE__ +#define __CRC32C_TABLE__ + +#include +#include + +bool table_is_available(); +void table_init(); +uint32_t table_calculate(uint32_t, const char *, size_t); + +#endif // __CRC32C_TABLE__ diff --git a/sse4_crc32.js b/sse4_crc32.js deleted file mode 100644 index de9e756..0000000 --- a/sse4_crc32.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @file Provides hardware-based CRC-32C calculation functionality, with software fallback - * - * @author Anand Suresh - * @copyright Copyright (C) 2007-2015 Voxer LLC. All rights reserved. - * @license MIT - */ -var Sse4Crc32 = require("bindings")("sse4_crc32"); - - - -/** - * Calculates CRC in software mode - * - * @param {String|Buffer} input The input string/buffer for which the CRC is to be calculated - * @param {Number} [initialCrc=0] An optional initial CRC - * @returns {Number} - */ -function swCrc32c(input, initialCrc) { - return Sse4Crc32.calculateCrc(false, input, initialCrc || 0); -} - - -/** - * Calculates CRC in hardware mode - * - * @param {String|Buffer} input The input string/buffer for which the CRC is to be calculated - * @param {Number} [initialCrc=0] An optional initial CRC - * @returns {Number} - */ -function hwCrc32c(input, initialCrc) { - return Sse4Crc32.calculateCrc(true, input, initialCrc || 0); -} - - - -/** - * Defines a progressive CRC-32C calculator - * - * @param {String|Buffer} input The input string for which the CRC is to be calculated - * @param {Number} [initialCrc=0] An optional initial CRC - * @constructor - */ -function Crc32C(input, initialCrc) { - this.crc32c = initialCrc || 0; - if (input) this.update(input); -} - - -/** - * Progressively calculates the 32-bit CRC - * - * @param input Additional input to calculate the CRC for - * @returns {Crc32C} - */ -Crc32C.prototype.update = function(input) { - this.crc32c = module.exports.calculate(input, this.crc32c); - return this; -}; - - -/** - * Returns the 32-bit CRC - * - * @returns {Number} - */ -Crc32C.prototype.crc = function() { - return this.crc32c; -}; - - - -/** - * Export the required functions/classes - * @type {Object} - */ -module.exports = { - isHardwareCrcSupported: Sse4Crc32.isHardwareCrcSupported, - calculateInSoftware: swCrc32c, - calculateOnHardware: hwCrc32c, - - CRC32: Crc32C, - calculate: Sse4Crc32.calculateCrc.bind(null, Sse4Crc32.isHardwareCrcSupported()) -}; diff --git a/test/fixture.txt b/test/fixture.txt new file mode 100644 index 0000000..4e9683f --- /dev/null +++ b/test/fixture.txt @@ -0,0 +1,223 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque ullamcorper porta nunc in vestibulum. Fusce id ante pulvinar, rhoncus sapien eu, rutrum ex. Donec sit amet lectus bibendum, euismod nisi a, feugiat enim. Curabitur volutpat tempus pulvinar. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur vel metus tempor, venenatis lacus id, porttitor sapien. Fusce pretium porta arcu et hendrerit. Duis sollicitudin tellus nec quam feugiat euismod. Nunc convallis dui ac tellus laoreet viverra. Nulla dignissim non ligula vitae gravida. Praesent iaculis neque in leo sollicitudin aliquam. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Duis semper felis ipsum, ac interdum tellus congue nec. Sed condimentum ante ac neque tincidunt bibendum. Aenean mollis, ex ut faucibus ultricies, eros mi vulputate orci, ut euismod lorem augue lacinia sapien. Donec dictum tortor et justo elementum elementum. Mauris in felis sem. Nunc ut facilisis ipsum. Aliquam erat volutpat. Ut a sem a velit luctus imperdiet sed sit amet erat. Aenean sagittis porta quam, ut tempor enim. Cras vitae diam dignissim, ultricies dui vitae, ultrices ex. Etiam sit amet sapien id mi blandit viverra. + +Integer eu eros eu arcu finibus ullamcorper sit amet nec ligula. Donec eros tortor, blandit quis nibh non, pulvinar maximus risus. Etiam auctor ac turpis nec facilisis. Phasellus venenatis dictum mollis. Praesent ac tellus sed turpis dictum suscipit. Integer sit amet iaculis nisl. Integer bibendum lacinia tortor, quis blandit sapien sollicitudin in. Phasellus mollis enim lorem, non laoreet urna finibus et. Suspendisse iaculis turpis velit, ut cursus dui venenatis non. Phasellus congue consequat nibh, ac fermentum felis fermentum vel. + +Curabitur ultrices turpis ac gravida viverra. Pellentesque blandit et elit in posuere. Suspendisse ut tristique tellus, at porttitor sem. Duis maximus lacus purus, in tempor diam dictum sed. Proin convallis tincidunt rutrum. Quisque mattis sem at consequat consequat. In fringilla tortor a sollicitudin pretium. Sed tempor felis lorem, sed fringilla augue placerat in. Maecenas accumsan mi vitae neque euismod, ullamcorper molestie lacus consequat. Integer accumsan et felis vitae suscipit. + +Proin vel felis leo. Pellentesque eu ligula fringilla, scelerisque justo nec, laoreet leo. Etiam placerat felis nec orci dapibus, in auctor nisi consectetur. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum interdum metus auctor tortor congue semper. Ut tempus tortor sit amet convallis blandit. Praesent et leo quis lacus laoreet convallis nec eu nunc. Curabitur consequat ultrices leo eget aliquet. Quisque a mattis mauris, ut tristique dolor. Nam vel massa convallis, cursus nunc vel, pretium justo. Aenean ac viverra dui, nec vestibulum orci. Aenean egestas nulla id justo condimentum, quis pulvinar neque faucibus. + +Etiam vitae diam iaculis, accumsan urna sed, pulvinar ligula. Aenean faucibus vulputate nulla in maximus. Suspendisse eu interdum est. Ut quam ex, rutrum vel mattis vitae, consectetur quis neque. Suspendisse aliquam rhoncus dolor. Donec rutrum libero eu urna imperdiet consectetur. Sed eu enim elit. Ut tincidunt, mi ut sagittis egestas, est augue volutpat ante, imperdiet faucibus dui tortor vel sapien. Nam pellentesque nisi sed dolor sagittis mollis. Pellentesque tincidunt felis vitae est venenatis ultricies. Nullam at volutpat augue. Etiam leo arcu, porttitor eget lorem non, interdum lobortis sem. Ut quis rutrum neque, eu lacinia ante. + +Sed porta eleifend condimentum. Maecenas ornare orci et lobortis pretium. Fusce sit amet sapien ac quam hendrerit facilisis. Donec aliquet lacus odio, at lacinia odio sollicitudin quis. Nam in dolor ut augue ultrices semper tincidunt nec lectus. Ut mauris felis, sagittis et dictum sed, volutpat et nibh. Quisque est urna, commodo sit amet nunc in, sagittis fringilla lectus. Mauris interdum est vitae lectus aliquam dignissim. Etiam dapibus, massa ut rutrum ultricies, tortor elit suscipit odio, sed aliquam nunc leo vel lorem. Vivamus convallis posuere aliquet. Nullam eu purus eleifend, suscipit tellus non, maximus nisl. Etiam rhoncus mollis mi, eget dictum tortor consequat efficitur. Fusce mollis, risus eget sollicitudin vulputate, lorem ante rutrum nisl, a faucibus nisl erat non sem. Nulla sodales faucibus posuere. + +Morbi interdum metus vitae enim tempus tincidunt. Morbi venenatis condimentum elementum. Curabitur a feugiat turpis. Mauris laoreet convallis nibh, in suscipit felis ultricies dignissim. Morbi orci quam, convallis tincidunt mauris ac, imperdiet porta lacus. Nam aliquet, mauris eu vestibulum fermentum, orci leo volutpat ligula, in tincidunt mauris nisl eu justo. Morbi consequat odio eu ante viverra, nec euismod ante convallis. Quisque sagittis eget ipsum consequat cursus. Proin pretium eleifend placerat. Fusce vel massa iaculis, ultricies ex sed, congue ligula. Fusce vel facilisis odio. Nullam vel porttitor diam, a pretium nisl. Maecenas nec massa et orci ornare iaculis at eu neque. Nam sagittis volutpat feugiat. Nulla venenatis arcu ac enim pharetra consequat. + +Nam consequat pellentesque magna sit amet pretium. Maecenas euismod nisl nec semper maximus. In hac habitasse platea dictumst. Proin ornare molestie ex. Etiam risus elit, ultricies in auctor ac, pulvinar venenatis felis. Phasellus ut massa a sapien bibendum egestas et a ipsum. Curabitur suscipit elit id ligula euismod, at scelerisque velit sollicitudin. + +Morbi porttitor tincidunt pretium. Proin pellentesque aliquam vehicula. In in quam a augue hendrerit maximus sit amet et lorem. Proin posuere lacus ut egestas porta. Sed commodo feugiat lectus, a gravida massa consequat ac. Pellentesque eget ipsum ligula. Proin interdum eleifend orci at aliquet. Praesent varius sed erat dignissim pulvinar. Nullam vitae tincidunt odio. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce non vehicula odio. + +Morbi eleifend lacus non neque sagittis aliquet. Nullam eu porttitor leo. Vivamus ut mauris non tellus ultrices finibus quis a neque. Aliquam erat volutpat. Nulla porttitor, dui eu eleifend mollis, purus eros placerat orci, non luctus ex turpis et dui. Cras a libero id dolor commodo suscipit non sodales tellus. Suspendisse sed massa eu dui sagittis viverra. Integer faucibus leo at aliquet feugiat. Sed leo lectus, pulvinar id neque sit amet, aliquet elementum odio. Suspendisse consectetur, quam sed tempor accumsan, libero tortor faucibus magna, a dignissim erat libero sed nisi. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus feugiat dictum lectus et congue. + +Suspendisse potenti. Aenean semper libero ut pulvinar blandit. Suspendisse quis lacus nec ipsum congue rhoncus. Sed malesuada dictum mi. Vestibulum vitae sem vitae mauris condimentum sodales. Mauris egestas nibh ultricies, tempor elit quis, fringilla est. Suspendisse potenti. Ut ultrices mollis sodales. Sed ut efficitur ipsum. + +Mauris at mi eu nibh tempus varius. Pellentesque rhoncus lectus non convallis vehicula. Vivamus finibus nibh elit, eu ullamcorper lacus congue finibus. Praesent semper magna eu mauris ornare, non malesuada diam vehicula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In nec diam a ipsum laoreet laoreet sit amet et justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean eget mauris nisl. Phasellus urna erat, iaculis nec pellentesque sit amet, efficitur a sem. In efficitur fermentum lacus, ac mattis neque commodo ac. + +Aliquam ut libero dui. Aliquam rutrum vulputate libero a mattis. Donec rutrum maximus lorem. Sed rhoncus eros nunc, in feugiat magna rutrum ut. Mauris mauris eros, maximus nec lacus ut, sagittis finibus augue. Mauris porta magna et urna vehicula molestie. Sed in molestie tortor. + +Ut condimentum augue ut quam auctor aliquam. Sed id scelerisque ligula. Donec blandit lorem sem, ac auctor mi malesuada convallis. Vivamus sollicitudin lectus nec nisi aliquam, in blandit ipsum interdum. Aliquam non vehicula massa. Aliquam dignissim interdum est, quis sollicitudin turpis mollis et. Aenean accumsan lectus erat, ac aliquam eros iaculis gravida. Duis rutrum at metus ac facilisis. + +Ut molestie nec metus in scelerisque. Aliquam tempor orci mauris, quis ullamcorper justo volutpat sit amet. Vestibulum sit amet elementum neque, commodo dapibus nunc. Aliquam consequat ligula odio, non blandit nibh ornare et. Integer suscipit velit et turpis dignissim feugiat. Duis tempor sapien nec consectetur vulputate. Vivamus semper accumsan auctor. + +Donec congue orci at odio ultricies semper. Ut congue orci vel tortor sollicitudin pharetra. Vivamus a odio egestas dui fringilla porta sit amet a erat. Etiam nec lectus nec urna scelerisque hendrerit. Aliquam erat volutpat. Aliquam erat volutpat. Praesent molestie vehicula sem nec laoreet. Phasellus auctor varius libero et ultrices. Mauris lacinia in urna nec feugiat. + +Nulla vitae orci nisi. Sed vestibulum enim quis ipsum rhoncus fermentum. Nunc aliquam magna sit amet venenatis sagittis. Ut non semper velit. Aliquam ultricies suscipit dui at dapibus. Aenean ultricies sem faucibus lectus suscipit lacinia. Maecenas at nibh vel lacus sagittis lacinia. Sed accumsan nisi id nibh sodales imperdiet. Aliquam mollis sem sit amet tortor pharetra, vitae congue diam dictum. Nulla mollis pellentesque sagittis. Nunc quis sem lacinia, sodales risus ut, maximus odio. Phasellus egestas posuere diam eu tempus. Nam volutpat felis dolor, sed suscipit ante posuere vel. In ipsum libero, molestie quis lobortis quis, tempus in metus. + +Proin a ex tellus. Quisque tincidunt, mauris eu tempus elementum, dui purus fermentum lorem, sed faucibus risus diam non eros. Nullam consectetur dui et augue porta placerat. Fusce facilisis justo tortor, sit amet ultricies massa hendrerit id. Morbi non lectus quis dolor rhoncus venenatis. Morbi mollis lacus risus, a hendrerit libero eleifend sed. Proin egestas eleifend rhoncus. + +Suspendisse ut nibh rhoncus, aliquet sapien at, tristique tellus. Nam odio nulla, condimentum eget quam ac, efficitur ullamcorper diam. Aliquam tristique massa non lectus dignissim vulputate sed vitae diam. Mauris nunc leo, dictum non dapibus molestie, auctor lacinia nisi. Proin volutpat vestibulum lacinia. Nullam volutpat, ligula ac tempor malesuada, mi dui scelerisque velit, non eleifend urna lacus non urna. Aenean dapibus libero non tellus lacinia, quis gravida lorem luctus. Proin placerat mi tortor, eget interdum nisl efficitur sit amet. Mauris auctor tempus neque. Pellentesque quis est at est dapibus mattis vitae in tortor. Phasellus rhoncus eros ac augue pellentesque porta. + +Aenean lorem erat, commodo sed ex sit amet, ornare tincidunt leo. Aliquam quis nibh molestie nisl condimentum condimentum nec quis nisl. Pellentesque orci est, luctus suscipit euismod sit amet, dapibus vel diam. Fusce et neque eget ligula hendrerit laoreet. Donec porta lacus at cursus dictum. Duis condimentum mattis augue eget scelerisque. Vivamus eu ipsum efficitur, congue nulla ut, volutpat eros. Morbi viverra, dolor vitae ullamcorper molestie, ipsum eros laoreet justo, at pretium est quam ut ex. Fusce nec tortor arcu. Donec pretium ullamcorper dictum. Pellentesque in justo orci. + +Donec quis diam vitae purus mollis dignissim sit amet tincidunt nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut ac lacus ultrices, volutpat sem sit amet, bibendum dui. Maecenas ut facilisis tortor. Nunc vitae libero ut arcu elementum porttitor. Vestibulum non tortor at lacus facilisis gravida. In hac habitasse platea dictumst. Integer quam magna, molestie non varius faucibus, luctus sollicitudin urna. Vestibulum cursus purus quis dolor lacinia vestibulum sed in odio. + +Etiam id vehicula dolor, ac sodales libero. Maecenas eget porta eros. Sed id condimentum enim. In nisl justo, ultricies sed augue at, interdum fermentum augue. Nulla quis sodales turpis, id tristique ex. In id tellus in ipsum cursus dapibus. Suspendisse at nulla euismod, dignissim leo non, lobortis enim. Cras eu tincidunt velit, at blandit velit. Vestibulum eleifend mi eget maximus porta. Fusce ut consequat sem, iaculis molestie eros. + +Vivamus metus nisl, finibus a neque sollicitudin, tincidunt euismod ligula. Donec at nisi ut tellus bibendum vulputate at nec tellus. Morbi interdum ligula in ex iaculis maximus. Proin efficitur felis in mattis tempor. Praesent venenatis mi eleifend ligula suscipit, commodo elementum purus commodo. Donec nec quam in mi pretium vestibulum. Quisque rhoncus, odio quis euismod venenatis, lacus nisl ullamcorper ligula, at tempor lorem ex quis elit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In imperdiet blandit sem nec tempus. Nulla fringilla imperdiet ipsum, at ultricies tortor varius ac. Vivamus ultrices ac ipsum quis volutpat. Nulla nec est aliquam, volutpat odio sed, porta risus. Praesent vitae hendrerit odio. + +Curabitur ut fermentum metus, ut tempus tortor. Ut eu consectetur sem. Ut congue sapien lacus, non vehicula purus maximus sit amet. Mauris vulputate at mauris elementum venenatis. Donec ac dapibus tortor. Cras ligula ex, varius a mauris et, tincidunt mollis purus. Fusce ligula sapien, interdum a eleifend et, vulputate vitae diam. Curabitur eget metus in nibh placerat suscipit. Vivamus faucibus tortor sed velit egestas, fringilla aliquet sapien aliquet. + +Nam vel lacus luctus, egestas neque ac, viverra erat. Ut consequat orci eu enim accumsan, sed tempus justo ullamcorper. Aliquam erat volutpat. Nullam molestie ipsum nec arcu cursus auctor. Morbi ultrices malesuada viverra. Sed semper turpis vitae turpis ultricies finibus. Etiam varius et purus in condimentum. Quisque condimentum augue quis nulla hendrerit, eget placerat augue varius. Aenean at facilisis est, at placerat diam. Curabitur lobortis mattis ante, ut euismod turpis. Vivamus erat magna, faucibus et arcu vitae, dapibus semper nisi. Praesent nisi purus, accumsan sit amet vehicula at, volutpat sit amet nibh. Vestibulum ut tincidunt est, eu pharetra justo. Integer commodo id risus id rutrum. + +Duis mi turpis, eleifend suscipit ligula eget, tincidunt consectetur leo. Sed congue volutpat magna, nec posuere purus mattis eget. Duis interdum quis odio ut sodales. Aenean imperdiet, sapien ut tempus luctus, neque nisl suscipit justo, eu ultrices risus justo ac quam. Aliquam vel neque vel sem ultrices varius sit amet quis sem. Pellentesque tempor pharetra felis, eget venenatis felis bibendum quis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer vel vestibulum purus. Fusce vulputate orci tortor, vel consectetur sem accumsan non. Pellentesque at orci id quam varius efficitur. Etiam dignissim, erat auctor elementum varius, sapien urna interdum magna, vel vestibulum quam justo vel arcu. Praesent gravida malesuada rutrum. Aenean convallis egestas ligula, ut ullamcorper nulla bibendum a. + +Donec convallis euismod commodo. Donec finibus, velit non gravida imperdiet, ipsum magna sagittis sapien, eu sagittis nunc justo at sem. Ut faucibus nisl est, ac varius lectus placerat non. Vestibulum at scelerisque massa, vitae mattis neque. Nunc a gravida neque. Maecenas id dignissim tellus. Vivamus fringilla purus sem, at finibus lacus scelerisque a. + +Donec nisi mi, feugiat id porttitor aliquam, luctus id orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer quam orci, laoreet non urna quis, porttitor sagittis dui. Morbi imperdiet tincidunt magna sit amet consequat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent ullamcorper accumsan ante, sed vehicula sem luctus id. Praesent convallis nisl eget risus iaculis gravida. Donec ac lorem non urna auctor consequat ac ut lorem. Fusce faucibus lacus non lacus vestibulum, in convallis eros feugiat. Fusce ut quam nibh. Nulla facilisi. Quisque vel facilisis tellus. + +Fusce et nisi non justo iaculis semper eu id leo. Curabitur ornare non est in faucibus. Nulla facilisi. Pellentesque sit amet venenatis elit. Duis porttitor scelerisque metus, vitae sodales tortor sagittis at. Aliquam erat volutpat. In efficitur mollis metus. Donec vel dolor finibus, ullamcorper diam sed, pharetra mi. Morbi ullamcorper convallis libero at feugiat. + +In tristique, enim a pulvinar iaculis, arcu sapien ultricies augue, non congue libero dolor porta ipsum. Ut eu aliquet lacus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse potenti. Vivamus pulvinar sollicitudin massa pharetra cursus. Pellentesque convallis tincidunt turpis ut tincidunt. Mauris semper lorem finibus, laoreet nisl vitae, ultricies metus. Ut ultrices erat eu leo pretium, vel scelerisque ligula elementum. Etiam libero eros, sodales nec lorem vehicula, imperdiet vehicula nunc. Curabitur purus quam, vestibulum nec fermentum nec, vehicula eu sapien. Phasellus eleifend viverra commodo. Donec sagittis imperdiet dignissim. + +Phasellus eget eleifend ipsum, id porta magna. Praesent vulputate turpis eu suscipit maximus. Nam cursus lacus augue, aliquam volutpat dolor consectetur sed. In hendrerit euismod lectus vitae elementum. Praesent accumsan faucibus metus ac aliquam. Duis efficitur porttitor urna nec convallis. Morbi non condimentum tortor. Nullam eget rutrum ipsum. + +Nam dignissim in lorem non varius. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur at cursus eros, a rutrum est. Integer risus nulla, eleifend nec arcu eget, ullamcorper hendrerit tellus. Sed sem mauris, suscipit at nibh quis, interdum facilisis arcu. Nam hendrerit, orci quis luctus mattis, tortor lorem interdum sapien, porttitor eleifend dui ex vitae enim. Nam iaculis libero a ante accumsan, quis luctus dui scelerisque. Vivamus et dui sit amet metus fringilla dapibus eu a turpis. Donec tincidunt dictum tortor non mattis. + +Integer euismod congue lacus sed venenatis. Suspendisse ligula turpis, cursus a porttitor ac, condimentum at elit. Suspendisse potenti. Etiam sagittis sapien non odio rutrum dictum. Quisque gravida blandit facilisis. Curabitur viverra nisl quis dui volutpat, eu venenatis quam aliquet. Aliquam erat volutpat. Interdum et malesuada fames ac ante ipsum primis in faucibus. + +Quisque suscipit nisl sit amet elit aliquet ultricies. Suspendisse quis dignissim est. Sed blandit nibh ut ligula convallis, tincidunt commodo risus tempor. Curabitur ac lacus at lectus accumsan pretium. Vivamus vitae lectus scelerisque, tempor dui in, accumsan velit. Nulla aliquet posuere lectus vel facilisis. Vivamus maximus velit odio, nec tincidunt lorem fringilla sit amet. Sed vestibulum arcu est, vitae imperdiet justo luctus nec. Phasellus ac purus at erat ullamcorper iaculis. Phasellus rutrum leo ac libero efficitur malesuada. Quisque eget velit at quam fringilla aliquet. + +Fusce maximus ante sed auctor malesuada. Donec eu nulla sit amet risus semper sodales sed non leo. Vivamus id facilisis ligula, in pulvinar sapien. Nam consequat at mi nec varius. Etiam viverra ex in auctor interdum. Nullam sed tellus non lacus varius luctus. Vivamus ut nisl sed justo vulputate eleifend eu in lectus. Maecenas ex lectus, vulputate a pulvinar ut, tincidunt vitae leo. Cras at augue suscipit, varius sem sed, rhoncus eros. Proin ac tempor nisi. Ut a libero nibh. Maecenas erat mi, fringilla vel velit sit amet, ultrices pulvinar orci. Cras ut urna ut purus sodales bibendum. Nunc eu placerat eros. Praesent porttitor sagittis neque ut blandit. + +Cras nec nunc nulla. Sed pulvinar dignissim lobortis. Integer quam sem, molestie vel quam ac, vulputate porttitor odio. Sed tincidunt luctus tempus. Proin sed purus id lacus posuere pellentesque. Vivamus tincidunt nibh quis velit molestie, tristique iaculis felis accumsan. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque scelerisque, nisi nec feugiat congue, lacus nisi lacinia sapien, non consequat orci elit id ante. Suspendisse vestibulum, nisi nec blandit imperdiet, augue tellus tincidunt risus, non bibendum nibh quam sed nulla. Sed mollis nec tortor id iaculis. Integer molestie tempor blandit. In fringilla dapibus interdum. Aliquam erat volutpat. + +Quisque porttitor mauris nisi, mollis viverra arcu bibendum ut. Duis non nunc ut mi posuere pharetra. Nullam a molestie sem, quis ultrices lectus. Aenean est ex, auctor quis quam eget, gravida pretium diam. Morbi tempor, ligula quis vehicula venenatis, felis eros consectetur quam, elementum porta lectus lacus id ante. Fusce justo nisi, condimentum in consequat sed, maximus posuere libero. Suspendisse egestas eros id magna commodo sodales. Vestibulum accumsan vel lorem at fringilla. Donec eget interdum urna, ac efficitur urna. In interdum consectetur porta. Aliquam commodo consequat porta. Nam sem elit, tristique at tellus rutrum, ornare venenatis leo. Nunc fermentum vitae lectus nec laoreet. Nam fermentum velit nec lorem gravida suscipit. + +Fusce sit amet bibendum leo. Suspendisse interdum at enim eget tempus. Curabitur mi dui, facilisis tempor libero eu, porttitor molestie urna. Aenean suscipit posuere ligula, sed lacinia nunc porta non. Aliquam turpis mauris, placerat id condimentum eu, vestibulum in felis. Vestibulum vitae diam placerat, placerat odio a, dictum nisl. Praesent non mi tellus. In imperdiet, dui et varius malesuada, enim libero hendrerit tellus, vel accumsan libero velit nec eros. Vivamus ut porttitor est. In orci nisl, ultricies in consectetur nec, aliquet quis nibh. Donec turpis elit, convallis vitae libero hendrerit, sagittis consequat dui. Vestibulum sagittis nisi risus, imperdiet luctus ex auctor nec. + +Donec tristique enim nisi, id semper dui pretium vitae. Vivamus in ligula aliquam, scelerisque tellus eget, imperdiet nunc. Ut imperdiet metus varius pulvinar consectetur. Mauris sit amet felis sit amet justo convallis fringilla sit amet ac lorem. Sed tincidunt, arcu non lobortis fermentum, justo mauris gravida sapien, eu vehicula ex risus quis enim. Etiam a nulla imperdiet, tristique tortor at, mattis libero. Maecenas sed metus et diam luctus ornare eget vel ex. Proin tincidunt justo vel ipsum pharetra vehicula. Ut sodales nisl est, et lacinia neque rhoncus in. Nullam a arcu facilisis, tempor nibh quis, tincidunt diam. + +Nulla non vestibulum tellus. Quisque et purus eleifend, tincidunt arcu a, aliquam neque. Fusce eu enim nisl. Pellentesque eget elit nec ligula egestas commodo. Nulla elementum commodo leo vel venenatis. Vestibulum ex ante, efficitur non maximus eu, posuere porta urna. Maecenas sit amet fermentum ante. Aenean tempor est et arcu condimentum varius. Nullam elementum ante sed mattis condimentum. Vivamus iaculis velit at ex gravida, id tincidunt mauris tincidunt. Sed auctor sit amet purus vitae posuere. Nam placerat imperdiet neque, vitae tempor diam vehicula nec. Donec consectetur, orci sed placerat luctus, ipsum tellus vehicula risus, vel scelerisque nunc diam porta sem. + +Duis eleifend egestas dui, eu imperdiet mi ullamcorper quis. Donec eros lorem, convallis sed lorem a, sagittis mollis lacus. Quisque aliquam eros at ante tincidunt suscipit. Ut ut dictum elit. Quisque eleifend felis a odio suscipit pulvinar. Quisque egestas sit amet ligula vel porta. Sed rhoncus risus at ipsum tincidunt, a vulputate eros semper. + +Vestibulum vulputate sit amet nulla eget dapibus. Praesent porttitor nulla sed fermentum pharetra. In id risus in dolor consectetur rhoncus. In quis suscipit tellus, et aliquet diam. Sed eget augue enim. Fusce ante est, molestie ac dolor ac, venenatis lobortis ipsum. Ut suscipit ex ex, vehicula semper ligula tincidunt in. + +Cras malesuada lorem sit amet quam laoreet maximus. Fusce mattis mattis ex, ut finibus neque. Donec eget pretium dui, hendrerit placerat nisi. Cras et lacinia nulla, id sagittis risus. Pellentesque vitae mollis augue. Proin dictum iaculis ligula, in tincidunt sem egestas vitae. Etiam cursus arcu vel arcu porta, vitae consectetur justo ultrices. Quisque at porta nisl. Quisque in elit sollicitudin, dictum risus sed, volutpat neque. Vivamus hendrerit nibh in est scelerisque, vel mollis turpis tempus. Ut in eros fermentum purus dignissim posuere eu vulputate elit. Cras id efficitur massa, vel pellentesque risus. Donec vulputate erat vel risus dapibus eleifend. + +Cras imperdiet lectus a nulla hendrerit dapibus. Nullam sollicitudin arcu id elit malesuada, ac convallis massa efficitur. Curabitur congue massa et orci imperdiet, ut dignissim metus rhoncus. Ut egestas augue sit amet enim finibus viverra. Integer ut elementum lorem. Vestibulum malesuada rutrum justo, in ultrices nisl porttitor et. Donec in est sed dolor maximus porttitor. Aenean pharetra velit nec diam auctor faucibus ut eget dolor. Duis commodo lacinia nisl nec porta. Vestibulum cursus aliquet condimentum. Duis iaculis neque enim. Suspendisse lacinia massa et velit consectetur, quis fermentum velit rhoncus. Quisque pretium, leo laoreet porttitor finibus, eros tortor imperdiet ante, eu posuere tortor ante id risus. + +Cras sodales auctor tempus. Ut laoreet laoreet molestie. Aliquam porttitor, justo feugiat convallis blandit, sapien libero consequat massa, in tincidunt lectus neque non lectus. Pellentesque maximus sollicitudin facilisis. Curabitur aliquam vel felis non ultrices. Vivamus a ex eget justo iaculis convallis. Mauris sed posuere eros. Morbi molestie iaculis imperdiet. Nunc non elit tincidunt, aliquam neque nec, consectetur ligula. Etiam cursus sodales nunc, in pellentesque diam dictum auctor. Proin hendrerit tristique diam, et vehicula lacus tempor eu. Morbi ac placerat ex. + +Phasellus cursus nibh ut rhoncus scelerisque. Ut arcu urna, malesuada sit amet est nec, malesuada interdum sapien. Nam in fringilla est, ac dapibus velit. Nulla nec pharetra nisi. Vivamus sagittis vel nisi et facilisis. Donec vitae dui sed diam sollicitudin scelerisque nec semper turpis. Aliquam pretium mi ac lorem consequat consectetur. Aliquam tristique est mi, sed lacinia arcu sodales sit amet. Nam sodales, est a condimentum mollis, urna nibh molestie quam, ut facilisis purus nisi eu risus. Mauris consequat ut nulla eu tincidunt. Phasellus sit amet massa ipsum. Integer est ex, pellentesque at augue sit amet, tincidunt malesuada eros. Aliquam pretium odio sed massa rhoncus commodo. Quisque fermentum porttitor mauris. Suspendisse potenti. Nulla magna sapien, fermentum sit amet odio semper, bibendum tempus metus. + +Curabitur ac sapien vehicula nibh dapibus pretium. Suspendisse eleifend turpis nisi. Donec ac nunc condimentum, volutpat leo non, eleifend velit. Maecenas posuere nulla quis lectus vestibulum laoreet. Nunc sit amet mauris eu urna commodo interdum sit amet quis tortor. Aenean ullamcorper felis ac nisl egestas tristique. Ut dapibus sed orci sit amet tristique. Aliquam ultrices ultrices est vitae pellentesque. Ut et semper urna, et bibendum lectus. Integer fermentum magna quis purus interdum eleifend. Pellentesque congue elit quis vehicula laoreet. Quisque vitae venenatis ipsum. Etiam facilisis elementum lectus, a egestas magna varius at. Sed auctor, ante ac venenatis commodo, justo diam pretium libero, ac euismod ipsum quam sit amet orci. Nullam sed est tempus, eleifend erat sed, aliquet massa. + +Quisque turpis lorem, malesuada vitae elementum vitae, gravida nec augue. Morbi dui erat, varius ut porttitor cursus, varius id velit. Nulla nec dapibus ante. Integer in mauris ut ipsum luctus placerat. Praesent nec velit fringilla, elementum purus non, tincidunt dui. Morbi ut maximus tortor, quis feugiat ex. Ut faucibus pellentesque hendrerit. Cras mattis ante vel varius laoreet. Phasellus consectetur urna nec cursus volutpat. Cras cursus consectetur felis vel ornare. Vestibulum non neque ut purus dictum iaculis. Praesent dictum pretium aliquet. Suspendisse sed nibh pellentesque felis molestie egestas. Vivamus sollicitudin nec est vel hendrerit. Quisque et tortor posuere, semper est sit amet, tempus ipsum. + +Ut faucibus rutrum libero vitae maximus. Mauris vel eros a tellus vulputate elementum vitae nec purus. Proin tristique commodo nulla. Cras eleifend eleifend efficitur. Morbi lacinia venenatis tristique. Proin tristique urna porttitor magna lacinia, a consectetur risus ultricies. Morbi cursus, mi sit amet commodo gravida, arcu risus porta metus, id ornare eros purus vel leo. Donec elementum aliquet vulputate. Vestibulum congue mauris nec quam fermentum, sed consequat ex placerat. Sed magna diam, pretium vitae quam vestibulum, sagittis congue urna. Nulla fringilla eget eros et posuere. In blandit risus sit amet aliquet porttitor. Nulla vel dolor et turpis tempus scelerisque. Suspendisse id iaculis turpis. + +Integer quis eleifend massa. Proin magna quam, finibus quis convallis non, rhoncus pulvinar elit. Donec scelerisque laoreet elit ac ultricies. Sed sodales lorem in augue convallis tincidunt vel sit amet diam. Curabitur nec turpis cursus, sollicitudin urna in, malesuada ligula. Etiam sed enim eget est posuere auctor. Sed orci orci, aliquam a erat sit amet, pellentesque volutpat dolor. Donec mauris justo, rhoncus a molestie ac, mattis dapibus magna. + +Integer rutrum tellus urna, quis lacinia eros viverra non. Morbi eget laoreet nisi. In nec dolor sit amet mi euismod volutpat in sit amet augue. Nullam ipsum velit, ultricies sed odio vel, mollis finibus turpis. Nam nec pretium ex. Morbi sed commodo ipsum, eu facilisis tortor. Maecenas condimentum elit ac nunc convallis tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Etiam ac fermentum ligula, et luctus urna. Fusce pulvinar vitae quam in consequat. Etiam at ligula at metus placerat condimentum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi lacinia ultricies nulla quis consectetur. Curabitur consequat, justo vitae placerat feugiat, dui nisi dictum nunc, nec rhoncus ex nisl laoreet tellus. Curabitur congue augue nibh, sed finibus odio consectetur vel. + +Nullam malesuada facilisis eros semper sodales. Suspendisse eu vehicula ipsum. Aenean tincidunt at eros non malesuada. In auctor dictum nibh in bibendum. Phasellus ultricies tellus a elit convallis, non euismod felis consequat. In id mi turpis. Phasellus porta enim sit amet leo lacinia malesuada. Aenean faucibus mauris vel libero hendrerit pellentesque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus sollicitudin ipsum a dui commodo tincidunt. Vestibulum pharetra mauris sit amet diam eleifend, vel luctus turpis vulputate. Etiam eleifend sapien ligula, nec vehicula erat fringilla sit amet. Donec dignissim vitae risus ut euismod. + +Suspendisse congue sagittis sapien, vel tincidunt libero aliquam at. Curabitur placerat velit nec nisi dictum finibus. Mauris at dui imperdiet elit posuere ultrices at nec felis. Fusce et lacinia augue. Aenean cursus dui vel ligula blandit efficitur. Donec neque elit, efficitur eget libero eu, scelerisque tempor metus. Proin vitae finibus libero, id dignissim est. Morbi id tortor elementum, maximus magna eget, rutrum quam. Duis congue vel leo sit amet porta. Proin lacinia ipsum vitae dolor mollis, quis bibendum justo luctus. Integer nec porttitor leo. Sed et dictum diam. Curabitur interdum et turpis et consectetur. In hac habitasse platea dictumst. + +Ut feugiat metus sit amet bibendum tincidunt. Morbi pharetra facilisis metus, vitae dapibus lacus tincidunt in. Nunc molestie vehicula sagittis. Sed blandit leo sit amet fermentum feugiat. Praesent fringilla fermentum arcu, ut ornare nisi auctor non. Etiam varius nisi magna, sed rhoncus sem porta vel. Nunc eget sagittis sem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus ac pellentesque sapien, quis varius quam. Fusce venenatis neque nisl, vitae blandit massa finibus hendrerit. Etiam porta dignissim ipsum, vel auctor nibh vestibulum id. Etiam non ex vulputate, dapibus orci a, ornare dui. + +Suspendisse eget tempus lorem. Fusce eu erat non eros viverra tempor. Ut euismod pulvinar eros, ut ultrices odio eleifend vel. Duis venenatis sem ac justo luctus tincidunt. Vivamus sit amet tempor lectus, vel ornare felis. Suspendisse eu augue quam. Suspendisse potenti. Nulla cursus tempor tempor. Donec pretium mattis mi, ac hendrerit ante mollis vitae. + +Vivamus gravida ut sem quis tempor. Quisque cursus auctor sodales. Mauris quis elit a est consectetur convallis non ac erat. Aenean magna turpis, scelerisque non diam et, venenatis tristique ipsum. Cras eu tellus quis nibh egestas mollis. Aliquam at tellus erat. Aenean et justo et eros eleifend aliquet. In ut diam vel felis dapibus viverra. + +Fusce eu lacus sagittis, interdum risus nec, condimentum odio. Vivamus vel leo est. Phasellus non lobortis mi, quis semper ante. Suspendisse in leo ultricies, tincidunt erat sed, placerat quam. Integer lectus leo, hendrerit mollis risus auctor, viverra dignissim augue. Etiam sit amet facilisis lacus. Nam in pulvinar tellus. Proin eget malesuada nunc. Duis commodo, turpis commodo rhoncus efficitur, orci purus auctor felis, quis tincidunt arcu magna ac augue. Fusce at dui et sem ullamcorper vestibulum eget eget enim. Duis accumsan ac elit a varius. + +Integer tincidunt commodo finibus. Nulla facilisi. Ut mauris leo, aliquet quis pretium id, vulputate vitae justo. Aenean vel augue nec neque mattis aliquam. Ut et eros interdum, mollis felis vel, interdum tellus. Maecenas sodales vulputate neque, vitae auctor quam auctor vitae. Curabitur risus elit, tempor vel aliquet sed, dictum id tortor. Ut vitae pellentesque neque. Integer tincidunt libero sit amet placerat laoreet. Donec bibendum felis vitae rutrum luctus. Nulla viverra velit sed ex interdum, consectetur ullamcorper orci consectetur. Phasellus nec metus libero. Nulla eget fermentum ipsum. Suspendisse et ligula eget metus pulvinar mollis. Nulla facilisi. Sed laoreet accumsan magna eu interdum. + +Sed bibendum venenatis lorem, vel maximus diam lobortis vel. Donec tristique ut purus quis posuere. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis euismod efficitur accumsan. Pellentesque luctus ac arcu dapibus pharetra. Curabitur fermentum tristique odio, a pretium lectus aliquam a. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras pulvinar arcu nec ligula sagittis, a faucibus metus fringilla. In nec dapibus libero, non fringilla ante. Maecenas sit amet posuere odio. Etiam vel placerat est. Nulla in neque sed lorem iaculis suscipit. Suspendisse potenti. Quisque et tempus mauris. + +Etiam lacus massa, semper vitae eros eget, pellentesque viverra enim. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec non ex nunc. Vivamus pharetra nulla arcu. Mauris vel dui non felis tincidunt vehicula ac at odio. Aliquam erat volutpat. Donec vel diam id lectus maximus faucibus id sed ante. Morbi cursus hendrerit magna ac mollis. Ut faucibus aliquet dui, non cursus metus interdum sit amet. Nam ligula lacus, rutrum non vulputate a, pharetra eu tortor. + +Pellentesque dignissim molestie metus. Aenean id magna in elit rhoncus tempus et dignissim arcu. Etiam vestibulum et arcu ut vestibulum. Nam pretium porta dignissim. Cras hendrerit lacus nec eleifend suscipit. Aliquam faucibus vitae dui sit amet auctor. Pellentesque eros est, luctus sit amet semper in, vestibulum sed orci. Cras in accumsan turpis. Suspendisse in consequat elit. Etiam facilisis mattis nibh vel feugiat. Praesent varius turpis ac hendrerit vestibulum. Donec consequat ipsum a iaculis dictum. + +Fusce nibh lorem, faucibus et tincidunt in, fermentum eu lacus. Sed aliquam vestibulum tellus sed varius. In dapibus nunc eget erat maximus malesuada. Phasellus vehicula ligula quis dolor convallis iaculis. Ut non arcu ultrices nisi tempor tristique. Curabitur scelerisque tortor nec metus dapibus, quis gravida ex condimentum. Ut facilisis volutpat molestie. Maecenas quis urna et urna fermentum mattis. Aenean non diam at sapien faucibus venenatis quis sit amet leo. Mauris nec nulla neque. In sagittis, risus nec tristique facilisis, quam est ultricies sapien, convallis scelerisque libero nisi sit amet mi. Integer consectetur pretium odio. Nulla purus sem, vehicula eu vulputate consectetur, semper eget lacus. In lacinia faucibus lectus, ac eleifend ipsum feugiat ac. Ut auctor tortor nec tristique tempus. + +Sed faucibus finibus aliquet. Mauris vitae libero eu dui gravida congue a et nulla. Morbi ac dolor eu odio interdum rhoncus et ultrices nulla. Maecenas sollicitudin sapien posuere elit ultrices posuere. Nam sit amet lacus eget neque sollicitudin volutpat. Vivamus id velit a erat congue venenatis vel non erat. Nullam ut tincidunt purus. + +Donec ut nisl ultrices, bibendum ligula quis, tristique libero. Proin vitae nisi felis. Sed maximus justo non aliquet tincidunt. Vestibulum ullamcorper augue sit amet tortor ornare, nec iaculis magna ullamcorper. Phasellus in dolor purus. In dignissim urna ex, in fermentum arcu commodo at. Mauris lobortis rhoncus libero quis sagittis. Mauris pellentesque dapibus lacinia. Suspendisse pellentesque molestie leo id fermentum. + +Duis erat quam, euismod non tempor ac, accumsan sed eros. Donec a auctor quam, eu interdum eros. Pellentesque vehicula diam est, sit amet tincidunt ligula aliquet at. Nunc sed nisi a nibh feugiat facilisis. Morbi ac sapien id dui tristique ultrices ut id nisl. Sed porttitor ultrices dolor et congue. Curabitur varius commodo pulvinar. Duis mattis dui vitae maximus porttitor. Etiam ut sagittis nunc. + +Suspendisse fermentum mi risus, ac hendrerit turpis elementum id. Mauris eget est eu nisi aliquet rutrum. Nulla facilisi. Ut id erat quis elit blandit laoreet. Pellentesque id justo dui. Suspendisse vitae tincidunt dui. In a varius enim. Pellentesque iaculis nulla ac risus ullamcorper, nec posuere nunc dignissim. Praesent nibh sapien, consequat vel mi at, pulvinar rhoncus augue. + +Duis quis placerat tellus, nec ullamcorper mauris. Nulla auctor, nisl nec condimentum vulputate, eros nulla pharetra orci, a tincidunt lorem nisi vel est. Praesent nisl eros, venenatis nec risus sed, luctus posuere metus. Fusce maximus neque non risus dictum tincidunt. Sed venenatis fringilla elit. Nunc sollicitudin ultricies nisl id condimentum. Maecenas sagittis diam vitae placerat sagittis. Mauris in condimentum elit. Curabitur fermentum maximus felis id pellentesque. In hac habitasse platea dictumst. Donec eu sodales ipsum. Praesent eleifend tincidunt tempus. Vestibulum porttitor dolor vitae felis tincidunt, ut ornare urna consectetur. Vivamus vel dolor dapibus, ultricies ante et, congue est. Quisque lacinia est purus, a semper ligula luctus et. + +Donec ut augue eget elit fermentum iaculis id eget ante. Phasellus eget risus sed velit hendrerit pulvinar. Nam imperdiet nunc justo. Vivamus lobortis sagittis purus in gravida. Donec finibus sit amet lacus non ultricies. Ut lacinia accumsan consequat. Nulla rutrum nibh eget dui varius convallis. Mauris mattis sed magna eu lacinia. Suspendisse dignissim mi lorem, nec elementum massa cursus vehicula. Nunc in leo mollis, imperdiet erat et, porta nulla. Integer et tempor lorem. + +Sed convallis lorem non suscipit imperdiet. Quisque bibendum urna lacus, id elementum lacus aliquet a. Fusce vitae aliquam eros. Aliquam venenatis fermentum libero a vestibulum. Cras augue augue, aliquet sit amet interdum vel, rhoncus a mi. Pellentesque vitae ex finibus ligula fringilla tincidunt. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent varius posuere sem. Sed id tellus vel diam fermentum dapibus. + +Morbi elementum justo dignissim odio rhoncus, vel blandit neque rutrum. Duis maximus ligula ante, eu auctor justo hendrerit ac. Donec eleifend lacus in elit aliquam, tincidunt bibendum arcu feugiat. Praesent mollis efficitur fermentum. Vestibulum tristique tincidunt luctus. Sed vel dictum lectus. Praesent maximus augue massa. Vestibulum non lacus eget eros imperdiet tempus. + +Integer sit amet pharetra diam. Sed hendrerit ipsum vitae risus gravida varius. Nunc placerat, risus in posuere dapibus, tortor velit viverra ligula, a luctus lectus dolor vitae orci. Morbi dapibus felis quis nulla ultricies egestas. Vivamus sed leo in dui tristique luctus. Nam ultrices velit at facilisis rhoncus. Curabitur est ex, pretium nec lacinia non, faucibus sit amet arcu. Suspendisse ac elit vitae sapien sodales ullamcorper. Pellentesque mi sem, feugiat sed odio eget, feugiat sagittis dolor. + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis scelerisque mollis ante, rutrum condimentum nulla rutrum nec. Quisque consequat arcu ante, eu interdum nulla mollis vel. Nunc nisl eros, tempor pharetra augue quis, congue auctor quam. Fusce vel pellentesque neque. Nulla facilisis, nulla quis lobortis volutpat, metus metus consectetur velit, a aliquet turpis mauris quis nulla. Phasellus id ipsum sed lectus efficitur vehicula. + +Quisque quis pellentesque erat. Nam ut imperdiet nisl. Ut pharetra nibh sodales, consectetur massa vitae, facilisis libero. Nullam ut sollicitudin enim. Quisque tristique, augue a elementum luctus, nulla nulla cursus massa, sit amet facilisis diam risus non tortor. Phasellus quis mauris libero. Praesent vitae nulla pellentesque diam pellentesque feugiat eu id metus. In dignissim ex quis tristique posuere. + +Etiam nec convallis ligula. Vestibulum suscipit elit posuere nunc auctor, eget vestibulum turpis posuere. Phasellus massa dui, aliquam vitae lectus sed, posuere tempor urna. Maecenas ac luctus ipsum, et feugiat libero. In a mauris at velit fringilla laoreet quis eu ex. Sed in felis feugiat, tincidunt lectus quis, imperdiet est. Cras scelerisque ex eu ante dignissim mollis. Curabitur eget nulla lobortis mi gravida pretium eu a mi. In hac habitasse platea dictumst. Cras porta turpis sit amet bibendum tincidunt. Proin eget fermentum nulla. + +Donec consequat, neque ut porttitor ultrices, augue mauris sollicitudin dolor, ac bibendum lacus elit a lorem. Cras id nunc ut tortor congue laoreet non ut felis. Maecenas euismod nibh sit amet mollis tincidunt. Pellentesque sit amet suscipit elit. Duis ullamcorper hendrerit magna id malesuada. Proin vehicula, magna non feugiat efficitur, augue tellus rhoncus odio, non blandit ligula ipsum ac sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec ut ante nunc. Phasellus sed sapien sit amet mi bibendum scelerisque sit amet a dui. Nullam sodales nunc nec lectus faucibus, at facilisis neque porta. Aenean nulla lectus, interdum quis cursus a, porttitor nec lorem. Donec imperdiet purus volutpat, imperdiet mauris ultricies, tristique risus. + +Integer risus dolor, rhoncus ac congue vel, vulputate a ante. Ut tristique mollis nisi, ac condimentum arcu sodales sit amet. In eu mi augue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam finibus erat vel lectus convallis, iaculis lobortis erat consequat. Praesent elementum ultrices dui, faucibus facilisis massa tempus id. Donec non nulla id urna dictum vulputate. Quisque non sagittis tellus, vitae dignissim velit. Nam in interdum mauris, non cursus libero. In pulvinar, ipsum sit amet mattis imperdiet, ligula felis finibus sapien, sit amet porta orci neque lobortis augue. Donec a tristique eros. Nunc at erat varius, fermentum mauris nec, ultrices metus. Etiam euismod pretium condimentum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. + +Donec mattis non nunc at congue. Nam a cursus ipsum. Nunc sodales luctus pretium. Phasellus consequat ultricies accumsan. In at nunc sapien. Mauris congue, erat sed malesuada tincidunt, neque turpis pharetra diam, et maximus enim mi in orci. Nulla sit amet eros sollicitudin enim tempor tincidunt. Phasellus nec scelerisque magna. In at porta lorem, sed fringilla est. Aliquam sed finibus tellus. Pellentesque pellentesque consequat pretium. Duis vitae tincidunt orci, quis eleifend orci. Cras eu mi non libero vulputate porta. Aliquam erat volutpat. Nunc vitae aliquet elit. Vivamus eget sagittis est, gravida placerat nisl. + +Nullam a ultricies nibh. Vivamus tempus nulla sed mauris tincidunt posuere. In porttitor, sem a fermentum pellentesque, ligula augue elementum nisl, quis viverra nulla sem vel lectus. Duis sed sapien id arcu luctus rhoncus. Phasellus dignissim blandit neque, ut congue nunc vestibulum non. Maecenas blandit in nunc non mollis. Nam eget est nisi. Ut laoreet purus a maximus sagittis. Maecenas non euismod leo. Duis mattis interdum finibus. Sed sed lorem in magna varius tempus. Praesent vitae metus sit amet arcu fermentum rutrum. Vestibulum sem dui, accumsan ac urna eget, interdum fermentum nisl. Praesent sed fringilla risus. Fusce auctor dui enim, in laoreet nisi facilisis et. + +Suspendisse sollicitudin, eros et porta fringilla, mi eros scelerisque augue, et consequat dolor leo in tellus. Sed sed neque viverra, vehicula urna sit amet, mollis tortor. Mauris felis augue, mollis vel nisl vitae, sodales egestas mi. In bibendum molestie felis eu gravida. Mauris auctor sed diam ut pellentesque. Aliquam facilisis arcu et sagittis maximus. Duis id elementum ipsum, ut fermentum odio. Donec quis dui feugiat, maximus purus ut, aliquet ipsum. Aliquam condimentum sapien quis erat mattis imperdiet. Aenean tincidunt condimentum tortor sed rhoncus. Integer ultricies, neque vitae placerat volutpat, mi nibh faucibus sem, at imperdiet lorem nisi eget odio. + +Fusce non accumsan mauris, et dignissim mi. Vivamus posuere lacus at metus venenatis, eget maximus purus tincidunt. Sed fermentum mi quis ex facilisis, eu pellentesque massa placerat. Quisque quis nibh eget ipsum pellentesque rutrum. Curabitur eros nisi, pretium ac commodo eu, gravida at metus. Cras quis sem eu leo condimentum dignissim vitae id magna. Duis volutpat rutrum varius. Suspendisse fringilla orci sed turpis commodo, at ultrices tortor lacinia. Nullam et est elit. Proin finibus vestibulum vestibulum. Nam finibus magna ac eros maximus tincidunt. + +Proin ac nisi non arcu fermentum sodales varius tincidunt ex. Nam viverra et est rhoncus dapibus. Nunc blandit aliquet nunc vitae tempor. Maecenas blandit, erat et gravida consequat, turpis nunc cursus augue, et iaculis erat arcu in sapien. Duis porta viverra leo, at vestibulum lacus condimentum mollis. Proin id aliquam ipsum, et bibendum nulla. Curabitur maximus mattis nulla. Pellentesque facilisis, felis eu efficitur hendrerit, urna dui ultrices arcu, sit amet vulputate nisi massa vel arcu. Vestibulum in risus nisi. Integer magna ligula, finibus vel tempus non, volutpat at libero. Aliquam iaculis libero sed purus faucibus placerat. + +Quisque a rhoncus arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Maecenas convallis, neque nec mattis pretium, risus nisi varius nisi, in commodo tellus tellus in elit. Nunc ipsum risus, ullamcorper in lacus a, efficitur tempor ligula. Praesent facilisis vestibulum pharetra. Sed hendrerit ultrices arcu ac tincidunt. Vestibulum eget erat odio. + +Phasellus consequat mi id dictum tempor. Nulla sagittis, dui in imperdiet fringilla, tortor massa fringilla dolor, non venenatis mi ipsum at metus. Nulla pharetra ut odio fringilla euismod. Praesent maximus convallis justo sit amet gravida. Fusce a mi ut lorem consectetur venenatis. Quisque pharetra faucibus dapibus. Mauris fermentum quis dolor sit amet ornare. Nam condimentum egestas fermentum. + +Curabitur tellus massa, vulputate nec libero eu, tincidunt semper leo. Donec et lorem nec elit elementum luctus. Donec vel dui arcu. Praesent eu nunc auctor, tincidunt urna sed, condimentum orci. Suspendisse sagittis augue non vestibulum gravida. Donec augue nisi, venenatis in malesuada ac, facilisis sit amet enim. Morbi nec commodo nisi. Aliquam non leo congue, suscipit sem id, porta arcu. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Quisque quis elit eu lorem lobortis aliquet. Fusce lacus magna, interdum quis felis ac, accumsan finibus mi. Sed pretium ipsum a orci consectetur posuere. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam tempor fermentum sem, porttitor pellentesque urna egestas egestas. Morbi quam ante, congue vitae ullamcorper eget, auctor eget velit. + +Curabitur faucibus ligula efficitur, malesuada diam ut, condimentum sapien. Donec congue tincidunt magna, et porta augue mattis sit amet. Sed tempor consequat justo, in fringilla augue fermentum eu. Cras accumsan ut nunc et rutrum. Nam et lorem ac erat malesuada gravida. Nulla fermentum placerat lacinia. Donec pellentesque, sem mattis condimentum bibendum, metus lacus sollicitudin purus, nec laoreet nibh mauris quis neque. Cras et porttitor augue. + +Aliquam posuere mauris arcu, quis euismod orci laoreet ac. Fusce ultricies, ligula a maximus cursus, libero leo sodales tortor, ut interdum nisi velit id leo. Mauris vel convallis dolor. Duis quis pharetra justo. Mauris nisi massa, molestie fermentum est a, convallis sodales orci. Morbi dapibus elit id accumsan tincidunt. Suspendisse non mi sit amet quam finibus volutpat. Duis aliquet sem nec ligula dignissim porta. Praesent commodo ex ex, sit amet pharetra justo pretium vel. Nulla eros purus, rhoncus id iaculis id, commodo et metus. + +Praesent vitae ligula mattis tortor ultricies convallis consequat vel mi. Suspendisse eu arcu eu erat maximus porttitor. Mauris tincidunt venenatis imperdiet. Nulla facilisi. Cras vitae felis et ligula volutpat mattis. Vestibulum bibendum diam non nisl mattis pharetra. Donec vestibulum nisi eu commodo viverra. Proin luctus odio lorem, at pulvinar lectus laoreet non. Nullam eget aliquet diam. Fusce pellentesque quam et urna egestas aliquam. Praesent sed efficitur magna. Aliquam erat volutpat. Pellentesque tempus viverra porta. Pellentesque vel neque et nulla sollicitudin dictum in eu lectus. Donec id dignissim massa. Morbi pulvinar porttitor felis. + +Nam venenatis ante at erat hendrerit venenatis. Donec iaculis mattis ante, non dignissim quam auctor sit amet. Nulla pharetra maximus ipsum, vitae luctus mi tincidunt vitae. Ut fermentum, felis et varius facilisis, enim metus malesuada eros, a eleifend purus eros vitae lectus. Donec ac sodales orci. Mauris sed vulputate est. Curabitur non nisl at diam placerat tempor. Integer commodo dui at est eleifend consectetur. In eu euismod elit, vitae tincidunt orci. Morbi malesuada lacus vel nulla fermentum, sit amet lobortis lacus convallis. Curabitur tincidunt enim ac vestibulum tincidunt. Pellentesque nec mauris at leo lacinia sagittis at at leo. Maecenas sed orci a sem tempor pulvinar in nec eros. Maecenas dignissim sodales arcu, volutpat commodo neque pellentesque sit amet. Cras fermentum ante vitae viverra vehicula. Pellentesque velit velit, tempus id aliquam at, lobortis ac turpis. + +Aenean sed dui dictum, gravida ligula a, fermentum risus. Cras semper commodo diam, sit amet malesuada dolor auctor ac. Praesent non mauris quis augue egestas maximus. Cras id elit et sem tincidunt sollicitudin. Nunc vel massa a felis convallis ornare. Proin sit amet massa porttitor, ultricies felis id, elementum purus. Integer condimentum magna in turpis aliquam iaculis et quis lacus. Maecenas luctus tincidunt turpis, eu tincidunt nulla vehicula in. Sed congue felis a elit tempus convallis. Duis mollis nisi ac pellentesque condimentum. Aliquam justo purus, pellentesque at laoreet id, molestie nec risus. Donec cursus id velit at ullamcorper. + +Morbi varius consectetur nibh, at aliquam orci lacinia eget. Nullam vel vulputate lorem, eu faucibus ex. Suspendisse bibendum luctus viverra. Proin id cursus nibh. Sed blandit diam vel tortor pulvinar, ut venenatis diam egestas. Mauris vestibulum urna vitae odio mattis suscipit. Suspendisse tortor tortor, dictum nec pretium a, luctus quis tellus. Praesent varius, lectus at consequat accumsan, mauris purus semper mauris, ut mattis ipsum sem eu ante. Sed suscipit varius nisi pharetra consequat. Maecenas laoreet ac lacus id bibendum. + +Mauris bibendum justo metus, et viverra orci iaculis quis. Praesent laoreet augue justo, ac semper metus suscipit egestas. Sed dictum tristique mauris, et aliquam elit facilisis ut. Donec tempor feugiat nibh vitae iaculis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec ac magna odio. Quisque a sem ac sapien eleifend feugiat vitae sit amet turpis. + +Cras commodo, orci nec ultrices commodo, dui magna molestie libero, sed varius neque diam bibendum metus. Vivamus luctus nisl sed metus viverra, vitae sodales leo viverra. Nam sed lectus volutpat, scelerisque tortor nec, consectetur quam. Vivamus ut est enim. Aenean porta dolor et justo condimentum ultrices. Integer luctus pellentesque nulla, luctus tristique dolor tempus vel. Suspendisse nisi ex, scelerisque et congue commodo, tincidunt ac mauris. In tristique, sapien sit amet malesuada volutpat, tellus leo mollis augue, nec maximus elit felis vitae lorem. Sed vitae mauris a libero aliquam volutpat vel vel lectus. Sed sed felis at sem dignissim ultricies. Aliquam quam purus, auctor at tincidunt vitae, facilisis ut neque. In hac habitasse platea dictumst. Cras a cursus nibh. + +Etiam ut nisl ultricies, sagittis tortor quis, malesuada quam. Vestibulum imperdiet nec erat vitae euismod. Nunc lacinia quam non augue tempor, in luctus sem imperdiet. Sed ac nulla lectus. Donec fermentum, nulla ac faucibus condimentum, justo nibh faucibus sem, eget cursus magna justo eu massa. Nunc ut suscipit massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum vehicula, dui at cursus condimentum, orci elit vehicula odio, et eleifend risus ligula eget sapien. Quisque vel odio tellus. Etiam ac leo sit amet turpis semper porta. Nulla facilisi. Nulla eu magna vel erat volutpat volutpat. Proin blandit ultricies orci, eget dapibus est dapibus ac. + +Donec a libero non lectus bibendum posuere. Phasellus egestas mi id pulvinar hendrerit. Nullam porta condimentum pulvinar. Nullam quis commodo tellus, lobortis ultrices tellus. Aliquam mattis rutrum faucibus. Aenean vel convallis ligula, vel fermentum nibh. In hac habitasse platea dictumst. Curabitur est felis, volutpat in arcu vitae, tincidunt viverra risus. Vivamus placerat nulla et tincidunt interdum. + +Duis iaculis, enim non pellentesque ultrices, ipsum nisl eleifend ex, ut luctus lorem tortor a est. Ut turpis leo, scelerisque nec euismod ut, bibendum id lorem. Nam vel purus lobortis erat tempus malesuada. Proin arcu lorem, tempor ut egestas in, rhoncus vitae purus. Sed eu lectus maximus, dictum ex a, suscipit metus. Nam id neque ipsum. Aliquam sit amet fringilla lorem, nec ornare lacus. Morbi a finibus arcu, non mattis magna. Phasellus at quam convallis, volutpat mi ut, egestas mi. Cras id odio lacinia, interdum velit sed, placerat ligula. + +Suspendisse vel velit a dui aliquet tempor. Vivamus nulla urna, porttitor a neque non, sollicitudin eleifend massa. Phasellus nec ipsum in dui sollicitudin eleifend. Vivamus posuere placerat elementum. Maecenas accumsan id mauris in commodo. Cras varius molestie risus non sollicitudin. Vestibulum sit amet pretium tellus. Proin vel tortor vestibulum, accumsan quam et, porttitor leo. Duis placerat lacus a turpis imperdiet, sit amet convallis urna ullamcorper. + +Suspendisse aliquam, quam ac accumsan imperdiet, magna ante scelerisque ante, ac convallis dui leo at dui. Nunc aliquet purus sed sollicitudin facilisis. In bibendum risus a scelerisque iaculis. Nam ac tristique quam. In sed lacinia nibh. Nunc porttitor a urna quis lacinia. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In mollis in ligula sit amet facilisis. Quisque tincidunt lobortis lacinia. + +Sed tristique quam sit amet mauris pretium faucibus. Morbi dui ligula, porta nec bibendum ut, imperdiet sed nunc. Aenean fringilla, dui eu tempor efficitur, nibh nisl venenatis lorem, nec dapibus justo augue non nisi. Cras viverra enim in justo rutrum rhoncus. Quisque maximus turpis sit amet condimentum consectetur. Sed fermentum purus purus, quis euismod leo scelerisque ac. Fusce massa tellus, volutpat vehicula eros et, semper dictum massa. Fusce vulputate lacus porta erat elementum, sed tincidunt lacus cursus. Aliquam lorem risus, lobortis et nisi id, mollis tristique ligula. + +Pellentesque rhoncus accumsan tristique. Sed eu venenatis ex. Mauris egestas, lectus sit amet accumsan egestas, diam purus maximus tortor, quis ornare urna ante pulvinar libero. Nam a tortor sodales, placerat enim vel, vulputate velit. Pellentesque ullamcorper nulla in nisi congue, eu lacinia neque facilisis. Proin at pretium magna. Maecenas sodales felis leo, vel tempor erat lobortis eu. Duis id turpis at justo ultrices commodo. Aenean eget nulla nec purus finibus posuere. Vestibulum id mi vel tortor condimentum fermentum. Maecenas eu diam sit amet purus efficitur suscipit. Nullam eleifend vestibulum tincidunt. Sed rhoncus, tortor vitae aliquam condimentum, ex ligula gravida nunc, sed ultrices neque purus a lorem. Vivamus vestibulum tellus eget est volutpat, quis fermentum purus elementum. Donec sit amet ex molestie, ultricies velit eu, dictum est. + +Etiam non tempus nisi. Nullam eget mi risus. Nullam urna purus, facilisis et pharetra ut, pretium id neque. Vestibulum gravida vitae ligula vitae posuere. In dapibus euismod vulputate. In rhoncus, augue eget rhoncus placerat, enim libero sodales libero, eget venenatis metus augue sit amet ex. Aenean tristique tortor sed vehicula tristique. Sed tempor est et lacus scelerisque, vitae interdum purus rutrum. + +Praesent sit amet tincidunt purus. Curabitur ut odio ultrices, laoreet odio et, pretium dolor. Duis nisl nisl, pulvinar id faucibus ut, feugiat et tellus. Donec hendrerit dolor rutrum, rhoncus justo sit amet, condimentum erat. Pellentesque ornare venenatis metus, in accumsan enim ultrices at. Donec dui neque, varius a pulvinar vitae, ultricies nec erat. Pellentesque at velit id ante vulputate volutpat quis non nunc. Cras at felis eget enim lacinia pulvinar. Aliquam erat volutpat. Sed maximus nisl massa, eu maximus dolor scelerisque ac. Vivamus venenatis facilisis metus ac feugiat. Donec mattis hendrerit est in condimentum. + +Curabitur id justo quis turpis facilisis lobortis. Suspendisse potenti. Aliquam erat volutpat. Aliquam hendrerit, nulla a elementum dignissim, massa ante consectetur lacus, non tristique tortor nibh eget tortor. Vivamus vel dictum mi. Fusce mauris tellus, faucibus ac pretium aliquet, faucibus et ligula. In hac habitasse platea dictumst. Suspendisse viverra turpis eget ultrices aliquam. Nam a metus eu odio cursus porttitor. Etiam id vulputate odio. In ultrices facilisis turpis, non luctus quam. Morbi fermentum ultricies diam, in auctor diam malesuada sed. Vivamus congue lorem quis odio molestie, vel vulputate orci egestas. Donec commodo accumsan erat non consectetur. Vivamus eget lobortis sapien. + +Nulla sapien leo, pharetra a quam in, vehicula vulputate leo. Proin sollicitudin laoreet consectetur. Nam lacus nisi, tempor sit amet enim ut, lobortis congue erat. Etiam lacinia interdum nisl vitae sollicitudin. Donec bibendum pretium tellus, quis pellentesque lacus ornare vel. Nullam feugiat, odio molestie dictum vehicula, purus nulla dictum libero, nec semper ante nisi et erat. Sed ornare, nibh laoreet facilisis ornare, nisi nisl molestie ipsum, a scelerisque odio ante vel nisi. Aenean vehicula sit amet mi a pulvinar. Suspendisse potenti. Phasellus dignissim dolor vel erat tristique suscipit. Nam sit amet est a ipsum varius mattis non rutrum tellus. Fusce finibus, neque vehicula vehicula mattis, urna nulla accumsan nunc, ac efficitur enim nunc vitae neque. Nunc aliquam risus eget est scelerisque bibendum et sit amet velit. Etiam vel lectus nisl. + +Ut egestas at purus eu vulputate. Cras pellentesque, dui non ornare fringilla, nunc lectus pretium purus, id feugiat mi dui eget tortor. Aenean venenatis purus in velit suscipit, et sodales ex tempus. Pellentesque et quam eget ex faucibus sodales. Curabitur fermentum sed mi quis gravida. Nulla non vulputate est. Proin efficitur, sapien eu laoreet tempor, tellus purus vestibulum neque, nec suscipit ligula elit id urna. Duis a euismod mi. Aliquam non fermentum dui. Nulla facilisi. Donec eleifend magna a ex pellentesque, ut vulputate ante volutpat. Aliquam vehicula urna mi. Maecenas eleifend nisl id neque ornare, consequat egestas mauris luctus. In risus diam, iaculis ac ultrices eu, consequat sit amet sapien. + +Morbi placerat lorem non venenatis euismod. Nullam efficitur tortor at nulla blandit, eget pellentesque diam sodales. Donec eu leo quis sapien condimentum porta in sit amet eros. Phasellus ultrices mi a justo dictum bibendum id id felis. Mauris mattis, nibh nec interdum scelerisque, odio nibh imperdiet metus, vitae accumsan urna nulla a lorem. Donec mi sapien, suscipit ut tellus eu, tempor feugiat libero. Cras nibh justo, luctus id iaculis non, posuere id libero. In scelerisque turpis iaculis enim sollicitudin, vitae elementum magna feugiat. Phasellus volutpat ultrices diam, vitae sollicitudin tellus vestibulum ut. Vestibulum quis porttitor lectus. + +Aliquam vehicula ut ante vitae posuere. Donec tempus ante purus, efficitur luctus orci tristique non. Nam a finibus odio. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse potenti. Vivamus lobortis nisl arcu, a ullamcorper libero maximus vitae. Ut auctor est sit amet tortor pulvinar aliquam. Vestibulum enim nisl, accumsan ut quam et, faucibus lobortis odio. Duis porta nulla sed dui molestie, sed varius felis imperdiet. + +Aliquam vehicula quam vitae odio placerat, ac molestie magna mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eu scelerisque nunc, et fringilla arcu. Cras ornare aliquet est. Donec ullamcorper risus sed ante suscipit placerat. Vivamus eget posuere mi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec et feugiat metus, in dignissim nibh. Donec viverra massa sit amet efficitur tempor. Nunc vel pretium lorem, sollicitudin euismod nibh. Mauris risus tortor, sollicitudin id sollicitudin vel, bibendum et nisi. Nam sollicitudin est in lorem sodales, a tristique libero volutpat. + +Cras elementum condimentum bibendum. Nulla facilisi. Proin consectetur vel mi eu aliquet. Mauris finibus ultricies lacinia. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nullam convallis, elit ut venenatis imperdiet, massa dolor placerat ligula, vel luctus massa est sed arcu. In convallis nibh metus, a facilisis tortor aliquam ultrices. Nulla nec ornare ante, et porttitor velit. Donec volutpat, tellus vel lobortis consequat, mi velit pharetra elit, id tempus eros ligula et eros. Etiam ex mauris, eleifend eget turpis eu, convallis faucibus lorem. Nulla venenatis dui urna, sed dapibus lectus tempus id. + +Vivamus justo nisi, viverra ac enim eu, facilisis efficitur arcu. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut eget ante eu leo interdum ultrices. Integer convallis euismod consequat. Suspendisse ut ex et risus ultrices porttitor. Maecenas venenatis scelerisque risus, vel vestibulum dolor bibendum vitae. Sed eu molestie urna. Aenean viverra rhoncus massa, et viverra arcu venenatis sit amet. Nunc viverra pharetra auctor. Curabitur mollis tempus erat, non aliquam erat. Aenean porttitor, lorem non tristique condimentum, felis mauris tincidunt nibh, ultricies blandit justo nulla eu est. In ultrices sodales orci, vitae interdum orci posuere sit amet. Nam euismod rhoncus dictum. Donec ultrices dolor vel libero dapibus tempus. + +Quisque eros elit, porttitor non libero id, vehicula suscipit turpis. Vestibulum pretium lectus vel nulla lacinia hendrerit. Etiam eu egestas sapien. Praesent consequat lorem a urna egestas, non suscipit felis commodo. Donec ut mollis ipsum, sit amet posuere magna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi pellentesque semper enim, quis hendrerit mi vehicula sed. Aliquam quam mauris, tristique in risus ut, blandit vulputate turpis. Aenean accumsan luctus mi, hendrerit fringilla est auctor eget. Nunc ornare pharetra odio vel venenatis. Morbi at diam rhoncus, ullamcorper mi id, pretium tellus. Nullam pharetra tincidunt mauris, quis ultrices nunc posuere et. Integer bibendum fermentum enim, a volutpat nisi iaculis sit amet. Suspendisse ante mauris, cursus non massa ut, imperdiet maximus quam. Aliquam eu neque vel felis dapibus fringilla. Donec at sapien et risus vulputate vehicula eu in arcu. + +Nunc maximus elit leo, ut hendrerit orci cursus in. Duis cursus vestibulum commodo. Vivamus efficitur sapien nec nunc sagittis, et sollicitudin dolor ultrices. Morbi euismod erat sit amet elit porttitor aliquam. Ut tristique ut tortor id sollicitudin. Ut nec tempus dolor, vitae consectetur justo. Nunc pellentesque justo nec augue fringilla ultrices. Fusce venenatis posuere metus in malesuada. Morbi in erat facilisis, molestie nisl in, posuere ex. Aenean iaculis condimentum nisl. Etiam ac quam risus. Nulla erat dolor, mollis id leo quis, accumsan ultricies neque. Interdum et malesuada fames ac ante ipsum primis metus. diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..9e3e6c9 --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,43 @@ +/** + * @file Unit tests for the library + * + * @author Anand Suresh + * @copyright Copyright (C) 2018-present Anand Suresh. All rights reserved. + * @license MIT + */ + +const expect = require('chai').expect +const fs = require('fs') +const path = require('path') +const Crc32C = require('..') + +describe('CRC-32C', function () { + describe('.calculate', function () { + const input = 'A hardware-accelerated CRC-32C implementation' + const output = 795910490 + const testCases = { + 'string': {input: input, output: output}, + 'buffer': {input: Buffer.from(input), output: output} + } + + Object.keys(testCases).forEach(function (inputType) { + it(`should handle input of type ${inputType}`, function () { + const input = testCases[inputType].input + const output = testCases[inputType].output + expect(Crc32C.calculate(input)).to.equal(output) + }) + }) + }) + + describe('.fromStream', function () { + it('should correctly calculate the CRC-32C of the stream', function (done) { + const input = fs.createReadStream(path.join(__dirname, 'fixture.txt')) + const output = Crc32C.fromStream(input) + + output.on('finish', function () { + expect(output.crc).to.equal(1240019588) + done() + }) + }) + }) +}) diff --git a/test/sse4_crc32.test.js b/test/sse4_crc32.test.js deleted file mode 100644 index e9683cd..0000000 --- a/test/sse4_crc32.test.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file Unit tests for the library - * - * @author Anand Suresh - * @copyright Copyright (C) 2007-2015 Voxer LLC. All rights reserved. - */ - -var expect = require('chai').expect; - -var Sse4Crc32 = require('../sse4_crc32'); - - - -describe('Sse4Crc32', function() { - describe('.calculate', function() { - var testCases = { - 'string': { - input : 'SSE4-CRC32: A hardware accelerated CRC32 implementation for node.js', - output: 3039989317 - }, - 'buffer': { - input : Buffer.from('SSE4-CRC32: A hardware accelerated CRC32 implementation for node.js'), - output: 3039989317 - } - }; - - Object.keys(testCases).forEach(function(inputType) { - it('should handle input of type ' + inputType, function() { - expect(Sse4Crc32.calculate(testCases[inputType].input)).to.equal(testCases[inputType].output); - }); - }); - }); - - - - describe('CRC32', function() { - it('should be callable', function() { - expect(Sse4Crc32.CRC32).to.be.a('function'); - }); - - - it('should not throw an error upon instantiation', function() { - expect(function() { return new Sse4Crc32.CRC32(); }).to.not.throw(Error); - }); - - - it('should default the initial CRC to 0', function() { - var input = null, - crc = new Sse4Crc32.CRC32(input); - - expect(crc.crc32c).to.equal(0); - }); - - - it('should accept an initial CRC value', function() { - var input = null, - initialCrc = 10, - crc = new Sse4Crc32.CRC32(input, initialCrc); - - expect(crc.crc32c).to.equal(initialCrc); - }); - - - it('should immediately calculate the value of the passed input', function() { - var input = 'input', - crc = new Sse4Crc32.CRC32(input); - - expect(crc.crc()).to.equal(3633258352); - }); - - - it('should progressively calculate CRC for additional input', function() { - var input = 'input', - additionalInput = ' progressive', - crc = new Sse4Crc32.CRC32(input); - - expect(crc.crc()).to.equal(3633258352); - - crc.update(additionalInput); - expect(crc.crc()).to.equal(1233760415); - }); - }); -});