Skip to content

Commit 9b7a0ad

Browse files
authored
Merge pull request #97 from Brooooooklyn/electron-21
feat: provide copyOutputData to compatible with electron >= 21
2 parents 749bebb + 772abc9 commit 9b7a0ad

File tree

4 files changed

+117
-54
lines changed

4 files changed

+117
-54
lines changed

__test__/index.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,24 @@ test('should be able to compress Buffer', (t) => {
77
t.deepEqual(compressSync(fixture), Buffer.from([11, 40, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]))
88
})
99

10+
test('should be able to compress Buffer with copied option', (t) => {
11+
const fixture = 'hello world'
12+
t.deepEqual(
13+
compressSync(fixture, { copyOutputData: true }),
14+
Buffer.from([11, 40, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]),
15+
)
16+
})
17+
1018
test('compress should be async version of compressSync', async (t) => {
1119
const fixture = 'hello world 😂 🎧 🚀'
1220
t.deepEqual(await compress(fixture), compressSync(fixture))
1321
})
1422

23+
test('should be able to compress with copyOutputData', async (t) => {
24+
const fixture = 'hello world 😂 🎧 🚀'
25+
t.deepEqual(await compress(fixture, { copyOutputData: true }), compressSync(fixture))
26+
})
27+
1528
test('should be able to uncompress sync', (t) => {
1629
const fixture = 'hello world 😂 🎧 🚀'
1730
t.deepEqual(uncompressSync(compressSync(fixture)), Buffer.from(fixture))

index.d.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,32 @@
33

44
/* auto-generated by NAPI-RS */
55

6-
export interface Options {
6+
export interface DecOptions {
77
asBuffer?: boolean
8+
/**
9+
* do not use `create_external_buffer` to create the output buffer
10+
11+
* set this option to `true` will make the API slower
12+
13+
* for compatibility with electron >= 21
14+
15+
* see https://www.electronjs.org/blog/v8-memory-cage and https://github.com/electron/electron/issues/35801#issuecomment-1261206333
16+
*/
17+
copyOutputData?: boolean
818
}
9-
export function compressSync(input: Buffer | string): Buffer
10-
export function compress(input: string | Buffer, signal?: AbortSignal | undefined | null): Promise<Buffer>
11-
export function uncompressSync(input: string | Buffer, asBuffer?: Options | undefined | null): string | Buffer
12-
export function uncompress(input: string | Buffer, options?: Options | undefined | null, signal?: AbortSignal | undefined | null): Promise<string | Buffer>
19+
export interface EncOptions {
20+
/**
21+
* do not use `create_external_buffer` to create the output buffer
22+
23+
* for compatibility with electron >= 21
24+
25+
* set this option to `true` will make the API slower
26+
27+
* see https://www.electronjs.org/blog/v8-memory-cage and https://github.com/electron/electron/issues/35801#issuecomment-1261206333
28+
*/
29+
copyOutputData?: boolean
30+
}
31+
export function compressSync(input: string | Buffer, options?: EncOptions | undefined | null): Buffer
32+
export function compress(input: string | Buffer, options?: EncOptions | undefined | null, signal?: AbortSignal | undefined | null): Promise<Buffer>
33+
export function uncompressSync(input: string | Buffer, options?: DecOptions | undefined | null): string | Buffer
34+
export function uncompress(input: string | Buffer, options?: DecOptions | undefined | null, signal?: AbortSignal | undefined | null): Promise<string | Buffer>

memory-leak-detect.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async function detect(job) {
1515
displayMemoryUsageFromNode(initial)
1616
}
1717

18-
if (process.memoryUsage().rss - initial.rss >= 1024 * 1024 * 100) {
18+
if (process.memoryUsage().rss - initial.rss >= 1024 * 1024 * 200) {
1919
throw new Error('Memory limit reached')
2020
}
2121
}

src/lib.rs

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,22 @@ pub enum Data {
1919
}
2020

2121
#[napi(object)]
22-
pub struct Options {
22+
pub struct DecOptions {
2323
pub as_buffer: Option<bool>,
24+
/// do not use `create_external_buffer` to create the output buffer \n
25+
/// set this option to `true` will make the API slower \n
26+
/// for compatibility with electron >= 21 \n
27+
/// see https://www.electronjs.org/blog/v8-memory-cage and https://github.com/electron/electron/issues/35801#issuecomment-1261206333
28+
pub copy_output_data: Option<bool>,
29+
}
30+
31+
#[napi(object)]
32+
pub struct EncOptions {
33+
/// do not use `create_external_buffer` to create the output buffer \n
34+
/// for compatibility with electron >= 21 \n
35+
/// set this option to `true` will make the API slower \n
36+
/// see https://www.electronjs.org/blog/v8-memory-cage and https://github.com/electron/electron/issues/35801#issuecomment-1261206333
37+
pub copy_output_data: Option<bool>,
2438
}
2539

2640
impl TryFrom<Either<String, JsBuffer>> for Data {
@@ -34,9 +48,10 @@ impl TryFrom<Either<String, JsBuffer>> for Data {
3448
}
3549
}
3650

37-
struct Enc {
51+
pub struct Enc {
3852
inner: Encoder,
3953
data: Data,
54+
options: Option<EncOptions>,
4055
}
4156

4257
#[napi]
@@ -55,7 +70,17 @@ impl Task for Enc {
5570
}
5671

5772
fn resolve(&mut self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
58-
env.create_buffer_with_data(output).map(|b| b.into_raw())
73+
if self
74+
.options
75+
.as_ref()
76+
.and_then(|o| o.copy_output_data)
77+
.unwrap_or(false)
78+
{
79+
env.create_buffer_copy(output)
80+
} else {
81+
env.create_buffer_with_data(output)
82+
}
83+
.map(|b| b.into_raw())
5984
}
6085

6186
fn finally(&mut self, env: Env) -> Result<()> {
@@ -66,16 +91,16 @@ impl Task for Enc {
6691
}
6792
}
6893

69-
struct Dec {
94+
pub struct Dec {
7095
inner: Decoder,
7196
data: Data,
72-
as_buffer: bool,
97+
options: Option<DecOptions>,
7398
}
7499

75100
#[napi]
76101
impl Task for Dec {
77102
type Output = Vec<u8>;
78-
type JsValue = Either<String, Buffer>;
103+
type JsValue = Either<String, JsBuffer>;
79104

80105
fn compute(&mut self) -> Result<Self::Output> {
81106
self
@@ -87,9 +112,18 @@ impl Task for Dec {
87112
.map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))
88113
}
89114

90-
fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> {
91-
if self.as_buffer {
92-
Ok(Either::B(output.into()))
115+
fn resolve(&mut self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
116+
let opt_ref = self.options.as_ref();
117+
if opt_ref.and_then(|o| o.as_buffer).unwrap_or(true) {
118+
if opt_ref.and_then(|o| o.copy_output_data).unwrap_or(false) {
119+
Ok(Either::B(
120+
env.create_buffer_copy(output).map(|o| o.into_raw())?,
121+
))
122+
} else {
123+
Ok(Either::B(
124+
env.create_buffer_with_data(output).map(|o| o.into_raw())?,
125+
))
126+
}
93127
} else {
94128
Ok(Either::A(String::from_utf8(output).map_err(|e| {
95129
Error::new(Status::GenericFailure, format!("{}", e))
@@ -106,26 +140,32 @@ impl Task for Dec {
106140
}
107141

108142
#[napi]
109-
pub fn compress_sync(input: Either<Buffer, String>) -> Result<Buffer> {
110-
let mut enc = Encoder::new();
111-
enc
112-
.compress_vec(match input {
113-
Either::A(ref b) => b.as_ref(),
114-
Either::B(ref s) => s.as_bytes(),
115-
})
116-
.map_err(|e| Error::new(napi::Status::GenericFailure, format!("{}", e)))
117-
.map(|v| v.into())
143+
pub fn compress_sync(
144+
env: Env,
145+
input: Either<String, JsBuffer>,
146+
options: Option<EncOptions>,
147+
) -> Result<JsBuffer> {
148+
let enc = Encoder::new();
149+
let mut encoder = Enc {
150+
inner: enc,
151+
data: Data::try_from(input)?,
152+
options,
153+
};
154+
let output = encoder.compute()?;
155+
encoder.resolve(env, output)
118156
}
119157

120158
#[napi]
121-
fn compress(
159+
pub fn compress(
122160
input: Either<String, JsBuffer>,
161+
options: Option<EncOptions>,
123162
signal: Option<AbortSignal>,
124163
) -> Result<AsyncTask<Enc>> {
125164
let enc = Encoder::new();
126165
let encoder = Enc {
127166
inner: enc,
128167
data: Data::try_from(input)?,
168+
options,
129169
};
130170
match signal {
131171
Some(s) => Ok(AsyncTask::with_signal(encoder, s)),
@@ -134,44 +174,32 @@ fn compress(
134174
}
135175

136176
#[napi]
137-
fn uncompress_sync(
138-
input: Either<String, Buffer>,
139-
as_buffer: Option<Options>,
140-
) -> Result<Either<String, Buffer>> {
141-
let as_buffer = as_buffer.and_then(|o| o.as_buffer).unwrap_or(true);
142-
let mut dec = Decoder::new();
143-
dec
144-
.decompress_vec(match input {
145-
Either::A(ref s) => s.as_bytes(),
146-
Either::B(ref b) => b.as_ref(),
147-
})
148-
.map_err(|e| Error::new(napi::Status::GenericFailure, format!("{}", e)))
149-
.and_then(|d| {
150-
if as_buffer {
151-
Ok(Either::B(d.into()))
152-
} else {
153-
Ok(Either::A(String::from_utf8(d).map_err(|e| {
154-
Error::new(Status::GenericFailure, format!("{}", e))
155-
})?))
156-
}
157-
})
177+
pub fn uncompress_sync(
178+
env: Env,
179+
input: Either<String, JsBuffer>,
180+
options: Option<DecOptions>,
181+
) -> Result<Either<String, JsBuffer>> {
182+
let dec = Decoder::new();
183+
let mut decoder = Dec {
184+
inner: dec,
185+
data: Data::try_from(input)?,
186+
options,
187+
};
188+
let output = decoder.compute()?;
189+
decoder.resolve(env, output)
158190
}
159191

160192
#[napi]
161-
fn uncompress(
193+
pub fn uncompress(
162194
input: Either<String, JsBuffer>,
163-
options: Option<Options>,
195+
options: Option<DecOptions>,
164196
signal: Option<AbortSignal>,
165197
) -> Result<AsyncTask<Dec>> {
166-
let as_buffer = options.and_then(|o| o.as_buffer).unwrap_or(true);
167198
let dec = Decoder::new();
168199
let decoder = Dec {
169200
inner: dec,
170201
data: Data::try_from(input)?,
171-
as_buffer,
202+
options,
172203
};
173-
match signal {
174-
Some(s) => Ok(AsyncTask::with_signal(decoder, s)),
175-
None => Ok(AsyncTask::new(decoder)),
176-
}
204+
Ok(AsyncTask::with_optional_signal(decoder, signal))
177205
}

0 commit comments

Comments
 (0)