@trkyshorty/node-lzf 1.0.0 → 1.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/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2011 Ian Babrou <ibobrik@gmail.com>
4
+ Copyright (c) 2025 Türkay Tanrikulu <trky.shorty@gmail.com>
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice,
10
+ this list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ ----------------------------------------------------------------------------
29
+
30
+ This package bundles liblzf by Marc Alexander Lehmann (src/lzf/), which is
31
+ distributed under the BSD 2-Clause license (or alternatively the GPL v2 or
32
+ later). See the license headers in src/lzf/*.h and src/lzf/*.cc for details.
package/README.md CHANGED
@@ -4,81 +4,146 @@
4
4
 
5
5
  LZF advantages:
6
6
 
7
- * Small code size (less then 500 lines including header files and docs).
8
- * Very fast compression speeds, rivaling a straight copy loop, especially for decompression which is basically at (unoptimized) memcpy-speed. Compression speed can be increased by 20% by sacrificing a few percent of compression ratio.
9
- * Mediocre compression ratios - you can usually expect about 40-50% compression for typical binary data
10
- * Easy to use (just two functions, no state attached)
11
- * Highly portable (written in C)
12
- * Tunable, see the file lzfP.h in the distribution, to tailor liblzf to your needs. The generated compressed blocks can be decompressed by any liblzf version regardless of the options used to compress.
13
- * Freely usable (BSD-type-license)
7
+ - Small code size (less then 500 lines including header files and docs).
8
+ - Very fast compression speeds, rivaling a straight copy loop, especially for decompression which is basically at (unoptimized) memcpy-speed. Compression speed can be increased by 20% by sacrificing a few percent of compression ratio.
9
+ - Mediocre compression ratios - you can usually expect about 40-50% compression for typical binary data
10
+ - Easy to use (just two functions, no state attached)
11
+ - Highly portable (written in C)
12
+ - Tunable, see the file lzfP.h in the distribution, to tailor liblzf to your needs. The generated compressed blocks can be decompressed by any liblzf version regardless of the options used to compress.
13
+ - Freely usable (BSD-type-license)
14
+
15
+ ### Install
16
+
17
+ ```bash
18
+ npm install @trkyshorty/node-lzf
19
+ ```
20
+
21
+ The module is N-API based and ships prebuilt binaries (`prebuilds/`) for
22
+ linux-x64, linux-arm64, win32-x64 and darwin-arm64 — no compiler toolchain
23
+ is needed on those platforms. On other platforms it falls back to compiling
24
+ from source via `node-gyp-build` (requires a C++ toolchain).
25
+
26
+ Prebuilds are produced by the `prebuild` GitHub Actions workflow
27
+ (`npm run prebuild` runs `prebuildify --napi --strip` for the current
28
+ platform). Before `npm publish`, download the merged `prebuilds` artifact
29
+ from the workflow run and place it in the package root as `prebuilds/`.
14
30
 
15
31
  ### Usage
16
32
 
17
33
  ```javascript
18
- var lzf = require("lzf");
34
+ const lzf = require('@trkyshorty/node-lzf');
19
35
 
36
+ const data = Buffer.from('some data to compress');
20
37
 
21
- var lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." +
22
- "Curabitur volutpat, nulla nec egestas semper," +
23
- "ante dui tristique nibh, quis feugiat.";
38
+ // sync — runs on the calling thread
39
+ const compressed = lzf.compress(data);
40
+ const restored = lzf.decompress(compressed, data.length);
24
41
 
25
- // create buffer of your data
26
- var loremBuffer = new Buffer(lorem);
42
+ // async — runs on the libuv thread pool, never blocks the event loop
43
+ const compressed2 = await lzf.compressAsync(data);
44
+ const restored2 = await lzf.decompressAsync(compressed2, data.length);
45
+ ```
27
46
 
28
- // compress your buffer and get another buffer
29
- var loremCompressed = lzf.compress(loremBuffer);
47
+ #### TypeScript
30
48
 
31
- // decompress compressed buffer
32
- var loremDecompressed = lzf.decompress(loremCompressed);
49
+ Type definitions are bundled, no `@types` package is needed. The module is
50
+ CommonJS (`export =`), so import it as a namespace — or as a default import
51
+ when `esModuleInterop` is enabled:
33
52
 
34
- ```
53
+ ```typescript
54
+ import * as lzf from '@trkyshorty/node-lzf';
55
+ // with "esModuleInterop": true you can also write:
56
+ // import lzf from '@trkyshorty/node-lzf';
35
57
 
36
- ### Benchmarks
58
+ const data: Buffer = Buffer.from('some data to compress');
37
59
 
38
- Benchmarks against [compress-buffer](https://github.com/egorFiNE/node-compress-buffer) library. Just to be short: it's faster.
60
+ // sync
61
+ const compressed: Buffer = lzf.compress(data);
62
+ const restored: Buffer = lzf.decompress(compressed, data.length);
39
63
 
64
+ // async
65
+ async function roundtrip(input: Buffer): Promise<Buffer> {
66
+ const packed = await lzf.compressAsync(input);
67
+ return lzf.decompressAsync(packed, input.length);
68
+ }
40
69
  ```
41
- Testing compression at 100 bytes
42
- lzf-compress x 139,495 ops/sec ±11.88% (58 runs sampled)
43
- zlib-compress x 29,879 ops/sec ±6.88% (88 runs sampled)
44
- Fastest is lzf-compress
45
-
46
- Testing decompression at 100 bytes
47
- lzf-decompress x 36,574 ops/sec ±7.74% (87 runs sampled)
48
- zlib-decompress x 26,153 ops/sec ±6.92% (89 runs sampled)
49
- Fastest is lzf-decompress
50
-
51
- Testing compression at 1000 bytes
52
- lzf-compress x 57,345 ops/sec ±9.49% (81 runs sampled)
53
- zlib-compress x 11,828 ops/sec ±3.93% (60 runs sampled)
54
- Fastest is lzf-compress
55
-
56
- Testing decompression at 1000 bytes
57
- lzf-decompress x 29,221 ops/sec ±7.06% (89 runs sampled)
58
- zlib-decompress x 19,351 ops/sec ±4.77% (92 runs sampled)
59
- Fastest is lzf-decompress
60
-
61
- Testing compression at 10000 bytes
62
- lzf-compress x 10,512 ops/sec ±3.26% (92 runs sampled)
63
- zlib-compress x 1,850 ops/sec ±0.20% (73 runs sampled)
64
- Fastest is lzf-compress
65
-
66
- Testing decompression at 10000 bytes
67
- lzf-decompress x 12,487 ops/sec ±3.85% (91 runs sampled)
68
- zlib-decompress x 6,939 ops/sec ±3.14% (93 runs sampled)
69
- Fastest is lzf-decompress
70
-
71
- Testing compression at 100000 bytes
72
- lzf-compress x 1,628 ops/sec ±1.50% (96 runs sampled)
73
- zlib-compress x 132 ops/sec ±0.13% (93 runs sampled)
74
- Fastest is lzf-compress
75
-
76
- Testing decompression at 100000 bytes
77
- lzf-decompress x 2,472 ops/sec ±1.09% (91 runs sampled)
78
- zlib-decompress x 1,430 ops/sec ±0.73% (95 runs sampled)
79
- Fastest is lzf-decompress
70
+
71
+ ### API
72
+
73
+ TypeScript definitions are bundled (`index.d.ts`).
74
+
75
+ #### `compress(data: Buffer): Buffer`
76
+
77
+ Returns an LZF-compressed Buffer sized exactly to the result. Throws
78
+ `TypeError` if `data` is not a Buffer, is empty, or exceeds 1 GiB.
79
+ Note: incompressible input can grow slightly (up to ~104%).
80
+
81
+ #### `decompress(data: Buffer, expectedLength: number): Buffer`
82
+
83
+ Returns the decompressed Buffer. `expectedLength` is **required** — pass the
84
+ exact decompressed size (or an upper bound); the result is shrunk to the
85
+ actual size. Throws `TypeError`/`RangeError` for invalid arguments
86
+ (`expectedLength` must be an integer between 1 and 1 GiB) and `Error` with
87
+ a descriptive message when the input is corrupted (`corrupted input`) or
88
+ `expectedLength` is too small (`expected length too small`). The
89
+ decompressor is safe on untrusted input: corrupt streams throw instead of
90
+ reading or writing out of bounds.
91
+
92
+ > **v1.1.0 note:** older versions allowed omitting `expectedLength` and
93
+ > silently allocated a 999 MB scratch buffer per call — that default has
94
+ > been removed.
95
+
96
+ #### `compressAsync(data: Buffer): Promise<Buffer>` / `decompressAsync(data: Buffer, expectedLength: number): Promise<Buffer>`
97
+
98
+ Same semantics as the sync variants, but the (de)compression runs on the
99
+ libuv thread pool and the returned promise rejects with the errors the sync
100
+ variants would throw. Prefer these for payloads larger than a few hundred
101
+ KB on latency-sensitive servers.
102
+
103
+ Compressed output is a valid LZF stream decodable by any liblzf build.
104
+ The exact compressed bytes are not guaranteed to be identical across calls
105
+ (liblzf's hash table is intentionally left uninitialized for speed) — only
106
+ the roundtrip contract holds.
107
+
108
+ ### Benchmarks
109
+
110
+ `npm run bench` compares against node's built-in zlib (`deflateRaw`,
111
+ levels 1 and 6) on deterministic datasets. Numbers below from a Windows
112
+ x64 machine, Node 24 (`ratio` = compressed/original; lower is better):
113
+
114
+ ```text
115
+ dataset codec ratio comp ms comp MB/s decomp ms decomp MB/s
116
+ text 100KB lzf 40% 0.158 433 0.094 726
117
+ text 100KB zlib-1 30% 0.354 194 0.122 562
118
+ text 100KB zlib-6 27% 1.421 48 0.120 570
119
+ json 64KB lzf 37% 0.039 917 0.037 963
120
+ json 64KB zlib-1 24% 0.122 291 0.054 658
121
+ json 64KB zlib-6 19% 0.486 73 0.049 722
122
+ json 2MB lzf 36% 1.837 617 1.156 980
123
+ json 2MB zlib-1 24% 4.383 259 1.511 750
124
+ json 2MB zlib-6 18% 17.541 65 1.311 865
125
+ binary 4KB lzf 79% 0.008 509 0.004 904
126
+ binary 4KB zlib-1 72% 0.039 100 0.011 346
127
+ binary 64KB lzf 76% 0.127 493 0.049 1286
128
+ binary 64KB zlib-1 71% 0.660 95 0.127 492
129
+ random 1MB lzf 103% 2.816 355 0.415 2410
130
+ random 1MB zlib-1 100% 15.080 66 0.385 2598
131
+ zeros 1MB lzf 1% 0.273 3660 1.531 653
132
+ zeros 1MB zlib-1 0% 0.350 2861 0.473 2115
80
133
  ```
81
134
 
135
+ In short: LZF compresses 2–10× faster than zlib at its fastest level, at
136
+ the cost of a worse ratio. Pick LZF when compression latency matters more
137
+ than size (hot network paths); pick zlib/brotli for cold storage.
138
+
82
139
  ---
140
+
83
141
  ### Authors
84
- - Ian Babrou (ibobrik@gmail.com)
142
+
143
+ - Ian Babrou (`ibobrik@gmail.com`) — original author
144
+ - Türkay Tanrikulu (`trky.shorty@gmail.com`) — fork maintainer
145
+
146
+ ### License
147
+
148
+ BSD-2-Clause, see [LICENSE](LICENSE). Bundles [liblzf](http://oldhome.schmorp.de/marc/liblzf.html)
149
+ by Marc Alexander Lehmann (BSD-2-Clause / GPL dual-licensed).
package/binding.gyp CHANGED
@@ -8,14 +8,21 @@
8
8
  "src/lzf/lzf_d.cc"
9
9
  ],
10
10
  "include_dirs": [
11
- "<!(node -e \"require('nan')\")",
11
+ "<!(node -p \"require('node-addon-api').include_dir\")",
12
12
  "src/lzf"
13
13
  ],
14
+ "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
15
+ # -Wno-implicit-fallthrough: upstream liblzf (src/lzf/lzf_d.cc) deliberately unrolls its
16
+ # copy loops with "case" fallthrough (Duff's device); the warning gcc enables with -Wextra
17
+ # is not a bug here. The vendored code and our own code (src/lzf.cc) build as a single
18
+ # target, so the suppression is target-wide; we do not rewrite the algorithm. MSVC does
19
+ # not emit this warning, so no flag is added on Windows.
14
20
  "conditions": [
15
21
  [
16
22
  'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"',
17
23
  {
18
- "cflags": [ "-O3" ],
24
+ "cflags": [ "-O3", "-Wno-implicit-fallthrough" ],
25
+ "cflags_cc": [ "-O3" ],
19
26
  "conditions": [
20
27
  ["target_arch=='x64'", { "cflags": [ "-fPIC" ] }]
21
28
  ]
@@ -25,7 +32,7 @@
25
32
  "OS=='mac'",
26
33
  {
27
34
  "xcode_settings": {
28
- "OTHER_CFLAGS": [ "-O3" ]
35
+ "OTHER_CFLAGS": [ "-O3", "-Wno-implicit-fallthrough" ]
29
36
  }
30
37
  }
31
38
  ],
package/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /// <reference types="node" />
2
+
3
+ declare namespace lzf {
4
+ /**
5
+ * Compresses a Buffer with LZF. Throws TypeError on invalid input
6
+ * (non-Buffer, empty, or larger than 1 GiB). Runs synchronously on
7
+ * the calling thread.
8
+ */
9
+ function compress(data: Buffer): Buffer;
10
+
11
+ /**
12
+ * Compresses a Buffer with LZF on the libuv thread pool without
13
+ * blocking the event loop. Rejects with the same errors compress()
14
+ * throws.
15
+ */
16
+ function compressAsync(data: Buffer): Promise<Buffer>;
17
+
18
+ /**
19
+ * Decompresses an LZF-compressed Buffer. expectedLength is the exact
20
+ * (or an upper bound of the) decompressed size, between 1 and 1 GiB.
21
+ * Throws TypeError/RangeError on invalid arguments and Error when the
22
+ * input is corrupted or expectedLength is too small.
23
+ */
24
+ function decompress(data: Buffer, expectedLength: number): Buffer;
25
+
26
+ /**
27
+ * Decompresses on the libuv thread pool without blocking the event
28
+ * loop. Rejects with the same errors decompress() throws.
29
+ */
30
+ function decompressAsync(data: Buffer, expectedLength: number): Promise<Buffer>;
31
+ }
32
+
33
+ export = lzf;
package/index.js CHANGED
@@ -1,5 +1,4 @@
1
- try {
2
- module.exports = require('./build/default/lzf.node');
3
- } catch(e) {
4
- module.exports = require('./build/Release/lzf.node');
5
- }
1
+ // Resolves a bundled prebuild for the current platform/arch when available
2
+ // (prebuilds/<platform>-<arch>/node.napi.node), otherwise falls back to a
3
+ // locally compiled build/Release/lzf.node.
4
+ module.exports = require('node-gyp-build')(__dirname);
package/package.json CHANGED
@@ -1,26 +1,48 @@
1
1
  {
2
- "name": "@trkyshorty/node-lzf",
3
- "version": "1.0.0",
4
- "description": "lzf compression module for nodejs (Topface/node-lzf fork with mem-leak fix)",
5
- "keywords": ["lzf", "compression", "buffer"],
6
- "author": "Ian Babrou <ibobrik@gmail.com>",
7
- "engines": [
8
- "node"
9
- ],
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/trkyshorty/node-lzf.git"
13
- },
14
- "dependencies": {
15
- "nan": "^2.18.0"
16
- },
17
- "directories": {
18
- "lib": "./lib"
19
- },
20
- "main": "./index",
21
- "gypfile": true,
22
- "scripts": {
23
- "test": "node test/test.js"
24
- },
25
- "license": "BSD-2-Clause"
2
+ "name": "@trkyshorty/node-lzf",
3
+ "version": "1.2.0",
4
+ "description": "lzf compression module for nodejs (Topface/node-lzf fork)",
5
+ "keywords": [
6
+ "lzf",
7
+ "compression",
8
+ "buffer",
9
+ "prebuilds",
10
+ "napi"
11
+ ],
12
+ "author": "Türkay TANRIKULU <trky.shorty@gmail.com>",
13
+ "contributors": [
14
+ "Ian Babrou <ibobrik@gmail.com>"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/trkyshorty/node-lzf.git"
22
+ },
23
+ "dependencies": {
24
+ "node-addon-api": "^8.3.0",
25
+ "node-gyp-build": "^4.8.4"
26
+ },
27
+ "devDependencies": {
28
+ "prebuildify": "^6.0.1"
29
+ },
30
+ "main": "./index.js",
31
+ "types": "./index.d.ts",
32
+ "gypfile": true,
33
+ "files": [
34
+ "index.js",
35
+ "index.d.ts",
36
+ "binding.gyp",
37
+ "src/",
38
+ "prebuilds/",
39
+ "README.md"
40
+ ],
41
+ "scripts": {
42
+ "install": "node-gyp-build",
43
+ "prebuild": "prebuildify --napi --strip",
44
+ "test": "node --test test/test.js",
45
+ "bench": "node benchmark/benchmark.js"
46
+ },
47
+ "license": "BSD-2-Clause"
26
48
  }
