├── .github └── workflows │ └── test.yml ├── .gitignore ├── .mversionrc ├── .npmignore ├── LICENSE ├── README.md ├── TODO ├── benchmark.js ├── bower.json ├── browser_support.js ├── build_browser_version.js ├── dist ├── benchmark │ ├── benchmark.js │ └── index.html ├── snappyjs.js ├── snappyjs.min.js └── test │ ├── index.html │ └── test.js ├── index.js ├── package-lock.json ├── package.json ├── snappy_compressor.js ├── snappy_decompressor.js └── test.js /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [10.x, 12.x, 14.x, 16.x, 18.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm ci 25 | - run: npm run build --if-present 26 | - run: npm test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.mversionrc: -------------------------------------------------------------------------------- 1 | { 2 | "commitMessage": "Bump v%s", 3 | "tagName": "v%s", 4 | "scripts": { 5 | "precommit": "node build_browser_version && git add dist/*" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | dist 3 | browser_support.js 4 | build_browser_version.js 5 | benchmark.js 6 | test.js 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Zhipeng Jia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SnappyJS [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) ![Node.js CI](https://github.com/zhipeng-jia/snappyjs/actions/workflows/test.yml/badge.svg) 2 | A pure JavaScript implementation of Google's [Snappy](https://github.com/google/snappy) compression library. 3 | 4 | This implementation is reasonably fast (see benchmark below). It takes advantage of `ArrayBuffer`. 5 | 6 | ## Install 7 | 8 | If using with Node.js, 9 | ~~~ 10 | npm install snappyjs 11 | ~~~ 12 | 13 | If using with Bower, 14 | ~~~ 15 | bower install snappyjs 16 | ~~~ 17 | 18 | ## Usage 19 | 20 | ### Node.js 21 | 22 | SnappyJS works with Node.js 10.x or later. 23 | ~~~javascript 24 | var SnappyJS = require('snappyjs') 25 | var buffer = new ArrayBuffer(100) 26 | // fill data in buffer 27 | var compressed = SnappyJS.compress(buffer) 28 | var uncompressed = SnappyJS.uncompress(compressed) 29 | ~~~ 30 | 31 | ### Browser 32 | 33 | You can also use SnappyJS in browser. Adding `dist/snappyjs.js` or `dist/snappyjs.min.js` will introduce `SnappyJS` in the global scope. 34 | 35 | SnappyJS relies on `ArrayBuffer`. All major browsers support it now ([http://caniuse.com/#feat=typedarrays](http://caniuse.com/#feat=typedarrays)). Also, as I tested, SnappyJS has high performance on latest version of Google Chrome, Safari, Firefox, and Microsoft Edge. 36 | 37 | **When using webpack to build your project**, and you plan to only use `ArrayBuffer` or `Uint8Array` as input parameters, make sure to put the following in your webpack config to prevent it from automatically bundling a `Buffer` polyfill: 38 | 39 | ```js 40 | node: { 41 | Buffer: false, 42 | } 43 | ``` 44 | 45 | ## API 46 | 47 | ### SnappyJS.compress(input) 48 | 49 | Compress `input`, which must be type of `ArrayBuffer`, `Buffer`, or `Uint8Array`. 50 | Compressed byte stream is returned, with same type of `input`. 51 | 52 | ### SnappyJS.uncompress(compressed, [maxLength]) 53 | 54 | Uncompress `compressed`, which must be type of `ArrayBuffer`, `Buffer`, or `Uint8Array`. 55 | Uncompressed byte stream is returned, with same type of `compressed`. 56 | 57 | If `maxLength` is provided, uncompress function will throw an exception if the data length 58 | encoded in the header exceeds `maxLength`. This is a protection mechanism for malicious data stream. 59 | 60 | ## Benchmark 61 | 62 | Although JavaScript is dynamic-typing, all major JS engines are highly optimized. 63 | Thus well-crafted JavaScript code can have competitive performance even compared to native C++ code. 64 | 65 | I benchmark SnappyJS against `node-snappy` (which is Node.js binding of native implementation). 66 | 67 | Command for benchmark is `node benchmark`. Below is the result running on Node.js v5.5.0. 68 | 69 | ~~~ 70 | Real text #1 (length 618425, byte length 618425), repeated 100 times: 71 | node-snappy#compress x 2.31 ops/sec ±1.47% (10 runs sampled) 72 | snappyjs#compress x 0.91 ops/sec ±0.92% (7 runs sampled) 73 | node-snappy#uncompress x 7.22 ops/sec ±4.07% (22 runs sampled) 74 | snappyjs#uncompress x 2.45 ops/sec ±1.53% (11 runs sampled) 75 | 76 | Real text #2 (length 3844590, byte length 3844591), repeated 10 times: 77 | node-snappy#compress x 7.68 ops/sec ±2.78% (23 runs sampled) 78 | snappyjs#compress x 3.56 ops/sec ±1.44% (13 runs sampled) 79 | node-snappy#uncompress x 17.94 ops/sec ±4.71% (33 runs sampled) 80 | snappyjs#uncompress x 7.24 ops/sec ±2.57% (22 runs sampled) 81 | 82 | Random string (length 1000000, byte length 1500098), repeated 50 times: 83 | node-snappy#compress x 6.69 ops/sec ±5.23% (21 runs sampled) 84 | snappyjs#compress x 2.39 ops/sec ±2.54% (10 runs sampled) 85 | node-snappy#uncompress x 14.94 ops/sec ±6.90% (40 runs sampled) 86 | snappyjs#uncompress x 5.92 ops/sec ±4.28% (19 runs sampled) 87 | 88 | Random string (length 100, byte length 147), repeated 100000 times: 89 | node-snappy#compress x 4.17 ops/sec ±2.96% (15 runs sampled) 90 | snappyjs#compress x 5.45 ops/sec ±1.51% (18 runs sampled) 91 | node-snappy#uncompress x 4.39 ops/sec ±3.83% (15 runs sampled) 92 | snappyjs#uncompress x 14.01 ops/sec ±2.06% (38 runs sampled) 93 | ~~~ 94 | 95 | From the result, we see that SnappyJS has 35%~45% performance of native implementation. 96 | If input size is small, SnappyJS may have better performance than `node-snappy`. 97 | It is because calling native function in JS is much more expensive than calling JS function. 98 | 99 | ## License 100 | 101 | MIT License 102 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 1. Fix coverage test 2 | 2. Add @napi-rs/snappy in the benchmark, and update benchmark results in README 3 | -------------------------------------------------------------------------------- /benchmark.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | var snappy = require('snappy') 26 | var snappyjs = require('./index') 27 | var Benchmark = require('benchmark') 28 | var Promise = require('bluebird') 29 | var rp = require('request-promise') 30 | 31 | function randomString (length) { 32 | var result = '' 33 | var i, code 34 | for (i = 0; i < length; i++) { 35 | code = Math.floor(Math.random() * 256) 36 | result += String.fromCharCode(code) 37 | } 38 | return result 39 | } 40 | 41 | function prepareData (target, text) { 42 | var uncompressedBuffer = Buffer.from(text) 43 | var compressedBuffer = snappy.compressSync(uncompressedBuffer) 44 | target.uncompressedBuffer = uncompressedBuffer 45 | target.compressedBuffer = compressedBuffer 46 | } 47 | 48 | var text1, text2 49 | 50 | function runBenchmark () { 51 | var data 52 | var suite = new Benchmark.Suite() 53 | suite.on('cycle', function (event) { 54 | console.log(String(event.target)) 55 | }) 56 | suite.add('node-snappy#compress', function () { 57 | var i 58 | if (data.repeatedTimes) { 59 | for (i = 0; i < data.repeatedTimes; i++) { 60 | snappy.compressSync(data.uncompressedBuffer) 61 | } 62 | } else { 63 | snappy.compressSync(data.uncompressedBuffer) 64 | } 65 | }).add('snappyjs#compress', function () { 66 | var i 67 | if (data.repeatedTimes) { 68 | for (i = 0; i < data.repeatedTimes; i++) { 69 | snappyjs.compress(data.uncompressedBuffer) 70 | } 71 | } else { 72 | snappyjs.compress(data.uncompressedBuffer) 73 | } 74 | }).add('node-snappy#uncompress', function () { 75 | var i 76 | if (data.repeatedTimes) { 77 | for (i = 0; i < data.repeatedTimes; i++) { 78 | snappy.uncompressSync(data.compressedBuffer) 79 | } 80 | } else { 81 | snappy.uncompressSync(data.compressedBuffer) 82 | } 83 | }).add('snappyjs#uncompress', function () { 84 | var i 85 | if (data.repeatedTimes) { 86 | for (i = 0; i < data.repeatedTimes; i++) { 87 | snappyjs.uncompress(data.compressedBuffer) 88 | } 89 | } else { 90 | snappyjs.uncompress(data.compressedBuffer) 91 | } 92 | }) 93 | 94 | data = {} 95 | prepareData(data, text1) 96 | data.repeatedTimes = 100 97 | console.log(`Real text #1 (length ${text1.length}, byte length ${data.uncompressedBuffer.length}), repeated 100 times:`) 98 | suite.reset().run() 99 | console.log() 100 | 101 | data = {} 102 | prepareData(data, text2) 103 | data.repeatedTimes = 10 104 | console.log(`Real text #2 (length ${text2.length}, byte length ${data.uncompressedBuffer.length}), repeated 10 times:`) 105 | suite.reset().run() 106 | console.log() 107 | 108 | data = {} 109 | prepareData(data, randomString(1000000)) 110 | data.repeatedTimes = 50 111 | console.log(`Random string (length 1000000, byte length ${data.uncompressedBuffer.length}), repeated 50 times:`) 112 | suite.reset().run() 113 | console.log() 114 | 115 | data = {} 116 | prepareData(data, randomString(100)) 117 | data.repeatedTimes = 100000 118 | console.log(`Random string (length 100, byte length ${data.uncompressedBuffer.length}), repeated 100000 times:`) 119 | suite.reset().run() 120 | console.log() 121 | } 122 | 123 | Promise.coroutine(function * () { 124 | text1 = yield rp('https://raw.githubusercontent.com/idc9/stor390/master/notes/natural_language_processing/orwell_novels/1984.txt') 125 | text2 = yield rp('http://www.ecma-international.org/ecma-262/6.0/') 126 | })().then(function () { 127 | runBenchmark() 128 | }) 129 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snappyjs", 3 | "main": "./dist/snappyjs.js", 4 | "version": "0.7.0", 5 | "homepage": "https://github.com/zhipeng-jia/snappyjs", 6 | "authors": [ 7 | "Zhipeng Jia " 8 | ], 9 | "description": "JavaScript implementation of Google's Snappy compression library", 10 | "license": "MIT", 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | ".npmignore", 16 | ".travis.yml", 17 | "benchmark.js", 18 | "test.js", 19 | "browser_support.js", 20 | "build_browser_version.js", 21 | "package.json", 22 | "index.js", 23 | "snappy_compressor.js", 24 | "snappy_decompressor.js" 25 | ], 26 | "keywords": [ 27 | "snappy" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /browser_support.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | var SnappyJS = window.SnappyJS || {} 24 | SnappyJS.uncompress = require('./index').uncompress 25 | SnappyJS.compress = require('./index').compress 26 | window.SnappyJS = SnappyJS 27 | -------------------------------------------------------------------------------- /build_browser_version.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | var browserify = require('browserify') 26 | var licensify = require('licensify') 27 | var fs = require('fs') 28 | 29 | var dest, b 30 | 31 | dest = fs.createWriteStream('dist/snappyjs.js') 32 | b = browserify('browser_support.js', { detectGlobals: false }) 33 | b.plugin(licensify) 34 | b.bundle().pipe(dest) 35 | 36 | dest = fs.createWriteStream('dist/snappyjs.min.js') 37 | b = browserify('browser_support.js', { detectGlobals: false }) 38 | b.plugin(licensify) 39 | b.transform({}, 'uglifyify') 40 | b.bundle().pipe(dest) 41 | -------------------------------------------------------------------------------- /dist/benchmark/benchmark.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | var fileInput = document.getElementById('input') 26 | var output = document.getElementById('output') 27 | 28 | fileInput.addEventListener('change', function (e) { 29 | output.innerHTML = '' 30 | var file = fileInput.files[0] 31 | var reader = new FileReader() 32 | reader.onload = function (e) { 33 | var contentBuffer = reader.result 34 | output.innerHTML += 'Original byte size: ' + contentBuffer.byteLength + '
' 35 | var compressed = SnappyJS.compress(contentBuffer) 36 | output.innerHTML += 'Compressed byte size: ' + compressed.byteLength + '
' 37 | 38 | var suite = new Benchmark.Suite() 39 | suite.add('SnappyJS#compress', function () { 40 | SnappyJS.compress(contentBuffer) 41 | }).add('SnappyJS#uncompress', function () { 42 | SnappyJS.uncompress(compressed) 43 | }).on('cycle', function (event) { 44 | output.innerHTML += String(event.target) + '
' 45 | }).run() 46 | } 47 | reader.readAsArrayBuffer(file) 48 | }) 49 | -------------------------------------------------------------------------------- /dist/benchmark/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SnappyJS benchmark page 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 |     
13 | 
14 | 
15 | 


--------------------------------------------------------------------------------
/dist/snappyjs.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * Modules in this bundle
  3 |  * @license
  4 |  *
  5 |  * snappyjs:
  6 |  *   license: MIT (http://opensource.org/licenses/MIT)
  7 |  *   author: Zhipeng Jia
  8 |  *   version: 0.7.0
  9 |  *
 10 |  * This header is generated by licensify (https://github.com/twada/licensify)
 11 |  */
 12 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i maxLength) {
114 |     throw new Error(`The uncompressed length of ${length} is too big, expect at most ${maxLength}`)
115 |   }
116 |   var uncompressed, uncompressedView
117 |   if (uint8Mode) {
118 |     uncompressed = new Uint8Array(length)
119 |     if (!decompressor.uncompressToBuffer(uncompressed)) {
120 |       throw new Error('Invalid Snappy bitstream')
121 |     }
122 |   } else if (arrayBufferMode) {
123 |     uncompressed = new ArrayBuffer(length)
124 |     uncompressedView = new Uint8Array(uncompressed)
125 |     if (!decompressor.uncompressToBuffer(uncompressedView)) {
126 |       throw new Error('Invalid Snappy bitstream')
127 |     }
128 |   } else {
129 |     uncompressed = Buffer.alloc(length)
130 |     if (!decompressor.uncompressToBuffer(uncompressed)) {
131 |       throw new Error('Invalid Snappy bitstream')
132 |     }
133 |   }
134 |   return uncompressed
135 | }
136 | 
137 | function compress (uncompressed) {
138 |   if (!isUint8Array(uncompressed) && !isArrayBuffer(uncompressed) && !isBuffer(uncompressed)) {
139 |     throw new TypeError(TYPE_ERROR_MSG)
140 |   }
141 |   var uint8Mode = false
142 |   var arrayBufferMode = false
143 |   if (isUint8Array(uncompressed)) {
144 |     uint8Mode = true
145 |   } else if (isArrayBuffer(uncompressed)) {
146 |     arrayBufferMode = true
147 |     uncompressed = new Uint8Array(uncompressed)
148 |   }
149 |   var compressor = new SnappyCompressor(uncompressed)
150 |   var maxLength = compressor.maxCompressedLength()
151 |   var compressed, compressedView
152 |   var length
153 |   if (uint8Mode) {
154 |     compressed = new Uint8Array(maxLength)
155 |     length = compressor.compressToBuffer(compressed)
156 |   } else if (arrayBufferMode) {
157 |     compressed = new ArrayBuffer(maxLength)
158 |     compressedView = new Uint8Array(compressed)
159 |     length = compressor.compressToBuffer(compressedView)
160 |   } else {
161 |     compressed = Buffer.alloc(maxLength)
162 |     length = compressor.compressToBuffer(compressed)
163 |   }
164 |   if (!compressed.slice) { // ie11
165 |     var compressedArray = new Uint8Array(Array.prototype.slice.call(compressed, 0, length))
166 |     if (uint8Mode) {
167 |       return compressedArray
168 |     } else if (arrayBufferMode) {
169 |       return compressedArray.buffer
170 |     } else {
171 |       throw new Error('Not implemented')
172 |     }
173 |   }
174 | 
175 |   return compressed.slice(0, length)
176 | }
177 | 
178 | exports.uncompress = uncompress
179 | exports.compress = compress
180 | 
181 | },{"./snappy_compressor":3,"./snappy_decompressor":4}],3:[function(require,module,exports){
182 | // The MIT License (MIT)
183 | //
184 | // Copyright (c) 2016 Zhipeng Jia
185 | //
186 | // Permission is hereby granted, free of charge, to any person obtaining a copy
187 | // of this software and associated documentation files (the "Software"), to deal
188 | // in the Software without restriction, including without limitation the rights
189 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
190 | // copies of the Software, and to permit persons to whom the Software is
191 | // furnished to do so, subject to the following conditions:
192 | //
193 | // The above copyright notice and this permission notice shall be included in all
194 | // copies or substantial portions of the Software.
195 | //
196 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
197 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
198 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
199 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
200 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
201 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
202 | // SOFTWARE.
203 | 
204 | 'use strict'
205 | 
206 | var BLOCK_LOG = 16
207 | var BLOCK_SIZE = 1 << BLOCK_LOG
208 | 
209 | var MAX_HASH_TABLE_BITS = 14
210 | var globalHashTables = new Array(MAX_HASH_TABLE_BITS + 1)
211 | 
212 | function hashFunc (key, hashFuncShift) {
213 |   return (key * 0x1e35a7bd) >>> hashFuncShift
214 | }
215 | 
216 | function load32 (array, pos) {
217 |   return array[pos] + (array[pos + 1] << 8) + (array[pos + 2] << 16) + (array[pos + 3] << 24)
218 | }
219 | 
220 | function equals32 (array, pos1, pos2) {
221 |   return array[pos1] === array[pos2] &&
222 |          array[pos1 + 1] === array[pos2 + 1] &&
223 |          array[pos1 + 2] === array[pos2 + 2] &&
224 |          array[pos1 + 3] === array[pos2 + 3]
225 | }
226 | 
227 | function copyBytes (fromArray, fromPos, toArray, toPos, length) {
228 |   var i
229 |   for (i = 0; i < length; i++) {
230 |     toArray[toPos + i] = fromArray[fromPos + i]
231 |   }
232 | }
233 | 
234 | function emitLiteral (input, ip, len, output, op) {
235 |   if (len <= 60) {
236 |     output[op] = (len - 1) << 2
237 |     op += 1
238 |   } else if (len < 256) {
239 |     output[op] = 60 << 2
240 |     output[op + 1] = len - 1
241 |     op += 2
242 |   } else {
243 |     output[op] = 61 << 2
244 |     output[op + 1] = (len - 1) & 0xff
245 |     output[op + 2] = (len - 1) >>> 8
246 |     op += 3
247 |   }
248 |   copyBytes(input, ip, output, op, len)
249 |   return op + len
250 | }
251 | 
252 | function emitCopyLessThan64 (output, op, offset, len) {
253 |   if (len < 12 && offset < 2048) {
254 |     output[op] = 1 + ((len - 4) << 2) + ((offset >>> 8) << 5)
255 |     output[op + 1] = offset & 0xff
256 |     return op + 2
257 |   } else {
258 |     output[op] = 2 + ((len - 1) << 2)
259 |     output[op + 1] = offset & 0xff
260 |     output[op + 2] = offset >>> 8
261 |     return op + 3
262 |   }
263 | }
264 | 
265 | function emitCopy (output, op, offset, len) {
266 |   while (len >= 68) {
267 |     op = emitCopyLessThan64(output, op, offset, 64)
268 |     len -= 64
269 |   }
270 |   if (len > 64) {
271 |     op = emitCopyLessThan64(output, op, offset, 60)
272 |     len -= 60
273 |   }
274 |   return emitCopyLessThan64(output, op, offset, len)
275 | }
276 | 
277 | function compressFragment (input, ip, inputSize, output, op) {
278 |   var hashTableBits = 1
279 |   while ((1 << hashTableBits) <= inputSize &&
280 |          hashTableBits <= MAX_HASH_TABLE_BITS) {
281 |     hashTableBits += 1
282 |   }
283 |   hashTableBits -= 1
284 |   var hashFuncShift = 32 - hashTableBits
285 | 
286 |   if (typeof globalHashTables[hashTableBits] === 'undefined') {
287 |     globalHashTables[hashTableBits] = new Uint16Array(1 << hashTableBits)
288 |   }
289 |   var hashTable = globalHashTables[hashTableBits]
290 |   var i
291 |   for (i = 0; i < hashTable.length; i++) {
292 |     hashTable[i] = 0
293 |   }
294 | 
295 |   var ipEnd = ip + inputSize
296 |   var ipLimit
297 |   var baseIp = ip
298 |   var nextEmit = ip
299 | 
300 |   var hash, nextHash
301 |   var nextIp, candidate, skip
302 |   var bytesBetweenHashLookups
303 |   var base, matched, offset
304 |   var prevHash, curHash
305 |   var flag = true
306 | 
307 |   var INPUT_MARGIN = 15
308 |   if (inputSize >= INPUT_MARGIN) {
309 |     ipLimit = ipEnd - INPUT_MARGIN
310 | 
311 |     ip += 1
312 |     nextHash = hashFunc(load32(input, ip), hashFuncShift)
313 | 
314 |     while (flag) {
315 |       skip = 32
316 |       nextIp = ip
317 |       do {
318 |         ip = nextIp
319 |         hash = nextHash
320 |         bytesBetweenHashLookups = skip >>> 5
321 |         skip += 1
322 |         nextIp = ip + bytesBetweenHashLookups
323 |         if (ip > ipLimit) {
324 |           flag = false
325 |           break
326 |         }
327 |         nextHash = hashFunc(load32(input, nextIp), hashFuncShift)
328 |         candidate = baseIp + hashTable[hash]
329 |         hashTable[hash] = ip - baseIp
330 |       } while (!equals32(input, ip, candidate))
331 | 
332 |       if (!flag) {
333 |         break
334 |       }
335 | 
336 |       op = emitLiteral(input, nextEmit, ip - nextEmit, output, op)
337 | 
338 |       do {
339 |         base = ip
340 |         matched = 4
341 |         while (ip + matched < ipEnd && input[ip + matched] === input[candidate + matched]) {
342 |           matched += 1
343 |         }
344 |         ip += matched
345 |         offset = base - candidate
346 |         op = emitCopy(output, op, offset, matched)
347 | 
348 |         nextEmit = ip
349 |         if (ip >= ipLimit) {
350 |           flag = false
351 |           break
352 |         }
353 |         prevHash = hashFunc(load32(input, ip - 1), hashFuncShift)
354 |         hashTable[prevHash] = ip - 1 - baseIp
355 |         curHash = hashFunc(load32(input, ip), hashFuncShift)
356 |         candidate = baseIp + hashTable[curHash]
357 |         hashTable[curHash] = ip - baseIp
358 |       } while (equals32(input, ip, candidate))
359 | 
360 |       if (!flag) {
361 |         break
362 |       }
363 | 
364 |       ip += 1
365 |       nextHash = hashFunc(load32(input, ip), hashFuncShift)
366 |     }
367 |   }
368 | 
369 |   if (nextEmit < ipEnd) {
370 |     op = emitLiteral(input, nextEmit, ipEnd - nextEmit, output, op)
371 |   }
372 | 
373 |   return op
374 | }
375 | 
376 | function putVarint (value, output, op) {
377 |   do {
378 |     output[op] = value & 0x7f
379 |     value = value >>> 7
380 |     if (value > 0) {
381 |       output[op] += 0x80
382 |     }
383 |     op += 1
384 |   } while (value > 0)
385 |   return op
386 | }
387 | 
388 | function SnappyCompressor (uncompressed) {
389 |   this.array = uncompressed
390 | }
391 | 
392 | SnappyCompressor.prototype.maxCompressedLength = function () {
393 |   var sourceLen = this.array.length
394 |   return 32 + sourceLen + Math.floor(sourceLen / 6)
395 | }
396 | 
397 | SnappyCompressor.prototype.compressToBuffer = function (outBuffer) {
398 |   var array = this.array
399 |   var length = array.length
400 |   var pos = 0
401 |   var outPos = 0
402 | 
403 |   var fragmentSize
404 | 
405 |   outPos = putVarint(length, outBuffer, outPos)
406 |   while (pos < length) {
407 |     fragmentSize = Math.min(length - pos, BLOCK_SIZE)
408 |     outPos = compressFragment(array, pos, fragmentSize, outBuffer, outPos)
409 |     pos += fragmentSize
410 |   }
411 | 
412 |   return outPos
413 | }
414 | 
415 | exports.SnappyCompressor = SnappyCompressor
416 | 
417 | },{}],4:[function(require,module,exports){
418 | // The MIT License (MIT)
419 | //
420 | // Copyright (c) 2016 Zhipeng Jia
421 | //
422 | // Permission is hereby granted, free of charge, to any person obtaining a copy
423 | // of this software and associated documentation files (the "Software"), to deal
424 | // in the Software without restriction, including without limitation the rights
425 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
426 | // copies of the Software, and to permit persons to whom the Software is
427 | // furnished to do so, subject to the following conditions:
428 | //
429 | // The above copyright notice and this permission notice shall be included in all
430 | // copies or substantial portions of the Software.
431 | //
432 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
433 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
434 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
435 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
436 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
437 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
438 | // SOFTWARE.
439 | 
440 | 'use strict'
441 | 
442 | var WORD_MASK = [0, 0xff, 0xffff, 0xffffff, 0xffffffff]
443 | 
444 | function copyBytes (fromArray, fromPos, toArray, toPos, length) {
445 |   var i
446 |   for (i = 0; i < length; i++) {
447 |     toArray[toPos + i] = fromArray[fromPos + i]
448 |   }
449 | }
450 | 
451 | function selfCopyBytes (array, pos, offset, length) {
452 |   var i
453 |   for (i = 0; i < length; i++) {
454 |     array[pos + i] = array[pos - offset + i]
455 |   }
456 | }
457 | 
458 | function SnappyDecompressor (compressed) {
459 |   this.array = compressed
460 |   this.pos = 0
461 | }
462 | 
463 | SnappyDecompressor.prototype.readUncompressedLength = function () {
464 |   var result = 0
465 |   var shift = 0
466 |   var c, val
467 |   while (shift < 32 && this.pos < this.array.length) {
468 |     c = this.array[this.pos]
469 |     this.pos += 1
470 |     val = c & 0x7f
471 |     if (((val << shift) >>> shift) !== val) {
472 |       return -1
473 |     }
474 |     result |= val << shift
475 |     if (c < 128) {
476 |       return result
477 |     }
478 |     shift += 7
479 |   }
480 |   return -1
481 | }
482 | 
483 | SnappyDecompressor.prototype.uncompressToBuffer = function (outBuffer) {
484 |   var array = this.array
485 |   var arrayLength = array.length
486 |   var pos = this.pos
487 |   var outPos = 0
488 | 
489 |   var c, len, smallLen
490 |   var offset
491 | 
492 |   while (pos < array.length) {
493 |     c = array[pos]
494 |     pos += 1
495 |     if ((c & 0x3) === 0) {
496 |       // Literal
497 |       len = (c >>> 2) + 1
498 |       if (len > 60) {
499 |         if (pos + 3 >= arrayLength) {
500 |           return false
501 |         }
502 |         smallLen = len - 60
503 |         len = array[pos] + (array[pos + 1] << 8) + (array[pos + 2] << 16) + (array[pos + 3] << 24)
504 |         len = (len & WORD_MASK[smallLen]) + 1
505 |         pos += smallLen
506 |       }
507 |       if (pos + len > arrayLength) {
508 |         return false
509 |       }
510 |       copyBytes(array, pos, outBuffer, outPos, len)
511 |       pos += len
512 |       outPos += len
513 |     } else {
514 |       switch (c & 0x3) {
515 |         case 1:
516 |           len = ((c >>> 2) & 0x7) + 4
517 |           offset = array[pos] + ((c >>> 5) << 8)
518 |           pos += 1
519 |           break
520 |         case 2:
521 |           if (pos + 1 >= arrayLength) {
522 |             return false
523 |           }
524 |           len = (c >>> 2) + 1
525 |           offset = array[pos] + (array[pos + 1] << 8)
526 |           pos += 2
527 |           break
528 |         case 3:
529 |           if (pos + 3 >= arrayLength) {
530 |             return false
531 |           }
532 |           len = (c >>> 2) + 1
533 |           offset = array[pos] + (array[pos + 1] << 8) + (array[pos + 2] << 16) + (array[pos + 3] << 24)
534 |           pos += 4
535 |           break
536 |         default:
537 |           break
538 |       }
539 |       if (offset === 0 || offset > outPos) {
540 |         return false
541 |       }
542 |       selfCopyBytes(outBuffer, outPos, offset, len)
543 |       outPos += len
544 |     }
545 |   }
546 |   return true
547 | }
548 | 
549 | exports.SnappyDecompressor = SnappyDecompressor
550 | 
551 | },{}]},{},[1]);
552 | 


