Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

arithmetic-coding

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

arithmetic-coding - npm Package Compare versions

Comparing version 1.2.0 to 1.2.1

README-zh.md

46

bin/index.js
#!/usr/bin/env node
const program = require('commander');
const pkg = require('../package.json');
const ariCoding = require('../src/index');
const path = require('path');
const chalk = require('chalk');
/* eslint-disable no-console */
function getAction(isEncode) {
return function(file, cmd) {
const program = require('commander')
const pkg = require('../package.json')
const ariCoding = require('../src/index')
const path = require('path')
const chalk = require('chalk')
function getAction (isEncode) {
return function (file, cmd) {
try {
let inputPath = path.resolve(file);
let outputPath = cmd.output || (file + (isEncode ? '.encoded' : '.decoded'));
isEncode && ariCoding.encode(inputPath, outputPath);
!isEncode && ariCoding.decode(inputPath, outputPath);
let inputPath = path.resolve(file)
let outputPath = cmd.output || (file + (isEncode ? '.encoded' : '.decoded'))
isEncode && ariCoding.encode(inputPath, outputPath)
!isEncode && ariCoding.decode(inputPath, outputPath)
console.log(
chalk.green('File ' + (isEncode ? 'encoded' : 'decoded') + ' successfully')
);
)
} catch (e) {
console.error(
chalk.red(e.message)
);
)
}
};
}
}
program
.version(pkg.version, '-v, --version');
.version(pkg.version, '-v, --version')
program

@@ -34,3 +36,3 @@ .command('encode <file>')

.option('-o, --output <file>', 'output file path')
.action(getAction(true));
.action(getAction(true))
program

@@ -41,10 +43,10 @@ .command('decode <file>')

.option('-o, --output <file>', 'output file path')
.action(getAction(false));
.action(getAction(false))
// error on unknown commands
program.on('command:*', function () {
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
process.exit(1);
});
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '))
process.exit(1)
})
program.parse(process.argv);
program.parse(process.argv)
# arithmetic-coding CHANGELOG
## v1.0.0
## v1.2.1
2019.03.29
2019.03.31
- Finished API and CLI support.
- Added Chinese documentation.
- Adopted Standard JS style.
- Updated benchmarks.
## v1.2.0
2019.03.30
- Removed `long.js` and change number of bits for better performance.
## v1.1.0

@@ -15,6 +23,6 @@

## v1.2.0
## v1.0.0
2019.03.30
2019.03.29
- Remove `long.js` and change number of bits for better performance.
- Finished API and CLI support.
{
"name": "arithmetic-coding",
"version": "1.2.0",
"version": "1.2.1",
"description": "Arithmetic coding implementation",

@@ -10,10 +10,15 @@ "main": "src/index.js",

"scripts": {
"test": "mocha --timeout 100000 && npm run clean",
"test": "standard && mocha --timeout 100000 && npm run clean",
"clean": "rm test/txt/*-encoded.txt test/txt/*-decoded.txt",
"cover": "istanbul cover ./node_modules/mocha/bin/_mocha",
"cover": "istanbul cover ./node_modules/mocha/bin/_mocha -- --timeout 100000",
"coveralls": "npm run cover -- --report lcovonly && cat ./coverage/lcov.info | coveralls",
"patch": "npm version patch && git push origin master && git push origin --tags",
"minor": "npm version minor && git push origin master && git push origin --tags",
"major": "npm version major && git push origin master && git push origin --tags"
"patch": "npm version patch && npm publish && git push origin master && git push origin --tags",
"minor": "npm version minor && npm publish && git push origin master && git push origin --tags",
"major": "npm version major && npm publish && git push origin master && git push origin --tags"
},
"standard": {
"env": [
"mocha"
]
},
"repository": {

@@ -39,6 +44,6 @@ "type": "git",

"concat-stream": "^2.0.0",
"mocha-lcov-reporter": "^1.3.0",
"which": "^1.3.1"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"chai": "^4.2.0",

@@ -48,4 +53,5 @@ "coveralls": "^3.0.3",

"mocha": "^6.0.2",
"should": "^13.2.3"
"should": "^13.2.3",
"standard": "*"
}
}
# arithmetic-coding
<p align="center">
<a href="https://travis-ci.com/upupming/arithmetic-coding/builds"><img src="https://img.shields.io/travis/com/upupming/arithmetic-coding.svg?style=popout-square" alt="travis build status"></a>
<a href="https://github.com/upupming/arithmetic-coding/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg?style=popout-square" alt="License"></a>
<a href="https://coveralls.io/github/upupming/arithmetic-coding?branch=master"><img src="https://img.shields.io/coveralls/github/upupming/arithmetic-coding.svg?style=popout-square" alt="Coveralls"></a>
<a href="https://www.npmjs.com/package/arithmetic-coding"><img src="https://img.shields.io/npm/v/arithmetic-coding.svg?style=flat-square" alt="npm"></a>
</p>
<a href="https://www.npmjs.com/package/arithmetic-coding"><img src="https://img.shields.io/npm/v/arithmetic-coding.svg?style=flat-square" alt="npm"></a>
<a href="https://standardjs.com"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square" alt="JavaScript Style Guide"></a>
<a href="https://coveralls.io/github/upupming/arithmetic-coding?branch=master"><img src="https://img.shields.io/coveralls/github/upupming/arithmetic-coding.svg?style=flat-square" alt="Coveralls"></a>
<a href="https://travis-ci.com/upupming/arithmetic-coding/builds"><img src="https://img.shields.io/travis/com/upupming/arithmetic-coding.svg?style=popout-square" alt="travis build status"></a>
<a href="https://github.com/upupming/arithmetic-coding/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg?style=popout-square" alt="License"></a>
- English Documentation
- [Chinese Documentation](./README-zh.md)
## Installation

@@ -31,5 +33,5 @@

// Encode from file
ariCoding.encode(__dirname + '/txt/long.txt', __dirname + '/txt/long-encoded.txt');
ariCoding.encode(path.resolve('/txt/long.txt'), path.resolve('/txt/long-encoded.txt'));
// Decode from file
ariCoding.decode(__dirname + '/txt/long-encoded.txt', __dirname + '/txt/long-decoded.txt');
ariCoding.decode(path.resolve('/txt/long-encoded.txt'), path.resolve('/txt/long-decoded.txt'));
```

@@ -44,3 +46,3 @@

console.log(`encoded = ${encoded}`);
// Decode from buffer
// Decode from Buffer
let decoded = decode.decodeFromBuffer(encoded);

@@ -63,2 +65,11 @@ console.log(`decoded = ${decoded}`);

decode|d [options] <file> decode a file
$ ari-coding encode -h
Usage: encode|e [options] <file>
encode a file
Options:
-o, --output <file> output file path
-h, --help output usage information
```

@@ -72,6 +83,6 @@

| File size (Bytes) | total time used | encode time | decode time |
| ----------------- | --------------- | ----------- | ----------- |
| 60640 | 110ms | small | 110ms |
| 2130640 | 7419ms | | |
| File size (Bytes) | total time | encode time | decode time |
| ----------------- | ---------- | ----------- | ----------- |
| 60640 | 110ms | small | 110ms |
| 2130640 | 2940ms | 426ms | 2514ms |

@@ -78,0 +89,0 @@ ## About the algorithm

@@ -1,58 +0,19 @@

