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