Skip to content

Commit 4fa04d6

Browse files
authored
Merge pull request #25 from myl7/fix-stream-input-offset
Replace stream last_input_offset & result with (de)compress result props
2 parents d2f7278 + e983db1 commit 4fa04d6

File tree

3 files changed

+209
-148
lines changed

3 files changed

+209
-148
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)