1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(zlib).
22
23-export([open/0,close/1,set_controlling_process/2,
24         deflateInit/1,deflateInit/2,deflateInit/6,
25         deflateSetDictionary/2,deflateReset/1,deflateParams/3,
26         deflate/2,deflate/3,deflateEnd/1,
27         inflateInit/1,inflateInit/2,inflateInit/3,
28         inflateSetDictionary/2,inflateGetDictionary/1, inflateReset/1,
29         inflate/2,inflate/3,inflateEnd/1,
30         inflateChunk/2,inflateChunk/1,
31         safeInflate/2,
32         setBufSize/2,getBufSize/1,
33         crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,
34         crc32_combine/4,adler32_combine/4,
35         compress/1,uncompress/1,zip/1,unzip/1,
36         gzip/1,gunzip/1]).
37
38-export([on_load/0]).
39
40-deprecated([{inflateChunk, 1, "use safeInflate/2 instead"},
41             {inflateChunk, 2, "use safeInflate/2 instead"},
42             {getBufSize, 1, "this function will be removed in a future release"},
43             {setBufSize, 2, "this function will be removed in a future release"},
44             {crc32, 1, "use erlang:crc32/1 on the uncompressed data instead"},
45             {crc32, 2, "use erlang:crc32/1 instead"},
46             {crc32, 3, "use erlang:crc32/2 instead"},
47             {adler32, 2, "use erlang:adler32/1 instead"},
48             {adler32, 3, "use erlang:adler32/2 instead"},
49             {crc32_combine, 4, "use erlang:crc32_combine/3 instead"},
50             {adler32_combine, 4, "use erlang:adler_combine/3 instead"}]).
51
52-export_type([zstream/0, zflush/0, zlevel/0, zwindowbits/0, zmemlevel/0,
53              zstrategy/0]).
54
55%% flush argument encoding
56-define(Z_NO_FLUSH,      0).
57-define(Z_SYNC_FLUSH,    2).
58-define(Z_FULL_FLUSH,    3).
59-define(Z_FINISH,        4).
60
61%% compression level
62-define(Z_NO_COMPRESSION,         0).
63-define(Z_BEST_SPEED,             1).
64-define(Z_BEST_COMPRESSION,       9).
65-define(Z_DEFAULT_COMPRESSION,  (-1)).
66
67%% compression strategy
68-define(Z_FILTERED,            1).
69-define(Z_HUFFMAN_ONLY,        2).
70-define(Z_RLE,                 3).
71-define(Z_DEFAULT_STRATEGY,    0).
72
73%% deflate compression method
74-define(Z_DEFLATED,  8).
75
76-define(MAX_WBITS, 15).
77
78-define(DEFAULT_MEMLEVEL, 8).
79-define(DEFAULT_WBITS, 15).
80
81-define(EOS_BEHAVIOR_ERROR, 0).
82-define(EOS_BEHAVIOR_RESET, 1).
83-define(EOS_BEHAVIOR_CUT, 2).
84
85%% Chunk sizes are hardcoded on account of them screwing with the
86%% predictability of the system. zlib is incapable of trapping so we need to
87%% ensure that it never operates on any significant amount of data.
88-define(DEFLATE_IN_CHUNKSIZE, 8 bsl 10).
89-define(DEFLATE_OUT_CHUNKSIZE, 8 bsl 10).
90-define(INFLATE_IN_CHUNKSIZE, 8 bsl 10).
91-define(INFLATE_OUT_CHUNKSIZE, 16 bsl 10).
92
93%%------------------------------------------------------------------------
94
95%% Public data types.
96-type zstream() :: reference().
97-type zflush() :: 'none' | 'sync' | 'full' | 'finish'.
98
99-type zlevel() ::
100    'none' | 'default' | 'best_compression' | 'best_speed' | 0..9.
101-type zstrategy() :: 'default' | 'filtered' | 'huffman_only' | 'rle'.
102
103-type zmemlevel() :: 1..9.
104-type zwindowbits() :: -15..-8 | 8..47.
105
106%% Private data types.
107
108-type zmethod()     :: 'deflated'.
109
110-record(zlib_opts, {
111        stream :: zstream() | 'undefined',
112        method :: function(),
113        input_chunk_size :: pos_integer(),
114        output_chunk_size :: pos_integer(),
115        flush :: non_neg_integer()
116    }).
117
118%%------------------------------------------------------------------------
119
120on_load() ->
121    case erlang:load_nif(atom_to_list(?MODULE), 0) of
122        ok -> ok
123    end.
124
125-spec open() -> zstream().
126open() ->
127    open_nif().
128open_nif() ->
129    erlang:nif_error(undef).
130
131-spec close(Z) -> 'ok' when
132      Z :: zstream().
133close(Z) ->
134    close_nif(Z).
135close_nif(_Z) ->
136    erlang:nif_error(undef).
137
138-spec set_controlling_process(Z, Pid) -> 'ok' when
139      Z :: zstream(),
140      Pid :: pid().
141set_controlling_process(Z, Pid) ->
142    set_controller_nif(Z, Pid).
143set_controller_nif(_Z, _Pid) ->
144    erlang:nif_error(undef).
145
146-spec deflateInit(Z) -> 'ok' when
147      Z :: zstream().
148deflateInit(Z) ->
149    deflateInit(Z, default).
150
151-spec deflateInit(Z, Level) -> 'ok' when
152      Z :: zstream(),
153      Level :: zlevel().
154deflateInit(Z, Level) ->
155    deflateInit(Z, Level, deflated, ?DEFAULT_WBITS, ?DEFAULT_MEMLEVEL, default).
156
157-spec deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) -> 'ok' when
158      Z :: zstream(),
159      Level :: zlevel(),
160      Method :: zmethod(),
161      WindowBits :: zwindowbits(),
162      MemLevel :: zmemlevel(),
163      Strategy :: zstrategy().
164deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) ->
165    deflateInit_nif(Z,
166                    arg_level(Level),
167                    arg_method(Method),
168                    arg_bitsz(WindowBits),
169                    arg_mem(MemLevel),
170                    arg_strategy(Strategy)).
171deflateInit_nif(_Z, _Level, _Method, _WindowBits, _MemLevel, _Strategy) ->
172    erlang:nif_error(undef).
173
174-spec deflateSetDictionary(Z, Dictionary) -> Adler32 when
175      Z :: zstream(),
176      Dictionary :: iodata(),
177      Adler32 :: non_neg_integer().
178deflateSetDictionary(Z, Dictionary) ->
179    deflateSetDictionary_nif(Z, Dictionary).
180deflateSetDictionary_nif(_Z, _Dictionary) ->
181    erlang:nif_error(undef).
182
183-spec deflateReset(Z) -> 'ok' when
184      Z :: zstream().
185deflateReset(Z) ->
186    deflateReset_nif(Z).
187deflateReset_nif(_Z) ->
188    erlang:nif_error(undef).
189
190-spec deflateParams(Z, Level, Strategy) -> ok when
191      Z :: zstream(),
192      Level :: zlevel(),
193      Strategy :: zstrategy().
194deflateParams(Z, Level0, Strategy0) ->
195    Level = arg_level(Level0),
196    Strategy = arg_strategy(Strategy0),
197    Progress = deflate(Z, <<>>, sync),
198    case deflateParams_nif(Z, Level, Strategy) of
199        ok ->
200            save_progress(Z, deflate, Progress),
201            ok;
202        Other ->
203            Other
204    end.
205deflateParams_nif(_Z, _Level, _Strategy) ->
206    erlang:nif_error(undef).
207
208-spec deflate(Z, Data) -> Compressed when
209      Z :: zstream(),
210      Data :: iodata(),
211      Compressed :: iolist().
212deflate(Z, Data) ->
213    deflate(Z, Data, none).
214
215-spec deflate(Z, Data, Flush) -> Compressed when
216      Z :: zstream(),
217      Data :: iodata(),
218      Flush :: zflush(),
219      Compressed :: iolist().
220deflate(Z, Data, Flush) ->
221    Progress = restore_progress(Z, deflate),
222    enqueue_input(Z, Data),
223    append_iolist(Progress, dequeue_all_chunks(Z, deflate_opts(Flush))).
224
225deflate_opts(Flush) ->
226    #zlib_opts{
227        method = fun deflate_nif/4,
228        input_chunk_size = ?DEFLATE_IN_CHUNKSIZE,
229        output_chunk_size = ?DEFLATE_OUT_CHUNKSIZE,
230        flush = arg_flush(Flush)
231    }.
232
233deflate_nif(_Z, _InputChSize, _OutputChSize, _Flush) ->
234    erlang:nif_error(undef).
235
236-spec deflateEnd(Z) -> 'ok' when
237      Z :: zstream().
238deflateEnd(Z) ->
239    deflateEnd_nif(Z).
240deflateEnd_nif(_Z) ->
241    erlang:nif_error(undef).
242
243-spec inflateInit(Z) -> 'ok' when
244      Z :: zstream().
245inflateInit(Z) ->
246    inflateInit(Z, ?DEFAULT_WBITS).
247
248-spec inflateInit(Z, WindowBits) -> 'ok' when
249      Z :: zstream(),
250      WindowBits :: zwindowbits().
251inflateInit(Z, WindowBits) ->
252    inflateInit(Z, WindowBits, cut).
253
254-spec inflateInit(Z, WindowBits, EoSBehavior) -> 'ok' when
255      Z :: zstream(),
256      WindowBits :: zwindowbits(),
257      EoSBehavior :: error | reset | cut.
258inflateInit(Z, WindowBits, EoSBehavior) ->
259    inflateInit_nif(Z, arg_bitsz(WindowBits), arg_eos_behavior(EoSBehavior)).
260inflateInit_nif(_Z, _WindowBits, _EoSBehavior) ->
261    erlang:nif_error(undef).
262
263-spec inflateSetDictionary(Z, Dictionary) -> 'ok' when
264      Z :: zstream(),
265      Dictionary :: iodata().
266inflateSetDictionary(Z, Dictionary) ->
267    inflateSetDictionary_nif(Z, Dictionary).
268inflateSetDictionary_nif(_Z, _Dictionary) ->
269    erlang:nif_error(undef).
270
271-spec inflateGetDictionary(Z) -> Dictionary when
272      Z :: zstream(),
273      Dictionary :: binary().
274inflateGetDictionary(Z) ->
275    case inflateGetDictionary_nif(Z) of
276        Dictionary when is_binary(Dictionary) ->
277            Dictionary;
278        not_supported ->
279            erlang:error(enotsup)
280    end.
281inflateGetDictionary_nif(_Z) ->
282    erlang:nif_error(undef).
283
284-spec inflateReset(Z) -> 'ok' when
285      Z :: zstream().
286inflateReset(Z) ->
287    inflateReset_nif(Z).
288inflateReset_nif(_Z) ->
289    erlang:nif_error(undef).
290
291-spec inflate(Z, Data) -> Decompressed when
292      Z :: zstream(),
293      Data :: iodata(),
294      Decompressed :: iolist().
295inflate(Z, Data) ->
296    inflate(Z, Data, []).
297
298-spec inflate(Z, Data, Options) -> Decompressed when
299      Z :: zstream(),
300      Data :: iodata(),
301      Options :: list({exception_on_need_dict, boolean()}),
302      Decompressed :: iolist() |
303                      {need_dictionary,
304                       Adler32 :: non_neg_integer(),
305                       Output :: iolist()}.
306inflate(Z, Data, Options) ->
307    enqueue_input(Z, Data),
308    Result = dequeue_all_chunks(Z, inflate_opts()),
309    case proplist_get_value(Options, exception_on_need_dict, true) of
310        true -> exception_on_need_dict(Z, Result);
311        false -> Result
312    end.
313
314inflate_nif(_Z, _InputChSize, _OutputChSize, _Flush) ->
315    erlang:nif_error(undef).
316
317inflate_opts() ->
318    #zlib_opts{
319        method = fun inflate_nif/4,
320        input_chunk_size = ?INFLATE_IN_CHUNKSIZE,
321        output_chunk_size = ?INFLATE_OUT_CHUNKSIZE,
322        flush = arg_flush(none)
323    }.
324
325-spec inflateChunk(Z, Data) -> Decompressed | {more, Decompressed} when
326      Z :: zstream(),
327      Data :: iodata(),
328      Decompressed :: iolist().
329inflateChunk(Z, Data) ->
330    enqueue_input(Z, Data),
331    inflateChunk(Z).
332
333-spec inflateChunk(Z) -> Decompressed | {more, Decompressed} when
334      Z :: zstream(),
335      Decompressed :: iolist().
336inflateChunk(Z) ->
337    Opts0 = inflate_opts(),
338    Opts = Opts0#zlib_opts { output_chunk_size = getBufSize(Z) },
339
340    Result0 = dequeue_next_chunk(Z, Opts),
341    Result1 = exception_on_need_dict(Z, Result0),
342    yield_inflateChunk(Z, Result1).
343
344yield_inflateChunk(_Z, {continue, Output}) ->
345    {more, lists:flatten(Output)};
346yield_inflateChunk(_Z, {finished, Output}) ->
347    lists:flatten(Output).
348
349exception_on_need_dict(Z, {need_dictionary, Adler, Output}) ->
350    Progress = restore_progress(Z, inflate),
351    save_progress(Z, inflate, append_iolist(Progress, Output)),
352    erlang:error({need_dictionary, Adler});
353exception_on_need_dict(Z, {Mark, Output}) ->
354    Progress = restore_progress(Z, inflate),
355    {Mark, append_iolist(Progress, Output)};
356exception_on_need_dict(Z, Output) when is_list(Output); is_binary(Output) ->
357    Progress = restore_progress(Z, inflate),
358    append_iolist(Progress, Output).
359
360-spec safeInflate(Z, Data) -> Result when
361      Z :: zstream(),
362      Data :: iodata(),
363      Result :: {continue, Output :: iolist()} |
364                {finished, Output :: iolist()} |
365                {need_dictionary,
366                 Adler32 :: non_neg_integer(),
367                 Output :: iolist()}.
368safeInflate(Z, Data) ->
369    enqueue_input(Z, Data),
370    dequeue_next_chunk(Z, inflate_opts()).
371
372-spec inflateEnd(Z) -> 'ok' when
373      Z :: zstream().
374inflateEnd(Z) ->
375    inflateEnd_nif(Z).
376inflateEnd_nif(_Z) ->
377    erlang:nif_error(undef).
378
379-spec setBufSize(Z, Size) -> 'ok' when
380      Z :: zstream(),
381      Size :: non_neg_integer().
382setBufSize(Z, Size) when is_integer(Size), Size > 16, Size < (1 bsl 24) ->
383    setBufSize_nif(Z, Size);
384setBufSize(_Z, _Size) ->
385    erlang:error(badarg).
386setBufSize_nif(_Z, _Size) ->
387    erlang:nif_error(undef).
388
389-spec getBufSize(Z) -> non_neg_integer() when
390      Z :: zstream().
391getBufSize(Z) ->
392    getBufSize_nif(Z).
393getBufSize_nif(_Z) ->
394    erlang:nif_error(undef).
395
396-spec crc32(Z) -> CRC when
397      Z :: zstream(),
398      CRC :: non_neg_integer().
399crc32(Z) ->
400    crc32_nif(Z).
401crc32_nif(_Z) ->
402    erlang:nif_error(undef).
403
404-spec crc32(Z, Data) -> CRC when
405      Z :: zstream(),
406      Data :: iodata(),
407      CRC :: non_neg_integer().
408crc32(Z, Data) when is_reference(Z) ->
409    erlang:crc32(Data);
410crc32(_Z, _Data) ->
411    erlang:error(badarg).
412
413-spec crc32(Z, PrevCRC, Data) -> CRC when
414      Z :: zstream(),
415      PrevCRC :: non_neg_integer(),
416      Data :: iodata(),
417      CRC :: non_neg_integer().
418crc32(Z, CRC, Data) when is_reference(Z) ->
419    erlang:crc32(CRC, Data);
420crc32(_Z, _CRC, _Data) ->
421    erlang:error(badarg).
422
423-spec crc32_combine(Z, CRC1, CRC2, Size2) -> CRC when
424      Z :: zstream(),
425      CRC :: non_neg_integer(),
426      CRC1 :: non_neg_integer(),
427      CRC2 :: non_neg_integer(),
428      Size2 :: non_neg_integer().
429crc32_combine(Z, CRC1, CRC2, Size2) when is_reference(Z) ->
430    erlang:crc32_combine(CRC1, CRC2, Size2);
431crc32_combine(_Z, _CRC1, _CRC2, _Size2) ->
432    erlang:error(badarg).
433
434-spec adler32(Z, Data) -> CheckSum when
435      Z :: zstream(),
436      Data :: iodata(),
437      CheckSum :: non_neg_integer().
438adler32(Z, Data) when is_reference(Z) ->
439    erlang:adler32(Data);
440adler32(_Z, _Data) ->
441    erlang:error(badarg).
442
443-spec adler32(Z, PrevAdler, Data) -> CheckSum when
444      Z :: zstream(),
445      PrevAdler :: non_neg_integer(),
446      Data :: iodata(),
447      CheckSum :: non_neg_integer().
448adler32(Z, Adler, Data) when is_reference(Z) ->
449    erlang:adler32(Adler, Data);
450adler32(_Z, _Adler, _Data) ->
451    erlang:error(badarg).
452
453-spec adler32_combine(Z, Adler1, Adler2, Size2) -> Adler when
454      Z :: zstream(),
455      Adler :: non_neg_integer(),
456      Adler1 :: non_neg_integer(),
457      Adler2 :: non_neg_integer(),
458      Size2 :: non_neg_integer().
459adler32_combine(Z, Adler1, Adler2, Size2) when is_reference(Z) ->
460    erlang:adler32_combine(Adler1, Adler2, Size2);
461adler32_combine(_Z, _Adler1, _Adler2, _Size2) ->
462    erlang:error(badarg).
463
464%% compress/uncompress zlib with header
465-spec compress(Data) -> Compressed when
466      Data :: iodata(),
467      Compressed :: binary().
468compress(Data) ->
469    Z = open(),
470    Bs = try
471             deflateInit(Z, default),
472             B = deflate(Z, Data, finish),
473             deflateEnd(Z),
474             B
475         after
476             close(Z)
477         end,
478    iolist_to_binary(Bs).
479
480-spec uncompress(Data) -> Decompressed when
481      Data  :: iodata(),
482      Decompressed :: binary().
483uncompress(Data) ->
484    try iolist_size(Data) of
485        Size ->
486            if
487                Size >= 8 ->
488                    Z = open(),
489                    Bs = try
490                             inflateInit(Z),
491                             B = inflate(Z, Data),
492                             inflateEnd(Z),
493                             B
494                         after
495                             close(Z)
496                         end,
497                    iolist_to_binary(Bs);
498                true ->
499                    erlang:error(data_error)
500            end
501    catch
502        _:_ ->
503            erlang:error(badarg)
504    end.
505
506%% unzip/zip zlib without header (zip members)
507-spec zip(Data) -> Compressed when
508      Data :: iodata(),
509      Compressed :: binary().
510zip(Data) ->
511    Z = open(),
512    Bs = try
513             deflateInit(Z, default, deflated, -?MAX_WBITS, 8, default),
514             B = deflate(Z, Data, finish),
515             deflateEnd(Z),
516             B
517         after
518             close(Z)
519         end,
520    iolist_to_binary(Bs).
521
522-spec unzip(Data) -> Decompressed when
523      Data :: iodata(),
524      Decompressed :: binary().
525unzip(Data) ->
526    Z = open(),
527    Bs = try
528             inflateInit(Z, -?MAX_WBITS),
529             B = inflate(Z, Data),
530             inflateEnd(Z),
531             B
532         after
533             close(Z)
534         end,
535    iolist_to_binary(Bs).
536
537-spec gzip(Data) -> Compressed when
538      Data :: iodata(),
539      Compressed :: binary().
540gzip(Data) ->
541    Z = open(),
542    Bs = try
543             deflateInit(Z, default, deflated, 16+?MAX_WBITS, 8, default),
544             B = deflate(Z, Data, finish),
545             deflateEnd(Z),
546             B
547         after
548             close(Z)
549         end,
550    iolist_to_binary(Bs).
551
552-spec gunzip(Data) -> Decompressed when
553      Data :: iodata(),
554      Decompressed :: binary().
555gunzip(Data) ->
556    Z = open(),
557    Bs = try
558             inflateInit(Z, 16+?MAX_WBITS, reset),
559             B = inflate(Z, Data),
560             inflateEnd(Z),
561             B
562         after
563             close(Z)
564         end,
565    iolist_to_binary(Bs).
566
567-spec dequeue_all_chunks(Z, Opts) -> Result when
568    Z :: zstream(),
569    Opts :: #zlib_opts{},
570    Result :: {need_dictionary, integer(), iolist()} |
571              iolist().
572dequeue_all_chunks(Z, Opts) ->
573    dequeue_all_chunks_1(Z, Opts, []).
574dequeue_all_chunks_1(Z, Opts, Output) ->
575    case dequeue_next_chunk(Z, Opts) of
576        {need_dictionary, _, _} = NeedDict ->
577            NeedDict;
578        {continue, Chunk} ->
579            dequeue_all_chunks_1(Z, Opts, append_iolist(Output, Chunk));
580        {finished, Chunk} ->
581            append_iolist(Output, Chunk)
582    end.
583
584-spec dequeue_next_chunk(Z, Opts) -> Result when
585    Z :: zstream(),
586    Opts :: #zlib_opts{},
587    Result :: {need_dictionary, integer(), iolist()} |
588              {continue, iolist()} |
589              {finished, iolist()}.
590dequeue_next_chunk(Z, Opts) ->
591    Method = Opts#zlib_opts.method,
592    IChSz = Opts#zlib_opts.input_chunk_size,
593    OChSz = Opts#zlib_opts.output_chunk_size,
594    Flush = Opts#zlib_opts.flush,
595    Method(Z, IChSz, OChSz, Flush).
596
597-spec append_iolist(IO, D) -> iolist() when
598    IO :: iodata(),
599    D :: iodata().
600append_iolist([], D) when is_list(D) -> D;
601append_iolist([], D) -> [D];
602append_iolist(IO, []) -> IO;
603append_iolist(IO, [D]) -> [IO, D];
604append_iolist(IO, D) -> [IO, D].
605
606%% inflate/2 and friends are documented as throwing an error on Z_NEED_DICT
607%% rather than simply returning something to that effect, and deflateParams/3
608%% may flush behind the scenes. This requires us to stow away our current
609%% progress in the handle and resume from that point on our next call.
610%%
611%% Generally speaking this is either a refc binary or nothing at all, so it's
612%% pretty cheap.
613
614-spec save_progress(Z, Kind, Output) -> ok when
615      Z :: zstream(),
616      Kind :: inflate | deflate,
617      Output :: iolist().
618save_progress(Z, Kind, Output) ->
619    ok = setStash_nif(Z, {Kind, Output}).
620
621-spec restore_progress(Z, Kind) -> iolist() when
622    Z :: zstream(),
623    Kind :: inflate | deflate.
624restore_progress(Z, Kind) ->
625    case getStash_nif(Z) of
626        {ok, {Kind, Output}} ->
627            ok = clearStash_nif(Z),
628            Output;
629        empty ->
630            []
631    end.
632
633-spec clearStash_nif(Z) -> ok when
634      Z :: zstream().
635clearStash_nif(_Z) ->
636    erlang:nif_error(undef).
637
638-spec setStash_nif(Z, Term) -> ok when
639      Z :: zstream(),
640      Term :: term().
641setStash_nif(_Z, _Term) ->
642    erlang:nif_error(undef).
643
644-spec getStash_nif(Z) -> {ok, term()} | empty when
645      Z :: zstream().
646getStash_nif(_Z) ->
647    erlang:nif_error(undef).
648
649%% The 'proplists' module isn't preloaded so we can't rely on its existence.
650proplist_get_value([], _Name, DefVal) -> DefVal;
651proplist_get_value([{Name, Value} | _Opts], Name, _DefVal) -> Value;
652proplist_get_value([_Head | Opts], Name, DefVal) ->
653    proplist_get_value(Opts, Name, DefVal).
654
655arg_flush(none)   -> ?Z_NO_FLUSH;
656%% ?Z_PARTIAL_FLUSH is deprecated in zlib -- deliberately not included.
657arg_flush(sync)   -> ?Z_SYNC_FLUSH;
658arg_flush(full)   -> ?Z_FULL_FLUSH;
659arg_flush(finish) -> ?Z_FINISH;
660arg_flush(_) -> erlang:error(bad_flush_mode).
661
662arg_level(none)             -> ?Z_NO_COMPRESSION;
663arg_level(best_speed)       -> ?Z_BEST_SPEED;
664arg_level(best_compression) -> ?Z_BEST_COMPRESSION;
665arg_level(default)          -> ?Z_DEFAULT_COMPRESSION;
666arg_level(Level) when is_integer(Level), Level >= 0, Level =< 9 -> Level;
667arg_level(_) -> erlang:error(bad_compression_level).
668
669arg_strategy(filtered) ->     ?Z_FILTERED;
670arg_strategy(huffman_only) -> ?Z_HUFFMAN_ONLY;
671arg_strategy(rle) -> ?Z_RLE;
672arg_strategy(default) ->      ?Z_DEFAULT_STRATEGY;
673arg_strategy(_) -> erlang:error(bad_compression_strategy).
674
675arg_method(deflated) -> ?Z_DEFLATED;
676arg_method(_) -> erlang:error(bad_compression_method).
677
678arg_eos_behavior(error) -> ?EOS_BEHAVIOR_ERROR;
679arg_eos_behavior(reset) -> ?EOS_BEHAVIOR_RESET;
680arg_eos_behavior(cut) -> ?EOS_BEHAVIOR_CUT;
681arg_eos_behavior(_) -> erlang:error(bad_eos_behavior).
682
683-spec arg_bitsz(zwindowbits()) -> zwindowbits().
684arg_bitsz(Bits) when is_integer(Bits) andalso
685                     ((8 =< Bits andalso Bits < 48) orelse
686                      (-15 =< Bits andalso Bits =< -8)) ->
687    Bits;
688arg_bitsz(_) -> erlang:error(bad_windowbits).
689
690-spec arg_mem(zmemlevel()) -> zmemlevel().
691arg_mem(Level) when is_integer(Level), 1 =< Level, Level =< 9 -> Level;
692arg_mem(_) -> erlang:error(bad_memlevel).
693
694-spec enqueue_input(Z, IOData) -> ok when
695      Z :: zstream(),
696      IOData :: iodata().
697enqueue_input(Z, IOData) ->
698    enqueue_input_1(Z, erlang:iolist_to_iovec(IOData)).
699
700enqueue_input_1(_Z, []) ->
701    ok;
702enqueue_input_1(Z, IOVec) ->
703    case enqueue_nif(Z, IOVec) of
704        {continue, Remainder} -> enqueue_input_1(Z, Remainder);
705        ok -> ok
706    end.
707
708enqueue_nif(_Z, _IOVec) ->
709    erlang:nif_error(undef).
710