Skip to content

Commit 6f788e2

Browse files
committed
Return result code and input offset
Other than keep them in the stream. New returned struct is added. Also change the error type to JsError. See wasm-bindgen/wasm-bindgen#2710 .
1 parent d2f7278 commit 6f788e2

File tree

2 files changed

+87
-79
lines changed

2 files changed

+87
-79
lines changed

example/web-next-transformstream/app/utils.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,28 @@ const brotliCompressTransformerBuilder: (
1818
stream: new brotliWasm.CompressStream(quality),
1919
start() {},
2020
transform(chunk, controller) {
21+
let resultCode
22+
let inputOffset = 0
2123
do {
22-
const inputOffset = this.stream.last_input_offset()
2324
const input = chunk.slice(inputOffset)
24-
const output = this.stream.compress(input, this.outputSize)
25-
controller.enqueue(output)
26-
} while (this.stream.result() === brotliWasm.BrotliStreamResult.NeedsMoreOutput)
27-
if (this.stream.result() !== brotliWasm.BrotliStreamResult.NeedsMoreInput) {
28-
controller.error(`Brotli compression failed when transforming with error code ${this.stream.result()}`)
25+
const result = this.stream.compress(input, this.outputSize)
26+
controller.enqueue(result.buf)
27+
resultCode = result.code
28+
inputOffset += result.input_offset
29+
} while (resultCode === brotliWasm.BrotliStreamResultCode.NeedsMoreOutput)
30+
if (resultCode !== brotliWasm.BrotliStreamResultCode.NeedsMoreInput) {
31+
controller.error(`Brotli compression failed when transforming with code ${resultCode}`)
2932
}
3033
},
3134
flush(controller) {
35+
let resultCode
3236
do {
33-
const output = this.stream.compress(undefined, this.outputSize)
34-
controller.enqueue(output)
35-
} while (this.stream.result() === brotliWasm.BrotliStreamResult.NeedsMoreOutput)
36-
if (this.stream.result() !== brotliWasm.BrotliStreamResult.ResultSuccess) {
37-
controller.error(`Brotli compression failed when flushing with error code ${this.stream.result()}`)
37+
const result = this.stream.compress(undefined, this.outputSize)
38+
controller.enqueue(result.buf)
39+
resultCode = result.code
40+
} while (resultCode === brotliWasm.BrotliStreamResultCode.NeedsMoreOutput)
41+
if (resultCode !== brotliWasm.BrotliStreamResultCode.ResultSuccess) {
42+
controller.error(`Brotli compression failed when flushing with code ${resultCode}`)
3843
}
3944
},
4045
})

src/stream.rs

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,33 @@ use brotli::enc::StandardAlloc; // Re-exported from alloc_stdlib::StandardAlloc
88
use brotli::{self, BrotliDecompressStream, BrotliResult, BrotliState};
99
use wasm_bindgen::prelude::*;
1010

