Skip to content

Commit d9df101

Browse files
committed
Fix for #3
1 parent 4566511 commit d9df101

File tree

4 files changed

+125
-3
lines changed

4 files changed

+125
-3
lines changed

FastIntegerCompression.js

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
function FastIntegerCompression() {
2222
}
2323

24+
// private function
2425
function bytelog(val) {
2526
if (val < (1 << 7)) {
2627
return 1;
@@ -34,7 +35,19 @@ function bytelog(val) {
3435
return 5;
3536
}
3637

38+
// private function
39+
function zigzag_encode(val) {
40+
return (val + val) ^ (val >> 31);;
41+
}
42+
43+
// private function
44+
function zigzag_decode(val) {
45+
return (val >> 1) ^ (- (val & 1));
46+
}
47+
48+
3749
// compute how many bytes an array of integers would use once compressed
50+
// input is expected to be an array of non-negative integers
3851
FastIntegerCompression.computeCompressedSizeInBytes = function(input) {
3952
var c = input.length;
4053
var answer = 0;
@@ -45,7 +58,20 @@ FastIntegerCompression.computeCompressedSizeInBytes = function(input) {
4558
};
4659

4760

48-
// compress an array of integers, return a compressed buffer (as an ArrayBuffer)
61+
// compute how many bytes an array of integers would use once compressed
62+
// input is expected to be an array of integers, some of them can be negative
63+
FastIntegerCompression.computeCompressedSizeInBytesSigned = function(input) {
64+
var c = input.length;
65+
var answer = 0;
66+
for(var i = 0; i < c; i++) {
67+
answer += bytelog(zigzag_encode(input[i]));
68+
}
69+
return answer;
70+
};
71+
72+
// Compress an array of integers, return a compressed buffer (as an ArrayBuffer).
73+
// It is expected that the integers are non-negative: the caller is responsible
74+
// for making this check. Floating-point numbers are not supported.
4975
FastIntegerCompression.compress = function(input) {
5076
var c = input.length;
5177
var buf = new ArrayBuffer(FastIntegerCompression.computeCompressedSizeInBytes(input));
@@ -89,6 +115,8 @@ FastIntegerCompression.computeHowManyIntegers = function(input) {
89115
return c - count;
90116
}
91117
// uncompress an array of integer from an ArrayBuffer, return the array
118+
// it is assumed that they were compressed using the compress function, the caller
119+
// is responsible for ensuring that it is the case.
92120
FastIntegerCompression.uncompress = function(input) {
93121
var array = []
94122
var inbyte = new Int8Array(input);
@@ -127,6 +155,80 @@ FastIntegerCompression.uncompress = function(input) {
127155
};
128156

129157

158+
// Compress an array of integers, return a compressed buffer (as an ArrayBuffer).
159+
// The integers can be signed (negative), but floating-point values are not supported.
160+
FastIntegerCompression.compressSigned = function(input) {
161+
var c = input.length;
162+
var buf = new ArrayBuffer(FastIntegerCompression.computeCompressedSizeInBytesSigned(input));
163+
var view = new Int8Array(buf);
164+
var pos = 0;
165+
for(var i = 0; i < c; i++) {
166+
var val = zigzag_encode(input[i]);
167+
if (val < (1 << 7)) {
168+
view[pos++] = val ;
169+
} else if (val < (1 << 14)) {
170+
view[pos++] = (val & 0x7F) | 0x80;
171+
view[pos++] = val >>> 7;
172+
} else if (val < (1 << 21)) {
173+
view[pos++] = (val & 0x7F) | 0x80;
174+
view[pos++] = ( (val >>> 7) & 0x7F ) | 0x80;
175+
view[pos++] = val >>> 14;
176+
} else if (val < (1 << 28)) {
177+
view[pos++] = (val & 0x7F ) | 0x80 ;
178+
view[pos++] = ( (val >>> 7) & 0x7F ) | 0x80;
179+
view[pos++] = ( (val >>> 14) & 0x7F ) | 0x80;
180+
view[pos++] = val >>> 21;
181+
} else {
182+
view[pos++] = ( val & 0x7F ) | 0x80;
183+
view[pos++] = ( (val >>> 7) & 0x7F ) | 0x80;
184+
view[pos++] = ( (val >>> 14) & 0x7F ) | 0x80;
185+
view[pos++] = ( (val >>> 21) & 0x7F ) | 0x80;
186+
view[pos++] = val >>> 28;
187+
}
188+
}
189+
return buf;
190+
};
191+
192+
// uncompress an array of integer from an ArrayBuffer, return the array
193+
// it is assumed that they were compressed using the compressSigned function, the caller
194+
// is responsible for ensuring that it is the case.
195+
FastIntegerCompression.uncompressSigned = function(input) {
196+
var array = []
197+
var inbyte = new Int8Array(input);
198+
var end = inbyte.length;
199+
var pos = 0;
200+
while (end > pos) {
201+
var c = inbyte[pos++];
202+
var v = c & 0x7F;
203+
if (c >= 0) {
204+
array.push(zigzag_decode(v))
205+
continue;
206+
}
207+
c = inbyte[pos++];
208+
v |= (c & 0x7F) << 7;
209+
if (c >= 0) {
210+
array.push(zigzag_decode(v))
211+
continue;
212+
}
213+
c = inbyte[pos++];
214+
v |= (c & 0x7F) << 14;
215+
if (c >= 0) {
216+
array.push(zigzag_decode(v))
217+
continue;
218+
}
219+
c = inbyte[pos++];
220+
v |= (c & 0x7F) << 21;
221+
if (c >= 0) {
222+
array.push(zigzag_decode(v))
223+
continue;
224+
}
225+
c = inbyte[pos++];
226+
v |= c << 28;
227+
array.push(zigzag_decode(v))
228+
}
229+
return array;
230+
};
231+
130232
///////////////
131233

132234
module.exports = FastIntegerCompression;

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![codebeat badge](https://codebeat.co/badges/fbff9479-9394-40c6-b12d-8c6b666c115e)](https://codebeat.co/projects/github-com-lemire-fastintegercompression-js)
44

55
This is an integer compression library in JavaScript, useful for work on indexes.
6-
Given an array of small non-negative integers, it produces an ArrayBuffer that uses far fewer bytes
6+
Given an array of small integers, it produces an ArrayBuffer that uses far fewer bytes
77
than the original (using VByte compression). It assumes a modern JavaScript engine with
88
typed arrays.
99

@@ -18,6 +18,17 @@ From the compressed data, you can later recover the original array quickly
1818
var back = FastIntegerCompression.uncompress(buf); // gets back [10,100000,65999,10,10,0,1,1,2000]
1919
```
2020

21+
By default, non-negative integers are expected. If you have signed (negative and positive) integers, then you must use distinct functions since we need to code the sign bit:
22+
23+
24+
```javascript
25+
// var FastIntegerCompression = require("fastintcompression");// if you use node
26+
var array = [10,100000,65999,10,10,0,-1,-1,-2000];
27+
var buf = FastIntegerCompression.compressSigned(array);
28+
var back = FastIntegerCompression.uncompressSigned(buf); // gets back [10,100000,65999,10,10,0,-1,-1,-2000]
29+
```
30+
31+
2132
You can install the library under node with the command line
2233
```bash
2334
npm install fastintcompression

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"homepage": "https://github.com/lemire/FastIntegerCompression.js#readme",
2323
"devDependencies": {
2424
"benchmark": "~1.0.0",
25-
"mocha": "^2.3.4"
25+
"mocha": "^2.5.3"
2626
},
2727
"author": "Daniel Lemire <lemire@gmail.com> (http://lemire.me/en/)"
2828
}

unit/basictests.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,13 @@ describe('FastIntegerCompression', function() {
2424
});
2525

2626

27+
28+
it('Testing simple compression (signed)', function() {
29+
var array = [10,100000,65999,10,10,0,-1,-1,-2000];
30+
var buf = FastIntegerCompression.compressSigned(array);
31+
if(! FastIntegerCompression.computeHowManyIntegers(buf) == array.length) throw "bad count";
32+
var back = FastIntegerCompression.uncompressSigned(buf);
33+
if(!arraysEquals(array,back)) throw "bad";
34+
35+
});
2736
});

0 commit comments

Comments
 (0)