--------------------------------------------------------------------------------
/dist/snappyjs.min.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Modules in this bundle
 3 |  * @license
 4 |  *
 5 |  * snappyjs:
 6 |  *   license: MIT (http://opensource.org/licenses/MIT)
 7 |  *   author: Zhipeng Jia
 8 |  *   version: 0.7.0
 9 |  *
10 |  * This header is generated by licensify (https://github.com/twada/licensify)
11 |  */
12 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;ie)throw new Error(`The uncompressed length of ${t} is too big, expect at most ${e}`);if(s){if(o=new Uint8Array(t),!i.uncompressToBuffer(o))throw new Error("Invalid Snappy bitstream")}else if(n){if(o=new ArrayBuffer(t),f=new Uint8Array(o),!i.uncompressToBuffer(f))throw new Error("Invalid Snappy bitstream")}else if(o=Buffer.alloc(t),!i.uncompressToBuffer(o))throw new Error("Invalid Snappy bitstream");return o}function compress(r){if(!isUint8Array(r)&&!isArrayBuffer(r)&&!isBuffer(r))throw new TypeError(TYPE_ERROR_MSG);var e=!1,s=!1;isUint8Array(r)?e=!0:isArrayBuffer(r)&&(s=!0,r=new Uint8Array(r));var n,o,f,i=new SnappyCompressor(r),t=i.maxCompressedLength();if(e?(n=new Uint8Array(t),f=i.compressToBuffer(n)):s?(n=new ArrayBuffer(t),o=new Uint8Array(n),f=i.compressToBuffer(o)):(n=Buffer.alloc(t),f=i.compressToBuffer(n)),!n.slice){var p=new Uint8Array(Array.prototype.slice.call(n,0,f));if(e)return p;if(s)return p.buffer;throw new Error("Not implemented")}return n.slice(0,f)}exports.uncompress=uncompress,exports.compress=compress;
17 | 
18 | },{"./snappy_compressor":3,"./snappy_decompressor":4}],3:[function(require,module,exports){
19 | "use strict";var BLOCK_LOG=16,BLOCK_SIZE=1<>>a}function load32(r,a){return r[a]+(r[a+1]<<8)+(r[a+2]<<16)+(r[a+3]<<24)}function equals32(r,a,e){return r[a]===r[e]&&r[a+1]===r[e+1]&&r[a+2]===r[e+2]&&r[a+3]===r[e+3]}function copyBytes(r,a,e,o,n){var t;for(t=0;t>>8,n+=3),copyBytes(r,a,o,n,e),n+e}function emitCopyLessThan64(r,a,e,o){return o<12&&e<2048?(r[a]=1+(o-4<<2)+(e>>>8<<5),r[a+1]=255&e,a+2):(r[a]=2+(o-1<<2),r[a+1]=255&e,r[a+2]=e>>>8,a+3)}function emitCopy(r,a,e,o){for(;o>=68;)a=emitCopyLessThan64(r,a,e,64),o-=64;return o>64&&(a=emitCopyLessThan64(r,a,e,60),o-=60),emitCopyLessThan64(r,a,e,o)}function compressFragment(r,a,e,o,n){for(var t=1;1<=15)for(p=B-15,l=hashFunc(load32(r,a+=1),s);b;){m=32,f=a;do{if(h=l,y=m>>>5,m+=1,f=(a=f)+y,a>p){b=!1;break}l=hashFunc(load32(r,f),s),c=S+u[h],u[h]=a-S}while(!equals32(r,a,c));if(!b)break;n=emitLiteral(r,_,a-_,o,n);do{for(L=a,C=4;a+C=p){b=!1;break}u[hashFunc(load32(r,a-1),s)]=a-1-S,c=S+u[T=hashFunc(load32(r,a),s)],u[T]=a-S}while(equals32(r,a,c));if(!b)break;l=hashFunc(load32(r,a+=1),s)}return _>>=7)>0&&(a[e]+=128),e+=1}while(r>0);return e}function SnappyCompressor(r){this.array=r}SnappyCompressor.prototype.maxCompressedLength=function(){var r=this.array.length;return 32+r+Math.floor(r/6)},SnappyCompressor.prototype.compressToBuffer=function(r){var a,e=this.array,o=e.length,n=0,t=0;for(t=putVarint(o,r,t);n>>t!==e)return-1;if(s|=e<>>2))>60){if(i+3>=n)return!1;t=s-60,s=1+((s=p[i]+(p[i+1]<<8)+(p[i+2]<<16)+(p[i+3]<<24))&WORD_MASK[t]),i+=t}if(i+s>n)return!1;copyBytes(p,i,r,a,s),i+=s,a+=s}else{switch(3&e){case 1:s=4+(e>>>2&7),o=p[i]+(e>>>5<<8),i+=1;break;case 2:if(i+1>=n)return!1;s=1+(e>>>2),o=p[i]+(p[i+1]<<8),i+=2;break;case 3:if(i+3>=n)return!1;s=1+(e>>>2),o=p[i]+(p[i+1]<<8)+(p[i+2]<<16)+(p[i+3]<<24),i+=4}if(0===o||o>a)return!1;selfCopyBytes(r,a,o,s),a+=s}return!0},exports.SnappyDecompressor=SnappyDecompressor;
23 | 
24 | },{}]},{},[1]);
25 | 


