@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 +32 -0
- package/README.md +125 -60
- package/binding.gyp +10 -3
- package/index.d.ts +33 -0
- package/index.js +4 -5
- package/package.json +46 -24
- package/prebuilds/darwin-arm64/@trkyshorty+node-lzf.node +0 -0
- package/prebuilds/linux-arm64/@trkyshorty+node-lzf.node +0 -0
- package/prebuilds/linux-x64/@trkyshorty+node-lzf.node +0 -0
- package/prebuilds/win32-x64/@trkyshorty+node-lzf.node +0 -0
- package/src/lzf/lzfP.h +16 -2
- package/src/lzf.cc +267 -61
- package/benchmark/benchmark.js +0 -69
- package/benchmark/data/100.txt +0 -1
- package/benchmark/data/1000.txt +0 -3
- package/benchmark/data/10000.txt +0 -39
- package/benchmark/data/100000.txt +0 -247
- package/test/test.js +0 -27
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
34
|
+
const lzf = require('@trkyshorty/node-lzf');
|
|
19
35
|
|
|
36
|
+
const data = Buffer.from('some data to compress');
|
|
20
37
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
38
|
+
// sync — runs on the calling thread
|
|
39
|
+
const compressed = lzf.compress(data);
|
|
40
|
+
const restored = lzf.decompress(compressed, data.length);
|
|
24
41
|
|
|
25
|
-
//
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
var loremCompressed = lzf.compress(loremBuffer);
|
|
47
|
+
#### TypeScript
|
|
30
48
|
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
58
|
+
const data: Buffer = Buffer.from('some data to compress');
|
|
37
59
|
|
|
38
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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 -
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
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
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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
|
-
#
|
|
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
|
-
|
|
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>
|