package/src/lzf/lzfP.h CHANGED
@@ -78,8 +78,17 @@
78
78
  /*
79
79
  * Unconditionally aligning does not cost very much, so do it if unsure
80
80
  */
81
+ /*
82
+ * node-lzf: upstream used `# define STRICT_ALIGN !(defined(__i386) || defined (__amd64))`;
83
+ * a `defined` operator produced by macro expansion is undefined behavior in the standard
84
+ * (gcc/clang -Wexpansion-to-defined). We select the same value directly with #if.
85
+ */
81
86
  #ifndef STRICT_ALIGN
82
- # define STRICT_ALIGN !(defined(__i386) || defined (__amd64))
87
+ # if defined(__i386) || defined (__amd64)
88
+ # define STRICT_ALIGN 0
89
+ # else
90
+ # define STRICT_ALIGN 1
91
+ # endif
83
92
  #endif
84
93
 
85
94
  /*
@@ -142,7 +151,12 @@ using namespace std;
142
151
 
143
152
  #ifndef LZF_USE_OFFSETS
144
153
  # if defined (WIN32)
145
- # define LZF_USE_OFFSETS defined(_M_X64)
154
+ /* node-lzf: replaces upstream's macro-expanded `defined(_M_X64)`; same rationale as STRICT_ALIGN. */
155
+ # if defined(_M_X64)
156
+ # define LZF_USE_OFFSETS 1
157
+ # else
158
+ # define LZF_USE_OFFSETS 0
159
+ # endif
146
160
  # else
147
161
  # if __cplusplus > 199711L
148
162
  # include <cstdint>