--------------------------------------------------------------------------------
/dist/test/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     SnappyJS test page
 5 |     
 6 | 
 7 | 
 8 |     
 9 |     

10 |     
11 | 
12 | 
13 | 


--------------------------------------------------------------------------------
/dist/test/test.js:
--------------------------------------------------------------------------------
 1 | // The MIT License (MIT)
 2 | //
 3 | // Copyright (c) 2016 Zhipeng Jia
 4 | //
 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | // of this software and associated documentation files (the "Software"), to deal
 7 | // in the Software without restriction, including without limitation the rights
 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 | 
23 | 'use strict'
24 | 
25 | function arrayBufferEquals (buffer1, buffer2) {
26 |   if (buffer1.byteLength !== buffer2.byteLength) {
27 |     return false
28 |   }
29 |   var view1 = new Uint8Array(buffer1)
30 |   var view2 = new Uint8Array(buffer1)
31 |   var i
32 |   for (i = 0; i < view1.length; i++) {
33 |     if (view1[i] !== view2[i]) {
34 |       return false
35 |     }
36 |   }
37 |   return true
38 | }
39 | 
40 | var fileInput = document.getElementById('input')
41 | var output = document.getElementById('output')
42 | 
43 | fileInput.addEventListener('change', function (e) {
44 |   var file = fileInput.files[0]
45 |   var reader = new FileReader()
46 |   reader.onload = function (e) {
47 |     var contentBuffer = reader.result
48 |     var compressed = SnappyJS.compress(contentBuffer)
49 |     var uncompressed = SnappyJS.uncompress(compressed)
50 |     if (arrayBufferEquals(uncompressed, contentBuffer)) {
51 |       output.innerHTML = 'Original byte size: ' + contentBuffer.byteLength + '
' + 52 | 'Compressed byte size: ' + compressed.byteLength 53 | } else { 54 | window.alert('Test failed!') 55 | } 56 | } 57 | reader.readAsArrayBuffer(file) 58 | }) 59 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | function isNode () { 26 | if (typeof process === 'object') { 27 | if (typeof process.versions === 'object') { 28 | if (typeof process.versions.node !== 'undefined') { 29 | return true 30 | } 31 | } 32 | } 33 | return false 34 | } 35 | 36 | function isUint8Array (object) { 37 | return object instanceof Uint8Array && (!isNode() || !Buffer.isBuffer(object)) 38 | } 39 | 40 | function isArrayBuffer (object) { 41 | return object instanceof ArrayBuffer 42 | } 43 | 44 | function isBuffer (object) { 45 | if (!isNode()) { 46 | return false 47 | } 48 | return Buffer.isBuffer(object) 49 | } 50 | 51 | var SnappyDecompressor = require('./snappy_decompressor').SnappyDecompressor 52 | var SnappyCompressor = require('./snappy_compressor').SnappyCompressor 53 | 54 | var TYPE_ERROR_MSG = 'Argument compressed must be type of ArrayBuffer, Buffer, or Uint8Array' 55 | 56 | function uncompress (compressed, maxLength) { 57 | if (!isUint8Array(compressed) && !isArrayBuffer(compressed) && !isBuffer(compressed)) { 58 | throw new TypeError(TYPE_ERROR_MSG) 59 | } 60 | var uint8Mode = false 61 | var arrayBufferMode = false 62 | if (isUint8Array(compressed)) { 63 | uint8Mode = true 64 | } else if (isArrayBuffer(compressed)) { 65 | arrayBufferMode = true 66 | compressed = new Uint8Array(compressed) 67 | } 68 | var decompressor = new SnappyDecompressor(compressed) 69 | var length = decompressor.readUncompressedLength() 70 | if (length === -1) { 71 | throw new Error('Invalid Snappy bitstream') 72 | } 73 | if (length > maxLength) { 74 | throw new Error(`The uncompressed length of ${length} is too big, expect at most ${maxLength}`) 75 | } 76 | var uncompressed, uncompressedView 77 | if (uint8Mode) { 78 | uncompressed = new Uint8Array(length) 79 | if (!decompressor.uncompressToBuffer(uncompressed)) { 80 | throw new Error('Invalid Snappy bitstream') 81 | } 82 | } else if (arrayBufferMode) { 83 | uncompressed = new ArrayBuffer(length) 84 | uncompressedView = new Uint8Array(uncompressed) 85 | if (!decompressor.uncompressToBuffer(uncompressedView)) { 86 | throw new Error('Invalid Snappy bitstream') 87 | } 88 | } else { 89 | uncompressed = Buffer.alloc(length) 90 | if (!decompressor.uncompressToBuffer(uncompressed)) { 91 | throw new Error('Invalid Snappy bitstream') 92 | } 93 | } 94 | return uncompressed 95 | } 96 | 97 | function compress (uncompressed) { 98 | if (!isUint8Array(uncompressed) && !isArrayBuffer(uncompressed) && !isBuffer(uncompressed)) { 99 | throw new TypeError(TYPE_ERROR_MSG) 100 | } 101 | var uint8Mode = false 102 | var arrayBufferMode = false 103 | if (isUint8Array(uncompressed)) { 104 | uint8Mode = true 105 | } else if (isArrayBuffer(uncompressed)) { 106 | arrayBufferMode = true 107 | uncompressed = new Uint8Array(uncompressed) 108 | } 109 | var compressor = new SnappyCompressor(uncompressed) 110 | var maxLength = compressor.maxCompressedLength() 111 | var compressed, compressedView 112 | var length 113 | if (uint8Mode) { 114 | compressed = new Uint8Array(maxLength) 115 | length = compressor.compressToBuffer(compressed) 116 | } else if (arrayBufferMode) { 117 | compressed = new ArrayBuffer(maxLength) 118 | compressedView = new Uint8Array(compressed) 119 | length = compressor.compressToBuffer(compressedView) 120 | } else { 121 | compressed = Buffer.alloc(maxLength) 122 | length = compressor.compressToBuffer(compressed) 123 | } 124 | if (!compressed.slice) { // ie11 125 | var compressedArray = new Uint8Array(Array.prototype.slice.call(compressed, 0, length)) 126 | if (uint8Mode) { 127 | return compressedArray 128 | } else if (arrayBufferMode) { 129 | return compressedArray.buffer 130 | } else { 131 | throw new Error('Not implemented') 132 | } 133 | } 134 | 135 | return compressed.slice(0, length) 136 | } 137 | 138 | exports.uncompress = uncompress 139 | exports.compress = compress 140 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snappyjs", 3 | "version": "0.7.0", 4 | "description": "JavaScript implementation of Google's Snappy compression library", 5 | "repository": "zhipeng-jia/snappyjs", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "standard && tap test.js" 9 | }, 10 | "author": "Zhipeng Jia", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "benchmark": "~2.1.4", 14 | "bluebird": "~3.5.0", 15 | "browserify": "~14.4.0", 16 | "licensify": "~3.1.3", 17 | "microtime": "~3.0.0", 18 | "request-promise": "~4.2.1", 19 | "snappy": "~6.3.5", 20 | "standard": "^17.0.0", 21 | "tap": "^15.0.9", 22 | "uglifyify": "^5.0.2" 23 | }, 24 | "keywords": [ 25 | "snappy" 26 | ], 27 | "standard": { 28 | "ignore": [ 29 | "/dist/snappyjs.js", 30 | "/dist/snappyjs.min.js" 31 | ], 32 | "globals": [ 33 | "FileReader", 34 | "Benchmark", 35 | "SnappyJS" 36 | ] 37 | }, 38 | "tap": { 39 | "check-coverage": false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /snappy_compressor.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | var BLOCK_LOG = 16 26 | var BLOCK_SIZE = 1 << BLOCK_LOG 27 | 28 | var MAX_HASH_TABLE_BITS = 14 29 | var globalHashTables = new Array(MAX_HASH_TABLE_BITS + 1) 30 | 31 | function hashFunc (key, hashFuncShift) { 32 | return (key * 0x1e35a7bd) >>> hashFuncShift 33 | } 34 | 35 | function load32 (array, pos) { 36 | return array[pos] + (array[pos + 1] << 8) + (array[pos + 2] << 16) + (array[pos + 3] << 24) 37 | } 38 | 39 | function equals32 (array, pos1, pos2) { 40 | return array[pos1] === array[pos2] && 41 | array[pos1 + 1] === array[pos2 + 1] && 42 | array[pos1 + 2] === array[pos2 + 2] && 43 | array[pos1 + 3] === array[pos2 + 3] 44 | } 45 | 46 | function copyBytes (fromArray, fromPos, toArray, toPos, length) { 47 | var i 48 | for (i = 0; i < length; i++) { 49 | toArray[toPos + i] = fromArray[fromPos + i] 50 | } 51 | } 52 | 53 | function emitLiteral (input, ip, len, output, op) { 54 | if (len <= 60) { 55 | output[op] = (len - 1) << 2 56 | op += 1 57 | } else if (len < 256) { 58 | output[op] = 60 << 2 59 | output[op + 1] = len - 1 60 | op += 2 61 | } else { 62 | output[op] = 61 << 2 63 | output[op + 1] = (len - 1) & 0xff 64 | output[op + 2] = (len - 1) >>> 8 65 | op += 3 66 | } 67 | copyBytes(input, ip, output, op, len) 68 | return op + len 69 | } 70 | 71 | function emitCopyLessThan64 (output, op, offset, len) { 72 | if (len < 12 && offset < 2048) { 73 | output[op] = 1 + ((len - 4) << 2) + ((offset >>> 8) << 5) 74 | output[op + 1] = offset & 0xff 75 | return op + 2 76 | } else { 77 | output[op] = 2 + ((len - 1) << 2) 78 | output[op + 1] = offset & 0xff 79 | output[op + 2] = offset >>> 8 80 | return op + 3 81 | } 82 | } 83 | 84 | function emitCopy (output, op, offset, len) { 85 | while (len >= 68) { 86 | op = emitCopyLessThan64(output, op, offset, 64) 87 | len -= 64 88 | } 89 | if (len > 64) { 90 | op = emitCopyLessThan64(output, op, offset, 60) 91 | len -= 60 92 | } 93 | return emitCopyLessThan64(output, op, offset, len) 94 | } 95 | 96 | function compressFragment (input, ip, inputSize, output, op) { 97 | var hashTableBits = 1 98 | while ((1 << hashTableBits) <= inputSize && 99 | hashTableBits <= MAX_HASH_TABLE_BITS) { 100 | hashTableBits += 1 101 | } 102 | hashTableBits -= 1 103 | var hashFuncShift = 32 - hashTableBits 104 | 105 | if (typeof globalHashTables[hashTableBits] === 'undefined') { 106 | globalHashTables[hashTableBits] = new Uint16Array(1 << hashTableBits) 107 | } 108 | var hashTable = globalHashTables[hashTableBits] 109 | var i 110 | for (i = 0; i < hashTable.length; i++) { 111 | hashTable[i] = 0 112 | } 113 | 114 | var ipEnd = ip + inputSize 115 | var ipLimit 116 | var baseIp = ip 117 | var nextEmit = ip 118 | 119 | var hash, nextHash 120 | var nextIp, candidate, skip 121 | var bytesBetweenHashLookups 122 | var base, matched, offset 123 | var prevHash, curHash 124 | var flag = true 125 | 126 | var INPUT_MARGIN = 15 127 | if (inputSize >= INPUT_MARGIN) { 128 | ipLimit = ipEnd - INPUT_MARGIN 129 | 130 | ip += 1 131 | nextHash = hashFunc(load32(input, ip), hashFuncShift) 132 | 133 | while (flag) { 134 | skip = 32 135 | nextIp = ip 136 | do { 137 | ip = nextIp 138 | hash = nextHash 139 | bytesBetweenHashLookups = skip >>> 5 140 | skip += 1 141 | nextIp = ip + bytesBetweenHashLookups 142 | if (ip > ipLimit) { 143 | flag = false 144 | break 145 | } 146 | nextHash = hashFunc(load32(input, nextIp), hashFuncShift) 147 | candidate = baseIp + hashTable[hash] 148 | hashTable[hash] = ip - baseIp 149 | } while (!equals32(input, ip, candidate)) 150 | 151 | if (!flag) { 152 | break 153 | } 154 | 155 | op = emitLiteral(input, nextEmit, ip - nextEmit, output, op) 156 | 157 | do { 158 | base = ip 159 | matched = 4 160 | while (ip + matched < ipEnd && input[ip + matched] === input[candidate + matched]) { 161 | matched += 1 162 | } 163 | ip += matched 164 | offset = base - candidate 165 | op = emitCopy(output, op, offset, matched) 166 | 167 | nextEmit = ip 168 | if (ip >= ipLimit) { 169 | flag = false 170 | break 171 | } 172 | prevHash = hashFunc(load32(input, ip - 1), hashFuncShift) 173 | hashTable[prevHash] = ip - 1 - baseIp 174 | curHash = hashFunc(load32(input, ip), hashFuncShift) 175 | candidate = baseIp + hashTable[curHash] 176 | hashTable[curHash] = ip - baseIp 177 | } while (equals32(input, ip, candidate)) 178 | 179 | if (!flag) { 180 | break 181 | } 182 | 183 | ip += 1 184 | nextHash = hashFunc(load32(input, ip), hashFuncShift) 185 | } 186 | } 187 | 188 | if (nextEmit < ipEnd) { 189 | op = emitLiteral(input, nextEmit, ipEnd - nextEmit, output, op) 190 | } 191 | 192 | return op 193 | } 194 | 195 | function putVarint (value, output, op) { 196 | do { 197 | output[op] = value & 0x7f 198 | value = value >>> 7 199 | if (value > 0) { 200 | output[op] += 0x80 201 | } 202 | op += 1 203 | } while (value > 0) 204 | return op 205 | } 206 | 207 | function SnappyCompressor (uncompressed) { 208 | this.array = uncompressed 209 | } 210 | 211 | SnappyCompressor.prototype.maxCompressedLength = function () { 212 | var sourceLen = this.array.length 213 | return 32 + sourceLen + Math.floor(sourceLen / 6) 214 | } 215 | 216 | SnappyCompressor.prototype.compressToBuffer = function (outBuffer) { 217 | var array = this.array 218 | var length = array.length 219 | var pos = 0 220 | var outPos = 0 221 | 222 | var fragmentSize 223 | 224 | outPos = putVarint(length, outBuffer, outPos) 225 | while (pos < length) { 226 | fragmentSize = Math.min(length - pos, BLOCK_SIZE) 227 | outPos = compressFragment(array, pos, fragmentSize, outBuffer, outPos) 228 | pos += fragmentSize 229 | } 230 | 231 | return outPos 232 | } 233 | 234 | exports.SnappyCompressor = SnappyCompressor 235 | -------------------------------------------------------------------------------- /snappy_decompressor.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | var WORD_MASK = [0, 0xff, 0xffff, 0xffffff, 0xffffffff] 26 | 27 | function copyBytes (fromArray, fromPos, toArray, toPos, length) { 28 | var i 29 | for (i = 0; i < length; i++) { 30 | toArray[toPos + i] = fromArray[fromPos + i] 31 | } 32 | } 33 | 34 | function selfCopyBytes (array, pos, offset, length) { 35 | var i 36 | for (i = 0; i < length; i++) { 37 | array[pos + i] = array[pos - offset + i] 38 | } 39 | } 40 | 41 | function SnappyDecompressor (compressed) { 42 | this.array = compressed 43 | this.pos = 0 44 | } 45 | 46 | SnappyDecompressor.prototype.readUncompressedLength = function () { 47 | var result = 0 48 | var shift = 0 49 | var c, val 50 | while (shift < 32 && this.pos < this.array.length) { 51 | c = this.array[this.pos] 52 | this.pos += 1 53 | val = c & 0x7f 54 | if (((val << shift) >>> shift) !== val) { 55 | return -1 56 | } 57 | result |= val << shift 58 | if (c < 128) { 59 | return result 60 | } 61 | shift += 7 62 | } 63 | return -1 64 | } 65 | 66 | SnappyDecompressor.prototype.uncompressToBuffer = function (outBuffer) { 67 | var array = this.array 68 | var arrayLength = array.length 69 | var pos = this.pos 70 | var outPos = 0 71 | 72 | var c, len, smallLen 73 | var offset 74 | 75 | while (pos < array.length) { 76 | c = array[pos] 77 | pos += 1 78 | if ((c & 0x3) === 0) { 79 | // Literal 80 | len = (c >>> 2) + 1 81 | if (len > 60) { 82 | if (pos + 3 >= arrayLength) { 83 | return false 84 | } 85 | smallLen = len - 60 86 | len = array[pos] + (array[pos + 1] << 8) + (array[pos + 2] << 16) + (array[pos + 3] << 24) 87 | len = (len & WORD_MASK[smallLen]) + 1 88 | pos += smallLen 89 | } 90 | if (pos + len > arrayLength) { 91 | return false 92 | } 93 | copyBytes(array, pos, outBuffer, outPos, len) 94 | pos += len 95 | outPos += len 96 | } else { 97 | switch (c & 0x3) { 98 | case 1: 99 | len = ((c >>> 2) & 0x7) + 4 100 | offset = array[pos] + ((c >>> 5) << 8) 101 | pos += 1 102 | break 103 | case 2: 104 | if (pos + 1 >= arrayLength) { 105 | return false 106 | } 107 | len = (c >>> 2) + 1 108 | offset = array[pos] + (array[pos + 1] << 8) 109 | pos += 2 110 | break 111 | case 3: 112 | if (pos + 3 >= arrayLength) { 113 | return false 114 | } 115 | len = (c >>> 2) + 1 116 | offset = array[pos] + (array[pos + 1] << 8) + (array[pos + 2] << 16) + (array[pos + 3] << 24) 117 | pos += 4 118 | break 119 | default: 120 | break 121 | } 122 | if (offset === 0 || offset > outPos) { 123 | return false 124 | } 125 | selfCopyBytes(outBuffer, outPos, offset, len) 126 | outPos += len 127 | } 128 | } 129 | return true 130 | } 131 | 132 | exports.SnappyDecompressor = SnappyDecompressor 133 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Zhipeng Jia 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 'use strict' 24 | 25 | var test = require('tap').test 26 | 27 | var snappy = require('snappy') 28 | var snappyjs = require('./index') 29 | 30 | var fs = require('fs') 31 | var textrandomInputString = fs.readFileSync('snappy_compressor.js', 'utf8') 32 | 33 | function bufferToUint8Array (buffer) { 34 | return new Uint8Array(buffer) 35 | } 36 | 37 | function bufferToArrayBuffer (buffer) { 38 | var arrayBuffer = new ArrayBuffer(buffer.length) 39 | var view = new Uint8Array(arrayBuffer) 40 | var i 41 | for (i = 0; i < buffer.length; i++) { 42 | view[i] = buffer[i] 43 | } 44 | return arrayBuffer 45 | } 46 | 47 | function uint8ArrayToBuffer (uint8Array) { 48 | var buffer = Buffer.alloc(uint8Array.length) 49 | var i 50 | for (i = 0; i < uint8Array.length; i++) { 51 | buffer[i] = uint8Array[i] 52 | } 53 | return buffer 54 | } 55 | 56 | function arrayBufferToBuffer (arrayBuffer) { 57 | var view = new Uint8Array(arrayBuffer) 58 | var buffer = Buffer.alloc(view.length) 59 | var i 60 | for (i = 0; i < view.length; i++) { 61 | buffer[i] = view[i] 62 | } 63 | return buffer 64 | } 65 | 66 | function stringToUint8Array (source) { 67 | var arrayBuffer = new ArrayBuffer(source.length * 2) 68 | var view = new Uint16Array(arrayBuffer) 69 | var i 70 | for (i = 0; i < source.length; i++) { 71 | view[i] = source.charCodeAt(i) 72 | } 73 | return new Uint8Array(arrayBuffer) 74 | } 75 | 76 | function stringToArrayBuffer (source) { 77 | var arrayBuffer = new ArrayBuffer(source.length * 2) 78 | var view = new Uint16Array(arrayBuffer) 79 | var i 80 | for (i = 0; i < source.length; i++) { 81 | view[i] = source.charCodeAt(i) 82 | } 83 | return arrayBuffer 84 | } 85 | 86 | function uint8ArrayToString (uint8Array) { 87 | var view = new Uint16Array(uint8Array.buffer) 88 | var result = '' 89 | var i 90 | for (i = 0; i < view.length; i++) { 91 | result += String.fromCharCode(view[i]) 92 | } 93 | return result 94 | } 95 | 96 | function arrayBufferToString (arrayBuffer) { 97 | var view = new Uint16Array(arrayBuffer) 98 | var result = '' 99 | var i 100 | for (i = 0; i < view.length; i++) { 101 | result += String.fromCharCode(view[i]) 102 | } 103 | return result 104 | } 105 | 106 | function randomString (length) { 107 | var result = '' 108 | var i, code 109 | for (i = 0; i < length; i++) { 110 | code = Math.floor(Math.random() * 256) 111 | result += String.fromCharCode(code) 112 | } 113 | return result 114 | } 115 | 116 | test('compress() normal text using Uint8Array', function (t) { 117 | var compressed = snappyjs.compress(stringToUint8Array(textrandomInputString)) 118 | compressed = arrayBufferToBuffer(compressed) 119 | t.equal(snappy.isValidCompressedSync(compressed), true) 120 | var uncompressed = snappy.uncompressSync(compressed) 121 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 122 | t.equal(uncompressedString, textrandomInputString) 123 | t.end() 124 | }) 125 | 126 | test('compress() normal text using ArrayBuffer', function (t) { 127 | var compressed = snappyjs.compress(stringToArrayBuffer(textrandomInputString)) 128 | compressed = arrayBufferToBuffer(compressed) 129 | t.equal(snappy.isValidCompressedSync(compressed), true) 130 | var uncompressed = snappy.uncompressSync(compressed) 131 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 132 | t.equal(uncompressedString, textrandomInputString) 133 | t.end() 134 | }) 135 | 136 | test('compress() normal text using Buffer', function (t) { 137 | var compressed = snappyjs.compress(arrayBufferToBuffer(stringToArrayBuffer(textrandomInputString))) 138 | t.equal(snappy.isValidCompressedSync(compressed), true) 139 | var uncompressed = snappy.uncompressSync(compressed) 140 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 141 | t.equal(uncompressedString, textrandomInputString) 142 | t.end() 143 | }) 144 | 145 | test('uncompress() normal text using Uint8Array', function (t) { 146 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(textrandomInputString))) 147 | t.equal(snappy.isValidCompressedSync(compressed), true) 148 | compressed = bufferToUint8Array(compressed) 149 | var uncompressed = snappyjs.uncompress(compressed) 150 | var uncompressedString = uint8ArrayToString(uncompressed) 151 | t.equal(uncompressedString, textrandomInputString) 152 | t.end() 153 | }) 154 | 155 | test('uncompress() normal text using ArrayBuffer', function (t) { 156 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(textrandomInputString))) 157 | t.equal(snappy.isValidCompressedSync(compressed), true) 158 | compressed = bufferToArrayBuffer(compressed) 159 | var uncompressed = snappyjs.uncompress(compressed) 160 | var uncompressedString = arrayBufferToString(uncompressed) 161 | t.equal(uncompressedString, textrandomInputString) 162 | t.end() 163 | }) 164 | 165 | test('uncompress() normal text using Buffer', function (t) { 166 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(textrandomInputString))) 167 | t.equal(snappy.isValidCompressedSync(compressed), true) 168 | var uncompressed = snappyjs.uncompress(compressed) 169 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 170 | t.equal(uncompressedString, textrandomInputString) 171 | t.end() 172 | }) 173 | 174 | test('compress() random string of length 100000 using Uint8Array', function (t) { 175 | var randomInputString = randomString(100000) 176 | var compressed = snappyjs.compress(stringToUint8Array(randomInputString)) 177 | compressed = uint8ArrayToBuffer(compressed) 178 | t.equal(snappy.isValidCompressedSync(compressed), true) 179 | var uncompressed = snappy.uncompressSync(compressed) 180 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 181 | t.equal(uncompressedString, randomInputString) 182 | t.end() 183 | }) 184 | 185 | test('compress() random string of length 100000 using ArrayBuffer', function (t) { 186 | var randomInputString = randomString(100000) 187 | var compressed = snappyjs.compress(stringToArrayBuffer(randomInputString)) 188 | compressed = arrayBufferToBuffer(compressed) 189 | t.equal(snappy.isValidCompressedSync(compressed), true) 190 | var uncompressed = snappy.uncompressSync(compressed) 191 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 192 | t.equal(uncompressedString, randomInputString) 193 | t.end() 194 | }) 195 | 196 | test('compress() random string of length 100000 using Buffer', function (t) { 197 | var randomInputString = randomString(100000) 198 | var compressed = snappyjs.compress(arrayBufferToBuffer(stringToArrayBuffer(randomInputString))) 199 | t.equal(snappy.isValidCompressedSync(compressed), true) 200 | var uncompressed = snappy.uncompressSync(compressed) 201 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 202 | t.equal(uncompressedString, randomInputString) 203 | t.end() 204 | }) 205 | 206 | test('compress() random string of length 100 using Uint8Array', function (t) { 207 | var randomInputString = randomString(100) 208 | var compressed = snappyjs.compress(stringToUint8Array(randomInputString)) 209 | compressed = uint8ArrayToBuffer(compressed) 210 | t.equal(snappy.isValidCompressedSync(compressed), true) 211 | var uncompressed = snappy.uncompressSync(compressed) 212 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 213 | t.equal(uncompressedString, randomInputString) 214 | t.end() 215 | }) 216 | 217 | test('compress() random string of length 100 using ArrayBuffer', function (t) { 218 | var randomInputString = randomString(100) 219 | var compressed = snappyjs.compress(stringToArrayBuffer(randomInputString)) 220 | compressed = arrayBufferToBuffer(compressed) 221 | t.equal(snappy.isValidCompressedSync(compressed), true) 222 | var uncompressed = snappy.uncompressSync(compressed) 223 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 224 | t.equal(uncompressedString, randomInputString) 225 | t.end() 226 | }) 227 | 228 | test('compress() random string of length 100 using Buffer', function (t) { 229 | var randomInputString = randomString(100) 230 | var compressed = snappyjs.compress(arrayBufferToBuffer(stringToArrayBuffer(randomInputString))) 231 | t.equal(snappy.isValidCompressedSync(compressed), true) 232 | var uncompressed = snappy.uncompressSync(compressed) 233 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 234 | t.equal(uncompressedString, randomInputString) 235 | t.end() 236 | }) 237 | 238 | test('uncompress() random string of length 100000 using Uint8Array', function (t) { 239 | var randomInputString = randomString(100000) 240 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(randomInputString))) 241 | t.equal(snappy.isValidCompressedSync(compressed), true) 242 | compressed = bufferToUint8Array(compressed) 243 | var uncompressed = snappyjs.uncompress(compressed) 244 | var uncompressedString = uint8ArrayToString(uncompressed) 245 | t.equal(uncompressedString, randomInputString) 246 | t.end() 247 | }) 248 | 249 | test('uncompress() random string of length 100000 using ArrayBuffer', function (t) { 250 | var randomInputString = randomString(100000) 251 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(randomInputString))) 252 | t.equal(snappy.isValidCompressedSync(compressed), true) 253 | compressed = bufferToArrayBuffer(compressed) 254 | var uncompressed = snappyjs.uncompress(compressed) 255 | var uncompressedString = arrayBufferToString(uncompressed) 256 | t.equal(uncompressedString, randomInputString) 257 | t.end() 258 | }) 259 | 260 | test('uncompress() random string of length 100000 using Buffer', function (t) { 261 | var randomInputString = randomString(100000) 262 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(randomInputString))) 263 | t.equal(snappy.isValidCompressedSync(compressed), true) 264 | var uncompressed = snappyjs.uncompress(compressed) 265 | var uncompressedString = arrayBufferToString(bufferToArrayBuffer(uncompressed)) 266 | t.equal(uncompressedString, randomInputString) 267 | t.end() 268 | }) 269 | 270 | test('uncompress() maxLength', function (t) { 271 | var randomInputString = randomString(100000) 272 | var compressed = snappy.compressSync(arrayBufferToBuffer(stringToArrayBuffer(randomInputString))) 273 | t.equal(snappy.isValidCompressedSync(compressed), true) 274 | compressed = bufferToUint8Array(compressed) 275 | t.throws(function () { snappyjs.uncompress(compressed, 99999) }, new Error('The uncompressed length of 200000 is too big, expect at most 99999'))// 276 | 277 | t.end() 278 | }) 279 | --------------------------------------------------------------------------------