arithmetic-coding
Advanced tools
Comparing version 1.2.0 to 1.2.1
#!/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')) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
31
89
2297238
5
1185
2
+ Addedmocha-lcov-reporter@^1.3.0
+ Addedmocha-lcov-reporter@1.3.0(transitive)