1
- use crate :: set_panic_hook;
1
+ use crate :: { set_panic_hook, Options } ;
2
+ use brotli:: enc:: encode:: {
3
+ BrotliEncoderCompressStream , BrotliEncoderCreateInstance , BrotliEncoderDestroyInstance ,
4
+ BrotliEncoderIsFinished , BrotliEncoderOperation , BrotliEncoderParameter ,
5
+ BrotliEncoderSetParameter , BrotliEncoderStateStruct ,
6
+ } ;
2
7
use brotli:: enc:: StandardAlloc ; // Re-exported from alloc_stdlib::StandardAlloc
3
- use brotli:: { BrotliDecompressStream , BrotliResult , BrotliState } ;
8
+ use brotli:: { self , BrotliDecompressStream , BrotliResult , BrotliState } ;
4
9
use wasm_bindgen:: prelude:: * ;
5
10
6
11
#[ wasm_bindgen]
@@ -9,17 +14,141 @@ pub enum BrotliStreamResult {
9
14
/// The stream is just initialized and no input is provided currently.
10
15
/// `BrotliResult` uses `ResultFailure = 0`, but as we will convert `ResultFailure` to a negative actual error code,
11
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.
12
18
Init = 0 ,
13
19
ResultSuccess = 1 ,
14
20
NeedsMoreInput = 2 ,
15
21
NeedsMoreOutput = 3 ,
16
22
}
17
23
18
24
#[ wasm_bindgen]
19
- pub struct CompressStream { }
25
+ pub struct CompressStream {
26
+ state : BrotliEncoderStateStruct < StandardAlloc > ,
27
+ result : i32 ,
28
+ total_out : usize ,
29
+ }
30
+
31
+ impl Drop for CompressStream {
32
+ fn drop ( & mut self ) {
33
+ BrotliEncoderDestroyInstance ( & mut self . state ) ;
34
+ }
35
+ }
20
36
21
37
#[ wasm_bindgen]
22
- impl CompressStream { }
38
+ impl CompressStream {
39
+ #[ wasm_bindgen( constructor) ]
40
+ pub fn new ( raw_options : & JsValue ) -> Result < CompressStream , JsValue > {
41
+ set_panic_hook ( ) ;
42
+ let options: Options = if raw_options. is_undefined ( ) {
43
+ serde_json:: from_str ( "{}" ) . unwrap ( )
44
+ } else if raw_options. is_object ( ) {
45
+ raw_options. into_serde ( ) . unwrap ( )
46
+ } else {
47
+ return Err ( JsValue :: from_str ( "Options is not an object" ) ) ;
48
+ } ;
49
+ let alloc = StandardAlloc :: default ( ) ;
50
+ let mut state = BrotliEncoderCreateInstance ( alloc) ;
51
+ BrotliEncoderSetParameter (
52
+ & mut state,
53
+ BrotliEncoderParameter :: BROTLI_PARAM_QUALITY ,
54
+ options. quality as u32 ,
55
+ ) ;
56
+ Ok ( Self {
57
+ state,
58
+ result : BrotliStreamResult :: Init as i32 ,
59
+ total_out : 0 ,
60
+ } )
61
+ }
62
+
63
+ pub fn compress (
64
+ & mut self ,
65
+ input_opt : Option < Box < [ u8 ] > > ,
66
+ output_size : usize ,
67
+ ) -> Result < Box < [ u8 ] > , JsValue > {
68
+ let mut nop_callback = |_data : & mut brotli:: interface:: PredictionModeContextMap <
69
+ brotli:: interface:: InputReferenceMut ,
70
+ > ,
71
+ _cmds : & mut [ brotli:: interface:: StaticCommand ] ,
72
+ _mb : brotli:: interface:: InputPair ,
73
+ _mfv : & mut StandardAlloc | ( ) ;
74
+ let mut output = vec ! [ 0 ; output_size] ;
75
+ let mut input_offset = 0 ;
76
+ let mut available_out = output_size;
77
+ let mut output_offset = 0 ;
78
+ match input_opt {
79
+ Some ( input) => {
80
+ let op = BrotliEncoderOperation :: BROTLI_OPERATION_PROCESS ;
81
+ let mut available_in = input. len ( ) ;
82
+ let ret = BrotliEncoderCompressStream (
83
+ & mut self . state ,
84
+ op,
85
+ & mut available_in,
86
+ & input,
87
+ & mut input_offset,
88
+ & mut available_out,
89
+ & mut output,
90
+ & mut output_offset,
91
+ & mut Some ( self . total_out ) ,
92
+ & mut nop_callback,
93
+ ) ;
94
+ if ret != 0 {
95
+ if available_out == 0 {
96
+ self . result = BrotliStreamResult :: NeedsMoreOutput as i32 ;
97
+ Ok ( output. into_boxed_slice ( ) )
98
+ } else if available_in == 0 {
99
+ output. truncate ( output_offset) ;
100
+ self . result = BrotliStreamResult :: NeedsMoreInput as i32 ;
101
+ Ok ( output. into_boxed_slice ( ) )
102
+ } else {
103
+ self . result = -1 ;
104
+ Err ( JsValue :: from_str ( "Unexpected Brotli streaming compress: both available_in & available_out are not 0 after a successful processing" ) )
105
+ }
106
+ } else {
107
+ self . result = -1 ;
108
+ Err ( JsValue :: from_str (
109
+ "Brotli streaming compress failed: When processing" ,
110
+ ) )
111
+ }
112
+ }
113
+ None => {
114
+ let op = BrotliEncoderOperation :: BROTLI_OPERATION_FINISH ;
115
+ let input = Vec :: new ( ) . into_boxed_slice ( ) ;
116
+ let mut available_in = 0 ;
117
+ while BrotliEncoderIsFinished ( & mut self . state ) == 0 {
118
+ let ret = BrotliEncoderCompressStream (
119
+ & mut self . state ,
120
+ op,
121
+ & mut available_in,
122
+ & input,
123
+ & mut input_offset,
124
+ & mut available_out,
125
+ & mut output,
126
+ & mut output_offset,
127
+ & mut Some ( self . total_out ) ,
128
+ & mut nop_callback,
129
+ ) ;
130
+ if ret == 0 {
131
+ self . result = -1 ;
132
+ return Err ( JsValue :: from_str (
133
+ "Brotli streaming compress failed: When finishing" ,
134
+ ) ) ;
135
+ }
136
+ }
137
+ output. truncate ( output_offset) ;
138
+ self . result = BrotliStreamResult :: ResultSuccess as i32 ;
139
+ Ok ( output. into_boxed_slice ( ) )
140
+ }
141
+ }
142
+ }
143
+
144
+ pub fn total_out ( & self ) -> usize {
145
+ self . total_out
146
+ }
147
+
148
+ pub fn result ( & self ) -> i32 {
149
+ self . result
150
+ }
151
+ }
23
152
24
153
#[ wasm_bindgen]
25
154
pub struct DecompressStream {
0 commit comments