11+
/// Returned by every successful (de)compression.
12+
//
13+
// Use `getter_with_clone` because `buf` does not implement `Copy`.
14+
#[wasm_bindgen(getter_with_clone)]
15+
pub struct BrotliStreamResult {
16+
/// Result code.
17+
///
18+
/// See [`BrotliStreamResultCode`] for available values.
19+
///
20+
/// When error, the error code is not passed here but rather goes to `Err`.
21+
pub code: BrotliStreamResultCode,
22+
/// Output buffer
23+
pub buf: Box<[u8]>,
24+
/// Consumed bytes of the input buffer
25+
pub input_offset: usize,
26+
}
27+
1128
#[wasm_bindgen]
12-
#[repr(i32)]
13-
pub enum BrotliStreamResult {
14-
/// The stream is just initialized and no input is provided currently.
15-
/// `BrotliResult` uses `ResultFailure = 0`, but as we will convert `ResultFailure` to a negative actual error code,
16-
/// 0 is reused as no input currently.
17-
/// As for Brotli compressing, since offical API does not provide a way to retrieve a detailed error code, -1 is used.
18-
Init = 0,
29+
/// Same as [`brotli::BrotliResult`] except [`brotli::BrotliResult::ResultFailure`].
30+
///
31+
/// Always `> 0`.
32+
///
33+
/// `ResultFailure` is removed
34+
/// because we will convert the failure to an actual negative error code (if available) and pass it elsewhere.
35+
#[repr(usize)]
36+
#[derive(Copy, Clone)]
37+
pub enum BrotliStreamResultCode {
1938
ResultSuccess = 1,
2039
NeedsMoreInput = 2,
2140
NeedsMoreOutput = 3,
@@ -24,9 +43,7 @@ pub enum BrotliStreamResult {
2443
#[wasm_bindgen]
2544
pub struct CompressStream {
2645
state: BrotliEncoderStateStruct<StandardAlloc>,
27-
result: i32,
2846
total_out: usize,
29-
last_input_offset: usize,
3047
}
3148

3249
impl Drop for CompressStream {
@@ -54,17 +71,15 @@ impl CompressStream {
5471
}
5572
Self {
5673
state,
57-
result: BrotliStreamResult::Init as i32,
5874
total_out: 0,
59-
last_input_offset: 0,
6075
}
6176
}
6277

6378
pub fn compress(
6479
&mut self,
6580
input_opt: Option<Box<[u8]>>,
6681
output_size: usize,
67-
) -> Result<Box<[u8]>, JsValue> {
82+
) -> Result<BrotliStreamResult, JsError> {
6883
let mut nop_callback = |_data: &mut brotli::interface::PredictionModeContextMap<
6984
brotli::interface::InputReferenceMut,
7085
>,
@@ -97,22 +112,22 @@ impl CompressStream {
97112
if ret != 0 {
98113
if available_in == 0 {
99114
output.truncate(output_offset);
100-
self.result = BrotliStreamResult::NeedsMoreInput as i32;
101-
self.last_input_offset = input.len();
102-
Ok(output.into_boxed_slice())
115+
Ok(BrotliStreamResult {
116+
code: BrotliStreamResultCode::NeedsMoreInput,
117+
buf: output.into_boxed_slice(),
118+
input_offset,
119+
})
103120
} else if available_out == 0 {
104-
self.result = BrotliStreamResult::NeedsMoreOutput as i32;
105-
self.last_input_offset = input_offset;
106-
Ok(output.into_boxed_slice())
121+
Ok(BrotliStreamResult {
122+
code: BrotliStreamResultCode::NeedsMoreOutput,
123+
buf: output.into_boxed_slice(),
124+
input_offset,
125+
})
107126
} else {
108-
self.result = -1;
109-
self.last_input_offset = 0;
110-
Err(JsValue::from_str("Unexpected Brotli streaming compress: both available_in & available_out are not 0 after a successful processing"))
127+
Err(JsError::new("Unexpected Brotli streaming compress: both available_in & available_out are not 0 after a successful processing"))
111128
}
112129
} else {
113-
self.result = -1;
114-
self.last_input_offset = 0;
115-
Err(JsValue::from_str(
130+
Err(JsError::new(
116131
"Brotli streaming compress failed: When processing",
117132
))
118133
}
@@ -135,43 +150,38 @@ impl CompressStream {
135150
&mut nop_callback,
136151
);
137152
if ret == 0 {
138-
self.result = -1;
139-
return Err(JsValue::from_str(
153+
return Err(JsError::new(
140154
"Brotli streaming compress failed: When finishing",
141155
));
142156
}
143157
}
144-
self.result = if available_out == 0 {
145-
BrotliStreamResult::NeedsMoreOutput as i32
158+
if available_out == 0 {
159+
Ok(BrotliStreamResult {
160+
code: BrotliStreamResultCode::NeedsMoreOutput,
161+
buf: output.into_boxed_slice(),
162+
input_offset,
163+
})
146164
} else {
147165
output.truncate(output_offset);
148-
BrotliStreamResult::ResultSuccess as i32
149-
};
150-
self.last_input_offset = 0;
151-
Ok(output.into_boxed_slice())
166+
Ok(BrotliStreamResult {
167+
code: BrotliStreamResultCode::ResultSuccess,
168+
buf: output.into_boxed_slice(),
169+
input_offset,
170+
})
171+
}
152172
}
153173
}
154174
}
155175

156176
pub fn total_out(&self) -> usize {
157177
self.total_out
158178
}
159-
160-
pub fn result(&self) -> i32 {
161-
self.result
162-
}
163-
164-
pub fn last_input_offset(&self) -> usize {
165-
self.last_input_offset
166-
}
167179
}
168180

169181
#[wasm_bindgen]
170182
pub struct DecompressStream {
171183
state: BrotliState<StandardAlloc, StandardAlloc, StandardAlloc>,
172-
result: i32,
173184
total_out: usize,
174-
last_input_offset: usize,
175185
}
176186

177187
#[wasm_bindgen]
@@ -183,17 +193,15 @@ impl DecompressStream {
183193
let alloc = StandardAlloc::default();
184194
Self {
185195
state: BrotliState::new(alloc, alloc, alloc),
186-
result: BrotliStreamResult::Init as i32,
187196
total_out: 0,
188-
last_input_offset: 0,
189197
}
190198
}
191199

192200
pub fn decompress(
193201
&mut self,
194202
input: Box<[u8]>,
195203
output_size: usize,
196-
) -> Result<Box<[u8]>, JsValue> {
204+
) -> Result<BrotliStreamResult, JsError> {
197205
let mut output = vec![0; output_size];
198206
let mut available_in = input.len();
199207
let mut input_offset = 0;
@@ -211,42 +219,37 @@ impl DecompressStream {
211219
) {
212220
BrotliResult::ResultFailure => {
213221
// It should be a negative error code
214-
self.result = self.state.error_code as i32;
215-
self.last_input_offset = 0;
216-
Err(JsValue::from_str(&format!(
222+
let err_code = self.state.error_code as i32;
223+
Err(JsError::new(&format!(
217224
"Brotli streaming decompress failed: Error code {}",
218-
self.result
225+
err_code
219226
)))
220227
}
221-
BrotliResult::NeedsMoreOutput => {
222-
self.result = BrotliStreamResult::NeedsMoreOutput as i32;
223-
self.last_input_offset = input_offset;
224-
Ok(output.into_boxed_slice())
225-
}
228+
BrotliResult::NeedsMoreOutput => Ok(BrotliStreamResult {
229+
code: BrotliStreamResultCode::NeedsMoreOutput,
230+
buf: output.into_boxed_slice(),
231+
input_offset,
232+
}),
226233
BrotliResult::ResultSuccess => {
227234
output.truncate(output_offset);
228-
self.result = BrotliStreamResult::ResultSuccess as i32;
229-
self.last_input_offset = input.len();
230-
Ok(output.into_boxed_slice())
235+
Ok(BrotliStreamResult {
236+
code: BrotliStreamResultCode::ResultSuccess,
237+
buf: output.into_boxed_slice(),
238+
input_offset,
239+
})
231240
}
232241
BrotliResult::NeedsMoreInput => {
233242
output.truncate(output_offset);
234-
self.result = BrotliStreamResult::NeedsMoreInput as i32;
235-
self.last_input_offset = input.len();
236-
Ok(output.into_boxed_slice())
243+
Ok(BrotliStreamResult {
244+
code: BrotliStreamResultCode::NeedsMoreInput,
245+
buf: output.into_boxed_slice(),
246+
input_offset,
247+
})
237248
}
238249
}
239250
}
240251

241252
pub fn total_out(&self) -> usize {
242253
self.total_out
243254
}
244-
245-
pub fn result(&self) -> i32 {
246-
self.result
247-
}
248-
249-
pub fn last_input_offset(&self) -> usize {
250-
self.last_input_offset
251-
}
252255
}

0 commit comments

Comments
 (0)