@thi.ng/rle-pack 3.1.117 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
8
8
 
9
9
  > [!NOTE]
10
- > This is one of 213 standalone projects, maintained as part
10
+ > This is one of 214 standalone projects, maintained as part
11
11
  > of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
12
12
  > and anti-framework.
13
13
  >
@@ -15,7 +15,10 @@
15
15
  > GitHub](https://github.com/sponsors/postspectacular). Thank you! ❤️
16
16
 
17
17
  - [About](#about)
18
- - [Encoding format](#encoding-format)
18
+ - [Simple RLE](#simple-rle)
19
+ - [Binary encoding](#binary-encoding)
20
+ - [Encoding format](#encoding-format)
21
+ - [Code example](#code-example)
19
22
  - [Status](#status)
20
23
  - [Related packages](#related-packages)
21
24
  - [Installation](#installation)
@@ -26,6 +29,31 @@
26
29
 
27
30
  ## About
28
31
 
32
+ The package provides two approaches for [Run-length
33
+ encoding/decoding](https://en.wikipedia.org/wiki/Run-length_encoding):
34
+
35
+ ### Simple RLE
36
+
37
+ The naive approach operates on arrays of arbitrary values and supports
38
+ user-defined predicates to determine if a consecutive input values are equal
39
+ (i.e. repeated). By default uses `===` strict comparison.
40
+
41
+ ```ts tangle:export/readme-simple.ts
42
+ import { encodeSimple, decodeSimple } from "@thi.ng/rle-pack";
43
+
44
+ const src = [..."aaaaaabbbbaaaxyxxx"];
45
+
46
+ const encoded = encodeSimple(src);
47
+ console.log(encoded);
48
+ // ["a", 6, "b", 4, "a", 3, "x", 1, "y", 1, "x", 3]
49
+
50
+ const decoded = decodeSimple(encoded);
51
+ console.log(decoded);
52
+ // ["a", "a", "a", "a", "a", "a", "b", "b", "b", "b", "a", "a", "a", "x", "y", "x", "x", "x"]
53
+ ```
54
+
55
+ ### Binary encoding
56
+
29
57
  Binary [run-length
30
58
  encoding](https://en.wikipedia.org/wiki/Run-length_encoding)
31
59
  packer/unpacker with support for customizable input word sizes (1 - 32
@@ -36,7 +64,7 @@ lengths is 16 bits (i.e. 65536 repetitions). If a value is repeated more
36
64
  often than that, the remainder will be encoded using additional RLE
37
65
  chunks...
38
66
 
39
- ### Encoding format
67
+ #### Encoding format
40
68
 
41
69
  ![data layout](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/rle/rle-layout.png)
42
70
 
@@ -54,6 +82,31 @@ Then per value:
54
82
  then split into chunks...)
55
83
  - n bits - value(s)
56
84
 
85
+ #### Code example
86
+
87
+ ```ts tangle:export/readme-binary.ts
88
+ import { encodeBinary, decodeBinary } from "@thi.ng/rle-pack";
89
+
90
+ // prepare dummy data
91
+ const src = new Uint8Array(1024);
92
+ src.set([1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,4,4,3,3,3,2,2,2,2,1,1,1,1,1], 512);
93
+
94
+ // pack data
95
+ const packed = encodeBinary(src, src.length);
96
+ console.log(packed.length);
97
+ // 30 => 2.93% of original
98
+
99
+ // pack with custom word size (3 bits, i.e. our value range is only 0-7)
100
+ // and use custom repeat group sizes suitable for our data
101
+ const alt = encodeBinary(src, src.length, 3, [1, 2, 3, 9]);
102
+ console.log(alt.length);
103
+ // 20 => 1.95% of original, 66% of default config
104
+
105
+ // unpack
106
+ const unpacked = decodeBinary(alt);
107
+ console.log(unpacked.length);
108
+ ```
109
+
57
110
  ## Status
58
111
 
59
112
  **STABLE** - used in production
@@ -92,39 +145,20 @@ For Node.js REPL:
92
145
  const rle = await import("@thi.ng/rle-pack");
93
146
  ```
94
147
 
95
- Package sizes (brotli'd, pre-treeshake): ESM: 636 bytes
148
+ Package sizes (brotli'd, pre-treeshake): ESM: 802 bytes
96
149
 
97
150
  ## Dependencies
98
151
 
152
+ - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
99
153
  - [@thi.ng/bitstream](https://github.com/thi-ng/umbrella/tree/develop/packages/bitstream)
100
154
  - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
101
155
 
156
+ Note: @thi.ng/api is in _most_ cases a type-only import (not used at runtime)
157
+
102
158
  ## API
103
159
 
104
160
  [Generated API docs](https://docs.thi.ng/umbrella/rle-pack/)
105
161
 
106
- ```ts
107
- import { encode, decode } from "@thi.ng/rle-pack";
108
-
109
- // prepare dummy data
110
- src = new Uint8Array(1024);
111
- src.set([1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,4,4,3,3,3,2,2,2,2,1,1,1,1,1], 512);
112
-
113
- // pack data
114
- packed = encode(src, src.length);
115
- packed.length
116
- // 30 => 2.93% of original
117
-
118
- // pack with custom word size (3 bits, i.e. our value range is only 0-7)
119
- // and use custom repeat group sizes suitable for our data
120
- alt = encode(src, src.length, 3, [1, 2, 3, 9]);
121
- alt.length
122
- // 20 => 1.95% of original, 66% of default config
123
-
124
- // unpack
125
- unpacked = new Uint8Array(decode(alt));
126
- ```
127
-
128
162
  ## Authors
129
163
 
130
164
  - [Karsten Schmidt](https://thi.ng)
package/binary.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ export type RLESizes = [number, number, number, number];
2
+ /**
3
+ * Compresses input using dynamically sized RLE compression and returns
4
+ * result as `Uint8Array`.
5
+ *
6
+ * @param src -
7
+ * @param num - number of input words
8
+ * @param wordSize - in bits, range 1 - 32
9
+ * @param rleSizes - run-length group sizes (in bits, max. 16)
10
+ */
11
+ export declare const encodeBinary: (src: Iterable<number>, num: number, wordSize?: number, rleSizes?: RLESizes) => Uint8Array<ArrayBuffer>;
12
+ export declare const decodeBinary: (src: Uint8Array) => Uint8Array<ArrayBuffer> | Uint16Array<ArrayBuffer> | Uint32Array<ArrayBuffer>;
13
+ /** @deprecated renamed to {@link encodeBinary} */
14
+ export declare const encode: (src: Iterable<number>, num: number, wordSize?: number, rleSizes?: RLESizes) => Uint8Array<ArrayBuffer>;
15
+ /** @deprecated renamed to {@link decodeBinary} */
16
+ export declare const decode: (src: Uint8Array) => Uint8Array<ArrayBuffer> | Uint16Array<ArrayBuffer> | Uint32Array<ArrayBuffer>;
17
+ //# sourceMappingURL=binary.d.ts.map
package/binary.js ADDED
@@ -0,0 +1,102 @@
1
+ import { BitInputStream } from "@thi.ng/bitstream/input";
2
+ import { BitOutputStream } from "@thi.ng/bitstream/output";
3
+ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
4
+ const encodeBinary = (src, num, wordSize = 8, rleSizes = [3, 4, 8, 16]) => {
5
+ (wordSize < 1 || wordSize > 32) && illegalArgs("word size (1-32 bits only)");
6
+ const out = new BitOutputStream(Math.ceil(num * wordSize / 8) + 4 + 2 + 1).write(num, 32).write(wordSize - 1, 5);
7
+ rleSizes.forEach((x) => {
8
+ (x < 1 || x > 16) && illegalArgs("RLE repeat size (1-16 bits only)");
9
+ out.write(x - 1, 4);
10
+ });
11
+ const [rle0, rle1, rle2, rle3] = rleSizes.map((x) => 1 << x);
12
+ const chunk = [];
13
+ const n1 = num - 1;
14
+ let val;
15
+ let tail = true;
16
+ let n = 0;
17
+ let i = 0;
18
+ const writeRLE = () => {
19
+ const t = n < rle0 ? 0 : n < rle1 ? 1 : n < rle2 ? 2 : 3;
20
+ out.writeBit(1);
21
+ out.write(t, 2);
22
+ out.write(n, rleSizes[t]);
23
+ out.write(val, wordSize);
24
+ n = 0;
25
+ };
26
+ const writeChunk = () => {
27
+ const m = chunk.length - 1;
28
+ const t = m < rle0 ? 0 : m < rle1 ? 1 : m < rle2 ? 2 : 3;
29
+ out.writeBit(0);
30
+ out.write(t, 2);
31
+ out.write(m, rleSizes[t]);
32
+ out.writeWords(chunk, wordSize);
33
+ chunk.length = 0;
34
+ };
35
+ for (const x of src) {
36
+ if (val === void 0) {
37
+ val = x;
38
+ } else if (x !== val) {
39
+ if (n > 0) {
40
+ writeRLE();
41
+ } else {
42
+ chunk.push(val);
43
+ if (chunk.length === rle3) {
44
+ writeChunk();
45
+ }
46
+ }
47
+ val = x;
48
+ } else {
49
+ if (chunk.length) {
50
+ writeChunk();
51
+ }
52
+ if (++n === rle3) {
53
+ n--;
54
+ writeRLE();
55
+ tail = i < n1;
56
+ }
57
+ }
58
+ if (i === n1) {
59
+ break;
60
+ }
61
+ i++;
62
+ }
63
+ if (chunk.length) {
64
+ chunk.push(val);
65
+ writeChunk();
66
+ } else if (tail) {
67
+ writeRLE();
68
+ }
69
+ return out.bytes();
70
+ };
71
+ const decodeBinary = (src) => {
72
+ const input = new BitInputStream(src);
73
+ const num = input.read(32);
74
+ const wordSize = input.read(5) + 1;
75
+ const rleSizes = [0, 0, 0, 0].map(() => input.read(4) + 1);
76
+ const out = __arrayForWordSize(wordSize, num);
77
+ let x, j;
78
+ for (let i = 0; i < num; ) {
79
+ x = input.readBit();
80
+ j = i + 1 + input.read(rleSizes[input.read(2)]);
81
+ if (x) {
82
+ out.fill(input.read(wordSize), i, j);
83
+ i = j;
84
+ } else {
85
+ for (; i < j; i++) {
86
+ out[i] = input.read(wordSize);
87
+ }
88
+ }
89
+ }
90
+ return out;
91
+ };
92
+ const __arrayForWordSize = (ws, n) => {
93
+ return new (ws < 9 ? Uint8Array : ws < 17 ? Uint16Array : Uint32Array)(n);
94
+ };
95
+ const encode = encodeBinary;
96
+ const decode = decodeBinary;
97
+ export {
98
+ decode,
99
+ decodeBinary,
100
+ encode,
101
+ encodeBinary
102
+ };
package/index.d.ts CHANGED
@@ -1,13 +1,3 @@
1
- export type RLESizes = [number, number, number, number];
2
- /**
3
- * Compresses input using dynamically sized RLE compression and returns
4
- * result as `Uint8Array`.
5
- *
6
- * @param src -
7
- * @param num - number of input words
8
- * @param wordSize - in bits, range 1 - 32
9
- * @param rleSizes - run-length group sizes (in bits, max. 16)
10
- */
11
- export declare const encode: (src: Iterable<number>, num: number, wordSize?: number, rleSizes?: RLESizes) => Uint8Array<ArrayBuffer>;
12
- export declare const decode: (src: Uint8Array) => Uint8Array<ArrayBuffer> | Uint16Array<ArrayBuffer> | Uint32Array<ArrayBuffer>;
1
+ export * from "./binary.js";
2
+ export * from "./simple.js";
13
3
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,98 +1,2 @@
1
- import { BitInputStream } from "@thi.ng/bitstream/input";
2
- import { BitOutputStream } from "@thi.ng/bitstream/output";
3
- import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
4
- const encode = (src, num, wordSize = 8, rleSizes = [3, 4, 8, 16]) => {
5
- (wordSize < 1 || wordSize > 32) && illegalArgs("word size (1-32 bits only)");
6
- const out = new BitOutputStream(Math.ceil(num * wordSize / 8) + 4 + 2 + 1).write(num, 32).write(wordSize - 1, 5);
7
- rleSizes.forEach((x) => {
8
- (x < 1 || x > 16) && illegalArgs("RLE repeat size (1-16 bits only)");
9
- out.write(x - 1, 4);
10
- });
11
- const [rle0, rle1, rle2, rle3] = rleSizes.map((x) => 1 << x);
12
- const chunk = [];
13
- const n1 = num - 1;
14
- let val;
15
- let tail = true;
16
- let n = 0;
17
- let i = 0;
18
- const writeRLE = () => {
19
- const t = n < rle0 ? 0 : n < rle1 ? 1 : n < rle2 ? 2 : 3;
20
- out.writeBit(1);
21
- out.write(t, 2);
22
- out.write(n, rleSizes[t]);
23
- out.write(val, wordSize);
24
- n = 0;
25
- };
26
- const writeChunk = () => {
27
- const m = chunk.length - 1;
28
- const t = m < rle0 ? 0 : m < rle1 ? 1 : m < rle2 ? 2 : 3;
29
- out.writeBit(0);
30
- out.write(t, 2);
31
- out.write(m, rleSizes[t]);
32
- out.writeWords(chunk, wordSize);
33
- chunk.length = 0;
34
- };
35
- for (const x of src) {
36
- if (val === void 0) {
37
- val = x;
38
- } else if (x !== val) {
39
- if (n > 0) {
40
- writeRLE();
41
- } else {
42
- chunk.push(val);
43
- if (chunk.length === rle3) {
44
- writeChunk();
45
- }
46
- }
47
- val = x;
48
- } else {
49
- if (chunk.length) {
50
- writeChunk();
51
- }
52
- if (++n === rle3) {
53
- n--;
54
- writeRLE();
55
- tail = i < n1;
56
- }
57
- }
58
- if (i === n1) {
59
- break;
60
- }
61
- i++;
62
- }
63
- if (chunk.length) {
64
- chunk.push(val);
65
- writeChunk();
66
- } else if (tail) {
67
- writeRLE();
68
- }
69
- return out.bytes();
70
- };
71
- const decode = (src) => {
72
- const input = new BitInputStream(src);
73
- const num = input.read(32);
74
- const wordSize = input.read(5) + 1;
75
- const rleSizes = [0, 0, 0, 0].map(() => input.read(4) + 1);
76
- const out = __arrayForWordSize(wordSize, num);
77
- let x, j;
78
- for (let i = 0; i < num; ) {
79
- x = input.readBit();
80
- j = i + 1 + input.read(rleSizes[input.read(2)]);
81
- if (x) {
82
- out.fill(input.read(wordSize), i, j);
83
- i = j;
84
- } else {
85
- for (; i < j; i++) {
86
- out[i] = input.read(wordSize);
87
- }
88
- }
89
- }
90
- return out;
91
- };
92
- const __arrayForWordSize = (ws, n) => {
93
- return new (ws < 9 ? Uint8Array : ws < 17 ? Uint16Array : Uint32Array)(n);
94
- };
95
- export {
96
- decode,
97
- encode
98
- };
1
+ export * from "./binary.js";
2
+ export * from "./simple.js";
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@thi.ng/rle-pack",
3
- "version": "3.1.117",
4
- "description": "Binary run-length encoding packer w/ flexible repeat bit widths",
3
+ "version": "3.2.0",
4
+ "description": "Binary run-length encoding packer w/ flexible repeat bit widths and a naive RLE encoder/decoder for arrays of arbitrary typed values",
5
5
  "type": "module",
6
6
  "module": "./index.js",
7
7
  "typings": "./index.d.ts",
8
8
  "sideEffects": false,
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/thi-ng/umbrella.git"
11
+ "url": "git+https://github.com/thi-ng/umbrella.git",
12
+ "directory": "packages/rle-pack"
12
13
  },
13
14
  "homepage": "https://thi.ng/rle-pack",
14
15
  "funding": [
@@ -39,8 +40,9 @@
39
40
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
41
  },
41
42
  "dependencies": {
42
- "@thi.ng/bitstream": "^2.4.39",
43
- "@thi.ng/errors": "^2.6.2"
43
+ "@thi.ng/api": "^8.12.14",
44
+ "@thi.ng/bitstream": "^2.4.41",
45
+ "@thi.ng/errors": "^2.6.3"
44
46
  },
45
47
  "devDependencies": {
46
48
  "esbuild": "^0.27.2",
@@ -69,6 +71,12 @@
69
71
  "exports": {
70
72
  ".": {
71
73
  "default": "./index.js"
74
+ },
75
+ "./binary": {
76
+ "default": "./binary.js"
77
+ },
78
+ "./simple": {
79
+ "default": "./simple.js"
72
80
  }
73
81
  },
74
82
  "thi.ng": {
@@ -80,5 +88,5 @@
80
88
  ],
81
89
  "year": 2017
82
90
  },
83
- "gitHead": "828ec2e9ffde7307231b03cff46598c0915b1857\n"
91
+ "gitHead": "b90a2f41eb0b3c89391bbb7cfff940192f23a83c\n"
84
92
  }
package/simple.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { Predicate2 } from "@thi.ng/api";
2
+ /**
3
+ * Performs basic RLE encoding on the given `src` array(like) input, using the
4
+ * optional `equiv` predicate to determine if the current value is a repetition
5
+ * of an earlier one (i.e consecutive values are considered repetitions as long
6
+ * as that predicate returns true). The default predicate is using `===` for
7
+ * comparison. Returns RLE result array of `[value1, count1, value2, count2...]`
8
+ *
9
+ * @example
10
+ * ```ts tangle:../export/encode-simple.ts
11
+ * import { encodeSimple } from "@thi.ng/rle-pack";
12
+ *
13
+ * const src = [..."aaaaaabbbbaaaxyxxx"];
14
+ *
15
+ * console.log(src);
16
+ * // ["a", "a", "a", "a", "a", "a", "b", "b", "b", "b", "a", "a", "a", "x", "y", "x", "x", "x"]
17
+ *
18
+ * console.log(encodeSimple(src));
19
+ * // ["a", 6, "b", 4, "a", 3, "x", 1, "y", 1, "x", 3]
20
+ * ```
21
+ *
22
+ * @param src
23
+ * @param equiv
24
+ */
25
+ export declare const encodeSimple: <T = any>(src: ArrayLike<T>, equiv?: Predicate2<T>) => any[];
26
+ /**
27
+ * Reverse op of {@link encodeSimple}. Takes an RLE array and returns restored
28
+ * original version. Throws an error if `src.length` is not even.
29
+ *
30
+ * @param src
31
+ */
32
+ export declare const decodeSimple: (src: any[]) => any[];
33
+ //# sourceMappingURL=simple.d.ts.map
package/simple.js ADDED
@@ -0,0 +1,32 @@
1
+ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
2
+ const encodeSimple = (src, equiv = (a, b) => a === b) => {
3
+ const result = [];
4
+ const n = src.length;
5
+ if (!n) return result;
6
+ let val = src[0];
7
+ let start = 0;
8
+ for (let i = 1; i < n; i++) {
9
+ if (!equiv(src[i], val)) {
10
+ result.push(val, i - start);
11
+ val = src[i];
12
+ start = i;
13
+ }
14
+ }
15
+ result.push(val, n - start);
16
+ return result;
17
+ };
18
+ const decodeSimple = (src) => {
19
+ const n = src.length;
20
+ if (n & 1) illegalArgs(`input length must be even`);
21
+ const result = [];
22
+ for (let i = 0; i < n; i += 2) {
23
+ const val = src[i];
24
+ const count = src[i + 1];
25
+ for (let j = 0; j < count; j++) result.push(val);
26
+ }
27
+ return result;
28
+ };
29
+ export {
30
+ decodeSimple,
31
+ encodeSimple
32
+ };