-
-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(range-coder): re-import @thi.ng/range-coder package from MB2010
- Loading branch information
1 parent
8389a2c
commit 76dc450
Showing
7 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
bench/* | ||
build/* | ||
dev/* | ||
node_modules | ||
src* | ||
test* | ||
bundle.* | ||
tsconfig.json | ||
webpack.config.js | ||
*.html | ||
*.tgz | ||
!doc/* | ||
!*.d.ts | ||
!*.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# @thi.ng/range-coder | ||
|
||
[![npm (scoped)](https://img.shields.io/npm/v/@thi.ng/range-coder.svg)](https://www.npmjs.com/package/@thi.ng/range-coder) | ||
|
||
This project is part of the | ||
[@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo. | ||
|
||
## About | ||
|
||
Range encoder / decoder for binary data, based on [Java implementation | ||
by Joe Halliwell](https://www.winterwell.com/software/compressor.php). | ||
|
||
## Installation | ||
|
||
```bash | ||
yarn add @thi.ng/range-coder | ||
``` | ||
|
||
## Dependencies | ||
|
||
- [@thi.ng/bitstream](https://github.com/thi-ng/umbrella/tree/master/packages/bitstream) | ||
|
||
## API | ||
|
||
```ts | ||
import * as rc "@thi.ng/range-coder"; | ||
``` | ||
```ts | ||
// prepare dummy data | ||
src = new Uint8Array(1024); | ||
src.set([1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,4,4,3,3,3,2,2,2,2,1,1,1,1,1], 512); | ||
|
||
// pack data | ||
packed = rc.encodeBytes(src); | ||
|
||
packed.length | ||
// 146 | ||
|
||
packed.length/src.length | ||
// 0.142578125 | ||
|
||
// unpack | ||
dest = rc.decodeBytes(packed); | ||
``` | ||
## Authors | ||
- Karsten Schmidt | ||
## License | ||
© 2017 Karsten Schmidt // Apache Software License 2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"name": "@thi.ng/range-coder", | ||
"version": "0.0.1", | ||
"description": "Binary data range encoder / decoder", | ||
"main": "./index.js", | ||
"typings": "./index.d.ts", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/thi-ng/umbrella.git" | ||
}, | ||
"homepage": "https://github.com/thi-ng/umbrella/tree/master/packages/rstream", | ||
"author": "Karsten Schmidt <k+npm@thi.ng>", | ||
"license": "Apache-2.0", | ||
"scripts": { | ||
"build": "yarn clean && tsc --declaration", | ||
"clean": "rm -rf *.js *.d.ts .nyc_output build coverage doc", | ||
"cover": "yarn test && nyc report --reporter=lcov", | ||
"doc": "node_modules/.bin/typedoc --mode modules --out doc src", | ||
"pub": "yarn build && yarn publish --access public", | ||
"test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" | ||
}, | ||
"devDependencies": { | ||
"@thi.ng/transducers": "1.14.1", | ||
"@types/mocha": "^5.2.0", | ||
"@types/node": "^10.0.6", | ||
"mocha": "^5.1.1", | ||
"nyc": "^11.7.1", | ||
"typedoc": "^0.11.1", | ||
"typescript": "^2.8.3" | ||
}, | ||
"dependencies": { | ||
"@thi.ng/bitstream": "0.4.14" | ||
}, | ||
"keywords": [ | ||
"ES6", | ||
"binary", | ||
"entropy", | ||
"packer", | ||
"range encoding", | ||
"typescript" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { BitInputStream, BitOutputStream } from "@thi.ng/bitstream"; | ||
|
||
const HIGH = 0x7fffff; | ||
const HALF = 0x400000; | ||
const QUARTER = 0x200000; | ||
const THREE_QUARTER = 0x600000; | ||
const INITIAL_READ = 23; | ||
const FREQ = 257; | ||
|
||
export const encodeBytes = (src: Uint8Array) => { | ||
const freq = new Uint32Array(FREQ).fill(1); | ||
const out = new BitOutputStream(Math.max(src.length >> 1, 1)); | ||
const len = src.length; | ||
let total = FREQ; | ||
let lo = 0; | ||
let hi = HIGH; | ||
let _lo = lo; | ||
let _hi = hi; | ||
let step; | ||
let scale = 0; | ||
let curr = 0; | ||
let i, j; | ||
|
||
for (i = 0; i <= len; i++) { | ||
if (i === len) { | ||
lo = total - 1; | ||
hi = total; | ||
} else { | ||
curr = src[i]; | ||
lo = 0; | ||
for (j = 0; j < curr; j++) { | ||
lo += freq[j]; | ||
} | ||
hi = lo + freq[curr]; | ||
} | ||
|
||
step = ((_hi - _lo + 1) / total) >>> 0; | ||
_hi = _lo + step * hi - 1; | ||
_lo += step * lo; | ||
|
||
while (true) { | ||
if (_hi < HALF) { | ||
out.writeBit(0); | ||
_lo <<= 1; | ||
_hi = (_hi << 1) + 1; | ||
scale && out.write(HIGH, scale); | ||
} else if (_lo >= HALF) { | ||
out.writeBit(1); | ||
_lo = (_lo - HALF) << 1; | ||
_hi = ((_hi - HALF) << 1) + 1; | ||
scale && out.write(0, scale); | ||
} else { | ||
break; | ||
} | ||
scale = 0; | ||
} | ||
|
||
while (_lo > QUARTER && _hi < THREE_QUARTER) { | ||
scale++; | ||
_lo = (_lo - QUARTER) << 1; | ||
_hi = ((_hi - QUARTER) << 1) + 1; | ||
} | ||
|
||
freq[curr]++; | ||
total++; | ||
} | ||
if (_lo < QUARTER) { | ||
out.writeBit(0); | ||
out.write(HIGH, scale + 1); | ||
} else { | ||
out.writeBit(1); | ||
} | ||
return out.bytes(); | ||
}; | ||
|
||
export const decodeBytes = (src: Uint8Array) => { | ||
const freq = new Uint32Array(FREQ).fill(1); | ||
const input = new BitInputStream(src) | ||
const nbits = input.length | ||
const out = [] | ||
let total = FREQ | ||
let current = 0 | ||
let lo = 0 | ||
let hi = HIGH | ||
let _lo = lo | ||
let _hi = hi | ||
let step = 0 | ||
let buf = input.read(INITIAL_READ) | ||
let val; | ||
|
||
const read = () => input.position < nbits ? input.readBit() : 0; | ||
|
||
while (true) { | ||
step = ((_hi - _lo + 1) / total) >>> 0; | ||
val = ((buf - _lo) / step) >>> 0; | ||
lo = 0; | ||
for (current = 0; current < 256 && lo + freq[current] <= val; current++) { | ||
lo += freq[current]; | ||
} | ||
if (current === 256) break; | ||
|
||
out.push(current); | ||
hi = lo + freq[current]; | ||
|
||
_hi = _lo + step * hi - 1; | ||
_lo += step * lo; | ||
|
||
while (true) { | ||
if (_hi < HALF) { | ||
buf <<= 1; | ||
_lo <<= 1; | ||
_hi = (_hi << 1) + 1; | ||
} else if (_lo >= HALF) { | ||
buf = (buf - HALF) << 1; | ||
_lo = (_lo - HALF) << 1; | ||
_hi = ((_hi - HALF) << 1) + 1; | ||
} else { | ||
break; | ||
} | ||
buf += read(); | ||
} | ||
|
||
while (_lo > QUARTER && _hi < THREE_QUARTER) { | ||
_lo = (_lo - QUARTER) << 1; | ||
_hi = ((_hi - QUARTER) << 1) + 1; | ||
buf = (buf - QUARTER) << 1; | ||
buf += read(); | ||
} | ||
|
||
freq[current]++; | ||
total++; | ||
} | ||
|
||
return new Uint8Array(out); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { repeat, repeatedly } from "@thi.ng/transducers"; | ||
import * as assert from "assert"; | ||
import { decodeBytes, encodeBytes } from "../src"; | ||
|
||
describe("range-coder", () => { | ||
|
||
it("fixed", () => { | ||
const src = new Uint8Array([10, 20, 30, 10, 10, 10]); | ||
const dest = encodeBytes(src); | ||
assert.deepEqual(dest, [10, 10, 224, 160, 49, 91, 88]); | ||
assert.deepEqual(src, decodeBytes(dest)); | ||
}); | ||
|
||
it("fuzz", () => { | ||
for (let i = 0; i < 10; i++) { | ||
const src = randomArray(640, 1024); | ||
const dest = encodeBytes(src); | ||
console.log(`${(dest.length / src.length * 100).toFixed(2)}%`); | ||
assert.deepEqual(src, decodeBytes(dest)); | ||
} | ||
}); | ||
}); | ||
|
||
function randomArray(n: number, len: number) { | ||
return new Uint8Array([...repeatedly(() => ~~(Math.random() * 256), n), ...repeat(0, len - n)]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"extends": "../../../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "../build" | ||
}, | ||
"include": [ | ||
"./**/*.ts", | ||
"../src/**/*.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "." | ||
}, | ||
"include": [ | ||
"./src/**/*.ts", | ||
] | ||
} |