File Coverage

c-lib/s-bsdipa-io.h
Criterion Covered Total %
statement 172 240 71.6
branch 80 130 61.5
condition n/a
subroutine n/a
pod n/a
total 252 370 68.1


line stmt bran cond sub pod time code
1             /*@ s-bsdipa-io: I/O (compression) layer for s-bsdipa-lib.
2             *@ Use as follows:
3             *@ - Define s_BSDIPA_IO to one of s_BSDIPA_IO_(BZ2|RAW|XZ|ZLIB|ZSTD);
4             *@ - Define s_BSDIPA_IO_READ and/or s_BSDIPA_IO_WRITE, as desired;
5             *@ - And include this header.
6             *@ It then provides the according s_BSDIPA_IO_NAME preprocessor literal
7             *@ and s_bsdipa_io_{read,write}_..(), which (are) fe(e)d data to/from hooks.
8             *@ Dependent upon the type a specialized io_cookie type may be available.
9             *@
10             *@ Notes:
11             *@ - The functions have s_BSDIPA_IO_LINKAGE storage, or static if not defined.
12             *@ There may be additional static helper functions.
13             *@ - It is up to the user to provide according linker flags, like -lz!
14             *@ - This is not a step-by-step filter: a complete s_bsdipa_diff() result
15             *@ is serialized, or serialized data is turned into a complete data set
16             *@ that then can be fed into s_bsdipa_patch().
17             *@ (A custom I/O (compression) layer may be less memory hungry.)
18             *@ - Layers default to the strongest possible compression, unless that is "excessive".
19             *@ (Note: s-bsdipa.c uses defaults, and needs (manual) adjustments on change.)
20             *@
21             *@ - s_BSDIPA_IO == s_BSDIPA_IO_BZ2 (-lbz2 (bzip2)):
22             *@ -- s_BSDIPA_IO_BZ2_BLOCKSIZE may be defined (default 9).
23             *@ -- s_BSDIPA_IO_BZ2_VERBOSITY may be defined (default 0).
24             *@ -- s_BSDIPA_IO_BZ2_SMALL may be defined to reduce memory usage (default 0).
25             *@ -- Note: INT_MAX is maximum allocation size! (XXX could "split" via n/size)
26             *@ - s_BSDIPA_IO == s_BSDIPA_IO_RAW:
27             *@ -- no checksum.
28             *@ - s_BSDIPA_IO == s_BSDIPA_IO_XZ (-llzma):
29             *@ -- s_BSDIPA_IO_XZ_PRESET may be defined as the "preset" argument of
30             *@ lzma_easy_encoder() (default 8).
31             *@ -- s_BSDIPA_IO_XZ_CHECK may be defined as the "check" argument of
32             *@ lzma_easy_encoder() (default is LZMA_CHECK_CRC32 for s_BSDIPA_32, and
33             *@ LZMA_CHECK_CRC64 otherwise).
34             *@ - s_BSDIPA_IO == s_BSDIPA_IO_ZLIB (-lz):
35             *@ -- s_BSDIPA_IO_ZLIB_LEVEL may be defined as the "level" argument of
36             *@ zlib's deflateInit() (default 9).
37             *@ -- Checksum Adler-32 (what inflate() gives you).
38             *@ -- Note: UINT_MAX is maximum allocation size! (XXX could "split" via n/size)
39             *@ - s_BSDIPA_IO == s_BSDIPA_IO_ZSTD (-lzstd):
40             *@ -- s_BSDIPA_IO_ZSTD_LEVEL is *not* zstd.h:ZSTD_c_compressionLevel, but instead in our own
41             *@ scale 1..9, but then mapped accordingly.
42             *@ -- s_BSDIPA_IO_ZSTD_CHECKSUM may be defined as 0 or 1 to dis-/enable ZSTD_c_checksumFlag
43             *@ (default 1, meaning XXH64).
44             *@ - The header may be included multiple times, shall multiple BSDIPA_IO
45             *@ variants be desired. Still, only the _IO_LINKAGE as well as _IO_READ
46             *@ and _IO_WRITE of the first inclusion are valid.
47             *@ - TODO For most compression I/O layers, try_oneshot could be used to drive the I/O layer
48             *@ TODO in a special oneshot mode; this optimization is not yet implemented.
49             *@
50             *@ Remarks:
51             *@ - Code requires ISO STD C99.
52             *
53             * Copyright (c) 2024 - 2026 Steffen Nurpmeso .
54             * SPDX-License-Identifier: ISC
55             *
56             * Permission to use, copy, modify, and/or distribute this software for any
57             * purpose with or without fee is hereby granted, provided that the above
58             * copyright notice and this permission notice appear in all copies.
59             *
60             * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
61             * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
62             * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
63             * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
64             * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
65             * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
66             * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
67             */
68             #ifndef s_BSDIPA_IO_H
69             # define s_BSDIPA_IO_H 0
70             #elif s_BSDIPA_IO_H == 0
71             # undef s_BSDIPA_IO_H
72             # define s_BSDIPA_IO_H 1
73             #elif s_BSDIPA_IO_H == 1
74             # undef s_BSDIPA_IO_H
75             # define s_BSDIPA_IO_H 2
76             #elif s_BSDIPA_IO_H == 2
77             # undef s_BSDIPA_IO_H
78             # define s_BSDIPA_IO_H 3
79             #elif s_BSDIPA_IO_H == 3
80             # undef s_BSDIPA_IO_H
81             # define s_BSDIPA_IO_H 4
82             #else
83             # error Only five I/O layers exist.
84             #endif
85              
86             #if s_BSDIPA_IO_H == 0
87             # include
88             #endif
89              
90             #ifdef __cplusplus
91             extern "C" {
92             #endif
93              
94             #if s_BSDIPA_IO_H == 0 /* {{{ */
95             # if !defined s_BSDIPA_IO_READ && !defined s_BSDIPA_IO_WRITE
96             # error At least one of s_BSDIPA_IO_READ and s_BSDIPA_IO_WRITE is needed
97             # endif
98              
99             /* Compression types and names (preprocessor so sources can adapt -- *alphabetical* order!) */
100             # define s_BSDIPA_IO_BZ2 0
101             # define s_BSDIPA_IO_NAME_BZ2 "BZ2"
102             # define s_BSDIPA_IO_RAW 1
103             # define s_BSDIPA_IO_NAME_RAW "RAW"
104             # define s_BSDIPA_IO_XZ 2
105             # define s_BSDIPA_IO_NAME_XZ "XZ"
106             # define s_BSDIPA_IO_ZLIB 3
107             # define s_BSDIPA_IO_NAME_ZLIB "ZLIB"
108             # define s_BSDIPA_IO_ZSTD 4
109             # define s_BSDIPA_IO_NAME_ZSTD "ZSTD"
110             # define s_BSDIPA_IO_MAX 4
111              
112             # ifndef s_BSDIPA_IO_LINKAGE
113             # define s_BSDIPA_IO_LINKAGE static
114             # endif
115              
116             /* An optional cookie that may be used by the I/O layer for caching purposes if set.
117             *
118             * The actual I/O layer may have a specific "subclass", plus an optional s_bsdipa_io_cookie_gut_*() function,
119             * in which the subclass must be used, and the gut function be called.
120             * It must be zeroed before first use, then .ioc_type be set to the s_BSDIPA_IO_.. type;
121             * setting .ioc_level is optional: it *must* be within 1-9, which the layers maps to mean minimum..maximum.
122             *
123             * A cookie may then be passed to read and write functions, from within one thread at a time, non-interchangeably.
124             * Once no more use is to be expected, the according _gut_*() function must be called.
125             *
126             * Note: during all the life time the memory allocator must not change!
127             * If the cookie is used for s_bsdipa_diff() as well as s_bsdipa_patch(), then the memory allocator and its cookie
128             * (if used) must be the same throughout the lifetime of the I/O cookie!
129             *
130             * Remarks: "super type" alignment may mismatch actual "subclass", but assumed "ok through instantiation". */
131             struct s_bsdipa_io_cookie{
132             uint8_t ioc_is_init;
133             uint8_t ioc_type; /* s_BSDIPA_IO_..: actual type */
134             uint8_t ioc_level; /* Always 0, or 1-9, then I/O layer puts meaning onto *that* */
135             uint8_t ioc__dummy[5];
136             };
137              
138             /* The function to free resources of a io_cookie, if any were ever allocated. */
139             typedef void (*s_bsdipa_io_gut_fun)(struct s_bsdipa_io_cookie *io_cookie);
140              
141             # ifdef s_BSDIPA_IO_WRITE
142             /* I/O write hook.
143             * If is_last is set the hook will not be called again; in this case len may be 0. */
144             typedef enum s_bsdipa_state (*s_bsdipa_io_write_ptf)(void *hook_cookie, uint8_t const *dat, s_bsdipa_off_t len,
145             s_bsdipa_off_t is_last);
146              
147             /* I/O write function, as provided by the I/O layer.
148             * If try_oneshot was given to an I/O layer to which it matters it will try to invoke the hook only once;
149             * if a negative try_oneshot was given, and if the layer succeeds to comply, then the hook's is_last will also
150             * be negative and the ownership of dat is transferred to the hook by definition -- and only then:
151             * in fact the absolute value of is_last is the buffer size, of which len bytes are useful;
152             * Note that *only* in this case the buffer size is at least one greater than len! */
153             typedef enum s_bsdipa_state (*s_bsdipa_io_write_fun)(struct s_bsdipa_diff_ctx const *dcp,
154             s_bsdipa_io_write_ptf hook, void *hook_cookie, int try_oneshot,
155             struct s_bsdipa_io_cookie *io_cookie_or_null);
156             # endif
157              
158             # ifdef s_BSDIPA_IO_READ
159             /* I/O read function, as provided by the I/O layer.
160             * It is assumed that pcp->pc_patch_dat and .pc_patch_len represent the entire (constant) patch data.
161             * Output is allocated via .pc_mem, and stored in .pc_restored_dat and .pc_restored_len as a continuous chunk.
162             * .pc_max_allowed_restored_len must also be set as it is already evaluated as documented.
163             * On error that memory, if any, will be freed, and .pc_restored_dat will be NULL.
164             * On success .pc_header is filled in; it is up to the user to update .pc_patch* with the .pc_restored* fields
165             * and call s_bsdipa_patch() to apply the real patch.
166             * (.pc_restored_dat will be overwritten by s_bsdipa_patch().) */
167             typedef enum s_bsdipa_state (*s_bsdipa_io_read_fun)(struct s_bsdipa_patch_ctx *pcp,
168             struct s_bsdipa_io_cookie *io_cookie_or_null);
169             # endif
170             #endif /* s_BSDIPA_IO_H==0 */
171             /* }}} */
172              
173             /* (Code blocks in implementation order) */
174             #undef s_BSDIPA_IO_NAME
175             #if !defined s_BSDIPA_IO || s_BSDIPA_IO == s_BSDIPA_IO_RAW /* {{{ */
176             # undef s_BSDIPA_IO
177             # ifdef s__BSDIPA_IO_RAW
178             # error s_BSDIPA_IO==s_BSDIPA_IO_RAW already defined
179             # endif
180             # define s__BSDIPA_IO_RAW
181             # define s_BSDIPA_IO s_BSDIPA_IO_RAW
182             # define s_BSDIPA_IO_NAME s_BSDIPA_IO_NAME_RAW
183              
184             # include
185              
186             # ifdef s_BSDIPA_IO_WRITE
187             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
188 158           s_bsdipa_io_write_raw(struct s_bsdipa_diff_ctx const *dcp, s_bsdipa_io_write_ptf hook, void *hook_cookie,
189             int try_oneshot, struct s_bsdipa_io_cookie *io_cookie_or_null){
190             struct s_bsdipa_ctrl_chunk *ccp;
191             enum s_bsdipa_state rv;
192             (void)try_oneshot;
193             (void)io_cookie_or_null;
194              
195 158 50         if((rv = (*hook)(hook_cookie, dcp->dc_header, sizeof(dcp->dc_header), 0)) != s_BSDIPA_OK)
196 0           goto jleave;
197              
198 316 100         s_BSDIPA_DIFF_CTX_FOREACH_CTRL(dcp, ccp){
199 158 50         if((rv = (*hook)(hook_cookie, ccp->cc_dat, ccp->cc_len, 0)) != s_BSDIPA_OK)
200 0           goto jleave;
201             }
202              
203 232           if(dcp->dc_diff_len > 0 &&
204 74           (rv = (*hook)(hook_cookie, dcp->dc_diff_dat, dcp->dc_diff_len, 0)) != s_BSDIPA_OK)
205 0           goto jleave;
206              
207 242           if(dcp->dc_extra_len > 0 &&
208 84           (rv = (*hook)(hook_cookie, dcp->dc_extra_dat, dcp->dc_extra_len, 0)) != s_BSDIPA_OK)
209 0           goto jleave;
210              
211 158           rv = (*hook)(hook_cookie, NULL, 0, 1);
212 158           jleave:
213 158           return rv;
214             }
215             # endif /* s_BSDIPA_IO_WRITE */
216              
217             # ifdef s_BSDIPA_IO_READ
218             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
219 237           s_bsdipa_io_read_raw(struct s_bsdipa_patch_ctx *pcp, struct s_bsdipa_io_cookie *io_cookie_or_null){
220             enum s_bsdipa_state rv;
221             uint64_t pl;
222             uint8_t const *pd;
223             uint8_t *rd;
224             (void)io_cookie_or_null;
225              
226 237           rd = NULL;
227 237           pd = pcp->pc_patch_dat;
228 237           pl = pcp->pc_patch_len;
229              
230 237 50         if(pl < sizeof(struct s_bsdipa_header)){
231 0           rv = s_BSDIPA_INVAL;
232 0           goto jleave;
233             }
234 237           rv = s_bsdipa_patch_parse_header(&pcp->pc_header, pd);
235 237 50         if(rv != s_BSDIPA_OK)
236 0           goto jleave;
237              
238             /* Do not perform any action at all on size excess */
239 237 100         if(pcp->pc_max_allowed_restored_len != 0 &&
240 158 100         pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len){
241 79           rv = s_BSDIPA_FBIG;
242 79           goto jleave;
243             }
244              
245 158           pl -= sizeof(pcp->pc_header);
246 158           pd += sizeof(pcp->pc_header);
247              
248             /* Not truly right, the latter at least, but good enough for now */
249 158 50         if(pl > s_BSDIPA_OFF_MAX - 1 || pl != (size_t)pl){
250 0           rv = s_BSDIPA_FBIG;
251 0           goto jleave;
252             }
253              
254 158 50         if(pl != (uint64_t)(pcp->pc_header.h_ctrl_len + pcp->pc_header.h_diff_len + pcp->pc_header.h_extra_len)){
255 0           rv = s_BSDIPA_INVAL;
256 0           goto jleave;
257             }
258              
259 158 50         rd = (uint8_t*)((pcp->pc_mem.mc_alloc != NULL) ? (*pcp->pc_mem.mc_alloc)((size_t)pl)
260 0           : (*pcp->pc_mem.mc_custom_alloc)(pcp->pc_mem.mc_custom_cookie, (size_t)pl));
261 158 50         if(rd == NULL){
262 0           rv = s_BSDIPA_NOMEM;
263 0           goto jleave;
264             }
265              
266 158           memcpy(rd, pd, (size_t)pl);
267              
268 158           rv = s_BSDIPA_OK;
269 237           jleave:
270 237 100         if(rv == s_BSDIPA_OK){
271 158           pcp->pc_restored_dat = rd;
272 158           pcp->pc_restored_len = (s_bsdipa_off_t)pl;
273             }else{
274             # if 0
275             if(rd != NULL)
276             (pcp->pc_mem.mc_alloc != NULL) ? (*pcp->pc_mem.mc_free)(rd)
277             : (*pcp->pc_mem.mc_custom_free)(pcp->pc_mem.mc_custom_cookie, rd);
278             # endif
279 79           pcp->pc_restored_dat = NULL;
280             }
281              
282 237           return rv;
283             }
284             # endif /* s_BSDIPA_IO_READ */
285             /* }}} */
286              
287             #elif s_BSDIPA_IO == s_BSDIPA_IO_ZLIB /* _IO_RAW {{{ */
288             /*# undef s_BSDIPA_IO*/
289             # ifdef s__BSDIPA_IO_ZLIB
290             # error s_BSDIPA_IO==s_BSDIPA_IO_ZLIB already defined
291             # endif
292             # define s__BSDIPA_IO_ZLIB
293             # define s_BSDIPA_IO_NAME s_BSDIPA_IO_NAME_ZLIB
294              
295             # include
296              
297             # include
298              
299             # ifndef s_BSDIPA_IO_ZLIB_LEVEL
300             # define s_BSDIPA_IO_ZLIB_LEVEL 9
301             # endif
302              
303             /* For testing purposes */
304             # define s__BSDIPA_IO_ZLIB_LIMIT (INT32_MAX - 1)
305              
306             static voidpf s__bsdipa_io_zlib_alloc(voidpf my_cookie, uInt no, uInt size);
307             static void s__bsdipa_io_zlib_free(voidpf my_cookie, voidpf dat);
308              
309             static voidpf
310 1580           s__bsdipa_io_zlib_alloc(voidpf my_cookie, uInt no, uInt size){
311             voidpf rv;
312             size_t memsz;
313             struct s_bsdipa_memory_ctx *mcp;
314              
315 1580           mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
316 1580           memsz = (size_t)no * size;
317              
318 1580 50         rv = (mcp->mc_alloc != NULL) ? (*mcp->mc_alloc)(memsz) : (*mcp->mc_custom_alloc)(mcp->mc_custom_cookie, memsz);
319              
320 1580           return rv;
321             }
322              
323             static void
324 1312           s__bsdipa_io_zlib_free(voidpf my_cookie, voidpf dat){
325             struct s_bsdipa_memory_ctx *mcp;
326              
327 1312           mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
328              
329 1312 50         (mcp->mc_alloc != NULL) ? (*mcp->mc_free)(dat) : (*mcp->mc_custom_free)(mcp->mc_custom_cookie, dat);
330 1312           }
331              
332             # ifdef s_BSDIPA_IO_WRITE /* {{{ */
333             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
334 158           s_bsdipa_io_write_zlib(struct s_bsdipa_diff_ctx const *dcp, s_bsdipa_io_write_ptf hook, void *hook_cookie,
335             int try_oneshot, struct s_bsdipa_io_cookie *io_cookie_or_null){
336             z_stream zs;
337             struct s_bsdipa_ctrl_chunk *ccp;
338             char x;
339             enum s_bsdipa_state rv;
340             uint8_t *obuf;
341             size_t olen;
342             s_bsdipa_off_t diflen, extlen;
343             z_streamp zsp;
344              
345 158           zsp = &zs;
346 158           zs.zalloc = &s__bsdipa_io_zlib_alloc;
347 158           zs.zfree = &s__bsdipa_io_zlib_free;
348 158           zs.opaque = (void*)&dcp->dc_mem;
349              
350             /* C99 */{
351             int level;
352              
353 158           level = s_BSDIPA_IO_ZLIB_LEVEL;
354 158 100         if(io_cookie_or_null != NULL && io_cookie_or_null->ioc_level != 0)
    50          
355 79           level = (int)io_cookie_or_null->ioc_level;
356              
357 158           switch(deflateInit(zsp, level)){
358 158           case Z_OK: break;
359 0           case Z_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jleave;
360 0           default: rv = s_BSDIPA_INVAL; goto jleave;
361             }
362             }
363              
364 158           diflen = dcp->dc_diff_len;
365 158           extlen = dcp->dc_extra_len;
366              
367             /* All lengths fit in s_BSDIPA_OFF_MAX, which is signed: addition and cast ok */
368 158           olen = (size_t)((s_bsdipa_off_t)sizeof(dcp->dc_header) + dcp->dc_ctrl_len + diflen + extlen);
369 158 100         if(try_oneshot){
370             uLong ulo;
371              
372 134           ulo = (uLong)olen; /* XXX check overflow? s_bsdipa_off_t>uLong case? */
373 134           ulo = deflateBound(zsp, ulo);
374 134 50         if(ulo >= s_BSDIPA_OFF_MAX){
375 0           try_oneshot = 0;
376 0           goto jolenmax;
377             }
378             /* Add "one additional byte" already here in case buffer takeover succeeds */
379 134           ++ulo;
380 134 50         if(ulo != (uInt)ulo){
381 0           try_oneshot = 0;
382 0           goto jolenmax;
383             }
384 134           olen = (size_t)ulo;
385 24 100         }else if(olen <= 1000 * 150)
386 20           olen = 4096 * 4;
387 4 100         else if(olen <= 1000 * 1000)
388 2           olen = 4096 * 31;
389             else
390 2           jolenmax:
391 2           olen = 4096 * 244;
392              
393 158           obuf = (uint8_t*)s__bsdipa_io_zlib_alloc((void*)&dcp->dc_mem, 1, (uInt)olen);
394 158 50         if(obuf == NULL){
395 0           rv = s_BSDIPA_NOMEM;
396 0           goto jdone;
397             }
398 158           olen -= (try_oneshot != 0);
399              
400 158           zsp->next_out = obuf;
401 158           zsp->avail_out = (uInt)olen;
402 158           ccp = dcp->dc_ctrl;
403              
404 790           for(x = 0;;){
405             int flusht;
406              
407 790           flusht = Z_NO_FLUSH;
408 790 100         if(x == 0){
409 158           zsp->next_in = (Bytef z_const*)(void*)dcp->dc_header;
410 158           zsp->avail_in = sizeof(dcp->dc_header);
411 158           x = 1;
412 632 100         }else if(x == 1){
413 158 50         if(ccp != NULL){
414 158           zsp->next_in = ccp->cc_dat;
415 158           zsp->avail_in = (uInt)ccp->cc_len;
416 158           ccp = ccp->cc_next;
417             }
418 158 50         if(ccp == NULL)
419 158           x = 2;
420 474 100         }else if(x < 4){
421 158 50         if(x == 2)
422 158           zsp->next_in = dcp->dc_diff_dat;
423 158 50         if(diflen > s__BSDIPA_IO_ZLIB_LIMIT){
424 0           zsp->avail_in = s__BSDIPA_IO_ZLIB_LIMIT;
425 0           diflen -= s__BSDIPA_IO_ZLIB_LIMIT;
426 0           x = 3;
427             }else{
428 158           zsp->avail_in = (uInt)diflen;
429 158           x = 4;
430             }
431 316 100         }else if(x < 6){
432 158 50         if(x == 4)
433 158           zsp->next_in = dcp->dc_extra_dat;
434 158 50         if(extlen > s__BSDIPA_IO_ZLIB_LIMIT){
435 0           zsp->avail_in = s__BSDIPA_IO_ZLIB_LIMIT;
436 0           extlen -= s__BSDIPA_IO_ZLIB_LIMIT;
437 0           x = 5;
438             }else{
439 158           zsp->avail_in = (uInt)extlen;
440 158           x = 6;
441             }
442             }else{
443 158           zsp->avail_in = 0; /* xxx redundant */
444 158           flusht = Z_FINISH;
445 158           x = 7;
446             }
447              
448 790 100         if(zsp->avail_in > 0 || flusht == Z_FINISH) for(;;){
    100          
449             s_bsdipa_off_t z;
450             int y;
451              
452 632           y = deflate(zsp, flusht);
453              
454 632           switch(y){
455 474           case Z_OK: break;
456 158           case Z_STREAM_END: assert(flusht == Z_FINISH); break;
457 0           case Z_BUF_ERROR: assert(zsp->avail_out == 0); break;
458 0           default: /* FALLTHRU */
459 0           case Z_STREAM_ERROR: rv = s_BSDIPA_INVAL; goto jdone;
460             }
461              
462 632           z = (s_bsdipa_off_t)(olen - zsp->avail_out);
463 632 100         if(y == Z_STREAM_END || (z > 0 && zsp->avail_out == 0)){
    50          
    50          
464             int xarg;
465              
466             /* */
467 158 50         if(y != Z_STREAM_END){
468 0 0         if(try_oneshot < 0)
469 0           try_oneshot = 1;
470 0           xarg = 0;
471             }else
472 158 100         xarg = (try_oneshot < 0) ? -(int)(s_bsdipa_off_t)++olen : 1;
473              
474 158 50         if((rv = (*hook)(hook_cookie, obuf, z, xarg)) != s_BSDIPA_OK)
475 0           goto jdone;
476              
477 158 50         if(xarg){
478             /* Did we transfer buffer ownership? */
479 158 100         if(xarg < 0)
480 110           obuf = NULL;
481 158           goto jdone;
482             }
483 0           zsp->next_out = obuf;
484 0           zsp->avail_out = (uInt)olen;
485             }
486              
487 474 50         if(flusht == Z_FINISH){
488             assert(y != Z_STREAM_END);
489 0           continue;
490             }
491 474 50         if(zsp->avail_in == 0)
492 474           break;
493             /* Different to documentation this happens! */
494             }
495             assert(x != 7);
496             }
497              
498 158           jdone:
499 158 100         if(obuf != NULL)
500 48           s__bsdipa_io_zlib_free((void*)&dcp->dc_mem, obuf);
501              
502 158           deflateEnd(zsp);
503 158           jleave:
504 158           return rv;
505             }
506             # endif /* }}} s_BSDIPA_IO_WRITE */
507              
508             # ifdef s_BSDIPA_IO_READ /* {{{ */
509             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
510 237           s_bsdipa_io_read_zlib(struct s_bsdipa_patch_ctx *pcp, struct s_bsdipa_io_cookie *io_cookie_or_null){
511             uint8_t hbuf[sizeof(struct s_bsdipa_header)];
512             z_stream zs;
513             s_bsdipa_off_t reslen;
514             enum s_bsdipa_state rv;
515             z_streamp zsp;
516             uint64_t patlen;
517             (void)io_cookie_or_null;
518              
519 237           pcp->pc_restored_dat = NULL;
520 237           patlen = pcp->pc_patch_len;
521              
522             /* make inflateEnd() callable; Without too much effort: we need to make available an entire frame */
523 237           zsp = &zs;
524 237           zs.zalloc = &s__bsdipa_io_zlib_alloc;
525 237           zs.zfree = &s__bsdipa_io_zlib_free;
526 237           zs.opaque = (void*)&pcp->pc_mem;
527              
528 237           zs.next_in = (Bytef z_const*)(void*)pcp->pc_patch_dat;
529 237 50         zs.avail_in = (patlen >= INT32_MAX - 1) ? INT32_MAX - 1 : (uInt)patlen;
530              
531 237           switch(inflateInit(zsp)){
532 237           case Z_OK: break;
533 0           case Z_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
534 0           default: rv = s_BSDIPA_INVAL; goto jdone;
535             }
536              
537 237           zsp->next_out = hbuf;
538 237           zsp->avail_out = sizeof(hbuf);
539              
540 237           switch(inflate(zsp, Z_SYNC_FLUSH)){
541 237           case Z_OK: break;
542 0           case Z_STREAM_END: break;
543 0           case Z_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
544 0           default: rv = s_BSDIPA_INVAL; goto jdone;
545             }
546 237 50         if(zsp->avail_out != 0){
547 0           rv = s_BSDIPA_INVAL;
548 0           goto jdone;
549             }
550              
551 237           rv = s_bsdipa_patch_parse_header(&pcp->pc_header, hbuf);
552 237 50         if(rv != s_BSDIPA_OK)
553 0           goto jdone;
554              
555             /* Do not perform any action at all on size excess */
556 237 100         if(pcp->pc_max_allowed_restored_len != 0 &&
557 158 100         pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len){
558 79           rv = s_BSDIPA_FBIG;
559 79           goto jdone;
560             }
561              
562             /* Guaranteed to work! */
563 158           reslen = pcp->pc_header.h_ctrl_len + pcp->pc_header.h_diff_len + pcp->pc_header.h_extra_len;
564              
565             /* But allocator may not deal */
566 158 50         if((size_t)reslen != (uInt)reslen){
567 0           rv = s_BSDIPA_NOMEM;
568 0           goto jdone;
569             }
570 158           pcp->pc_restored_len = reslen;
571 158           pcp->pc_restored_dat = (uint8_t*)s__bsdipa_io_zlib_alloc(&pcp->pc_mem, 1, (uInt)reslen);
572 158 50         if(pcp->pc_restored_dat == NULL){
573 0           rv = s_BSDIPA_NOMEM;
574 0           goto jdone;
575             }
576              
577 158           zsp->next_out = pcp->pc_restored_dat;
578 158 50         zsp->avail_out = (reslen > s__BSDIPA_IO_ZLIB_LIMIT) ? s__BSDIPA_IO_ZLIB_LIMIT : (uInt)reslen;
579 158           reslen -= zsp->avail_out;
580              
581 158           patlen -= (char const*)zsp->next_in - (char*)(void*)pcp->pc_patch_dat;
582 158 50         zsp->avail_in = (patlen > s__BSDIPA_IO_ZLIB_LIMIT) ? s__BSDIPA_IO_ZLIB_LIMIT : (uInt)patlen;
583 158           patlen -= zsp->avail_in;
584              
585 0           for(;;){
586             int x, y;
587              
588 158 50         x = (reslen == 0 && patlen == 0) ? Z_FINISH : Z_NO_FLUSH;
    50          
589 158           y = inflate(zsp, x);
590              
591 158           switch(y){
592 0           case Z_OK: break;
593 0           case Z_BUF_ERROR:
594 0 0         if(x == Z_FINISH){
595 0           rv = s_BSDIPA_INVAL;
596 0           goto jdone;
597             }
598 0           break;
599 158           case Z_STREAM_END:
600 158 50         if(x == Z_FINISH){
601 158           rv = s_BSDIPA_OK;
602 158           goto jdone;
603             }
604 0           break;
605 0           case Z_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
606 0           default: rv = s_BSDIPA_INVAL; goto jdone;
607             }
608              
609 0 0         if(zsp->avail_out == 0){
610 0 0         zsp->avail_out = (uInt)((reslen > s__BSDIPA_IO_ZLIB_LIMIT) ? s__BSDIPA_IO_ZLIB_LIMIT : reslen);
611 0           reslen -= (s_bsdipa_off_t)zsp->avail_out;
612             }
613 0 0         if(zsp->avail_in == 0){
614 0 0         zsp->avail_in = (uInt)((patlen > s__BSDIPA_IO_ZLIB_LIMIT) ? s__BSDIPA_IO_ZLIB_LIMIT : patlen);
615 0           patlen -= zsp->avail_in;
616             }
617             }
618              
619 237           jdone:
620 237           inflateEnd(zsp);
621              
622 237 100         if(rv != s_BSDIPA_OK && pcp->pc_restored_dat != NULL){
    50          
623 0           s__bsdipa_io_zlib_free(&pcp->pc_mem, pcp->pc_restored_dat);
624 0           pcp->pc_restored_dat = NULL;
625             }
626              
627 237           return rv;
628             }
629             # endif /* }}} s_BSDIPA_IO_READ */
630              
631             # undef s__BSDIPA_IO_ZLIB_LIMIT
632             # undef s_BSDIPA_IO_ZLIB_LEVEL
633             /* }}} */
634              
635             #elif s_BSDIPA_IO == s_BSDIPA_IO_XZ /* _IO_ZLIB {{{ */
636             /*# undef s_BSDIPA_IO*/
637             # ifdef s__BSDIPA_IO_XZ
638             # error s_BSDIPA_IO==s_BSDIPA_IO_XZ already defined
639             # endif
640             # define s__BSDIPA_IO_XZ
641             # define s_BSDIPA_IO_NAME s_BSDIPA_IO_NAME_XZ
642              
643             # include
644              
645             # include
646              
647             # ifndef s_BSDIPA_IO_XZ_PRESET
648             # define s_BSDIPA_IO_XZ_PRESET 8
649             # endif
650             # ifndef s_BSDIPA_IO_XZ_CHECK
651             # ifdef s_BSDIPA_32
652             # define s_BSDIPA_IO_XZ_CHECK LZMA_CHECK_CRC32
653             # else
654             # define s_BSDIPA_IO_XZ_CHECK LZMA_CHECK_CRC64
655             # endif
656             # endif
657              
658             /* For testing purposes */
659             # define s__BSDIPA_IO_XZ_LIMIT (INT32_MAX - 1)
660              
661             struct s_bsdipa_io_cookie_xz{
662             struct s_bsdipa_io_cookie iocx_super;
663             struct s_bsdipa_memory_ctx iocx_mctx;
664             lzma_stream iocx_s;
665             lzma_allocator iocx_a;
666             };
667              
668             /* fun {{{ */
669             static void *s__bsdipa_io_xz_alloc(void *my_cookie, size_t no, size_t size);
670             static void s__bsdipa_io_xz_free(void *my_cookie, void *dat);
671             static inline lzma_stream *s__bsdipa_io_cookie_create_xz(struct s_bsdipa_io_cookie *iocp,
672             struct s_bsdipa_memory_ctx const *mcp);
673             s_BSDIPA_IO_LINKAGE void s_bsdipa_io_cookie_gut_xz(struct s_bsdipa_io_cookie *iocp);
674              
675             static void *
676             s__bsdipa_io_xz_alloc(void *my_cookie, size_t no, size_t size){
677             void *rv;
678             size_t memsz;
679             struct s_bsdipa_memory_ctx *mcp;
680              
681             mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
682             memsz = no * size;
683              
684             rv = (mcp->mc_alloc != NULL) ? (*mcp->mc_alloc)(memsz) : (*mcp->mc_custom_alloc)(mcp->mc_custom_cookie, memsz);
685              
686             return rv;
687             }
688              
689             static void
690             s__bsdipa_io_xz_free(void *my_cookie, void *dat){
691             struct s_bsdipa_memory_ctx *mcp;
692              
693             mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
694              
695             /* (lzma/base.h does not say, but not only what came via alloc()..) */
696             if(dat != NULL)
697             (mcp->mc_alloc != NULL) ? (*mcp->mc_free)(dat) : (*mcp->mc_custom_free)(mcp->mc_custom_cookie, dat);
698             }
699              
700             static inline lzma_stream *
701             s__bsdipa_io_cookie_create_xz(struct s_bsdipa_io_cookie *iocp, struct s_bsdipa_memory_ctx const *mcp){
702             struct s_bsdipa_io_cookie_xz *iocxp;
703              
704             iocxp = (struct s_bsdipa_io_cookie_xz*)(void*)iocp;
705              
706             if(!iocxp->iocx_super.ioc_is_init){
707             iocxp->iocx_super.ioc_is_init = 1;
708             if(iocxp->iocx_super.ioc_level == 0)
709             iocxp->iocx_super.ioc_level = s_BSDIPA_IO_XZ_PRESET;
710             iocxp->iocx_mctx = *mcp;
711             iocxp->iocx_a.alloc = &s__bsdipa_io_xz_alloc;
712             iocxp->iocx_a.free = &s__bsdipa_io_xz_free;
713             iocxp->iocx_a.opaque = (void*)&iocxp->iocx_mctx;
714             iocxp->iocx_s.allocator = &iocxp->iocx_a;
715             }
716              
717             return &iocxp->iocx_s;
718             }
719              
720             s_BSDIPA_IO_LINKAGE void
721             s_bsdipa_io_cookie_gut_xz(struct s_bsdipa_io_cookie *iocp){
722             if(iocp != NULL && iocp->ioc_is_init && iocp->ioc_type == s_BSDIPA_IO_XZ){
723             struct s_bsdipa_io_cookie_xz *iocxp;
724              
725             iocxp = (struct s_bsdipa_io_cookie_xz*)(void*)iocp;
726             lzma_end(&iocxp->iocx_s);
727             }
728             }
729             /* }}} */
730              
731             # ifdef s_BSDIPA_IO_WRITE /* {{{ */
732             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
733             s_bsdipa_io_write_xz(struct s_bsdipa_diff_ctx const *dcp, s_bsdipa_io_write_ptf hook, void *hook_cookie,
734             int try_oneshot, struct s_bsdipa_io_cookie *io_cookie_or_null){
735             lzma_stream zs, *zsp;
736             lzma_allocator za;
737             struct s_bsdipa_ctrl_chunk *ccp;
738             char x;
739             enum s_bsdipa_state rv;
740             uint8_t *obuf;
741             size_t olen;
742             s_bsdipa_off_t diflen, extlen;
743             uint32_t preset;
744              
745             if(io_cookie_or_null == NULL || io_cookie_or_null->ioc_type != s_BSDIPA_IO_XZ){
746             io_cookie_or_null = NULL;
747             memset(zsp = &zs, 0, sizeof(zs));
748             za.alloc = &s__bsdipa_io_xz_alloc;
749             za.free = &s__bsdipa_io_xz_free;
750             za.opaque = (void*)&dcp->dc_mem;
751             zsp->allocator = &za;
752             preset = s_BSDIPA_IO_XZ_PRESET;
753             }else{
754             zsp = s__bsdipa_io_cookie_create_xz(io_cookie_or_null, &dcp->dc_mem);
755             preset = io_cookie_or_null->ioc_level;
756             }
757              
758             switch(lzma_easy_encoder(zsp, preset, s_BSDIPA_IO_XZ_CHECK)){
759             case LZMA_OK: break;
760             case LZMA_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jleave;
761             /* LZMA_OPTIONS_ERROR: */
762             /* LZMA_UNSUPPORTED_CHECK: */
763             /* LZMA_PROG_ERROR: */
764             default: rv = s_BSDIPA_INVAL; goto jleave;
765             }
766              
767             diflen = dcp->dc_diff_len;
768             extlen = dcp->dc_extra_len;
769              
770             /* All lengths fit in s_BSDIPA_OFF_MAX, which is signed: addition and cast ok */
771             olen = (size_t)((s_bsdipa_off_t)sizeof(dcp->dc_header) + dcp->dc_ctrl_len + diflen + extlen);
772             if(try_oneshot){
773             size_t ulo;
774              
775             ulo = olen; /* XXX check overflow? s_bsdipa_off_t>size_t case? */
776             ulo = lzma_stream_buffer_bound(ulo);
777             if(ulo >= s_BSDIPA_OFF_MAX){
778             try_oneshot = 0;
779             goto jolenmax;
780             }
781             /* Add "one additional byte" already here in case buffer takeover succeeds */
782             if(ulo >= (size_t)-1 - 1){
783             try_oneshot = 0;
784             goto jolenmax;
785             }
786             olen = ++ulo;
787             }else if(olen <= 1000 * 150)
788             olen = 4096 * 4;
789             else if(olen <= 1000 * 1000)
790             olen = 4096 * 31;
791             else
792             jolenmax:
793             olen = 4096 * 244;
794              
795             obuf = (uint8_t*)s__bsdipa_io_xz_alloc((void*)&dcp->dc_mem, 1, olen);
796             if(obuf == NULL){
797             rv = s_BSDIPA_NOMEM;
798             goto jdone;
799             }
800             olen -= (try_oneshot != 0);
801              
802             zsp->next_out = obuf;
803             zsp->avail_out = olen;
804             ccp = dcp->dc_ctrl;
805              
806             for(x = 0;;){
807             lzma_action flusht;
808              
809             flusht = LZMA_RUN;
810             if(x == 0){
811             zsp->next_in = dcp->dc_header;
812             zsp->avail_in = sizeof(dcp->dc_header);
813             x = 1;
814             }else if(x == 1){
815             if(ccp != NULL){
816             zsp->next_in = ccp->cc_dat;
817             zsp->avail_in = (size_t)ccp->cc_len;
818             ccp = ccp->cc_next;
819             }
820             if(ccp == NULL)
821             x = 2;
822             }else if(x < 4){
823             if(x == 2)
824             zsp->next_in = dcp->dc_diff_dat;
825             if(diflen > s__BSDIPA_IO_XZ_LIMIT){
826             zsp->avail_in = s__BSDIPA_IO_XZ_LIMIT;
827             diflen -= s__BSDIPA_IO_XZ_LIMIT;
828             x = 3;
829             }else{
830             zsp->avail_in = (size_t)diflen;
831             x = 4;
832             }
833             }else if(x < 6){
834             if(x == 4)
835             zsp->next_in = dcp->dc_extra_dat;
836             if(extlen > s__BSDIPA_IO_XZ_LIMIT){
837             zsp->avail_in = s__BSDIPA_IO_XZ_LIMIT;
838             extlen -= s__BSDIPA_IO_XZ_LIMIT;
839             x = 5;
840             }else{
841             zsp->avail_in = (size_t)extlen;
842             x = 6;
843             }
844             }else{
845             zsp->avail_in = 0; /* xxx redundant */
846             flusht = LZMA_FINISH;
847             x = 7;
848             }
849              
850             if(zsp->avail_in > 0 || flusht == LZMA_FINISH) for(;;){
851             s_bsdipa_off_t z;
852             lzma_ret y;
853              
854             y = lzma_code(zsp, flusht);
855              
856             switch(y){
857             case LZMA_OK: break;
858             case LZMA_MEM_ERROR: rv = s_BSDIPA_NOMEM; break;
859             case LZMA_STREAM_END: assert(flusht == LZMA_FINISH); break;
860             case LZMA_BUF_ERROR: assert(zsp->avail_out == 0); break;
861             default: rv = s_BSDIPA_INVAL; goto jdone;
862             }
863              
864             z = (s_bsdipa_off_t)(olen - zsp->avail_out);
865             if(y == LZMA_STREAM_END || (z > 0 && zsp->avail_out == 0)){
866             int xarg;
867              
868             /* */
869             if(y != LZMA_STREAM_END){
870             if(try_oneshot < 0)
871             try_oneshot = 1;
872             xarg = 0;
873             }else
874             xarg = (try_oneshot < 0) ? -(int)(s_bsdipa_off_t)++olen : 1;
875              
876             if((rv = (*hook)(hook_cookie, obuf, z, xarg)) != s_BSDIPA_OK)
877             goto jdone;
878              
879             if(xarg){
880             /* Did we transfer buffer ownership? */
881             if(xarg < 0)
882             obuf = NULL;
883             goto jdone;
884             }
885             zsp->next_out = obuf;
886             zsp->avail_out = olen;
887             }
888              
889             if(flusht == LZMA_FINISH){
890             assert(y != LZMA_STREAM_END);
891             continue;
892             }
893             if(zsp->avail_in == 0)
894             break;
895             }
896             assert(x != 7);
897             }
898              
899             jdone:
900             if(obuf != NULL)
901             s__bsdipa_io_xz_free((void*)&dcp->dc_mem, obuf);
902              
903             if(io_cookie_or_null == NULL)
904             lzma_end(zsp);
905              
906             jleave:
907             return rv;
908             }
909             # endif /* }}} s_BSDIPA_IO_WRITE */
910              
911             # ifdef s_BSDIPA_IO_READ /* {{{ */
912             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
913             s_bsdipa_io_read_xz(struct s_bsdipa_patch_ctx *pcp, struct s_bsdipa_io_cookie *io_cookie_or_null){
914             uint8_t hbuf[sizeof(struct s_bsdipa_header)];
915             lzma_stream zs, *zsp;
916             lzma_allocator za;
917             s_bsdipa_off_t reslen;
918             enum s_bsdipa_state rv;
919             uint64_t patlen;
920              
921             pcp->pc_restored_dat = NULL;
922             patlen = pcp->pc_patch_len;
923              
924             if(io_cookie_or_null == NULL || io_cookie_or_null->ioc_type != s_BSDIPA_IO_XZ){
925             io_cookie_or_null = NULL;
926             memset(zsp = &zs, 0, sizeof(zs));
927             za.alloc = &s__bsdipa_io_xz_alloc;
928             za.free = &s__bsdipa_io_xz_free;
929             za.opaque = (void*)&pcp->pc_mem;
930             zsp->allocator = &za;
931             }else
932             zsp = s__bsdipa_io_cookie_create_xz(io_cookie_or_null, &pcp->pc_mem);
933              
934             /* Without too much effort: we need to make available an entire frame */
935             zsp->next_in = pcp->pc_patch_dat;
936             zsp->avail_in = (patlen >= INT32_MAX - 1) ? INT32_MAX - 1 : (size_t)patlen;
937              
938             switch(lzma_stream_decoder(zsp, UINT64_MAX, 0
939             # ifdef LZMA_FAIL_FAST
940             | LZMA_FAIL_FAST
941             # endif
942             )){
943             case LZMA_OK: break;
944             case LZMA_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
945             /* LZMA_OPTIONS_ERROR: */
946             /* LZMA_PROG_ERROR: */
947             default: rv = s_BSDIPA_INVAL; goto jdone;
948             }
949              
950             zsp->next_out = hbuf;
951             zsp->avail_out = sizeof(hbuf);
952              
953             switch(lzma_code(zsp, LZMA_RUN)){
954             case LZMA_OK: break;
955             case LZMA_STREAM_END: break;
956             case LZMA_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
957             default: rv = s_BSDIPA_INVAL; goto jdone;
958             }
959             if(zsp->avail_out != 0){
960             rv = s_BSDIPA_INVAL;
961             goto jdone;
962             }
963              
964             rv = s_bsdipa_patch_parse_header(&pcp->pc_header, hbuf);
965             if(rv != s_BSDIPA_OK)
966             goto jdone;
967              
968             /* Do not perform any action at all on size excess */
969             if(pcp->pc_max_allowed_restored_len != 0 &&
970             pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len){
971             rv = s_BSDIPA_FBIG;
972             goto jdone;
973             }
974              
975             /* Guaranteed to work! */
976             reslen = pcp->pc_header.h_ctrl_len + pcp->pc_header.h_diff_len + pcp->pc_header.h_extra_len;
977              
978             pcp->pc_restored_len = reslen;
979             pcp->pc_restored_dat = (uint8_t*)s__bsdipa_io_xz_alloc(&pcp->pc_mem, 1, (size_t)reslen);
980             if(pcp->pc_restored_dat == NULL){
981             rv = s_BSDIPA_NOMEM;
982             goto jdone;
983             }
984              
985             zsp->next_out = pcp->pc_restored_dat;
986             zsp->avail_out = (size_t)((reslen > s__BSDIPA_IO_XZ_LIMIT) ? s__BSDIPA_IO_XZ_LIMIT : reslen);
987             reslen -= zsp->avail_out;
988              
989             patlen -= zsp->next_in - pcp->pc_patch_dat;
990             zsp->avail_in = (size_t)((patlen > s__BSDIPA_IO_XZ_LIMIT) ? s__BSDIPA_IO_XZ_LIMIT : patlen);
991             patlen -= zsp->avail_in;
992              
993             for(;;){
994             lzma_ret y;
995             lzma_action x;
996              
997             x = (reslen == 0 && patlen == 0) ? LZMA_FINISH : LZMA_RUN;
998             y = lzma_code(zsp, x);
999              
1000             switch(y){
1001             case LZMA_OK: break;
1002             case LZMA_BUF_ERROR:
1003             if(x == LZMA_FINISH){
1004             rv = s_BSDIPA_INVAL;
1005             goto jdone;
1006             }
1007             break;
1008             case LZMA_STREAM_END:
1009             if(x == LZMA_FINISH){
1010             rv = s_BSDIPA_OK;
1011             goto jdone;
1012             }
1013             break;
1014             case LZMA_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
1015             default: rv = s_BSDIPA_INVAL; goto jdone;
1016             }
1017              
1018             if(zsp->avail_out == 0){
1019             zsp->avail_out = (size_t)((reslen > s__BSDIPA_IO_XZ_LIMIT) ? s__BSDIPA_IO_XZ_LIMIT : reslen);
1020             reslen -= (s_bsdipa_off_t)zsp->avail_out;
1021             }
1022             if(zsp->avail_in == 0){
1023             zsp->avail_in = (size_t)((patlen > s__BSDIPA_IO_XZ_LIMIT) ? s__BSDIPA_IO_XZ_LIMIT : patlen);
1024             patlen -= zsp->avail_in;
1025             }
1026             }
1027              
1028             jdone:
1029             if(io_cookie_or_null == NULL)
1030             lzma_end(zsp);
1031              
1032             if(rv != s_BSDIPA_OK && pcp->pc_restored_dat != NULL){
1033             s__bsdipa_io_xz_free(&pcp->pc_mem, pcp->pc_restored_dat);
1034             pcp->pc_restored_dat = NULL;
1035             }
1036              
1037             return rv;
1038             }
1039             # endif /* }}} s_BSDIPA_IO_READ */
1040              
1041             # undef s__BSDIPA_IO_XZ_LIMIT
1042             # undef s_BSDIPA_IO_XZ_CHECK
1043             # undef s_BSDIPA_IO_XZ_PRESET
1044             /* }}} */
1045              
1046             #elif s_BSDIPA_IO == s_BSDIPA_IO_BZ2 /* _IO_XZ {{{ */
1047             /*# undef s_BSDIPA_IO*/
1048             # ifdef s__BSDIPA_IO_BZ2
1049             # error s_BSDIPA_IO==s_BSDIPA_IO_BZ2 already defined
1050             # endif
1051             # define s__BSDIPA_IO_BZ2
1052             # define s_BSDIPA_IO_NAME s_BSDIPA_IO_NAME_BZ2
1053              
1054             # include
1055              
1056             # include
1057              
1058             # ifndef s_BSDIPA_IO_BZ2_BLOCKSIZE
1059             # define s_BSDIPA_IO_BZ2_BLOCKSIZE 9
1060             # endif
1061             # ifndef s_BSDIPA_IO_BZ2_VERBOSITY
1062             # define s_BSDIPA_IO_BZ2_VERBOSITY 0
1063             # endif
1064             # ifndef s_BSDIPA_IO_BZ2_SMALL
1065             # define s_BSDIPA_IO_BZ2_SMALL 0
1066             # endif
1067              
1068             /* For testing purposes */
1069             # define s__BSDIPA_IO_BZ2_LIMIT (INT32_MAX - 1)
1070              
1071             static void *s__bsdipa_io_bz2_alloc(void *my_cookie, int no, int size);
1072             static void s__bsdipa_io_bz2_free(void *my_cookie, void *dat);
1073              
1074             static void *
1075             s__bsdipa_io_bz2_alloc(void *my_cookie, int no, int size){
1076             void *rv;
1077             size_t memsz;
1078             struct s_bsdipa_memory_ctx *mcp;
1079              
1080             mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
1081             memsz = (size_t)no * (size_t)size;
1082              
1083             rv = (mcp->mc_alloc != NULL) ? (*mcp->mc_alloc)(memsz) : (*mcp->mc_custom_alloc)(mcp->mc_custom_cookie, memsz);
1084              
1085             return rv;
1086             }
1087              
1088             static void
1089             s__bsdipa_io_bz2_free(void *my_cookie, void *dat){
1090             struct s_bsdipa_memory_ctx *mcp;
1091              
1092             mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
1093              
1094             (mcp->mc_alloc != NULL) ? (*mcp->mc_free)(dat) : (*mcp->mc_custom_free)(mcp->mc_custom_cookie, dat);
1095             }
1096              
1097             # ifdef s_BSDIPA_IO_WRITE /* {{{ */
1098             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
1099             s_bsdipa_io_write_bz2(struct s_bsdipa_diff_ctx const *dcp, s_bsdipa_io_write_ptf hook, void *hook_cookie,
1100             int try_oneshot, struct s_bsdipa_io_cookie *io_cookie_or_null){
1101             bz_stream bzs;
1102             struct s_bsdipa_ctrl_chunk *ccp;
1103             char x;
1104             enum s_bsdipa_state rv;
1105             uint8_t *obuf;
1106             size_t olen;
1107             s_bsdipa_off_t diflen, extlen;
1108              
1109             bzs.bzalloc = &s__bsdipa_io_bz2_alloc;
1110             bzs.bzfree = &s__bsdipa_io_bz2_free;
1111             bzs.opaque = (void*)&dcp->dc_mem;
1112              
1113             /* C99 */{
1114             int level;
1115              
1116             level = s_BSDIPA_IO_BZ2_BLOCKSIZE;
1117             if(io_cookie_or_null != NULL && io_cookie_or_null->ioc_level != 0)
1118             level = (int)io_cookie_or_null->ioc_level;
1119              
1120             switch(BZ2_bzCompressInit(&bzs, level, s_BSDIPA_IO_BZ2_VERBOSITY, 0)){
1121             case BZ_OK: break;
1122             case BZ_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jleave;
1123             default: rv = s_BSDIPA_INVAL; goto jleave;
1124             }
1125             }
1126              
1127             diflen = dcp->dc_diff_len;
1128             extlen = dcp->dc_extra_len;
1129              
1130             /* All lengths fit in s_BSDIPA_OFF_MAX, which is signed: addition and cast ok */
1131             olen = (size_t)((s_bsdipa_off_t)sizeof(dcp->dc_header) + dcp->dc_ctrl_len + diflen + extlen);
1132             if(try_oneshot){
1133             size_t i;
1134              
1135             /* Random data (eg compressor output) requires ~0.5% expansion; be safe and go for 10% */
1136             i = olen / 10;
1137             if(olen >= s_BSDIPA_OFF_MAX - i){
1138             try_oneshot = 0;
1139             goto jolenmax;
1140             }
1141             /* Add "one additional byte" already here in case buffer takeover succeeds */
1142             i += ++olen;
1143             if(i != (size_t)(int)i){
1144             try_oneshot = 0;
1145             goto jolenmax;
1146             }
1147             olen = i;
1148             }else if(olen <= 1000 * 150)
1149             olen = 4096 * 4;
1150             else if(olen <= 1000 * 1000)
1151             olen = 4096 * 31;
1152             else
1153             jolenmax:
1154             olen = 4096 * 244;
1155              
1156             obuf = (uint8_t*)s__bsdipa_io_bz2_alloc((void*)&dcp->dc_mem, 1, (int)olen);
1157             if(obuf == NULL){
1158             rv = s_BSDIPA_NOMEM;
1159             goto jdone;
1160             }
1161             olen -= (try_oneshot != 0);
1162              
1163             bzs.next_out = (char*)obuf;
1164             bzs.avail_out = (unsigned int)olen;
1165             ccp = dcp->dc_ctrl;
1166              
1167             for(x = 0;;){
1168             int flusht;
1169              
1170             flusht = BZ_RUN;
1171             if(x == 0){
1172             bzs.next_in = (char*)(void*)dcp->dc_header;
1173             bzs.avail_in = sizeof(dcp->dc_header);
1174             x = 1;
1175             }else if(x == 1){
1176             if(ccp != NULL){
1177             bzs.next_in = (char*)ccp->cc_dat;
1178             bzs.avail_in = (unsigned int)ccp->cc_len;
1179             ccp = ccp->cc_next;
1180             }
1181             if(ccp == NULL)
1182             x = 2;
1183             }else if(x < 4){
1184             if(x == 2)
1185             bzs.next_in = (char*)dcp->dc_diff_dat;
1186             if(diflen > s__BSDIPA_IO_BZ2_LIMIT){
1187             bzs.avail_in = s__BSDIPA_IO_BZ2_LIMIT;
1188             diflen -= s__BSDIPA_IO_BZ2_LIMIT;
1189             x = 3;
1190             }else{
1191             bzs.avail_in = (unsigned int)diflen;
1192             x = 4;
1193             }
1194             }else if(x < 6){
1195             if(x == 4)
1196             bzs.next_in = (char*)dcp->dc_extra_dat;
1197             if(extlen > s__BSDIPA_IO_BZ2_LIMIT){
1198             bzs.avail_in = s__BSDIPA_IO_BZ2_LIMIT;
1199             extlen -= s__BSDIPA_IO_BZ2_LIMIT;
1200             x = 5;
1201             }else{
1202             bzs.avail_in = (unsigned int)extlen;
1203             x = 6;
1204             }
1205             }else{
1206             bzs.avail_in = 0; /* xxx redundant */
1207             flusht = BZ_FINISH;
1208             x = 7;
1209             }
1210              
1211             if(bzs.avail_in > 0 || flusht == BZ_FINISH) for(;;){
1212             s_bsdipa_off_t z;
1213             int y;
1214              
1215             y = BZ2_bzCompress(&bzs, flusht);
1216              
1217             switch(y){
1218             case BZ_OK: /* FALLTHRU */
1219             case BZ_RUN_OK: /* FALLTHRU */
1220             case BZ_FINISH_OK: break;
1221             case BZ_STREAM_END: assert(flusht == BZ_FINISH); break;
1222             case BZ_OUTBUFF_FULL: assert(bzs.avail_out == 0); break;
1223             default: rv = s_BSDIPA_INVAL; goto jdone;
1224             }
1225              
1226             z = (s_bsdipa_off_t)(olen - bzs.avail_out);
1227             if(y == BZ_STREAM_END || (z > 0 && bzs.avail_out == 0)){
1228             int xarg;
1229              
1230             /* */
1231             if(y != BZ_STREAM_END){
1232             if(try_oneshot < 0)
1233             try_oneshot = 1;
1234             xarg = 0;
1235             }else
1236             xarg = (try_oneshot < 0) ? -(int)(s_bsdipa_off_t)++olen : 1;
1237              
1238             if((rv = (*hook)(hook_cookie, obuf, z, xarg)) != s_BSDIPA_OK)
1239             goto jdone;
1240              
1241             if(xarg){
1242             /* Did we transfer buffer ownership? */
1243             if(xarg < 0)
1244             obuf = NULL;
1245             goto jdone;
1246             }
1247             bzs.next_out = (char*)obuf;
1248             bzs.avail_out = (unsigned int)olen;
1249             }
1250              
1251             if(flusht == BZ_FINISH){
1252             assert(y != BZ_STREAM_END);
1253             continue;
1254             }
1255             if(bzs.avail_in == 0)
1256             break;
1257             }
1258             assert(x != 7);
1259             }
1260              
1261             jdone:
1262             if(obuf != NULL)
1263             s__bsdipa_io_bz2_free((void*)&dcp->dc_mem, obuf);
1264              
1265             BZ2_bzCompressEnd(&bzs);
1266             jleave:
1267             return rv;
1268             }
1269             # endif /* }}} s_BSDIPA_IO_WRITE */
1270              
1271             # ifdef s_BSDIPA_IO_READ /* {{{ */
1272             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
1273             s_bsdipa_io_read_bz2(struct s_bsdipa_patch_ctx *pcp, struct s_bsdipa_io_cookie *io_cookie_or_null){
1274             uint8_t hbuf[sizeof(struct s_bsdipa_header)];
1275             bz_stream bzs;
1276             s_bsdipa_off_t reslen;
1277             enum s_bsdipa_state rv;
1278             uint64_t patlen;
1279             (void)io_cookie_or_null;
1280              
1281             pcp->pc_restored_dat = NULL;
1282             patlen = pcp->pc_patch_len;
1283              
1284             bzs.bzalloc = &s__bsdipa_io_bz2_alloc;
1285             bzs.bzfree = &s__bsdipa_io_bz2_free;
1286             bzs.opaque = (void*)&pcp->pc_mem;
1287              
1288             /* Without too much effort: we need to make available an entire frame */
1289             bzs.next_in = (char*)(void*)pcp->pc_patch_dat;
1290             bzs.avail_in = (patlen >= INT32_MAX - 1) ? INT32_MAX - 1 : (unsigned int)patlen;
1291              
1292             switch(BZ2_bzDecompressInit(&bzs, s_BSDIPA_IO_BZ2_VERBOSITY, s_BSDIPA_IO_BZ2_SMALL)){
1293             case BZ_OK: break;
1294             case BZ_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
1295             default: rv = s_BSDIPA_INVAL; goto jdone;
1296             }
1297              
1298             bzs.next_out = (char*)hbuf;
1299             bzs.avail_out = sizeof(hbuf);
1300              
1301             switch(BZ2_bzDecompress(&bzs)){
1302             case BZ_OK: assert(bzs.next_out != NULL); break;
1303             case BZ_STREAM_END: bzs.next_out = NULL; break;
1304             case BZ_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
1305             default: rv = s_BSDIPA_INVAL; goto jdone;
1306             }
1307             if(bzs.avail_out != 0){
1308             rv = s_BSDIPA_INVAL;
1309             goto jdone;
1310             }
1311              
1312             rv = s_bsdipa_patch_parse_header(&pcp->pc_header, hbuf);
1313             if(rv != s_BSDIPA_OK)
1314             goto jdone;
1315              
1316             /* Do not perform any action at all on size excess */
1317             if(pcp->pc_max_allowed_restored_len != 0 &&
1318             pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len){
1319             rv = s_BSDIPA_FBIG;
1320             goto jdone;
1321             }
1322              
1323             /* Guaranteed to work! */
1324             reslen = pcp->pc_header.h_ctrl_len + pcp->pc_header.h_diff_len + pcp->pc_header.h_extra_len;
1325              
1326             /* But allocator may not deal */
1327             if((size_t)reslen != (size_t)(int)reslen){
1328             rv = s_BSDIPA_NOMEM;
1329             goto jdone;
1330             }
1331             pcp->pc_restored_len = reslen;
1332             pcp->pc_restored_dat = (uint8_t*)s__bsdipa_io_bz2_alloc(&pcp->pc_mem, 1, (int)reslen);
1333             if(pcp->pc_restored_dat == NULL){
1334             rv = s_BSDIPA_NOMEM;
1335             goto jdone;
1336             }
1337              
1338             /* BZ2 does not like continuing after a STREAM_END! */
1339             if(bzs.next_out == NULL){
1340             rv = s_BSDIPA_OK;
1341             goto jdone;
1342             }
1343              
1344             bzs.next_out = (char*)pcp->pc_restored_dat;
1345             bzs.avail_out = (reslen > s__BSDIPA_IO_BZ2_LIMIT) ? s__BSDIPA_IO_BZ2_LIMIT : (unsigned int)reslen;
1346             reslen -= bzs.avail_out;
1347              
1348             patlen -= bzs.next_in - (char const*)pcp->pc_patch_dat;
1349             bzs.avail_in = (patlen > s__BSDIPA_IO_BZ2_LIMIT) ? s__BSDIPA_IO_BZ2_LIMIT : (unsigned int)patlen;
1350             patlen -= (unsigned int)bzs.avail_in;
1351              
1352             for(;;){
1353             int x, y;
1354              
1355             x = (reslen == 0 && patlen == 0) ? BZ_FINISH : BZ_RUN;
1356             y = BZ2_bzDecompress(&bzs);
1357              
1358             switch(y){
1359             case BZ_OK: break;
1360             case BZ_STREAM_END:
1361             if(x == BZ_FINISH){
1362             rv = s_BSDIPA_OK;
1363             goto jdone;
1364             }
1365             break;
1366             case BZ_MEM_ERROR: rv = s_BSDIPA_NOMEM; goto jdone;
1367             default: rv = s_BSDIPA_INVAL; goto jdone;
1368             }
1369              
1370             if(bzs.avail_out == 0){
1371             bzs.avail_out = (unsigned int)((reslen > s__BSDIPA_IO_BZ2_LIMIT)
1372             ? s__BSDIPA_IO_BZ2_LIMIT : reslen);
1373             reslen -= (s_bsdipa_off_t)bzs.avail_out;
1374             }
1375             if(bzs.avail_in == 0){
1376             bzs.avail_in = (unsigned int)((patlen > s__BSDIPA_IO_BZ2_LIMIT)
1377             ? s__BSDIPA_IO_BZ2_LIMIT : patlen);
1378             patlen -= bzs.avail_in;
1379             }
1380             }
1381              
1382             jdone:
1383             BZ2_bzDecompressEnd(&bzs);
1384              
1385             if(rv != s_BSDIPA_OK && pcp->pc_restored_dat != NULL){
1386             s__bsdipa_io_bz2_free(&pcp->pc_mem, pcp->pc_restored_dat);
1387             pcp->pc_restored_dat = NULL;
1388             }
1389              
1390             return rv;
1391             }
1392             # endif /* }}} s_BSDIPA_IO_READ */
1393              
1394             # undef s__BSDIPA_IO_BZ2_LIMIT
1395             # undef s_BSDIPA_IO_BZ2_SMALL
1396             # undef s_BSDIPA_IO_BZ2_VERBOSITY
1397             # undef s_BSDIPA_IO_BZ2_BLOCKSIZE
1398             /* }}} */
1399              
1400             #elif s_BSDIPA_IO == s_BSDIPA_IO_ZSTD /* _IO_BZ2 {{{ */
1401             /*# undef s_BSDIPA_IO*/
1402             # ifdef s__BSDIPA_IO_ZSTD
1403             # error s_BSDIPA_IO==s_BSDIPA_IO_ZSTD already defined
1404             # endif
1405             # define s__BSDIPA_IO_ZSTD
1406             # define s_BSDIPA_IO_NAME s_BSDIPA_IO_NAME_ZSTD
1407              
1408             /* Give us the interface we want; libraries export it, anyway */
1409             # define ZSTD_STATIC_LINKING_ONLY
1410             # include
1411              
1412             # if ZSTD_VERSION_MAJOR < 1 || (ZSTD_VERSION_MAJOR == 1 && ZSTD_VERSION_MINOR < 4)
1413             # error S-bsdipa ZSTD I/O believes it requires zstd v1.4.0 or above
1414             # endif
1415              
1416             # ifndef s_BSDIPA_IO_ZSTD_LEVEL
1417             # define s_BSDIPA_IO_ZSTD_LEVEL 8
1418             # endif
1419             # ifndef s_BSDIPA_IO_ZSTD_CHECKSUM
1420             # define s_BSDIPA_IO_ZSTD_CHECKSUM 1
1421             # endif
1422              
1423             /* For testing purposes */
1424             # define s__BSDIPA_IO_ZSTD_LIMIT (INT32_MAX - 1)
1425              
1426             struct s_bsdipa_io_cookie_zstd{
1427             struct s_bsdipa_io_cookie iocZ_super;
1428             struct s_bsdipa_memory_ctx iocZ_mctx;
1429             ZSTD_customMem iocZ_zcm;
1430             ZSTD_CCtx *iocZ_zcp;
1431             ZSTD_DCtx *iocZ_zdp;
1432             };
1433              
1434             /* fun {{{ */
1435             static void *s__bsdipa_io_zstd_alloc(void *my_cookie, size_t size);
1436             static void s__bsdipa_io_zstd_free(void *my_cookie, void *dat);
1437             static enum s_bsdipa_state s__bsdipa_io_cookie_create_zstd(int xwrite, struct s_bsdipa_io_cookie *iocp,
1438             struct s_bsdipa_memory_ctx const *mcp);
1439             s_BSDIPA_IO_LINKAGE void s_bsdipa_io_cookie_gut_zstd(struct s_bsdipa_io_cookie *iocp);
1440              
1441             static void *
1442             s__bsdipa_io_zstd_alloc(void *my_cookie, size_t size){
1443             void *rv;
1444             struct s_bsdipa_memory_ctx *mcp;
1445              
1446             mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
1447              
1448             rv = (mcp->mc_alloc != NULL) ? (*mcp->mc_alloc)(size) : (*mcp->mc_custom_alloc)(mcp->mc_custom_cookie, size);
1449              
1450             return rv;
1451             }
1452              
1453             static void
1454             s__bsdipa_io_zstd_free(void *my_cookie, void *dat){
1455             struct s_bsdipa_memory_ctx *mcp;
1456              
1457             mcp = (struct s_bsdipa_memory_ctx*)my_cookie;
1458              
1459             (mcp->mc_alloc != NULL) ? (*mcp->mc_free)(dat) : (*mcp->mc_custom_free)(mcp->mc_custom_cookie, dat);
1460             }
1461              
1462             static enum s_bsdipa_state
1463             s__bsdipa_io_cookie_create_zstd(int xwrite, struct s_bsdipa_io_cookie *iocp, struct s_bsdipa_memory_ctx const *mcp){
1464             enum s_bsdipa_state rv;
1465             struct s_bsdipa_io_cookie_zstd *iocZp;
1466              
1467             iocZp = (struct s_bsdipa_io_cookie_zstd*)(void*)iocp;
1468              
1469             if(!iocZp->iocZ_super.ioc_is_init){
1470             iocZp->iocZ_super.ioc_is_init = 1;
1471             iocZp->iocZ_super.ioc_type = s_BSDIPA_IO_ZSTD;
1472             if(iocZp->iocZ_super.ioc_level == 0)
1473             iocZp->iocZ_super.ioc_level = s_BSDIPA_IO_ZSTD_LEVEL;
1474             iocZp->iocZ_mctx = *mcp;
1475             iocZp->iocZ_zcm.customAlloc = &s__bsdipa_io_zstd_alloc;
1476             iocZp->iocZ_zcm.customFree = &s__bsdipa_io_zstd_free;
1477             iocZp->iocZ_zcm.opaque = (void*)&iocZp->iocZ_mctx;
1478             }
1479              
1480             if(xwrite){
1481             size_t r;
1482             ZSTD_CCtx* cp;
1483              
1484             if((cp = iocZp->iocZ_zcp) == NULL){
1485             cp = iocZp->iocZ_zcp = ZSTD_createCCtx_advanced(iocZp->iocZ_zcm);
1486             if(cp == NULL){
1487             rv = s_BSDIPA_NOMEM;
1488             goto jleave;
1489             }
1490             }else
1491             (void)ZSTD_CCtx_reset(cp, ZSTD_reset_session_and_parameters);
1492              
1493             rv = s_BSDIPA_INVAL;
1494             /* Convert from our 1-9 scale to zstd's 1..x scale */
1495             /* C99 */{
1496             int myp, zp;
1497              
1498             myp = ((int)iocZp->iocZ_super.ioc_level * 100) / ((9 * 100) / 100);
1499             zp = (((ZSTD_maxCLevel() * 100) / 100) * myp) / 100;
1500             r = ZSTD_CCtx_setParameter(cp, ZSTD_c_compressionLevel, zp);
1501             }
1502             if(ZSTD_isError(r))
1503             goto jleave;
1504             r = ZSTD_CCtx_setParameter(cp, ZSTD_c_strategy, iocZp->iocZ_super.ioc_level);
1505             if(ZSTD_isError(r))
1506             goto jleave;
1507             /* Checksum default is 0 */
1508             # if s_BSDIPA_IO_ZSTD_CHECKSUM
1509             r = ZSTD_CCtx_setParameter(cp, ZSTD_c_checksumFlag, s_BSDIPA_IO_ZSTD_CHECKSUM);
1510             if(ZSTD_isError(r))
1511             goto jleave;
1512             # endif
1513             }else{
1514             ZSTD_DCtx* dp;
1515              
1516             if((dp = iocZp->iocZ_zdp) == NULL){
1517             dp = iocZp->iocZ_zdp = ZSTD_createDCtx_advanced(iocZp->iocZ_zcm);
1518             if(dp == NULL){
1519             rv = s_BSDIPA_NOMEM;
1520             goto jleave;
1521             }
1522             }else
1523             (void)ZSTD_DCtx_reset(dp, ZSTD_reset_session_and_parameters);
1524             }
1525              
1526             rv = s_BSDIPA_OK;
1527             jleave:
1528             return rv;
1529             }
1530              
1531             s_BSDIPA_IO_LINKAGE void
1532             s_bsdipa_io_cookie_gut_zstd(struct s_bsdipa_io_cookie *iocp){
1533             if(iocp != NULL && iocp->ioc_is_init && iocp->ioc_type == s_BSDIPA_IO_ZSTD){
1534             struct s_bsdipa_io_cookie_zstd *iocZp;
1535              
1536             iocZp = (struct s_bsdipa_io_cookie_zstd*)(void*)iocp;
1537              
1538             if(iocZp->iocZ_zcp != NULL)
1539             (void)ZSTD_freeCCtx(iocZp->iocZ_zcp);
1540             if(iocZp->iocZ_zdp != NULL)
1541             (void)ZSTD_freeDCtx(iocZp->iocZ_zdp);
1542             }
1543             }
1544             /* }}} */
1545              
1546             # ifdef s_BSDIPA_IO_WRITE /* {{{ */
1547             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
1548             s_bsdipa_io_write_zstd(struct s_bsdipa_diff_ctx const *dcp, s_bsdipa_io_write_ptf hook, void *hook_cookie,
1549             int try_oneshot, struct s_bsdipa_io_cookie *io_cookie_or_null){
1550             ZSTD_inBuffer zib;
1551             ZSTD_outBuffer zob;
1552             struct s_bsdipa_io_cookie_zstd iocZ;
1553             char x;
1554             struct s_bsdipa_ctrl_chunk *ccp;
1555             enum s_bsdipa_state rv;
1556             uint8_t *obuf;
1557             size_t olen;
1558             s_bsdipa_off_t diflen, extlen;
1559              
1560             if(io_cookie_or_null == NULL || io_cookie_or_null->ioc_type != s_BSDIPA_IO_ZSTD)
1561             memset(io_cookie_or_null = &iocZ.iocZ_super, 0, sizeof(iocZ));
1562              
1563             rv = s__bsdipa_io_cookie_create_zstd(1, io_cookie_or_null, &dcp->dc_mem);
1564             if(rv != s_BSDIPA_OK)
1565             goto jleave;
1566              
1567             diflen = dcp->dc_diff_len;
1568             extlen = dcp->dc_extra_len;
1569              
1570             /* All lengths fit in s_BSDIPA_OFF_MAX, which is signed: addition and cast ok */
1571             olen = (size_t)((s_bsdipa_off_t)sizeof(dcp->dc_header) + dcp->dc_ctrl_len + diflen + extlen);
1572             if(try_oneshot){
1573             size_t ulo;
1574              
1575             ulo = olen; /* XXX check overflow? s_bsdipa_off_t>size_t case? */
1576             ulo = ZSTD_COMPRESSBOUND(ulo);
1577             if(ulo == 0 || ulo >= s_BSDIPA_OFF_MAX){
1578             try_oneshot = 0;
1579             goto jolenmax;
1580             }
1581             /* Add "one additional byte" already here in case buffer takeover succeeds */
1582             if(ulo >= (size_t)-1 - 1){
1583             try_oneshot = 0;
1584             goto jolenmax;
1585             }
1586             olen = ++ulo;
1587             }else if(olen <= 1000 * 150)
1588             olen = 4096 * 4;
1589             else if(olen <= 1000 * 1000)
1590             olen = 4096 * 31;
1591             else
1592             jolenmax:
1593             olen = 4096 * 244;
1594              
1595             obuf = (uint8_t*)s__bsdipa_io_zstd_alloc((void*)&dcp->dc_mem, olen);
1596             if(obuf == NULL){
1597             rv = s_BSDIPA_NOMEM;
1598             goto jdone;
1599             }
1600             olen -= (try_oneshot != 0);
1601              
1602             zob.dst = obuf;
1603             zob.size = olen;
1604             zob.pos = 0;
1605             ccp = dcp->dc_ctrl;
1606              
1607             for(x = 0;;){
1608             ZSTD_EndDirective zed;
1609              
1610             zed = ZSTD_e_continue;
1611             if(x == 0){
1612             zib.src = dcp->dc_header;
1613             zib.size = sizeof(dcp->dc_header);
1614             zib.pos = 0;
1615             x = 1;
1616             }else if(x == 1){
1617             if(ccp != NULL){
1618             zib.src = ccp->cc_dat;
1619             zib.size = (size_t)ccp->cc_len;
1620             zib.pos = 0;
1621             ccp = ccp->cc_next;
1622             }
1623             if(ccp == NULL)
1624             x = 2;
1625             }else if(x < 4){
1626             if(x == 2)
1627             zib.src = dcp->dc_diff_dat;
1628             else
1629             zib.src = &((char*)zib.src)[zib.pos];
1630             zib.pos = 0;
1631             if(diflen > s__BSDIPA_IO_ZSTD_LIMIT){
1632             zib.size = s__BSDIPA_IO_ZSTD_LIMIT;
1633             diflen -= s__BSDIPA_IO_ZSTD_LIMIT;
1634             x = 3;
1635             }else{
1636             zib.size = (size_t)diflen;
1637             x = 4;
1638             }
1639             }else if(x < 6){
1640             if(x == 4)
1641             zib.src = dcp->dc_extra_dat;
1642             else
1643             zib.src = &((char*)zib.src)[zib.pos];
1644             zib.pos = 0;
1645             if(extlen > s__BSDIPA_IO_ZSTD_LIMIT){
1646             zib.size = s__BSDIPA_IO_ZSTD_LIMIT;
1647             extlen -= s__BSDIPA_IO_ZSTD_LIMIT;
1648             x = 5;
1649             }else{
1650             zib.size = (size_t)extlen;
1651             x = 6;
1652             }
1653             }else{
1654             zib.size = zib.pos = 0; /* xxx redundant? */
1655             zed = ZSTD_e_end;
1656             x = 7;
1657             }
1658              
1659             if(zib.size > 0 || zed == ZSTD_e_end) for(;;){
1660             int xarg;
1661             s_bsdipa_off_t z;
1662             size_t y;
1663              
1664             y = ZSTD_compressStream2(((struct s_bsdipa_io_cookie_zstd*)io_cookie_or_null)->iocZ_zcp,
1665             &zob, &zib, zed);
1666             if(y != 0 && ZSTD_isError(y)){
1667             if(ZSTD_getErrorCode(y) == ZSTD_error_memory_allocation)
1668             rv = s_BSDIPA_NOMEM;
1669             else
1670             rv = s_BSDIPA_INVAL;
1671             goto jleave;
1672             }
1673              
1674             /* We have progress says manual */
1675             if(y != 0 || x != 7){
1676             if(try_oneshot < 0)
1677             try_oneshot = 1;
1678             xarg = 0;
1679             }else
1680             xarg = (try_oneshot < 0) ? -(int)(s_bsdipa_off_t)++olen : 1;
1681              
1682             z = (s_bsdipa_off_t)zob.pos;
1683             if((xarg || z > 0) && (rv = (*hook)(hook_cookie, obuf, z, xarg)) != s_BSDIPA_OK)
1684             goto jdone;
1685              
1686             if(xarg){
1687             /* Did we transfer buffer ownership? */
1688             if(xarg < 0)
1689             obuf = NULL;
1690             goto jdone;
1691             }
1692              
1693             assert(zob.dst == obuf);
1694             assert(zob.size == olen);
1695             zob.pos = 0;
1696              
1697             if(zed == ZSTD_e_end)
1698             continue;
1699             if(zib.size == zib.pos)
1700             break;
1701             }
1702             assert(x != 7);
1703             }
1704              
1705             jdone:
1706             if(obuf != NULL)
1707             s__bsdipa_io_zstd_free((void*)&dcp->dc_mem, obuf);
1708              
1709             jleave:
1710             if(io_cookie_or_null == &iocZ.iocZ_super)
1711             s_bsdipa_io_cookie_gut_zstd(io_cookie_or_null);
1712              
1713             return rv;
1714             }
1715             # endif /* }}} s_BSDIPA_IO_WRITE */
1716              
1717             # ifdef s_BSDIPA_IO_READ /* {{{ */
1718             s_BSDIPA_IO_LINKAGE enum s_bsdipa_state
1719             s_bsdipa_io_read_zstd(struct s_bsdipa_patch_ctx *pcp, struct s_bsdipa_io_cookie *io_cookie_or_null){
1720             uint8_t hbuf[sizeof(struct s_bsdipa_header)];
1721             ZSTD_inBuffer zib;
1722             ZSTD_outBuffer zob;
1723             struct s_bsdipa_io_cookie_zstd iocZ;
1724             s_bsdipa_off_t reslen;
1725             enum s_bsdipa_state rv;
1726             uint64_t patlen;
1727              
1728             pcp->pc_restored_dat = NULL;
1729             patlen = pcp->pc_patch_len;
1730              
1731             if(io_cookie_or_null == NULL || io_cookie_or_null->ioc_type != s_BSDIPA_IO_ZSTD)
1732             memset(io_cookie_or_null = &iocZ.iocZ_super, 0, sizeof(iocZ));
1733              
1734             rv = s__bsdipa_io_cookie_create_zstd(0, io_cookie_or_null, &pcp->pc_mem);
1735             if(rv != s_BSDIPA_OK)
1736             goto jdone;
1737              
1738             /* Read bsdipa_header */
1739             /* C99 */{
1740             size_t y;
1741              
1742             zob.dst = hbuf;
1743             zob.size = sizeof(hbuf);
1744             zob.pos = 0;
1745              
1746             /* Without too much effort: we need to make available an entire frame */
1747             zib.src = pcp->pc_patch_dat;
1748             zib.size = (size_t)patlen;
1749             zib.pos = 0;
1750              
1751             y = ZSTD_decompressStream(((struct s_bsdipa_io_cookie_zstd*)io_cookie_or_null)->iocZ_zdp, &zob, &zib);
1752             if(y != 0 && ZSTD_isError(y)){
1753             if(ZSTD_getErrorCode(y) == ZSTD_error_memory_allocation)
1754             rv = s_BSDIPA_NOMEM;
1755             else
1756             rv = s_BSDIPA_INVAL;
1757             goto jdone;
1758             }
1759              
1760             if(zob.size != zob.pos){
1761             rv = s_BSDIPA_INVAL;
1762             goto jdone;
1763             }
1764             }
1765              
1766             rv = s_bsdipa_patch_parse_header(&pcp->pc_header, hbuf);
1767             if(rv != s_BSDIPA_OK)
1768             goto jdone;
1769              
1770             /* Do not perform any action at all on size excess */
1771             if(pcp->pc_max_allowed_restored_len != 0 &&
1772             pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len){
1773             rv = s_BSDIPA_FBIG;
1774             goto jdone;
1775             }
1776              
1777             /* Guaranteed to work! */
1778             reslen = pcp->pc_header.h_ctrl_len + pcp->pc_header.h_diff_len + pcp->pc_header.h_extra_len;
1779              
1780             pcp->pc_restored_len = reslen;
1781             pcp->pc_restored_dat = (uint8_t*)s__bsdipa_io_zstd_alloc(&pcp->pc_mem, (size_t)reslen);
1782             if(pcp->pc_restored_dat == NULL){
1783             rv = s_BSDIPA_NOMEM;
1784             goto jdone;
1785             }
1786              
1787             zob.dst = pcp->pc_restored_dat;
1788              
1789             zib.src = &((char const*)zib.src)[zib.pos];
1790             patlen -= zib.pos;
1791             zib.pos = 0;
1792              
1793             if(patlen > 0) for(;;){
1794             size_t y;
1795              
1796             zob.size = (size_t)((reslen > s__BSDIPA_IO_ZSTD_LIMIT) ? s__BSDIPA_IO_ZSTD_LIMIT : reslen);
1797             reslen -= (s_bsdipa_off_t)zob.size;
1798             zob.pos = 0;
1799              
1800             zib.src = &((char*)zib.src)[zib.pos];
1801             zib.size = (size_t)((patlen > s__BSDIPA_IO_ZSTD_LIMIT) ? s__BSDIPA_IO_ZSTD_LIMIT : patlen);
1802             patlen -= zib.size;
1803             zib.pos = 0;
1804              
1805             y = ZSTD_decompressStream(((struct s_bsdipa_io_cookie_zstd*)io_cookie_or_null)->iocZ_zdp, &zob, &zib);
1806             if(y != 0 && ZSTD_isError(y)){
1807             if(ZSTD_getErrorCode(y) == ZSTD_error_memory_allocation)
1808             rv = s_BSDIPA_NOMEM;
1809             else
1810             rv = s_BSDIPA_INVAL;
1811             goto jdone;
1812             }
1813              
1814             if(zob.pos > 0){
1815             zob.dst = &((char*)zob.dst)[zob.pos];
1816             zob.size -= zob.pos;
1817             }
1818             reslen += zob.size;
1819              
1820             zib.size -= zib.pos;
1821             if(y == 0 && patlen == 0 && zib.size == 0)
1822             break;
1823             patlen += zib.size;
1824             }
1825              
1826             jdone:
1827             if(io_cookie_or_null == &iocZ.iocZ_super)
1828             s_bsdipa_io_cookie_gut_zstd(io_cookie_or_null);
1829              
1830             if(rv != s_BSDIPA_OK && pcp->pc_restored_dat != NULL){
1831             s__bsdipa_io_zstd_free(&pcp->pc_mem, pcp->pc_restored_dat);
1832             pcp->pc_restored_dat = NULL;
1833             }
1834              
1835             return rv;
1836             }
1837             # endif /* }}} s_BSDIPA_IO_READ */
1838              
1839             # undef s__BSDIPA_IO_ZSTD_LIMIT
1840             # undef s_BSDIPA_IO_ZSTD_CHECKSUM
1841             # undef s_BSDIPA_IO_ZSTD_LEVEL
1842             /* }}} */
1843              
1844             #else /* _IO_ZSTD */
1845             # error Unknown s_BSDIPA_IO value
1846             #endif
1847              
1848             #ifdef __cplusplus
1849             }
1850             #endif
1851             /* s-itt-mode */