const assert = require('assert');
const assert = require('assert')
const Coder = require('./coder')
module.exports = class ArithmeticDecoder {
module.exports = class ArithmeticDecoder extends Coder {
/**
*
* @param {number} numbits
* @param {BitInputStream} bitin
*
* @param {number} numbits
* @param {BitInputStream} bitin
*/
constructor(numbits, bitin) {
if (numbits < 1) {
throw Error('State size out of range');
}
// -- Configuration fields --
// Number of bits for the 'low' and 'high' state variables. Must be at least 1.
// - Larger values are generally better - they allow a larger maximum frequency total (maximum_total),
// and they reduce the approximation error inherent in adapting fractions to integers;
// both effects reduce the data encoding loss and asymptotically approach the efficiency
// of arithmetic coding using exact fractions.
// - But larger state sizes increase the computation time for integer arithmetic,
// and compression gains beyond ~30 bits essentially zero in real-world applications.
// - Python has native bigint arithmetic, so there is no upper limit to the state size.
// For Java and C++ where using native machine-sized integers makes the most sense,
// they have a recommended value of num_state_bits=32 as the most versatile setting.
this._num_state_bits = numbits;
// console.log(`this._num_state_bits: ${this._num_state_bits}`);
// Maximum range (high+1-low) during coding (trivial), which is 2^num_state_bits = 1000...000.
this._full_range = 1 << numbits >>> 0;
// console.log(`this._full_range: ${this._full_range.toString(16)}`);
// The top bit at width num_state_bits, which is 0100...000.
this._half_range = this._full_range >>> 1;
// The second highest bit at width num_state_bits, which is 0010...000. This is zero when num_state_bits=1.
this._quarter_range = this._half_range >>> 1; // Can be zero
// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010.
this._minimum_range = this._quarter_range + 2; // At least 2
// Maximum allowed total from a frequency table at all times during coding. This differs from Java
// and C++ because Python's native bigint avoids constraining the size of intermediate computations.
this._maximum_total = this._minimum_range;
// console.log(`this._maximum_total: ${this._maximum_total.toString(16)}`);
// Bit mask of num_state_bits ones, which is 0111...111.
this._state_mask = this._full_range - 1;
// console.log(`this._state_mask: ${this._state_mask.toString(16)}`);
constructor (numbits, bitin) {
super(numbits)
// -- State fields --
// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s.
this._low = 0;
// console.log(`this._low: ${this._low.toString(16)}`);
// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s.
this._high = this._state_mask;
// console.log(`this._high: ${this._high.toString(16)}`);
// The underlying bit input stream.
this._input = bitin;
this._input = bitin
// The current raw code bits being buffered, which is always in the range [low, high].
this._code = 0;
this._code = 0
for (let i = 0; i < this._num_state_bits; i++) {
this._code = (this._code << 1) | this.readCodeBit();
this._code = (this._code << 1) | this.readCodeBit()
// console.log(`this._code_init = ${this._code}`);

@@ -66,15 +27,15 @@ }

* Also updates this arithmetic coder's state and may read in some bits.
* @param {FrequencyTable} freqs
* @param {FrequencyTable} freqs
*/
read(freqs) {
read (freqs) {
// Translate from coding range scale to frequency table scale
let total = freqs.total;
let total = freqs.total
if (this._maximum_total >>> 0 < total >>> 0) {
throw RangeError('Cannot decode symbol because total is too large');
throw new RangeError('Cannot decode symbol because total is too large')
}
let range = ((this._high - this._low) + 1) >>> 0;
let offset = this._code - this._low;
let value = Math.floor((((offset + 1) * total) - 1) / range);
let range = ((this._high - this._low) + 1) >>> 0
let offset = this._code - this._low
let value = Math.floor((((offset + 1) * total) - 1) / range)
// console.log(`this._code_cal = ${this._code}, offset = ${offset}, value = ${value}`);
assert(Math.floor((value * range) / total) >>> 0 <= offset >>> 0);
assert(Math.floor((value * range) / total) >>> 0 <= offset >>> 0)
// console.log(`range = ${range.toString(16)}`);

@@ -84,16 +45,16 @@ // console.log(`offset = ${offset.toString(16)}`);

// console.log(`total = ${total.toString(16)}, ${typeof total}`);
assert((value >>> 0 >= 0) && (value >>> 0 < total >>> 0));
assert((value >>> 0 >= 0) && (value >>> 0 < total >>> 0))
// A kind of binary search.
// A kind of binary search.
// Find highest symbol such that freqs.get_low(symbol) <= value.
let start = 0;
let end = freqs.symbolLimit;
let start = 0
let end = freqs.symbolLimit
// console.log(`start = ${start}, end = ${end}, value = ${value.toNumber()}`);
while (end - start > 1) {
let middle = (start + end) >>> 1;
let middle = (start + end) >>> 1
// console.log(`freqs.getLow(middle) = ${freqs.getLow(middle)}`);
if (value >>> 0 < freqs.getLow(middle)) {
end = middle;
end = middle
} else {
start = middle;
start = middle
}

@@ -103,15 +64,15 @@ // console.log(`start = ${start}, end = ${end}`);

assert(start + 1 === end);
assert(start + 1 === end)
let symbol = start;
let symbol = start
assert(
(Math.floor((range * (freqs.getLow(symbol))) / (total)) >>> 0 <= offset >>> 0) &&
offset >>> 0 <= Math.floor(range * (freqs.getHigh(symbol)) / (total)) >>> 0
);
this.update(freqs, symbol);
)
this.update(freqs, symbol)
if (!(this._low >>> 0 <= this._code >>> 0 && this._code >>> 0 <= this._high >>> 0)) {
throw new RangeError('Code out of range');
throw new RangeError('Code out of range')
}
// console.log('symbol', symbol);
return symbol;
return symbol
}

@@ -121,94 +82,24 @@

// of stream is treated as an infinite number of trailing zeros.
readCodeBit() {
let temp = this._input.read();
readCodeBit () {
let temp = this._input.read()
// console.log(`readCodeBit: ${temp}`);
if (temp === -1) {
temp = 0;
temp = 0
}
return temp;
return temp
}
// Updates the code range (low and high) of this arithmetic coder as a result
// of processing the given symbol with the given frequency table.
// Invariants that are true before and after encoding/decoding each symbol
// (letting full_range = 2^num_state_bits):
// - 0 <= low <= code <= high < full_range. ('code' exists only in the decoder.)
// Therefore these variables are unsigned integers of num_state_bits bits.
// - low < 1/2 * full_range <= high.
// In other words, they are in different halves of the full range.
// - (low < 1/4 * full_range) || (high >= 3/4 * full_range).
// In other words, they are not both in the middle two quarters.
// - Let range = high - low + 1, then full_range/4 < minimum_range
// <= range <= full_range. These invariants for 'range' essentially
// dictate the maximum total that the incoming frequency table can have.
update(freqs, symbol) {
// State check
let low = this._low;
let high = this._high;
// console.log(`======== Updating ${symbol} =========`);
// console.log(`this._low = ${this._low.toString(16)}`);
// console.log(`this._high = ${this._high.toString(16)}`);
// console.log(`low & this._state_mask = ${low & this._state_mask.toString(16)}`);
// console.log(`high & (this._state_mask) = ${high & (this._state_mask).toString(16)}`);
if (low >>> 0 >= high >>> 0 || ((low & this._state_mask) !== low) || ((high & (this._state_mask)) !== high)) {
throw RangeError(`Low or high out of range, low = ${low}, high = ${high}`);
}
let range = high - low + 1;
// console.log(`range = ${range.toString(16)}`);
if (!(this._minimum_range >>> 0 <= range >>> 0 && range >>> 0 <= this._full_range >>> 0)) {
throw RangeError('Range out of range');
}
// Frequency table values check
let total = freqs.total;
let symlow = freqs.getLow(symbol);
let symhigh = freqs.getHigh(symbol);
// console.log(`symlow = ${symlow.toString(16)}`);
// console.log(`symhigh = ${symhigh.toString(16)}`);
if (symlow === symhigh) {
throw Error('Symbol has zero frequency');
}
if (this._maximum_total >>> 0 <= total >>> 0) {
throw Error('Cannot code symbol because total is too large');
}
// Update
// console.log(`total = ${total.toString(16)}`);
let newlow = low + Math.floor(range * symlow / total);
let newhigh = low + Math.floor(range * symhigh / total) - 1;
// console.log(`newlow = ${newlow.toString(16)}`);
// console.log(`newhigh = ${newhigh.toString(16)}`);
this._low = newlow;
this._high = newhigh;
// While low and high have the same top bit value, shift them out
while (((this._low ^ this._high) & (this._half_range)) === 0) {
this._shift();
this._low = (this._low << 1) & (this._state_mask);
this._high = (this._high << 1) & (this._state_mask) | 1;
}
// Now low's top bit must be 0 and high's top bit must be 1
// While low's top two bits are 01 and high's are 10, delete the second highest bit of both
while ((this._low & (~this._high) & (this._quarter_range)) !== 0) {
this._underflow();
this._low = (this._low << 1) ^ (this._half_range);
this._high = ((this._high ^ (this._half_range)) << 1) | this._half_range | 1;
}
}
_shift() {
this._code = (this._code << 1) & (this._state_mask) | (this.readCodeBit());
_shift () {
this._code = (this._code << 1) & (this._state_mask) | (this.readCodeBit())
// console.log(`this._code_shift = ${this._code}`);
}
_underflow() {
_underflow () {
this._code = this._code & (this._half_range) | (
this._code << 1 & (this._state_mask >>> 1)
) | (this.readCodeBit());
) | (this.readCodeBit())
// console.log(`this._code_underflow = ${this._code}`);
}
finish() {
this._input.close();
finish () {
this._input.close()
}
};
}

@@ -1,135 +0,27 @@

module.exports = class ArithmeticEncoder {
const Coder = require('./coder')
module.exports = class ArithmeticEncoder extends Coder {
/**
*
* @param {number} numbits
* @param {BitOutputStream} bitout
*
* @param {number} numbits
* @param {BitOutputStream} bitout
*/
constructor(numbits, bitout) {
if (numbits < 1) {
throw Error('State size out of range');
}
// -- Configuration fields --
// Number of bits for the 'low' and 'high' state variables. Must be at least 1.
// - Larger values are generally better - they allow a larger maximum frequency total (maximum_total),
// and they reduce the approximation error inherent in adapting fractions to integers;
// both effects reduce the data encoding loss and asymptotically approach the efficiency
// of arithmetic coding using exact fractions.
// - But larger state sizes increase the computation time for integer arithmetic,
// and compression gains beyond ~30 bits essentially zero in real-world applications.
// - Python has native bigint arithmetic, so there is no upper limit to the state size.
// For Java and C++ where using native machine-sized integers makes the most sense,
// they have a recommended value of num_state_bits=32 as the most versatile setting.
this._num_state_bits = numbits;
// console.log(`this._num_state_bits: ${this._num_state_bits}`);
// Maximum range (high+1-low) during coding (trivial), which is 2^num_state_bits = 1000...000.
this._full_range = (1 << numbits) >>> 0;
// console.log(`this._full_range: ${this._full_range.toString(16)}`);
// The top bit at width num_state_bits, which is 0100...000.
this._half_range = this._full_range >>> 1; // Non-zero
// The second highest bit at width num_state_bits, which is 0010...000. This is zero when num_state_bits=1.
this._quarter_range = this._half_range >>> 1; // Can be zero
// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010.
this._minimum_range = this._quarter_range + 2; // At least 2
// Maximum allowed total from a frequency table at all times during coding. This differs from Java
// and C++ because Python's native bigint avoids constraining the size of intermediate computations.
this._maximum_total = this._minimum_range;
// console.log(`this._maximum_total: ${this._maximum_total.toString(16)}`);
// Bit mask of num_state_bits ones, which is 0111...111.
this._state_mask = this._full_range - 1;
// console.log(`this._state_mask: ${this._state_mask.toString(16)}`);
constructor (numbits, bitout) {
super(numbits)
// -- State fields --
// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s.
this._low = 0;
// console.log(`this._low: ${this._low.toString(16)}`);
// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s.
this._high = this._state_mask;
// console.log(`this._high: ${this._high.toString(16)}`);
// The underlying bit output stream.
this._output = bitout;
this._output = bitout
// Number of saved underflow bits. This value can grow without bound.
this._num_underflow = 0;
this._num_underflow = 0
}
// Updates the code range (low and high) of this arithmetic coder as a result
// of processing the given symbol with the given frequency table.
// Invariants that are true before and after encoding/decoding each symbol
// (letting full_range = 2^num_state_bits):
// - 0 <= low <= code <= high < full_range. ('code' exists only in the decoder.)
// Therefore these variables are unsigned integers of num_state_bits bits.
// - low < 1/2 * full_range <= high.
// In other words, they are in different halves of the full range.
// - (low < 1/4 * full_range) || (high >= 3/4 * full_range).
// In other words, they are not both in the middle two quarters.
// - Let range = high - low + 1, then full_range/4 < minimum_range
// <= range <= full_range. These invariants for 'range' essentially
// dictate the maximum total that the incoming frequency table can have.
update(freqs, symbol) {
// State check
let low = this._low;
let high = this._high;
// console.log(`======== Updating ${symbol} =========`);
// console.log(`this._low = ${this._low.toString(16)}`);
// console.log(`this._high = ${this._high.toString(16)}`);
// console.log(`low & this._state_mask = ${low & this._state_mask.toString(16)}`);
// console.log(`high & (this._state_mask) = ${high & (this._state_mask).toString(16)}`);
if (low >>> 0 >= high >>> 0 || ((low & this._state_mask) !== low) || ((high & (this._state_mask)) !== high)) {
throw RangeError(`Low or high out of range, low = ${low}, high = ${high}`);
}
let range = high - low + 1;
// console.log(`range = ${range.toString(16)}`);
if (!(this._minimum_range >>> 0 <= range >>> 0 && range >>> 0 <= this._full_range >>> 0)) {
throw RangeError('Range out of range');
}
// Frequency table values check
let total = freqs.total;
let symlow = freqs.getLow(symbol);
let symhigh = freqs.getHigh(symbol);
// console.log(`symlow = ${symlow.toString(16)}`);
// console.log(`symhigh = ${symhigh.toString(16)}`);
if (symlow === symhigh) {
throw Error('Symbol has zero frequency');
}
if (this._maximum_total >>> 0 <= total >>> 0) {
throw Error('Cannot code symbol because total is too large');
}
// Update
// console.log(`total = ${total.toString(16)}`);
let newlow = low + Math.floor(range * symlow / total);
let newhigh = low + Math.floor(range * symhigh / total) - 1;
// console.log(`newlow = ${newlow.toString(16)}`);
// console.log(`newhigh = ${newhigh.toString(16)}`);
this._low = newlow;
this._high = newhigh;
// While low and high have the same top bit value, shift them out
while (((this._low ^ this._high) & (this._half_range)) === 0) {
this._shift();
this._low = (this._low << 1) & (this._state_mask);
this._high = (this._high << 1) & (this._state_mask) | 1;
}
// Now low's top bit must be 0 and high's top bit must be 1
// While low's top two bits are 01 and high's are 10, delete the second highest bit of both
while ((this._low & (~this._high) & (this._quarter_range)) !== 0) {
this._underflow();
this._low = (this._low << 1) ^ (this._half_range);
this._high = ((this._high ^ (this._half_range)) << 1) | this._half_range | 1;
}
}
/**
* Encodes the given symbol based on the given frequency table.
* This updates this arithmetic coder's state and may write out some bits.
* @param {*} freqs
* @param {*} symbol
* This updates this arithmetic coder's state and may write out some bits.
* @param {*} freqs
* @param {*} symbol
*/
write(freqs, symbol) {
write (freqs, symbol) {
// console.log('writing symbol', symbol);
this.update(freqs, symbol);
this.update(freqs, symbol)
}

@@ -141,10 +33,10 @@ /**

*/
finish() {
this._output.write(1);
this._output.close();
finish () {
this._output.write(1)
this._output.close()
}
_shift() {
let bit = this._low >>> (this._num_state_bits - 1);
_shift () {
let bit = this._low >>> (this._num_state_bits - 1)
// console.log(`bit = ${bit}`);
this._output.write(bit);
this._output.write(bit)

@@ -154,9 +46,9 @@ // Write out the saved underflow bits

// console.log(`bit ^ 1 = ${bit ^ 1}`);
this._output.write(bit ^ 1);
this._output.write(bit ^ 1)
}
this._num_underflow = 0;
this._num_underflow = 0
}
_underflow() {
this._num_underflow += 1;
_underflow () {
this._num_underflow += 1
}
};
}
/**
* A stream where bits can be read.
* A stream where bits can be read.
* Because they come from an underlying byte stream,
* the total number of bits is always a multiple of 8.
* the total number of bits is always a multiple of 8.
* The bits are read in big endian.

@@ -9,8 +9,8 @@ */

/**
*
* @param {Buffer} inBuffer
*
* @param {Buffer} inBuffer
*/
constructor(inBuffer) {
constructor (inBuffer) {
// The underlying buffer to read from
this._buffer = inBuffer;
this._buffer = inBuffer

@@ -20,44 +20,44 @@ // The accumulated bits for the current byte,

// or -1 if end of stream is reached
this._currentbyte = 0;
this._currentbyte = 0
// Number of accumulated bits in the current byte,
// always in range [0, 7)
this._numbitsremaining = 0;
this._numbitsremaining = 0
}
/**
* Reads a bit from this stream.
* Returns 0 or 1 if a bit is available,
* Reads a bit from this stream.
* Returns 0 or 1 if a bit is available,
* or -1 if the end of stream is reached.
* The end of stream always occurs on a byte boundary.
*/
read() {
read () {
if (this._currentbyte === -1) {
return -1;
return -1
}
if (this._numbitsremaining === 0) {
if (this._buffer.length === 0) {
this._currentbyte = -1;
return -1;
this._currentbyte = -1
return -1
}
// console.log(`this._cachedBytes = ${this._cachedBytes}`);
this._currentbyte = this._buffer[0];
this._buffer = this._buffer.slice(1);
this._currentbyte = this._buffer[0]
this._buffer = this._buffer.slice(1)
// console.log(this._currentbyte);
this._numbitsremaining = 8;
this._numbitsremaining = 8
// console.log(`this._numbitsremaining1 = ${this._numbitsremaining}`);
}
// console.log(`this._numbitsremaining2 = ${this._numbitsremaining}`);
require('assert')(this._numbitsremaining > 0, 'Number of bits remaining should be positive');
this._numbitsremaining -= 1;
require('assert')(this._numbitsremaining > 0, 'Number of bits remaining should be positive')
this._numbitsremaining -= 1
// console.log(`this._currentbyte = ${this._currentbyte}`);
// console.log(`this._numbitsremaining = ${this._numbitsremaining}`);
return (this._currentbyte >> this._numbitsremaining) & 1;
return (this._currentbyte >> this._numbitsremaining) & 1
}
readNoEOF() {
let result = this.read();
readNoEOF () {
let result = this.read()
if (result !== -1) {
return result;
return result
} else {
throw Error('Unexpected EOF');
throw new Error('Unexpected EOF')
}

@@ -69,6 +69,6 @@ }

*/
close() {
this._currentbyte = -1;
this._numbitsremaining = 0;
close () {
this._currentbyte = -1
this._numbitsremaining = 0
}
};
}

@@ -1,9 +0,9 @@

const fs = require('fs');
const fs = require('fs')
const CACHE_SIZE = 20000;
const CACHE_SIZE = 20000
/**
* A stream where bits can be read.
* A stream where bits can be read.
* Because they come from an underlying byte stream,
* the total number of bits is always a multiple of 8.
* the total number of bits is always a multiple of 8.
* The bits are read in big endian.

@@ -13,8 +13,8 @@ */

/**
*
* @param {string} input file path
*
* @param {string} input file path
*/
constructor(inputfile) {
constructor (inputfile) {
// The underlying file descriptor to read from
this._input = fs.openSync(inputfile, 'r');
this._input = fs.openSync(inputfile, 'r')

@@ -24,20 +24,20 @@ // The accumulated bits for the current byte,

// or -1 if end of stream is reached
this._currentbyte = 0;
this._currentbyte = 0
// Number of accumulated bits in the current byte,
// always in range [0, 7)
this._numbitsremaining = 0;
this._numbitsremaining = 0
this._cachedBytes = Buffer.alloc(0);
this._streamEnded = false;
this._cachedBytes = Buffer.alloc(0)
this._streamEnded = false
}
/**
* Reads a bit from this stream.
* Returns 0 or 1 if a bit is available,
* Reads a bit from this stream.
* Returns 0 or 1 if a bit is available,
* or -1 if the end of stream is reached.
* The end of stream always occurs on a byte boundary.
*/
read() {
read () {
if (this._currentbyte === -1) {
return -1;
return -1
}

@@ -47,10 +47,10 @@ if (this._numbitsremaining === 0) {

if (this._streamEnded) {
this._currentbyte = -1;
return -1;
this._currentbyte = -1
return -1
} else {
let temp = Buffer.alloc(CACHE_SIZE);
let numOfBytesRead = fs.readSync(this._input, temp, 0, CACHE_SIZE, null);
this._cachedBytes = temp.slice(0, numOfBytesRead);
let temp = Buffer.alloc(CACHE_SIZE)
let numOfBytesRead = fs.readSync(this._input, temp, 0, CACHE_SIZE, null)
this._cachedBytes = temp.slice(0, numOfBytesRead)
if (numOfBytesRead < CACHE_SIZE) {
this._streamEnded = true;
this._streamEnded = true
}

@@ -60,23 +60,23 @@ }

// console.log(`this._cachedBytes = ${this._cachedBytes}`);
this._currentbyte = this._cachedBytes[0];
this._cachedBytes = this._cachedBytes.slice(1);
this._currentbyte = this._cachedBytes[0]
this._cachedBytes = this._cachedBytes.slice(1)
// console.log('Byte read:', temp);
// console.log(this._currentbyte);
this._numbitsremaining = 8;
this._numbitsremaining = 8
// console.log(`this._numbitsremaining1 = ${this._numbitsremaining}`);
}
// console.log(`this._numbitsremaining2 = ${this._numbitsremaining}`);
require('assert')(this._numbitsremaining > 0, 'Number of bits remaining should be positive');
this._numbitsremaining -= 1;
require('assert')(this._numbitsremaining > 0, 'Number of bits remaining should be positive')
this._numbitsremaining -= 1
// console.log(`this._currentbyte = ${this._currentbyte}`);
// console.log(`this._numbitsremaining = ${this._numbitsremaining}`);
return (this._currentbyte >> this._numbitsremaining) & 1;
return (this._currentbyte >> this._numbitsremaining) & 1
}
readNoEOF() {
let result = this.read();
readNoEOF () {
let result = this.read()
if (result !== -1) {
return result;
return result
} else {
throw Error('Unexpected EOF');
throw new Error('Unexpected EOF')
}

@@ -88,7 +88,7 @@ }

*/
close() {
fs.closeSync(this._input);
this._currentbyte = -1;
this._numbitsremaining = 0;
close () {
fs.closeSync(this._input)
this._currentbyte = -1
this._numbitsremaining = 0
}
};
}
/**
* A stream where bits can be written to.
* Because they are written to an underlying byte stream,
* A stream where bits can be written to.
* Because they are written to an underlying byte stream,
* the end of the stream is padded with 0's up to a multiple of 8 bits.

@@ -8,11 +8,11 @@ * The bits are written in big endian.

module.exports = class BitOutputStreamToBuffer {
constructor() {
constructor () {
// The underlying file stream to write to
this._bytes = [];
this._bytes = []
// The accumulated bits for the current byte,
// always in range [0x00, 0xFF]
this._currentbyte = 0;
this._currentbyte = 0
// Number of accumulated bits in the current byte,
// always in range [0, 7)
this._numbitsfilled = 0;
this._numbitsfilled = 0
}

@@ -23,22 +23,22 @@

*/
write(b) {
write (b) {
if (b !== 0 && b !== 1) {
throw RangeError(`Bit to write must be 0 or 1, but got ${b.toString()}`);
throw new RangeError(`Bit to write must be 0 or 1, but got ${b.toString()}`)
}
this._currentbyte = (this._currentbyte << 1) | b;
this._numbitsfilled += 1;
this._currentbyte = (this._currentbyte << 1) | b
this._numbitsfilled += 1
if (this._numbitsfilled === 8) {
this._bytes.push(this._currentbyte);
this._currentbyte = 0;
this._numbitsfilled = 0;
this._bytes.push(this._currentbyte)
this._currentbyte = 0
this._numbitsfilled = 0
}
}
close() {
while(this._numbitsfilled !== 0) {
this.write(0);
close () {
while (this._numbitsfilled !== 0) {
this.write(0)
}
}
get buffer() {
return Buffer.from(this._bytes);
get buffer () {
return Buffer.from(this._bytes)
}
};
}

@@ -1,5 +0,5 @@

const fs = require('fs');
const fs = require('fs')
/**
* A stream where bits can be written to.
* Because they are written to an underlying byte stream,
* A stream where bits can be written to.
* Because they are written to an underlying byte stream,
* the end of the stream is padded with 0's up to a multiple of 8 bits.

@@ -10,15 +10,15 @@ * The bits are written in big endian.

/**
*
*
* @param {string} outputfile file path
*/
constructor(outputfile) {
constructor (outputfile) {
// The underlying file stream to write to
this._output = fs.openSync(outputfile, 'w');
this._bytes = [];
this._output = fs.openSync(outputfile, 'w')
this._bytes = []
// The accumulated bits for the current byte,
// always in range [0x00, 0xFF]
this._currentbyte = 0;
this._currentbyte = 0
// Number of accumulated bits in the current byte,
// always in range [0, 7)
this._numbitsfilled = 0;
this._numbitsfilled = 0
}

@@ -29,21 +29,21 @@

*/
write(b) {
write (b) {
if (b !== 0 && b !== 1) {
throw RangeError(`Bit to write must be 0 or 1, but got ${b.toString()}`);
throw new RangeError(`Bit to write must be 0 or 1, but got ${b.toString()}`)
}
this._currentbyte = (this._currentbyte << 1) | b;
this._numbitsfilled += 1;
this._currentbyte = (this._currentbyte << 1) | b
this._numbitsfilled += 1
if (this._numbitsfilled === 8) {
this._bytes.push(this._currentbyte);
this._currentbyte = 0;
this._numbitsfilled = 0;
this._bytes.push(this._currentbyte)
this._currentbyte = 0
this._numbitsfilled = 0
}
}
close() {
while(this._numbitsfilled !== 0) {
this.write(0);
close () {
while (this._numbitsfilled !== 0) {
this.write(0)
}
fs.writeSync(this._output, Buffer.from(this._bytes), 0, this._bytes.length, null);
fs.closeSync(this._output);
fs.writeSync(this._output, Buffer.from(this._bytes), 0, this._bytes.length, null)
fs.closeSync(this._output)
}
};
}

@@ -1,8 +0,8 @@

const fs = require('fs');
const FrequencyTable = require('./frequency-table');
const BitInputStream = require('./bit-input-stream');
const BitInputStreamFromBuffer = require('./bit-input-stream-buffer');
const ArithmeticDecoder = require('./arithmetic-decoder');
const fs = require('fs')
const FrequencyTable = require('./frequency-table')
const BitInputStream = require('./bit-input-stream')
const BitInputStreamFromBuffer = require('./bit-input-stream-buffer')
const ArithmeticDecoder = require('./arithmetic-decoder')
const NUM_OF_BITS = 31;
const NUM_OF_BITS = 31

@@ -14,9 +14,9 @@ /**

*/
function decode(inputfile, outputfile) {
function decode (inputfile, outputfile) {
// const inStream = fs.createReadStream(inputfile);
// const outStream = fs.createWriteStream(outputfile);
const bitin = new BitInputStream(inputfile);
let freqs = readFrequencies(bitin);
const bitin = new BitInputStream(inputfile)
let freqs = readFrequencies(bitin)
// console.log(`freqs.total = ${freqs.total}`);
decompress(freqs, bitin, outputfile);
decompress(freqs, bitin, outputfile)
}

@@ -28,25 +28,25 @@

*/
function decodeFromBuffer(inBuffer) {
function decodeFromBuffer (inBuffer) {
// const inStream = fs.createReadStream(inputfile);
// const outStream = fs.createWriteStream(outputfile);
const bitin = new BitInputStreamFromBuffer(inBuffer);
let freqs = readFrequencies(bitin);
const bitin = new BitInputStreamFromBuffer(inBuffer)
let freqs = readFrequencies(bitin)
// console.log(`freqs.total = ${freqs.total}`);
return decompressToBuffer(freqs, bitin);
return decompressToBuffer(freqs, bitin)
}
function readFrequencies(bitin) {
function readInt(n) {
let result = 0;
function readFrequencies (bitin) {
function readInt (n) {
let result = 0
for (let i = 0; i < n; i++) {
let tmp = bitin.readNoEOF();
let tmp = bitin.readNoEOF()
// console.log(tmp);
result = result << 1 | tmp; // Big endian
result = result << 1 | tmp // Big endian
}
return result;
return result
}
let freqs = [];
let freqs = []
// let acc = 0;
for (let i = 0; i < 256; i++) {
freqs[i] = readInt(NUM_OF_BITS);
freqs[i] = readInt(NUM_OF_BITS)
// console.log(`freqs[${i}] = `, freqs[i]);

@@ -58,46 +58,46 @@ // acc += freqs[i];

// EOF symbol
freqs.push(1);
return new FrequencyTable(freqs);
freqs.push(1)
return new FrequencyTable(freqs)
}
function decompress(freqs, bitin, outputfile) {
let output = fs.openSync(outputfile, 'w');
let bytes = [];
const dec = new ArithmeticDecoder(NUM_OF_BITS, bitin);
function decompress (freqs, bitin, outputfile) {
let output = fs.openSync(outputfile, 'w')
let bytes = []
const dec = new ArithmeticDecoder(NUM_OF_BITS, bitin)
for (;;) {
let symbol = dec.read(freqs);
let symbol = dec.read(freqs)
// EOF symbol
if (symbol === 256) {
dec.finish();
break;
dec.finish()
break
}
// console.log(`writing ${symbol}`);
// output.wr();
bytes.push(symbol);
bytes.push(symbol)
}
fs.writeSync(output, Buffer.from(bytes), 0, bytes.length, null);
fs.closeSync(output);
fs.writeSync(output, Buffer.from(bytes), 0, bytes.length, null)
fs.closeSync(output)
}
/**
*
* @param {FrequencyTable} freqs
* @param {BitInputStreamFromBuffer} bitin
*
* @param {FrequencyTable} freqs
* @param {BitInputStreamFromBuffer} bitin
*/
function decompressToBuffer(freqs, bitin) {
let buffer = Buffer.alloc(0);
let bytes = [];
const dec = new ArithmeticDecoder(NUM_OF_BITS, bitin);
function decompressToBuffer (freqs, bitin) {
let buffer = Buffer.alloc(0)
let bytes = []
const dec = new ArithmeticDecoder(NUM_OF_BITS, bitin)
for (;;) {
let symbol = dec.read(freqs);
let symbol = dec.read(freqs)
// EOF symbol
if (symbol === 256) {
dec.finish();
break;
dec.finish()
break
}
// console.log(`writing ${symbol}`);
bytes.push(symbol);
bytes.push(symbol)
}
buffer = Buffer.from(bytes);
return buffer;
buffer = Buffer.from(bytes)
return buffer
}

@@ -109,2 +109,2 @@

decodeFromBuffer
};
}

@@ -1,12 +0,12 @@

const fs = require('fs');
const FrequencyTable = require('./frequency-table');
const BitOutputStream = require('./bit-output-stream');
const BitOutputStreamToBuffer = require('./bit-output-stream-buffer');
const ArithmeticEncoder = require('./arithmetic-encoder');
const fs = require('fs')
const FrequencyTable = require('./frequency-table')
const BitOutputStream = require('./bit-output-stream')
const BitOutputStreamToBuffer = require('./bit-output-stream-buffer')
const ArithmeticEncoder = require('./arithmetic-encoder')
const CACHE_SIZE = 20000;
const NUM_OF_BITS = 31;
const CACHE_SIZE = 20000
const NUM_OF_BITS = 31
/**
* Returns a frequency table based on the bytes
* Returns a frequency table based on the bytes
* in the given file.

@@ -17,36 +17,36 @@ * Also contains an extra entry for symbol 256,

*/
function getFrequencies(inputfile) {
let input = fs.openSync(inputfile, 'r');
function getFrequencies (inputfile) {
let input = fs.openSync(inputfile, 'r')
let freqs = new FrequencyTable(
new Array(257).fill(0)
);
const temp = Buffer.alloc(CACHE_SIZE);
let bytesRead;
)
const temp = Buffer.alloc(CACHE_SIZE)
let bytesRead
for (;;) {
if((bytesRead = fs.readSync(input, temp, 0, temp.length, null)) === 0) {
break;
if ((bytesRead = fs.readSync(input, temp, 0, temp.length, null)) === 0) {
break
} else {
for (let i = 0; i < bytesRead; i++) {
freqs.increment(temp[i]);
freqs.increment(temp[i])
}
}
}
return freqs;
return freqs
}
/**
* Returns a frequency table based on the bytes
* Returns a frequency table based on the bytes
* in the given Buffer.
* Also contains an extra entry for symbol 256,
* whose frequency is set to 0.
* @param {Buffer} buffer
* @param {Buffer} buffer
*/
function getFrequenciesFromBuffer(buffer) {
function getFrequenciesFromBuffer (buffer) {
let freqs = new FrequencyTable(
new Array(257).fill(0)
);
)
for (let byte of buffer) {
freqs.increment(byte);
freqs.increment(byte)
}
return freqs;
return freqs
}

@@ -59,11 +59,11 @@

*/
function encode(inputfile, outputfile) {
let freqs = getFrequencies(inputfile);
function encode (inputfile, outputfile) {
let freqs = getFrequencies(inputfile)
// EOF symbol gets a frequency of 1
freqs.increment(256);
const bitout = new BitOutputStream(outputfile);
writeFrequencies(bitout, freqs);
compress(freqs, inputfile, bitout);
freqs.increment(256)
const bitout = new BitOutputStream(outputfile)
writeFrequencies(bitout, freqs)
compress(freqs, inputfile, bitout)
}

@@ -75,12 +75,12 @@

*/
function encodeFromBuffer(inBuffer) {
let freqs = getFrequenciesFromBuffer(inBuffer);
function encodeFromBuffer (inBuffer) {
let freqs = getFrequenciesFromBuffer(inBuffer)
// EOF symbol gets a frequency of 1
freqs.increment(256);
const bitout = new BitOutputStreamToBuffer();
writeFrequencies(bitout, freqs);
compressFromBuffer(freqs, inBuffer, bitout);
return bitout.buffer;
freqs.increment(256)
const bitout = new BitOutputStreamToBuffer()
writeFrequencies(bitout, freqs)
compressFromBuffer(freqs, inBuffer, bitout)
return bitout.buffer
}

@@ -91,8 +91,8 @@

* @param {BitOutputStream} bitout the output stream
* @param {FrequencyTable} freqs
* @param {FrequencyTable} freqs
*/
function writeFrequencies(bitout, freqs) {
function writeFrequencies (bitout, freqs) {
for (let i = 0; i < 256; i++) {
// console.log(freqs);
write_int(bitout, NUM_OF_BITS, freqs.get(i));
writeInt(bitout, NUM_OF_BITS, freqs.get(i))
}

@@ -102,11 +102,11 @@ }

/**
*
*
* @param {BitOutputStream} bitout the output stream
* @param {number} numbits
* @param {byte} value
* @param {number} numbits
* @param {byte} value
*/
function write_int(bitout, numbits, value) {
function writeInt (bitout, numbits, value) {
for (let i = numbits - 1; i >= 0; i--) {
// Big endian
bitout.write((value >> i) & 1);
bitout.write((value >> i) & 1)
}

@@ -116,18 +116,18 @@ }

/**
*
* @param {FrequencyTable} freqs
* @param {string} inputfile
* @param {BitOutputStream} bitout
*
* @param {FrequencyTable} freqs
* @param {string} inputfile
* @param {BitOutputStream} bitout
*/
function compress(freqs, inputfile, bitout) {
let enc = new ArithmeticEncoder(NUM_OF_BITS, bitout);
let input = fs.openSync(inputfile, 'r');
for(;;) {
const temp = Buffer.alloc(CACHE_SIZE);
let bytesRead;
if ((bytesRead = fs.readSync(input, temp, 0, temp.length, null)) === 0) {
break;
function compress (freqs, inputfile, bitout) {
let enc = new ArithmeticEncoder(NUM_OF_BITS, bitout)
let input = fs.openSync(inputfile, 'r')
for (;;) {
const temp = Buffer.alloc(CACHE_SIZE)
let bytesRead = fs.readSync(input, temp, 0, temp.length, null)
if (bytesRead === 0) {
break
} else {
for (let i = 0; i < bytesRead; i++) {
enc.write(freqs, temp[i]);
enc.write(freqs, temp[i])
}

@@ -137,5 +137,5 @@ }

// EOF
enc.write(freqs, 256);
enc.write(freqs, 256)
// Flush remaining code bit
enc.finish();
enc.finish()
// console.log('Encoded okay!');

@@ -145,16 +145,16 @@ }

/**
*
* @param {FrequencyTable} freqs
* @param {Buffer} inBuffer
* @param {BitOutputStreamToBuffer} bitout
*
* @param {FrequencyTable} freqs
* @param {Buffer} inBuffer
* @param {BitOutputStreamToBuffer} bitout
*/
function compressFromBuffer(freqs, inBuffer, bitout) {
let enc = new ArithmeticEncoder(NUM_OF_BITS, bitout);
function compressFromBuffer (freqs, inBuffer, bitout) {
let enc = new ArithmeticEncoder(NUM_OF_BITS, bitout)
for (let byte of inBuffer) {
enc.write(freqs, byte);
enc.write(freqs, byte)
}
// EOF
enc.write(freqs, 256);
enc.write(freqs, 256)
// Flush remaining code bit
enc.finish();
enc.finish()
// console.log('Encoded okay!');

@@ -167,2 +167,2 @@ }

encodeFromBuffer
};
}

@@ -1,2 +0,2 @@

const assert = require('assert');
const assert = require('assert')

@@ -9,3 +9,3 @@ /**

* algorithms such as Fenwick trees.
*
*
* For example:

@@ -18,3 +18,2 @@ * symbols: 0, 1, 2, 3

module.exports = class FrequencyTable {
/**

@@ -27,11 +26,11 @@ * Constructs a simple frequency table in one of two ways:

*/
constructor(freqs) {
constructor (freqs) {
if (freqs instanceof FrequencyTable) {
let symbolLimit = freqs.symbolLimit;
this._frequencies = [];
let symbolLimit = freqs.symbolLimit
this._frequencies = []
for (let i = 0; i < symbolLimit; i++) {
this._frequencies[i] = freqs.get(i);
this._frequencies[i] = freqs.get(i)
}
} else { // Assume it is an array sequence
this._frequencies = Array.from(freqs);
this._frequencies = Array.from(freqs)
}

@@ -42,33 +41,33 @@

if (this._frequencies.length < 1) {
throw Error('At least 1 symbol needed');
throw new Error('At least 1 symbol needed')
}
this._frequencies.forEach(freq => {
if (freq < 0) {
throw RangeError('Negative frequency');
throw new RangeError('Negative frequency')
}
});
})
// Always equal to the sum of `frequencies`
this._total = this._frequencies.reduce((partial_sum, a) => partial_sum + a);
this._total = this._frequencies.reduce((partialSum, a) => partialSum + a)
// _cumulative[i] is the sum of `frequencies` in range [0, i)
// Initialized lazily. When it is no None, the data is valid.
this._cumulative = null;
this._cumulative = null
}
/**
* Returns the number of symbols in this frequencies table,
* Returns the number of symbols in this frequencies table,
* which is at least 1.
*/
get symbolLimit() {
return this._frequencies.length;
get symbolLimit () {
return this._frequencies.length
}
/**
* Returns the total of all symbol frequencies.
* Returns the total of all symbol frequencies.
* The returned value is at least 0 and is always equal to
* `getHigh(symbolLimit - 1)`
*/
get total() {
get total () {
// console.log(this._frequencies.toString());
return this._total;
return this._total
}

@@ -80,21 +79,21 @@ /**

*/
getLow(symbol) {
if(symbol === 0) return 0;
this._checkSymbol(symbol - 1);
getLow (symbol) {
if (symbol === 0) return 0
this._checkSymbol(symbol - 1)
if (this._cumulative === null) {
this._init_cumulative();
this._initCumulative()
}
return this._cumulative[symbol - 1];
return this._cumulative[symbol - 1]
}
/**
* Returns the sum of the frequencies of the given symbol and all the
* Returns the sum of the frequencies of the given symbol and all the
* symbols below the given symbol value.
* The returned value is at least 0.
*/
getHigh(symbol) {
this._checkSymbol(symbol);
getHigh (symbol) {
this._checkSymbol(symbol)
if (this._cumulative === null) {
this._init_cumulative();
this._initCumulative()
}
return this._cumulative[symbol];
return this._cumulative[symbol]
}

@@ -107,5 +106,5 @@

*/
get(symbol) {
this._checkSymbol(symbol);
return this._frequencies[symbol];
get (symbol) {
this._checkSymbol(symbol)
return this._frequencies[symbol]
}

@@ -116,34 +115,34 @@ /**

* If an error is thrown, then the state is left unchanged.
* @param {number} symbol
* @param {number} symbol
* @param {number} freq >= 0
*/
set(symbol, freq) {
this._checkSymbol(symbol);
set (symbol, freq) {
this._checkSymbol(symbol)
if (freq < 0) {
throw RangeError('Negative frequency');
throw new RangeError('Negative frequency')
}
let sumFreqOfOthers = this._total - this._frequencies[symbol];
let sumFreqOfOthers = this._total - this._frequencies[symbol]
assert(
sumFreqOfOthers >= 0,
'Sum of frequency of other symbols should be non-negative'
);
this._total = sumFreqOfOthers + freq;
this._frequencies[symbol] = freq;
this._cumulative = null;
)
this._total = sumFreqOfOthers + freq
this._frequencies[symbol] = freq
this._cumulative = null
}
/**
* Increments the frequency of the given symbol
* @param {number} symbol
* @param {number} symbol
*/
increment(symbol) {
this._checkSymbol(symbol);
this._total += 1;
this._frequencies[symbol] += 1;
this._cumulative = null;
increment (symbol) {
this._checkSymbol(symbol)
this._total += 1
this._frequencies[symbol] += 1
this._cumulative = null
}
_checkSymbol(symbol) {
if (0 <= symbol && symbol < this._frequencies.length) {
return;
_checkSymbol (symbol) {
if (symbol >= 0 && symbol < this._frequencies.length) {
} else {
throw RangeError('Symbol out of range');
throw new RangeError('Symbol out of range')
}

@@ -157,8 +156,8 @@ }

*/
_init_cumulative() {
let cumul = Array.from(this._frequencies);
_initCumulative () {
let cumul = Array.from(this._frequencies)
for (let i = 1; i < cumul.length; i++) {
cumul[i] += cumul[i-1];
cumul[i] += cumul[i - 1]
}
this._cumulative = cumul;
this._cumulative = cumul
}

@@ -169,9 +168,9 @@ /**

*/
toString() {
let result = '';
toString () {
let result = ''
for (let i = 0; i < this._frequencies.length; i++) {
result += `${i}\t${this._frequencies[i]}\n`;
result += `${i}\t${this._frequencies[i]}\n`
}
return result;
return result
}
};
}

@@ -1,3 +0,3 @@

let encode = require('./encode');
let decode = require('./decode');
let encode = require('./encode')
let decode = require('./decode')
module.exports = {

@@ -8,2 +8,2 @@ encode: encode.encode,

decodeFromBuffer: decode.decodeFromBuffer
};
}

@@ -1,77 +0,77 @@

require('should');
const FrequencyTable = require('../src/frequency-table');
require('should')
const FrequencyTable = require('../src/frequency-table')
let frequencyTable = new FrequencyTable(
new Array(257).fill(0)
);
)
describe('FrequencyTable', function() {
describe('constructor', function() {
it('should construct from an array', function() {
frequencyTable.toString().should.not.eql('');
});
it('should construct from another FrequencyTable', function() {
let anotherFrequencyTanble = new FrequencyTable(frequencyTable);
anotherFrequencyTanble.toString().should.not.eql('');
});
});
describe('getters and setters', function() {
it('should get symbol limit', function() {
frequencyTable.symbolLimit.should.eql(257);
});
it('should get total', function() {
frequencyTable.total.should.eql(0);
});
it('should set and get symbol', function() {
frequencyTable.set(2, 20);
frequencyTable.get(2).should.eql(20);
});
it('should get low and high', function() {
describe('FrequencyTable', function () {
describe('constructor', function () {
it('should construct from an array', function () {
frequencyTable.toString().should.not.eql('')
})
it('should construct from another FrequencyTable', function () {
let anotherFrequencyTanble = new FrequencyTable(frequencyTable)
anotherFrequencyTanble.toString().should.not.eql('')
})
})
describe('getters and setters', function () {
it('should get symbol limit', function () {
frequencyTable.symbolLimit.should.eql(257)
})
it('should get total', function () {
frequencyTable.total.should.eql(0)
})
it('should set and get symbol', function () {
frequencyTable.set(2, 20)
frequencyTable.get(2).should.eql(20)
})
it('should get low and high', function () {
frequencyTable = new FrequencyTable(
[...Array(257).keys()]
);
frequencyTable.getLow(0).should.eql(0);
frequencyTable.getHigh(0).should.eql(0);
)
frequencyTable.getLow(1).should.eql(0);
frequencyTable.getHigh(1).should.eql(1);
frequencyTable.getLow(0).should.eql(0)
frequencyTable.getHigh(0).should.eql(0)
frequencyTable.symbolLimit.should.eql(257);
frequencyTable.getLow(1).should.eql(0)
frequencyTable.getHigh(1).should.eql(1)
frequencyTable.getLow(256).should.eql(32640);
frequencyTable.getHigh(256).should.eql(32896);
frequencyTable.symbolLimit.should.eql(257)
frequencyTable.getHigh(256).should.eql(frequencyTable.total);
});
it('should increment', function() {
frequencyTable.increment(0);
frequencyTable.getLow(256).should.eql(32640 + 1);
frequencyTable.getHigh(256).should.eql(32896 + 1);
});
});
describe('throw error', function() {
it('should throw Error when construct with empty frequencies', function() {
frequencyTable.getLow(256).should.eql(32640)
frequencyTable.getHigh(256).should.eql(32896)
frequencyTable.getHigh(256).should.eql(frequencyTable.total)
})
it('should increment', function () {
frequencyTable.increment(0)
frequencyTable.getLow(256).should.eql(32640 + 1)
frequencyTable.getHigh(256).should.eql(32896 + 1)
})
})
describe('throw new error', function () {
it('should throw new Error when construct with empty frequencies', function () {
(() => {
// eslint-disable-next-line no-unused-vars
let test = new FrequencyTable([]);
}).should.throw('At least 1 symbol needed');
});
it('should throw Error when construct with negative frequencies', function() {
let test = new FrequencyTable([])
}).should.throw('At least 1 symbol needed')
})
it('should throw new Error when construct with negative frequencies', function () {
(() => {
// eslint-disable-next-line no-unused-vars
let test = new FrequencyTable([-1]);
}).should.throw('Negative frequency');
});
it('should throw Error when set with negative frequencies', function() {
let test = new FrequencyTable([-1])
}).should.throw('Negative frequency')
})
it('should throw new Error when set with negative frequencies', function () {
(() => {
frequencyTable.set(3, -1);
}).should.throw('Negative frequency');
});
it('should throw RangeError when symbol out of range', function() {
frequencyTable.set(3, -1)
}).should.throw('Negative frequency')
})
it('should throw new RangeError when symbol out of range', function () {
(() => {
frequencyTable.get(10000);
}).should.throw('Symbol out of range');
});
});
});
frequencyTable.get(10000)
}).should.throw('Symbol out of range')
})
})
})

@@ -1,25 +0,25 @@

require('should');
const encode = require('../src/encode');
const fileInfo = require('./file-info');
require('should')
const encode = require('../src/encode')
const fileInfo = require('./file-info')
const path = require('path')
describe('encode', function() {
describe('getFrequencies ' + fileInfo(__dirname + '/txt/short.txt'), function() {
it('should construct frequency table', function() {
let freqs = encode.getFrequencies(__dirname + '/txt/short.txt');
describe('encode', function () {
describe('getFrequencies ' + fileInfo(path.resolve(__dirname, './txt/short.txt')), function () {
it('should construct frequency table', function () {
let freqs = encode.getFrequencies(path.resolve(__dirname, './txt/short.txt'))
// console.log((Buffer.from('t'))[0]);
// console.log(freqs);
freqs.get((Buffer.from('t'))[0]).should.eql(2);
freqs.get((Buffer.from('i'))[0]).should.eql(4);
});
it('should error if no such file', function() {
freqs.get((Buffer.from('t'))[0]).should.eql(2)
freqs.get((Buffer.from('i'))[0]).should.eql(4)
})
it('should error if no such file', function () {
// eslint-disable-next-line no-unused-vars
(() => {let freqs = encode.getFrequencies(__dirname + '/txt/nosuchfile.txt');}).should.throw(Error);
});
});
describe('encode ' + fileInfo(__dirname + '/txt/short.txt'), function() {
it('should encode okay', function() {
encode.encode(__dirname + '/txt/short.txt', __dirname + '/txt/short-encoded.txt');
});
});
});
(() => { let freqs = encode.getFrequencies(path.resolve(__dirname, './txt/nosuchfile.txt')) }).should.throw(Error)
})
})
describe('encode ' + fileInfo(path.resolve(__dirname, './txt/short.txt')), function () {
it('should encode okay', function () {
encode.encode(path.resolve(__dirname, './txt/short.txt'), path.resolve(__dirname, './txt/short-encoded.txt'))
})
})
})

@@ -1,17 +0,18 @@

var fs = require('fs');
require('should');
const decode = require('../src/decode');
const fileInfo = require('./file-info');
var fs = require('fs')
require('should')
const decode = require('../src/decode')
const fileInfo = require('./file-info')
const path = require('path')
describe('decode', function () {
it('should decode okay ' + fileInfo(__dirname + '/txt/short.txt'), function () {
decode.decode(__dirname + '/txt/short-encoded.txt', __dirname + '/txt/short-decoded.txt');
});
it('should decode okay ' + fileInfo(path.resolve(__dirname, './txt/short.txt')), function () {
decode.decode(path.resolve(__dirname, './txt/short-encoded.txt'), path.resolve(__dirname, './txt/short-decoded.txt'))
})
it('should decode & encode equal', function () {
let originalText = fs.readFileSync(__dirname + '/txt/short.txt');
let decodedText = fs.readFileSync(__dirname + '/txt/short-decoded.txt');
let originalText = fs.readFileSync(path.resolve(__dirname, './txt/short.txt'))
let decodedText = fs.readFileSync(path.resolve(__dirname, './txt/short-decoded.txt'))
// console.log(`originalText = ${originalText.toString()}`);
// console.log(`decodedText = ${decodedText.toString()}`);
originalText.toString().should.eql(decodedText.toString());
});
});
originalText.toString().should.eql(decodedText.toString())
})
})

@@ -1,30 +0,31 @@

require('should');
const ariCoding = require('../src/index');
var fs = require('fs');
const fileInfo = require('./file-info');
require('should')
const ariCoding = require('../src/index')
var fs = require('fs')
const fileInfo = require('./file-info')
const path = require('path')
describe('Package test', function() {
describe('encode & decode ' + fileInfo(__dirname + '/txt/long.txt'), function() {
it('should encode', function() {
ariCoding.encode(__dirname + '/txt/long.txt', __dirname + '/txt/long-encoded.txt');
});
it('should decode', function() {
ariCoding.decode(__dirname + '/txt/long-encoded.txt', __dirname + '/txt/long-decoded.txt');
});
it('should equal', function() {
let originalText = fs.readFileSync(__dirname + '/txt/long.txt');
let decodedText = fs.readFileSync(__dirname + '/txt/long-decoded.txt');
originalText.should.eql(decodedText);
});
});
describe('Buffer support', function() {
it('should encode and decode Buffer okay', function() {
let data = Buffer.from('Example data', 'utf8');
let encoded = ariCoding.encodeFromBuffer(data);
describe('Package test', function () {
describe('encode & decode ' + fileInfo(path.resolve(__dirname, './txt/long.txt')), function () {
it('should encode', function () {
ariCoding.encode(path.resolve(__dirname, './txt/long.txt'), path.resolve(__dirname, './txt/long-encoded.txt'))
})
it('should decode', function () {
ariCoding.decode(path.resolve(__dirname, './txt/long-encoded.txt'), path.resolve(__dirname, './txt/long-decoded.txt'))
})
it('should equal', function () {
let originalText = fs.readFileSync(path.resolve(__dirname, './txt/long.txt'))
let decodedText = fs.readFileSync(path.resolve(__dirname, './txt/long-decoded.txt'))
originalText.should.eql(decodedText)
})
})
describe('Buffer support', function () {
it('should encode and decode Buffer okay', function () {
let data = Buffer.from('Example data', 'utf8')
let encoded = ariCoding.encodeFromBuffer(data)
// console.log(`encoded = ${encoded}`);
let decoded = ariCoding.decodeFromBuffer(encoded);
let decoded = ariCoding.decodeFromBuffer(encoded)
// console.log(`decoded = ${decoded}`);
data.should.eql(decoded);
});
});
});
data.should.eql(decoded)
})
})
})

@@ -1,8 +0,8 @@

const cmd = require('./cmd');
const path = require('path');
require('should');
const fs = require('fs');
const fileInfo = require('./file-info');
const cmd = require('./cmd')
const path = require('path')
require('should')
const fs = require('fs')
const fileInfo = require('./file-info')
describe('CLI test ' + fileInfo(__dirname + '/txt/long.txt'), () => {
describe('CLI test ' + fileInfo(path.resolve(__dirname, './txt/long.txt')), () => {
it('should print help if input is invalid', async () => {

@@ -13,7 +13,7 @@ try {

['ssss']
);
)
} catch (err) {
err.should.eql('Invalid command: ssss\nSee --help for a list of available commands.\n');
err.should.eql('Invalid command: ssss\nSee --help for a list of available commands.\n')
}
});
})
it('should encode okay', async () => {

@@ -23,4 +23,4 @@ await cmd.execute(

['encode', path.resolve(__dirname, './txt/long.txt'), '-o', path.resolve(__dirname, './txt/long-encoded.txt')]
);
});
)
})
it('should decode okay', async () => {

@@ -30,9 +30,9 @@ await cmd.execute(

['decode', path.resolve(__dirname, './txt/long-encoded.txt'), '-o', path.resolve(__dirname, './txt/long-decoded.txt')]
);
});
)
})
it('should encode & decode equal', async () => {
let originalText = fs.readFileSync(__dirname + '/txt/long.txt');
let decodedText = fs.readFileSync(__dirname + '/txt/long-decoded.txt');
originalText.should.eql(decodedText);
});
});
let originalText = fs.readFileSync(path.resolve(__dirname, './txt/long.txt'))
let decodedText = fs.readFileSync(path.resolve(__dirname, './txt/long-decoded.txt'))
originalText.should.eql(decodedText)
})
})

@@ -1,19 +0,23 @@

require('should');
const encode = require('../src/encode');
const decode = require('../src/decode');
const fs = require('fs');
const fileInfo = require('./file-info');
require('should')
const encode = require('../src/encode')
const decode = require('../src/decode')
const fs = require('fs')
const fileInfo = require('./file-info')
const path = require('path')
const filepath = path.resolve(__dirname, './txt/sample5.ref')
const filepath = __dirname + '/txt/sample5.ref';
describe('Buffer support', function () {
it('should encode and decode Buffer okay ' + fileInfo(filepath), function() {
let data = fs.readFileSync(filepath);
let encoded = encode.encodeFromBuffer(data);
let data = fs.readFileSync(filepath)
let encoded, decoded
it('should encode Buffer okay ' + fileInfo(filepath), function () {
encoded = encode.encodeFromBuffer(data)
// console.log(`encoded = ${encoded}`);
let decoded = decode.decodeFromBuffer(encoded);
})
it('should decode Buffer okay ' + fileInfo(filepath), function () {
decoded = decode.decodeFromBuffer(encoded)
// console.log(`decoded = ${decoded}`);
data.should.eql(decoded);
});
});
})
it('should encode and decode equal', function () {
data.should.eql(decoded)
})
})

@@ -1,11 +0,11 @@

require('should');
require('should')
// https://medium.com/@zorrodg/integration-tests-on-node-js-cli-part-1-why-and-how-fa5b1ba552fe
const spawn = require('child_process').spawn;
function createProcess(processPath, args = [], env = null) {
args = [processPath].concat(args);
const spawn = require('child_process').spawn
function createProcess (processPath, args = [], env = null) {
args = [processPath].concat(args)
// Require which and child_process
const which = require('which');
const which = require('which')
// Find node in PATH
const node = which.sync('node');
const node = which.sync('node')

@@ -19,23 +19,23 @@ return spawn(node, args, {

)
});
})
}
const concat = require('concat-stream');
function execute(processPath, args = [], opts = {}) {
const { env = null } = opts;
const childProcess = createProcess(processPath, args, env);
childProcess.stdin.setEncoding('utf-8');
const concat = require('concat-stream')
function execute (processPath, args = [], opts = {}) {
const { env = null } = opts
const childProcess = createProcess(processPath, args, env)
childProcess.stdin.setEncoding('utf-8')
const promise = new Promise((resolve, reject) => {
childProcess.stderr.once('data', err => {
reject(err.toString());
});
childProcess.on('error', reject);
reject(err.toString())
})
childProcess.on('error', reject)
childProcess.stdout.pipe(
concat(result => {
resolve(result.toString());
resolve(result.toString())
})
);
});
return promise;
)
})
return promise
}
module.exports = { execute };
module.exports = { execute }
module.exports = function (filepath) {
const fs = require('fs'); //Load the filesystem module
const stats = fs.statSync(filepath);
const fileSizeInBytes = stats.size;
return `file: ${filepath.replace(/^.*[\\/]/, '')}, size: ${fileSizeInBytes}`;
};
const fs = require('fs') // Load the filesystem module
const stats = fs.statSync(filepath)
const fileSizeInBytes = stats.size
return `file: ${filepath.replace(/^.*[\\/]/, '')}, size: ${fileSizeInBytes}`
}

@@ -1,6 +0,7 @@

const encode = require('../src/encode');
const decode = require('../src/decode');
const encode = require('../src/encode')
const decode = require('../src/decode')
const path = require('path')
encode.encode(__dirname + '/txt/short.txt', __dirname + '/txt/short-encoded.txt');
encode.encode(path.resolve(__dirname, './txt/short.txt'), path.resolve(__dirname, './txt/short-encoded.txt'))
decode.decode(__dirname + '/txt/short-encoded.txt', __dirname + '/txt/short-decoded.txt');
decode.decode(path.resolve(__dirname, './txt/short-encoded.txt'), path.resolve(__dirname, './txt/short-decoded.txt'))
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc