1 | /* |
2 | * This file is part of the MicroPython project, http://micropython.org/ |
3 | * |
4 | * The MIT License (MIT) |
5 | * |
6 | * Copyright (c) 2014-2016 Paul Sokolovsky |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | * of this software and associated documentation files (the "Software"), to deal |
10 | * in the Software without restriction, including without limitation the rights |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | * copies of the Software, and to permit persons to whom the Software is |
13 | * furnished to do so, subject to the following conditions: |
14 | * |
15 | * The above copyright notice and this permission notice shall be included in |
16 | * all copies or substantial portions of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
24 | * THE SOFTWARE. |
25 | */ |
26 | |
27 | #include <stdio.h> |
28 | #include <string.h> |
29 | |
30 | #include "py/runtime.h" |
31 | #include "py/stream.h" |
32 | #include "py/mperrno.h" |
33 | |
34 | #if MICROPY_PY_UZLIB |
35 | |
36 | #include "uzlib/tinf.h" |
37 | |
38 | #if 0 // print debugging info |
39 | #define DEBUG_printf DEBUG_printf |
40 | #else // don't print debugging info |
41 | #define DEBUG_printf(...) (void)0 |
42 | #endif |
43 | |
44 | typedef struct _mp_obj_decompio_t { |
45 | mp_obj_base_t base; |
46 | mp_obj_t src_stream; |
47 | TINF_DATA decomp; |
48 | bool eof; |
49 | } mp_obj_decompio_t; |
50 | |
51 | STATIC int read_src_stream(TINF_DATA *data) { |
52 | byte *p = (void *)data; |
53 | p -= offsetof(mp_obj_decompio_t, decomp); |
54 | mp_obj_decompio_t *self = (mp_obj_decompio_t *)p; |
55 | |
56 | const mp_stream_p_t *stream = mp_get_stream(self->src_stream); |
57 | int err; |
58 | byte c; |
59 | mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err); |
60 | if (out_sz == MP_STREAM_ERROR) { |
61 | mp_raise_OSError(err); |
62 | } |
63 | if (out_sz == 0) { |
64 | mp_raise_type(&mp_type_EOFError); |
65 | } |
66 | return c; |
67 | } |
68 | |
69 | STATIC mp_obj_t decompio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { |
70 | mp_arg_check_num(n_args, n_kw, 1, 2, false); |
71 | mp_get_stream_raise(args[0], MP_STREAM_OP_READ); |
72 | mp_obj_decompio_t *o = m_new_obj(mp_obj_decompio_t); |
73 | o->base.type = type; |
74 | memset(&o->decomp, 0, sizeof(o->decomp)); |
75 | o->decomp.readSource = read_src_stream; |
76 | o->src_stream = args[0]; |
77 | o->eof = false; |
78 | |
79 | mp_int_t dict_opt = 0; |
80 | int dict_sz; |
81 | if (n_args > 1) { |
82 | dict_opt = mp_obj_get_int(args[1]); |
83 | } |
84 | |
85 | if (dict_opt >= 16) { |
86 | int st = uzlib_gzip_parse_header(&o->decomp); |
87 | if (st != TINF_OK) { |
88 | goto header_error; |
89 | } |
90 | dict_sz = 1 << (dict_opt - 16); |
91 | } else if (dict_opt >= 0) { |
92 | dict_opt = uzlib_zlib_parse_header(&o->decomp); |
93 | if (dict_opt < 0) { |
94 | : |
95 | mp_raise_ValueError(MP_ERROR_TEXT("compression header" )); |
96 | } |
97 | dict_sz = 1 << dict_opt; |
98 | } else { |
99 | dict_sz = 1 << -dict_opt; |
100 | } |
101 | |
102 | uzlib_uncompress_init(&o->decomp, m_new(byte, dict_sz), dict_sz); |
103 | return MP_OBJ_FROM_PTR(o); |
104 | } |
105 | |
106 | STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { |
107 | mp_obj_decompio_t *o = MP_OBJ_TO_PTR(o_in); |
108 | if (o->eof) { |
109 | return 0; |
110 | } |
111 | |
112 | o->decomp.dest = buf; |
113 | o->decomp.dest_limit = (byte *)buf + size; |
114 | int st = uzlib_uncompress_chksum(&o->decomp); |
115 | if (st == TINF_DONE) { |
116 | o->eof = true; |
117 | } |
118 | if (st < 0) { |
119 | *errcode = MP_EINVAL; |
120 | return MP_STREAM_ERROR; |
121 | } |
122 | return o->decomp.dest - (byte *)buf; |
123 | } |
124 | |
125 | #if !MICROPY_ENABLE_DYNRUNTIME |
126 | STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = { |
127 | { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, |
128 | { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, |
129 | { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, |
130 | }; |
131 | |
132 | STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table); |
133 | #endif |
134 | |
135 | STATIC const mp_stream_p_t decompio_stream_p = { |
136 | .read = decompio_read, |
137 | }; |
138 | |
139 | #if !MICROPY_ENABLE_DYNRUNTIME |
140 | STATIC const mp_obj_type_t decompio_type = { |
141 | { &mp_type_type }, |
142 | .name = MP_QSTR_DecompIO, |
143 | .make_new = decompio_make_new, |
144 | .protocol = &decompio_stream_p, |
145 | .locals_dict = (void *)&decompio_locals_dict, |
146 | }; |
147 | #endif |
148 | |
149 | STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) { |
150 | mp_obj_t data = args[0]; |
151 | mp_buffer_info_t bufinfo; |
152 | mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); |
153 | |
154 | TINF_DATA *decomp = m_new_obj(TINF_DATA); |
155 | memset(decomp, 0, sizeof(*decomp)); |
156 | DEBUG_printf("sizeof(TINF_DATA)=" UINT_FMT "\n" , sizeof(*decomp)); |
157 | uzlib_uncompress_init(decomp, NULL, 0); |
158 | mp_uint_t dest_buf_size = (bufinfo.len + 15) & ~15; |
159 | byte *dest_buf = m_new(byte, dest_buf_size); |
160 | |
161 | decomp->dest = dest_buf; |
162 | decomp->dest_limit = dest_buf + dest_buf_size; |
163 | DEBUG_printf("uzlib: Initial out buffer: " UINT_FMT " bytes\n" , dest_buf_size); |
164 | decomp->source = bufinfo.buf; |
165 | decomp->source_limit = (byte *)bufinfo.buf + bufinfo.len; |
166 | |
167 | int st; |
168 | bool is_zlib = true; |
169 | |
170 | if (n_args > 1 && MP_OBJ_SMALL_INT_VALUE(args[1]) < 0) { |
171 | is_zlib = false; |
172 | } |
173 | |
174 | if (is_zlib) { |
175 | st = uzlib_zlib_parse_header(decomp); |
176 | if (st < 0) { |
177 | goto error; |
178 | } |
179 | } |
180 | |
181 | while (1) { |
182 | st = uzlib_uncompress_chksum(decomp); |
183 | if (st < 0) { |
184 | goto error; |
185 | } |
186 | if (st == TINF_DONE) { |
187 | break; |
188 | } |
189 | size_t offset = decomp->dest - dest_buf; |
190 | dest_buf = m_renew(byte, dest_buf, dest_buf_size, dest_buf_size + 256); |
191 | dest_buf_size += 256; |
192 | decomp->dest = dest_buf + offset; |
193 | decomp->dest_limit = decomp->dest + 256; |
194 | } |
195 | |
196 | mp_uint_t final_sz = decomp->dest - dest_buf; |
197 | DEBUG_printf("uzlib: Resizing from " UINT_FMT " to final size: " UINT_FMT " bytes\n" , dest_buf_size, final_sz); |
198 | dest_buf = (byte *)m_renew(byte, dest_buf, dest_buf_size, final_sz); |
199 | mp_obj_t res = mp_obj_new_bytearray_by_ref(final_sz, dest_buf); |
200 | m_del_obj(TINF_DATA, decomp); |
201 | return res; |
202 | |
203 | error: |
204 | nlr_raise(mp_obj_new_exception_arg1(&mp_type_ValueError, MP_OBJ_NEW_SMALL_INT(st))); |
205 | } |
206 | STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uzlib_decompress_obj, 1, 3, mod_uzlib_decompress); |
207 | |
208 | #if !MICROPY_ENABLE_DYNRUNTIME |
209 | STATIC const mp_rom_map_elem_t mp_module_uzlib_globals_table[] = { |
210 | { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uzlib) }, |
211 | { MP_ROM_QSTR(MP_QSTR_decompress), MP_ROM_PTR(&mod_uzlib_decompress_obj) }, |
212 | { MP_ROM_QSTR(MP_QSTR_DecompIO), MP_ROM_PTR(&decompio_type) }, |
213 | }; |
214 | |
215 | STATIC MP_DEFINE_CONST_DICT(mp_module_uzlib_globals, mp_module_uzlib_globals_table); |
216 | |
217 | const mp_obj_module_t mp_module_uzlib = { |
218 | .base = { &mp_type_module }, |
219 | .globals = (mp_obj_dict_t *)&mp_module_uzlib_globals, |
220 | }; |
221 | #endif |
222 | |
223 | // Source files #include'd here to make sure they're compiled in |
224 | // only if module is enabled by config setting. |
225 | |
226 | #include "uzlib/tinflate.c" |
227 | #include "uzlib/tinfzlib.c" |
228 | #include "uzlib/tinfgzip.c" |
229 | #include "uzlib/adler32.c" |
230 | #include "uzlib/crc32.c" |
231 | |
232 | #endif // MICROPY_PY_UZLIB |
233 | |