1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-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_SUITE).
22
23-include_lib("common_test/include/ct.hrl").
24-include_lib("common_test/include/ct_event.hrl").
25
26-export([suite/0, all/0, groups/0]).
27
28%% API group
29-export([api_open_close/1]).
30-export([api_deflateInit/1, api_deflateSetDictionary/1, api_deflateReset/1,
31         api_deflateParams/1, api_deflate/1, api_deflateEnd/1]).
32-export([api_inflateInit/1, api_inflateReset/1, api_inflate2/1, api_inflate3/1,
33         api_inflateChunk/1, api_safeInflate/1, api_inflateEnd/1]).
34-export([api_inflateSetDictionary/1, api_inflateGetDictionary/1]).
35-export([api_crc32/1, api_adler32/1]).
36-export([api_un_compress/1, api_un_zip/1, api_g_un_zip/1]).
37
38%% Examples group
39-export([intro/1]).
40
41%% Usage group
42-export([zip_usage/1, gz_usage/1, gz_usage2/1, compress_usage/1,
43         dictionary_usage/1, large_deflate/1, crc/1, adler/1,
44         only_allow_owner/1, sub_heap_binaries/1]).
45
46%% Bench group
47-export([inflate_bench_zeroed/1, inflate_bench_rand/1,
48       deflate_bench_zeroed/1, deflate_bench_rand/1,
49       chunk_bench_zeroed/1, chunk_bench_rand/1]).
50
51%% Others
52-export([smp/1, otp_9981/1, otp_7359/1]).
53
54-define(m(Guard, Expression),
55    fun() ->
56        Actual = (catch (Expression)),
57        case Actual of
58            Guard -> Actual;
59            _Other ->
60                ct:fail("Failed to match ~p, actual result was ~p",
61                    [??Guard, Actual])
62        end
63    end()).
64
65-define(EXIT(Reason), {'EXIT',{Reason,[{_,_,_,_}|_]}}).
66
67suite() ->
68    [{ct_hooks,[ts_install_cth]},
69     {timetrap,{minutes,1}}].
70
71all() ->
72    [{group, api}, {group, examples}, {group, func},
73     {group, bench}, smp,
74     otp_9981,
75     otp_7359].
76
77groups() ->
78    [{api, [],
79      [api_open_close, api_deflateInit,
80       api_deflateSetDictionary, api_deflateReset,
81       api_deflateParams, api_deflate, api_deflateEnd,
82       api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary,
83       api_inflateReset, api_inflate2, api_inflate3, api_inflateChunk,
84       api_safeInflate, api_inflateEnd, api_crc32,
85       api_adler32, api_un_compress, api_un_zip,
86       api_g_un_zip]},
87     {examples, [], [intro]},
88     {func, [],
89      [zip_usage, gz_usage, gz_usage2, compress_usage,
90       dictionary_usage, large_deflate, crc, adler,
91       only_allow_owner, sub_heap_binaries]},
92     {bench,
93      [inflate_bench_zeroed, inflate_bench_rand,
94       deflate_bench_zeroed, deflate_bench_rand,
95       chunk_bench_zeroed, chunk_bench_rand]}].
96
97%% Test open/0 and close/1.
98api_open_close(Config) when is_list(Config) ->
99    Fd1 = zlib:open(),
100    Fd2 = zlib:open(),
101    ?m(false,Fd1 == Fd2),
102    ?m(ok,zlib:close(Fd1)),
103    ?m(?EXIT(not_initialized), zlib:close(Fd1)),
104    ?m(ok,zlib:close(Fd2)),
105
106    %% Make sure that we don't get any EXIT messages if trap_exit is enabled.
107    process_flag(trap_exit, true),
108    Fd3 = zlib:open(),
109    ?m(ok,zlib:close(Fd3)),
110    receive
111	Any -> ct:fail({unexpected_message,Any})
112    after 10 -> ok
113    end.
114
115%% Test deflateInit/2 and /6.
116api_deflateInit(Config) when is_list(Config) ->
117    Z1 = zlib:open(),
118
119    ?m(?EXIT(badarg), zlib:deflateInit(gurka, none)),
120
121    ?m(?EXIT(bad_compression_level), zlib:deflateInit(gurka, gurka)),
122    ?m(?EXIT(bad_compression_level), zlib:deflateInit(Z1, gurka)),
123    Levels = [none, default, best_speed, best_compression] ++ lists:seq(0,9),
124    lists:foreach(fun(Level) ->
125			  Z = zlib:open(),
126			  ?m(ok, zlib:deflateInit(Z, Level)),
127			  ?m(ok,zlib:close(Z))
128		  end, Levels),
129    %% /6
130    ?m(?EXIT(bad_compression_level),
131        zlib:deflateInit(Z1,gurka,deflated,-15,8,default)),
132
133    ?m(?EXIT(bad_compression_method),
134        zlib:deflateInit(Z1,default,undefined,-15,8,default)),
135
136    ?m(?EXIT(bad_compression_strategy),
137        zlib:deflateInit(Z1,default,deflated,-15,8,0)),
138    ?m(?EXIT(bad_compression_strategy),
139        zlib:deflateInit(Z1,default,deflated,-15,8,undefined)),
140
141    ?m(?EXIT(bad_windowbits),
142        zlib:deflateInit(Z1,default,deflated,48,8,default)),
143    ?m(?EXIT(bad_windowbits),
144        zlib:deflateInit(Z1,default,deflated,-20,8,default)),
145    ?m(?EXIT(bad_windowbits),
146        zlib:deflateInit(Z1,default,deflated,-7,8,default)),
147    ?m(?EXIT(bad_windowbits),
148        zlib:deflateInit(Z1,default,deflated,7,8,default)),
149
150    ?m(?EXIT(bad_memlevel),
151        zlib:deflateInit(Z1,default,deflated,-15,0,default)),
152    ?m(?EXIT(bad_memlevel),
153        zlib:deflateInit(Z1,default,deflated,-15,10,default)),
154
155    lists:foreach(fun(Level) ->
156			  Z = zlib:open(),
157			  ?m(ok, zlib:deflateInit(Z, Level, deflated, -15, 8, default)),
158			  ?m(ok,zlib:close(Z))
159		  end, Levels),
160
161    lists:foreach(fun(Wbits) ->
162			  Z11 = zlib:open(),
163			  ?m(ok, zlib:deflateInit(Z11,best_compression,deflated,
164						  Wbits,8,default)),
165			  Z12 = zlib:open(),
166			  ?m(ok, zlib:deflateInit(Z12,default,deflated,-Wbits,8,default)),
167			  ?m(ok,zlib:close(Z11)),
168			  ?m(ok,zlib:close(Z12))
169		  end, lists:seq(9, 15)),
170
171    lists:foreach(fun(MemLevel) ->
172			  Z = zlib:open(),
173			  ?m(ok, zlib:deflateInit(Z,default,deflated,-15,
174						  MemLevel,default)),
175			  ?m(ok,zlib:close(Z))
176		  end, lists:seq(1,8)),
177
178    Strategies = [filtered,huffman_only,rle,default],
179    lists:foreach(fun(Strategy) ->
180			  Z = zlib:open(),
181			  ?m(ok, zlib:deflateInit(Z,best_speed,deflated,-15,8,Strategy)),
182			  ?m(ok,zlib:close(Z))
183		  end, Strategies),
184    ?m(ok, zlib:deflateInit(Z1,default,deflated,-15,8,default)),
185
186    %% Let it crash for any reason; we don't care about the order in which the
187    %% parameters are checked.
188    ?m(?EXIT(_), zlib:deflateInit(Z1,none,deflated,-15,8,default)),
189
190    ?m(ok, zlib:close(Z1)).
191
192%% Test deflateSetDictionary.
193api_deflateSetDictionary(Config) when is_list(Config) ->
194    Z1 = zlib:open(),
195    ?m(ok, zlib:deflateInit(Z1, default)),
196    ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, <<1,1,2,3,4,5,1>>)),
197    ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, [1,1,2,3,4,5,1])),
198    ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, gurka)),
199    ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, 128)),
200    ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
201    ?m(?EXIT(stream_error), zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)),
202    ?m(ok, zlib:close(Z1)).
203
204%% Test deflateReset.
205api_deflateReset(Config) when is_list(Config) ->
206    Z1 = zlib:open(),
207    ?m(ok, zlib:deflateInit(Z1, default)),
208    ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
209    ?m(ok, zlib:deflateReset(Z1)),
210    ?m(ok, zlib:deflateReset(Z1)),
211    %% FIXME how do I make this go wrong??
212    ?m(ok, zlib:close(Z1)).
213
214%% Test deflateParams.
215api_deflateParams(Config) when is_list(Config) ->
216    Levels = [none, default, best_speed, best_compression] ++ lists:seq(0, 9),
217    Strategies = [filtered, huffman_only, rle, default],
218
219    Z1 = zlib:open(),
220    ?m(ok, zlib:deflateInit(Z1, default)),
221
222    ApiTest =
223        fun(Level, Strategy) ->
224            ?m(ok, zlib:deflateParams(Z1, Level, Strategy)),
225            ?m(ok, zlib:deflateReset(Z1))
226        end,
227
228    [ ApiTest(Level, Strategy) || Level <- Levels, Strategy <- Strategies ],
229
230    ?m(ok, zlib:close(Z1)),
231
232    FlushTest =
233        fun FlushTest(Size, Level, Strategy) ->
234            Z = zlib:open(),
235            ok = zlib:deflateInit(Z, default),
236            Data = gen_determ_rand_bytes(Size),
237            case zlib:deflate(Z, Data, none) of
238                [<<120, 156>>] ->
239                    %% All data is present in the internal zlib state, and will
240                    %% be flushed on deflateParams.
241
242                    ok = zlib:deflateParams(Z, Level, Strategy),
243                    Compressed = [<<120, 156>>, zlib:deflate(Z, <<>>, finish)],
244                    Data = zlib:uncompress(Compressed),
245                    zlib:close(Z),
246
247                    FlushTest(Size + (1 bsl 10), Level, Strategy);
248                _Other ->
249                    ok
250            end
251        end,
252
253    [ FlushTest(1, Level, Strategy) || Level <- Levels, Strategy <- Strategies ],
254
255    ok.
256
257%% Test deflate.
258api_deflate(Config) when is_list(Config) ->
259    Z1 = zlib:open(),
260    ?m(ok, zlib:deflateInit(Z1, default)),
261    ?m([B] when is_binary(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, finish)),
262    ?m(ok, zlib:deflateReset(Z1)),
263    ?m([B] when is_binary(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, finish)),
264    ?m(ok, zlib:deflateReset(Z1)),
265    ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>)),
266    ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)),
267    ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)),
268    ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, full)),
269    ?m(B when is_list(B), zlib:deflate(Z1, <<>>, finish)),
270
271    ?m(?EXIT(badarg), zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)),
272
273    ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)),
274    ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)),
275
276    %% Causes problems ERROR REPORT
277    ?m(?EXIT(badarg), zlib:deflate(Z1, [asdj,asd], none)),
278
279    ?m(ok, zlib:close(Z1)).
280
281%% Test deflateEnd.
282api_deflateEnd(Config) when is_list(Config) ->
283    Z1 = zlib:open(),
284    ?m(ok, zlib:deflateInit(Z1, default)),
285    ?m(ok, zlib:deflateEnd(Z1)),
286    ?m(?EXIT(not_initialized), zlib:deflateEnd(Z1)),
287    ?m(?EXIT(badarg), zlib:deflateEnd(gurka)),
288    ?m(ok, zlib:deflateInit(Z1, default)),
289    ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)),
290    ?m(?EXIT(data_error), zlib:deflateEnd(Z1)),
291    ?m(ok, zlib:deflateInit(Z1, default)),
292    ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)),
293    ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>, finish)),
294    ?m(ok, zlib:deflateEnd(Z1)),
295
296    ?m(ok, zlib:close(Z1)).
297
298%% Test inflateInit /1 and /2.
299api_inflateInit(Config) when is_list(Config) ->
300    Z1 = zlib:open(),
301    ?m(?EXIT(badarg), zlib:inflateInit(gurka)),
302    ?m(ok, zlib:inflateInit(Z1)),
303    ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 15)),
304    lists:foreach(fun(Wbits) ->
305			  Z11 = zlib:open(),
306			  ?m(ok, zlib:inflateInit(Z11,Wbits)),
307			  Z12 = zlib:open(),
308			  ?m(ok, zlib:inflateInit(Z12,-Wbits)),
309			  ?m(ok,zlib:close(Z11)),
310			  ?m(ok,zlib:close(Z12))
311		  end, lists:seq(8,15)),
312    ?m(?EXIT(badarg), zlib:inflateInit(gurka, -15)),
313    ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 7)),
314    ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -7)),
315    ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 48)),
316    ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -16)),
317    ?m(ok, zlib:close(Z1)).
318
319%% Test inflateSetDictionary.
320api_inflateSetDictionary(Config) when is_list(Config) ->
321    Z1 = zlib:open(),
322    ?m(ok, zlib:inflateInit(Z1)),
323    ?m(?EXIT(badarg), zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)),
324    ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,102)),
325    ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,gurka)),
326    Dict = <<1,1,1,1,1>>,
327    ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z1,Dict)),
328    ?m(ok, zlib:close(Z1)).
329
330%% Test inflateGetDictionary.
331api_inflateGetDictionary(Config) when is_list(Config) ->
332    Z1 = zlib:open(),
333    zlib:inflateInit(Z1),
334    IsOperationSupported =
335        case catch zlib:inflateGetDictionary(Z1) of
336            ?EXIT(not_supported) -> false;
337            _ -> true
338        end,
339    zlib:close(Z1),
340    api_inflateGetDictionary_if_supported(IsOperationSupported).
341
342api_inflateGetDictionary_if_supported(false) ->
343    {skip, "inflateGetDictionary/1 unsupported in current setup"};
344api_inflateGetDictionary_if_supported(true) ->
345    % Compress payload using custom dictionary
346    Z1 = zlib:open(),
347    ?m(ok, zlib:deflateInit(Z1)),
348    Dict = <<"foobar barfoo foo bar far boo">>,
349    Checksum = zlib:deflateSetDictionary(Z1, Dict),
350    Payload = <<"foobarbarbar">>,
351    Compressed = zlib:deflate(Z1, Payload, finish),
352    ?m(ok, zlib:close(Z1)),
353
354    % Decompress and test dictionary extraction with inflate/2
355    Z2 = zlib:open(),
356    ?m(ok, zlib:inflateInit(Z2)),
357    ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))),
358    ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z2, Dict)),
359    ?m(?EXIT({need_dictionary,Checksum}), zlib:inflate(Z2, Compressed)),
360    ?m(ok, zlib:inflateSetDictionary(Z2, Dict)),
361    ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))),
362    Payload = iolist_to_binary(zlib:inflate(Z2, [])),
363    ?m(ok, zlib:close(Z2)),
364    ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z2, Dict)),
365
366    %% ... And do the same for inflate/3
367    Z3 = zlib:open(),
368    ?m(ok, zlib:inflateInit(Z3)),
369    ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z3))),
370    ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z3, Dict)),
371
372    {need_dictionary, Checksum, _Output = []} =
373        zlib:inflate(Z3, Compressed, [{exception_on_need_dict, false}]),
374
375    ?m(ok, zlib:inflateSetDictionary(Z3, Dict)),
376    ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z3))),
377
378    Payload = iolist_to_binary(
379        zlib:inflate(Z3, [], [{exception_on_need_dict, false}])),
380
381    ?m(ok, zlib:close(Z3)),
382    ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z3, Dict)),
383
384    ok.
385
386%% Test inflateReset.
387api_inflateReset(Config) when is_list(Config) ->
388    Z1 = zlib:open(),
389    ?m(ok, zlib:inflateInit(Z1)),
390    ?m(?EXIT(badarg), zlib:inflateReset(gurka)),
391    ?m(ok, zlib:inflateReset(Z1)),
392    ?m(ok, zlib:close(Z1)).
393
394%% Test inflate/2
395api_inflate2(Config) when is_list(Config) ->
396    Data = [<<1,2,2,3,3,3,4,4,4,4>>],
397    Compressed = zlib:compress(Data),
398
399    Z1 = zlib:open(),
400    ?m(ok, zlib:inflateInit(Z1)),
401    ?m([], zlib:inflate(Z1, <<>>)),
402    ?m(Data, zlib:inflate(Z1, Compressed)),
403    ?m(ok, zlib:inflateEnd(Z1)),
404    ?m(ok, zlib:inflateInit(Z1)),
405    ?m(Data, zlib:inflate(Z1, Compressed)),
406    ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed)),
407    ?m(?EXIT(badarg), zlib:inflate(Z1, 4384)),
408    ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list])),
409    ?m(ok, zlib:inflateEnd(Z1)),
410    ?m(ok, zlib:inflateInit(Z1)),
411    ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>)),
412    ?m(ok, zlib:close(Z1)),
413
414    %% OTP-17299: we failed to fully flush the zlib state if we ran out of
415    %% input and filled the internal output buffer at the same time.
416    EdgeCaseData = <<"gurka", 0:16384/integer-unit:8>>,
417    EdgeCaseZipped = zlib:zip(EdgeCaseData),
418    Z2 = zlib:open(),
419    ?m(ok, zlib:inflateInit(Z2, -15)),
420    Unzipped = iolist_to_binary(zlib:inflate(Z2, EdgeCaseZipped)),
421    ?m(EdgeCaseData, Unzipped),
422    ?m(ok, zlib:inflateEnd(Z2)),
423    ?m(ok, zlib:close(Z2)),
424
425    ok.
426
427%% Test inflate/3; same as inflate/2 but with the default options inverted.
428api_inflate3(Config) when is_list(Config) ->
429    Data = [<<1,2,2,3,3,3,4,4,4,4>>],
430    Options = [{exception_on_need_dict, false}],
431    Compressed = zlib:compress(Data),
432    Z1 = zlib:open(),
433    ?m(ok, zlib:inflateInit(Z1)),
434    ?m([], zlib:inflate(Z1, <<>>, Options)),
435    ?m(Data, zlib:inflate(Z1, Compressed)),
436    ?m(ok, zlib:inflateEnd(Z1)),
437    ?m(ok, zlib:inflateInit(Z1)),
438    ?m(Data, zlib:inflate(Z1, Compressed, Options)),
439    ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed, Options)),
440    ?m(?EXIT(badarg), zlib:inflate(Z1, 4384, Options)),
441    ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list], Options)),
442    ?m(ok, zlib:inflateEnd(Z1)),
443    ?m(ok, zlib:inflateInit(Z1)),
444    ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>, Options)),
445    ?m(ok, zlib:close(Z1)).
446
447%% Test inflateChunk.
448api_inflateChunk(Config) when is_list(Config) ->
449    ChunkSize = 1024,
450    Data = << <<(I rem 150)>> || I <- lists:seq(1, 3 * ChunkSize) >>,
451    Part1 = binary:part(Data, 0, ChunkSize),
452    Part2 = binary:part(Data, ChunkSize, ChunkSize),
453    Part3 = binary:part(Data, ChunkSize * 2, ChunkSize),
454
455    Compressed = zlib:compress(Data),
456    Z1 = zlib:open(),
457
458    zlib:setBufSize(Z1, ChunkSize),
459
460    ?m(ok, zlib:inflateInit(Z1)),
461
462    0 = iolist_size(zlib:inflateChunk(Z1, <<>>)),
463
464    {more, Part1AsIOList} = zlib:inflateChunk(Z1, Compressed),
465    {more, Part2AsIOList} = zlib:inflateChunk(Z1),
466    {more, Part3AsIOList} = zlib:inflateChunk(Z1),
467
468    [] = zlib:inflateChunk(Z1),
469    [] = zlib:inflateChunk(Z1),
470    [] = zlib:inflateChunk(Z1),
471
472    ?m(Part1, iolist_to_binary(Part1AsIOList)),
473    ?m(Part2, iolist_to_binary(Part2AsIOList)),
474    ?m(Part3, iolist_to_binary(Part3AsIOList)),
475
476    ?m(ok, zlib:inflateEnd(Z1)),
477    ?m(ok, zlib:inflateInit(Z1)),
478
479    ?m({more, Part1AsIOList}, zlib:inflateChunk(Z1, Compressed)),
480
481    ?m(ok, zlib:inflateReset(Z1)),
482
483    zlib:setBufSize(Z1, byte_size(Data) + 1),
484
485    DataAsIOList = zlib:inflateChunk(Z1, Compressed),
486    ?m(Data, iolist_to_binary(DataAsIOList)),
487
488    ?m(ok, zlib:inflateEnd(Z1)),
489    ?m(ok, zlib:inflateInit(Z1)),
490
491    ?m(?EXIT(badarg), zlib:inflateChunk(gurka, Compressed)),
492    ?m(?EXIT(badarg), zlib:inflateChunk(Z1, 4384)),
493
494    ?m(?EXIT(data_error), zlib:inflateEnd(Z1)),
495
496    ?m(ok, zlib:close(Z1)).
497
498%% Test safeInflate as a mirror of inflateChunk, but ignore the stuff about
499%% exact chunk sizes.
500api_safeInflate(Config) when is_list(Config) ->
501    Data = << <<(I rem 150)>> || I <- lists:seq(1, 20 bsl 10) >>,
502    Compressed = zlib:compress(Data),
503    Z1 = zlib:open(),
504
505    ?m(ok, zlib:inflateInit(Z1)),
506
507    SafeInflateLoop =
508        fun
509            Loop({continue, Chunk}, Output) ->
510                Loop(zlib:safeInflate(Z1, []), [Output, Chunk]);
511            Loop({finished, Chunk}, Output) ->
512                [Output, Chunk]
513        end,
514
515    Decompressed = SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []),
516    Data = iolist_to_binary(Decompressed),
517
518    ?m(ok, zlib:inflateEnd(Z1)),
519    ?m(ok, zlib:inflateInit(Z1)),
520
521    {continue, Partial} = zlib:safeInflate(Z1, Compressed),
522    PBin = iolist_to_binary(Partial),
523    PSize = byte_size(PBin),
524    <<PBin:PSize/binary, Rest/binary>> = Data,
525
526    ?m(ok, zlib:inflateReset(Z1)),
527
528    {continue, Partial} = zlib:safeInflate(Z1, Compressed),
529    PBin = iolist_to_binary(Partial),
530    PSize = byte_size(PBin),
531    <<PBin:PSize/binary, Rest/binary>> = Data,
532
533    ?m(ok, zlib:inflateReset(Z1)),
534
535    SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []),
536
537    ?m({finished, []}, zlib:safeInflate(Z1, Compressed)),
538    ?m({finished, []}, zlib:safeInflate(Z1, Compressed)),
539
540    ?m(ok, zlib:inflateReset(Z1)),
541    ?m(?EXIT(badarg), zlib:safeInflate(gurka, Compressed)),
542    ?m(?EXIT(badarg), zlib:safeInflate(Z1, 4384)),
543    ?m(?EXIT(data_error), zlib:inflateEnd(Z1)),
544    ?m(ok, zlib:close(Z1)).
545
546%% Test inflateEnd.
547api_inflateEnd(Config) when is_list(Config) ->
548    Z1 = zlib:open(),
549    ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)),
550    ?m(ok, zlib:inflateInit(Z1)),
551    ?m(?EXIT(badarg), zlib:inflateEnd(gurka)),
552    ?m(?EXIT(data_error), zlib:inflateEnd(Z1)),
553    ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)),
554    ?m(ok, zlib:inflateInit(Z1)),
555    ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))),
556    ?m(ok, zlib:inflateEnd(Z1)),
557    ?m(ok, zlib:close(Z1)).
558
559%% Test crc32.
560api_crc32(Config) when is_list(Config) ->
561    Z1 = zlib:open(),
562    ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)),
563    Bin = <<1,1,1,1,1,1,1,1,1>>,
564    Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)),
565    Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)),
566    Compressed = list_to_binary(Compressed1 ++ Compressed2),
567    CRC1 = ?m( CRC1 when is_integer(CRC1), zlib:crc32(Z1)),
568    ?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,Bin)),
569    ?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,binary_to_list(Bin))),
570    ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,Compressed)),
571    CRC2 = ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,0,Compressed)),
572    ?m(CRC3 when CRC2 /= CRC3, zlib:crc32(Z1,234,Compressed)),
573    ?m(?EXIT(badarg), zlib:crc32(gurka)),
574    ?m(?EXIT(badarg), zlib:crc32(Z1, not_a_binary)),
575    ?m(?EXIT(badarg), zlib:crc32(gurka, <<1,1,2,4,4>>)),
576    ?m(?EXIT(badarg), zlib:crc32(Z1, 2298929, not_a_binary)),
577    ?m(?EXIT(badarg), zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)),
578    ?m(?EXIT(badarg), zlib:crc32_combine(Z1, not_an_int, 123123, 123)),
579    ?m(?EXIT(badarg), zlib:crc32_combine(Z1, noint, 123123, 123)),
580    ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, noint, 123)),
581    ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, 123, noint)),
582    ?m(ok, zlib:deflateEnd(Z1)),
583    ?m(ok, zlib:close(Z1)).
584
585%% Test adler32.
586api_adler32(Config) when is_list(Config) ->
587    Z1 = zlib:open(),
588    ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)),
589    Bin = <<1,1,1,1,1,1,1,1,1>>,
590    Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)),
591    Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)),
592    Compressed = list_to_binary(Compressed1 ++ Compressed2),
593    ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,Bin)),
594    ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,binary_to_list(Bin))),
595    ADLER2 = ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,Compressed)),
596    ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,1,Compressed)),
597    ?m(ADLER3 when ADLER2 /= ADLER3, zlib:adler32(Z1,234,Compressed)),
598    ?m(?EXIT(badarg), zlib:adler32(Z1, not_a_binary)),
599    ?m(?EXIT(badarg), zlib:adler32(gurka, <<1,1,2,4,4>>)),
600    ?m(?EXIT(badarg), zlib:adler32(Z1, 2298929, not_a_binary)),
601    ?m(?EXIT(badarg), zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)),
602    ?m(?EXIT(badarg), zlib:adler32_combine(Z1, noint, 123123, 123)),
603    ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, noint, 123)),
604    ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, 123, noint)),
605    ?m(ok, zlib:deflateEnd(Z1)),
606    ?m(ok, zlib:close(Z1)).
607
608%% Test compress.
609api_un_compress(Config) when is_list(Config) ->
610    ?m(?EXIT(badarg),zlib:compress(not_a_binary)),
611    Bin = <<1,11,1,23,45>>,
612    Comp = zlib:compress(Bin),
613    ?m(?EXIT(badarg),zlib:uncompress(not_a_binary)),
614    ?m(?EXIT(data_error), zlib:uncompress(<<171,171,171,171,171>>)),
615    ?m(?EXIT(data_error), zlib:uncompress(<<>>)),
616    ?m(?EXIT(data_error), zlib:uncompress(<<120>>)),
617    ?m(?EXIT(data_error), zlib:uncompress(<<120,156>>)),
618    ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3>>)),
619    ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3,0>>)),
620    ?m(?EXIT(data_error), zlib:uncompress(<<0,156,3,0,0,0,0,1>>)),
621    ?m(Bin, zlib:uncompress(binary_to_list(Comp))),
622    ?m(Bin, zlib:uncompress(Comp)).
623
624%% Test zip.
625api_un_zip(Config) when is_list(Config) ->
626    ?m(?EXIT(badarg),zlib:zip(not_a_binary)),
627    Bin = <<1,11,1,23,45>>,
628    Comp = zlib:zip(Bin),
629    ?m(Comp, zlib:zip(binary_to_list(Bin))),
630    ?m(?EXIT(badarg),zlib:unzip(not_a_binary)),
631    ?m(?EXIT(data_error), zlib:unzip(<<171,171,171,171,171>>)),
632    ?m(?EXIT(data_error), zlib:unzip(<<>>)),
633    ?m(Bin, zlib:unzip(Comp)),
634    ?m(Bin, zlib:unzip(binary_to_list(Comp))),
635
636    %% OTP-6396
637    B =
638        <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97,
639          1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2,
640          10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97,
641          116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113,
642          107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197,
643          31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,
644          64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10
645          ,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97,
646          112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,
647          103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101,
648          104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112,
649          114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101,
650          100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100,
651          100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101,
652          102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100,
653          101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,
654          100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100,
655          101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181,
656          107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100,
657          0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102,
658          105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,
659          16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11,
660          97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97,
661          11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7,
662          214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57,
663          57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4,
664          10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105,
665          110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125,
666          248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203,
667          251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0,
668          200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94,
669          13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120,
670          5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198,
671          118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0,
672          108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,
673          12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,
674          104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0,
675          33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101,
676          114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,
677          50,52,48,4,103,112,114,115,8,0,106>>,
678
679    Z = zlib:zip(B),
680    ?m(B, zlib:unzip(Z)).
681
682%% Test gunzip.
683api_g_un_zip(Config) when is_list(Config) ->
684    ?m(?EXIT(badarg),zlib:gzip(not_a_binary)),
685    Bin = <<1,11,1,23,45>>,
686    Comp = zlib:gzip(Bin),
687
688    ?m(Comp, zlib:gzip(binary_to_list(Bin))),
689    ?m(?EXIT(badarg), zlib:gunzip(not_a_binary)),
690    ?m(?EXIT(data_error), zlib:gunzip(<<171,171,171,171,171>>)),
691    ?m(?EXIT(data_error), zlib:gunzip(<<>>)),
692    ?m(Bin, zlib:gunzip(Comp)),
693    ?m(Bin, zlib:gunzip(binary_to_list(Comp))),
694
695    %% RFC 1952:
696    %%
697    %% "A gzip file consists of a series of "members" (compressed data
698    %% sets). [...] The members simply appear one after another in the file,
699    %% with no additional information before, between, or after them."
700    Concatenated = <<Bin/binary, Bin/binary>>,
701    ?m(Concatenated, zlib:gunzip([Comp, Comp])),
702
703    %% Don't explode if the uncompressed size is a perfect multiple of the
704    %% internal inflate chunk size.
705    ChunkSizedData = <<0:16384/unit:8>>,
706    ?m(ChunkSizedData, zlib:gunzip(zlib:gzip(ChunkSizedData))),
707
708    %% Bad CRC; bad length.
709    BadCrc = bad_crc_data(),
710    ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))),
711    BadLen = bad_len_data(),
712    ?m(?EXIT(data_error),(catch zlib:gunzip(BadLen))),
713    ok.
714
715bad_crc_data() ->
716    %% zlib:zip(<<42>>), one byte changed.
717    <<31,139,8,0,0,0,0,0,0,3,211,2,0,91,39,185,9,1,0,0,0>>.
718
719bad_len_data() ->
720    %% zlib:zip(<<42>>), one byte changed.
721    <<31,139,8,0,0,0,0,0,0,3,211,2,0,91,38,185,9,2,0,0,0>>.
722
723
724intro(Config) when is_list(Config) ->
725    D = <<"This is a binary">>,
726    [put({ex, N}, <<"This is a binary">>) || N <- [0,1,2,3,4]],
727    put({ex, 5}, end_of_data),
728    put(ex,0),
729    Read = fun() ->
730		   N = get(ex),
731		   put(ex,N+1),
732		   get({ex,N})
733	   end,
734
735    Z = zlib:open(),
736    ok = zlib:deflateInit(Z,default),
737
738    Compress = fun(end_of_data, _Cont) -> [];
739		  (Data, Cont) ->
740		       [zlib:deflate(Z, Data)|Cont(Read(),Cont)]
741	       end,
742    Compressed = Compress(Read(),Compress),
743    Last = zlib:deflate(Z, [], finish),
744    ok = zlib:deflateEnd(Z),
745    zlib:close(Z),
746    Res = list_to_binary([Compressed|Last]),
747    Orig = list_to_binary(lists:duplicate(5, D)),
748    ?m(Orig, zlib:uncompress(Res)).
749
750
751%% Test deflate large file, which had a bug reported on erlang-bugs.
752large_deflate(Config) when is_list(Config) ->
753    large_deflate_do().
754large_deflate_do() ->
755    Plain = gen_determ_rand_bytes(64 bsl 10),
756    Deflated = zlib:zip(Plain),
757    ?m(Plain, zlib:unzip(Deflated)).
758
759%% Test a standard compressed zip file.
760zip_usage(Config) when is_list(Config) ->
761    zip_usage(zip_usage({get_arg,Config}));
762zip_usage({get_arg,Config}) ->
763    Out = get_data_dir(Config),
764    {ok,ZIP} = file:read_file(filename:join(Out,"zipdoc.zip")),
765    {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")),
766    {run,ZIP,ORIG};
767zip_usage({run,ZIP,ORIG}) ->
768    <<_:14/binary, CRC:32/little,
769      CompSz:32/little, UnCompSz:32/little,_:31/binary,
770      Compressed:CompSz/binary, _/binary>> = ZIP,
771
772    %%io:format("CRC ~p CSz ~p UnCSz ~p ~n", [CRC,CompSz,UnCompSz]),
773    Split = split_bin(Compressed,[]),
774    Z = zlib:open(),
775
776    ?m(ok, zlib:inflateInit(Z, -15)),
777    Bs = [zlib:inflate(Z, Part) || Part <- Split],
778    UC0 = list_to_binary(Bs),
779    ?m(UnCompSz, byte_size(UC0)),
780    ?m(CRC, zlib:crc32(Z)),
781    ?m(true, zlib:crc32(Z,UC0) == zlib:crc32(Z,ORIG)),
782    ?m(ok, zlib:inflateEnd(Z)),
783
784    UC1 = zlib:unzip(Compressed),
785    ?m(UnCompSz, byte_size(UC1)),
786    ?m(true, zlib:crc32(Z,UC1) == zlib:crc32(Z,ORIG)),
787
788    ?m(ok, zlib:inflateInit(Z, -15)),
789    UC2 = zlib:inflate(Z, Compressed),
790    ?m(UnCompSz, byte_size(list_to_binary(UC2))),
791    ?m(CRC, zlib:crc32(Z)),
792    ?m(true, zlib:crc32(Z,UC2) == zlib:crc32(Z,ORIG)),
793    ?m(ok, zlib:inflateEnd(Z)),
794
795    ?m(ok, zlib:inflateInit(Z, -15)),
796    UC3 = zlib:inflate(Z, Split), % Test multivec.
797    ?m(UnCompSz, byte_size(list_to_binary(UC3))),
798    ?m(true, zlib:crc32(Z,UC3) == zlib:crc32(Z,ORIG)),
799    ?m(CRC, zlib:crc32(Z)),
800    ?m(ok, zlib:inflateEnd(Z)),
801
802    ?m(ok, zlib:inflateInit(Z, -15)),
803    ?m(ok, zlib:setBufSize(Z, UnCompSz *2)),
804    UC4 = zlib:inflate(Z, Compressed),
805    ?m(UnCompSz, byte_size(list_to_binary(UC4))),
806    ?m(CRC, zlib:crc32(Z)),
807    ?m(CRC, zlib:crc32(Z,UC4)),
808    ?m(true, zlib:crc32(Z,UC4) == zlib:crc32(Z,ORIG)),
809    ?m(ok, zlib:inflateEnd(Z)),
810
811    C1 = zlib:zip(ORIG),
812    UC5 =  zlib:unzip(C1),
813    ?m(CRC, zlib:crc32(Z,UC5)),
814    ?m(true,zlib:crc32(Z,UC5) == zlib:crc32(Z,ORIG)),
815
816    ?m(ok, zlib:deflateInit(Z, default, deflated, -15, 8, default)),
817    C2 = zlib:deflate(Z, ORIG, finish),
818    ?m(ORIG, zlib:unzip(C2)),
819    ?m(ok, zlib:deflateEnd(Z)),
820
821    ?m(ok, zlib:deflateInit(Z, none, deflated, -15, 8, filtered)),
822    ?m(ok, zlib:deflateParams(Z, default, default)),
823    C3 = zlib:deflate(Z, ORIG, finish),
824    ?m(ORIG, zlib:unzip(C3)),
825    ?m(ok, zlib:deflateEnd(Z)),
826
827    ok = zlib:close(Z),
828    ok.
829
830%% Test a standard compressed gzipped file.
831gz_usage(Config) when is_list(Config) ->
832    gz_usage(gz_usage({get_arg,Config}));
833gz_usage({get_arg,Config}) ->
834    Out = get_data_dir(Config),
835    {ok,GZIP} = file:read_file(filename:join(Out,"zipdoc.1.gz")),
836    {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")),
837    {ok,GZIP2} = file:read_file(filename:join(Out,"zipdoc.txt.gz")),
838    {run,GZIP,ORIG,GZIP2};
839gz_usage({run,GZIP,ORIG,GZIP2}) ->
840    Z = zlib:open(),
841    UC1 = zlib:gunzip(GZIP),
842    ?m(true,zlib:crc32(Z,UC1) == zlib:crc32(Z,ORIG)),
843    UC3 = zlib:gunzip(GZIP2),
844    ?m(true,zlib:crc32(Z,UC3) == zlib:crc32(Z,ORIG)),
845    Compressed = zlib:gzip(ORIG),
846    UC5 = zlib:gunzip(Compressed),
847    ?m(true,zlib:crc32(Z,UC5) == zlib:crc32(Z,ORIG)),
848    ok = zlib:close(Z).
849
850%% Test more of a standard compressed gzipped file.
851gz_usage2(Config) ->
852    case os:find_executable("gzip") of
853	Name when is_list(Name) ->
854	    Z = zlib:open(),
855	    Out = get_data_dir(Config),
856	    {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")),
857	    Compressed = zlib:gzip(ORIG),
858	    GzOutFile = filename:join(Out,"out.gz"),
859	    OutFile = filename:join(Out,"out.txt"),
860	    ?m(ok, file:write_file(GzOutFile,Compressed)),
861	    os:cmd("gzip -c -d " ++ GzOutFile ++ " > " ++ OutFile),
862	    case file:read_file(OutFile) of
863		{ok,ExtDecompressed} ->
864		    ?m(true,
865		       zlib:crc32(Z,ExtDecompressed) == zlib:crc32(Z,ORIG));
866		Error ->
867		    io:format("Couldn't test external decompressor ~p\n",
868			      [Error])
869	    end,
870	    ok = zlib:close(Z),
871	    ok;
872	false ->
873	    {skipped,"No gzip in path"}
874    end.
875
876
877
878%% Test that (de)compress funcs work with standard tools, for example
879%% a chunk from a png file.
880compress_usage(Config) when is_list(Config) ->
881    compress_usage(compress_usage({get_arg,Config}));
882compress_usage({get_arg,Config}) ->
883    Out = get_data_dir(Config),
884    {ok,C1} = file:read_file(filename:join(Out,"png-compressed.zlib")),
885    {run,C1};
886compress_usage({run,C1}) ->
887    Z = zlib:open(),
888    %% See that we can uncompress a file generated with external prog.
889    UC1 = zlib:uncompress(C1),
890    %% Check that the crc are correct.
891    ?m(4125865008,zlib:crc32(Z,UC1)),
892    C2 = zlib:compress(UC1),
893    UC2 = zlib:uncompress(C2),
894    %% Check that the crc are correct.
895    ?m(4125865008,zlib:crc32(Z,UC2)),
896
897    ok = zlib:close(Z),
898
899    D = [<<"We tests some partial">>,
900	 <<"data, sent over">>,
901	 <<"the stream">>,
902	 <<"we check that we can unpack">>,
903	 <<"every message we get">>],
904
905    ZC = zlib:open(),
906    ZU = zlib:open(),
907    Test = fun(finish, {_,Tot}) ->
908		   Compressed = zlib:deflate(ZC, <<>>, finish),
909		   Data = zlib:inflate(ZU, Compressed),
910		   [Tot|Data];
911	      (Data, {Op,Tot}) ->
912		   Compressed = zlib:deflate(ZC, Data, Op),
913		   Res1 = ?m([Data],zlib:inflate(ZU, Compressed)),
914		   {Op, [Tot|Res1]}
915	   end,
916    zlib:deflateInit(ZC),
917    zlib:inflateInit(ZU),
918    T1 = lists:foldl(Test,{sync,[]},D++[finish]),
919    ?m(true, list_to_binary(D) == list_to_binary(T1)),
920    zlib:deflateEnd(ZC),
921    zlib:inflateEnd(ZU),
922
923    zlib:deflateInit(ZC),
924    zlib:inflateInit(ZU),
925    T2 = lists:foldl(Test,{full,[]},D++[finish]),
926    ?m(true, list_to_binary(D) == list_to_binary(T2)),
927    zlib:deflateEnd(ZC),
928    zlib:inflateEnd(ZU),
929
930    ok = zlib:close(ZC),
931    ok = zlib:close(ZU).
932
933
934%% Check that crc works as expected.
935crc(Config) when is_list(Config) ->
936    crc(crc({get_arg,Config}));
937crc({get_arg,Config}) ->
938    Out = get_data_dir(Config),
939    {ok,C1} = file:read_file(filename:join(Out,"zipdoc")),
940    {run,C1};
941crc({run,C1}) ->
942    Z = zlib:open(),
943    Crc = zlib:crc32(Z, C1),
944    Bins = split_bin(C1,[]),
945    %%io:format("Length ~p ~p ~n", [length(Bins), [size(Bin) || Bin <- Bins]]),
946    Last = lists:last(Bins),
947    SCrc = lists:foldl(fun(Bin,Crc0) ->
948			       Crc1 = zlib:crc32(Z, Crc0, Bin),
949			       ?m(false, Crc == Crc1 andalso Bin /= Last),
950			       Crc1
951		       end, 0, Bins),
952    ?m(Crc,SCrc),
953    [First|Rest] = Bins,
954    Combine = fun(Bin, CS1) ->
955		      CS2 = zlib:crc32(Z, Bin),
956		      S2 = byte_size(Bin),
957		      zlib:crc32_combine(Z,CS1,CS2,S2)
958	      end,
959    Comb = lists:foldl(Combine, zlib:crc32(Z, First), Rest),
960    ?m(Crc,Comb),
961    ok = zlib:close(Z).
962
963%% Check that adler works as expected.
964adler(Config) when is_list(Config) ->
965    adler(adler({get_arg,Config}));
966adler({get_arg,Config}) ->
967    Out = get_data_dir(Config),
968    File1 = filename:join(Out,"zipdoc"),
969    {ok,C1} = file:read_file(File1),
970    {run,C1};
971adler({run,C1}) ->
972    Z = zlib:open(),
973    ?m(1, zlib:adler32(Z,<<>>)),
974    Crc = zlib:adler32(Z, C1),
975    Bins = split_bin(C1,[]),
976    Last = lists:last(Bins),
977    SCrc = lists:foldl(fun(Bin,Crc0) ->
978			       Crc1 = zlib:adler32(Z, Crc0, Bin),
979			       ?m(false, Crc == Crc1 andalso Bin /= Last),
980			       Crc1
981		       end, zlib:adler32(Z,<<>>), Bins),
982    ?m(Crc,SCrc),
983    [First|Rest] = Bins,
984    Combine = fun(Bin, CS1) ->
985		      CS2 = zlib:adler32(Z, Bin),
986		      S2 = byte_size(Bin),
987		      zlib:adler32_combine(Z,CS1,CS2,S2)
988	      end,
989    Comb = lists:foldl(Combine, zlib:adler32(Z, First), Rest),
990    ?m(Crc,Comb),
991    ok = zlib:close(Z).
992
993%% Test dictionary usage.
994dictionary_usage(Config) when is_list(Config) ->
995    dictionary_usage(dictionary_usage({get_arg,Config}));
996dictionary_usage({get_arg,_Config}) ->
997    {run}; % no args
998dictionary_usage({run}) ->
999    Z1 = zlib:open(),
1000    Dict = <<"Anka">>,
1001    Data = <<"Kalle Anka">>,
1002    ?m(ok, zlib:deflateInit(Z1)),
1003    DictID = zlib:deflateSetDictionary(Z1, Dict),
1004    %% io:format("DictID = ~p\n", [DictID]),
1005    B1 = zlib:deflate(Z1, Data),
1006    B2 = zlib:deflate(Z1, <<>>, finish),
1007    ?m(ok, zlib:deflateEnd(Z1)),
1008    ?m(ok, zlib:close(Z1)),
1009    Compressed = list_to_binary([B1,B2]),
1010    %% io:format("~p\n", [Compressed]),
1011
1012    %% Now uncompress.
1013    Z2 = zlib:open(),
1014    ?m(ok, zlib:inflateInit(Z2)),
1015
1016    ?m(?EXIT({need_dictionary, DictID}), zlib:inflate(Z2, Compressed)),
1017
1018    ?m(ok, zlib:inflateSetDictionary(Z2, Dict)),
1019    ?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))),
1020
1021    Uncompressed = ?m(B when is_list(B), zlib:inflate(Z2, [])),
1022
1023    ?m(ok, zlib:inflateEnd(Z2)),
1024    ?m(ok, zlib:close(Z2)),
1025    ?m(Data, list_to_binary(Uncompressed)).
1026
1027split_bin(<<Part:1997/binary,Rest/binary>>, Acc) ->
1028    split_bin(Rest, [Part|Acc]);
1029split_bin(Last,Acc) ->
1030    lists:reverse([Last|Acc]).
1031
1032only_allow_owner(Config) when is_list(Config) ->
1033    Z = zlib:open(),
1034    Owner = self(),
1035
1036    ?m(ok, zlib:inflateInit(Z)),
1037    ?m(ok, zlib:inflateReset(Z)),
1038
1039    {Pid, Ref} = spawn_monitor(
1040        fun() ->
1041            ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)),
1042            Owner ! '$transfer_ownership',
1043            receive
1044                '$ownership_transferred' ->
1045                    ?m(ok, zlib:inflateReset(Z))
1046            after 200 ->
1047                ct:fail("Never received transfer signal.")
1048            end
1049        end),
1050    ownership_transfer_check(Z, Pid, Ref).
1051
1052ownership_transfer_check(Z, WorkerPid, Ref) ->
1053    receive
1054        '$transfer_ownership' ->
1055            zlib:set_controlling_process(Z, WorkerPid),
1056            WorkerPid ! '$ownership_transferred',
1057            ownership_transfer_check(Z, WorkerPid, Ref);
1058        {'DOWN', Ref, process, WorkerPid, normal} ->
1059            ok;
1060        {'DOWN', Ref, process, WorkerPid, Reason} ->
1061            ct:fail("Spawned worker crashed with reason ~p.", [Reason])
1062    after 200 ->
1063        ct:fail("Spawned worker timed out.")
1064    end.
1065
1066sub_heap_binaries(Config) when is_list(Config) ->
1067    Compressed = zlib:compress(<<"gurka">>),
1068    ConfLen = erlang:length(Config),
1069
1070    HeapBin = <<ConfLen:8/integer, Compressed/binary>>,
1071    <<_:8/integer, SubHeapBin/binary>> = HeapBin,
1072
1073    ?m(<<"gurka">>, zlib:uncompress(SubHeapBin)),
1074    ok.
1075
1076%% Check concurrent access to zlib driver.
1077smp(Config) ->
1078    NumOfProcs = lists:min([8,erlang:system_info(schedulers)]),
1079    io:format("smp starting ~p workers\n",[NumOfProcs]),
1080
1081    %% Tests to run in parallel.
1082    Funcs =
1083        [zip_usage, gz_usage, compress_usage, dictionary_usage,
1084            crc, adler],
1085
1086    %% We get all function arguments here to avoid repeated parallel
1087    %% file read access.
1088    UsageArgs =
1089        list_to_tuple([{F, ?MODULE:F({get_arg,Config})} || F <- Funcs]),
1090    Parent = self(),
1091
1092    WorkerFun =
1093        fun() ->
1094            worker(rand:uniform(9999), UsageArgs, Parent)
1095        end,
1096
1097    Pids = [spawn_link(WorkerFun) || _ <- lists:seq(1, NumOfProcs)],
1098    wait_pids(Pids).
1099
1100worker(Seed, FnATpl, Parent) ->
1101    io:format("smp worker ~p, seed=~p~n",[self(),Seed]),
1102    rand:seed(exsplus, {Seed,Seed,Seed}),
1103    worker_loop(100, FnATpl),
1104    Parent ! self().
1105
1106worker_loop(0, _FnATpl) ->
1107    large_deflate_do(), % the time consuming one as finale
1108    ok;
1109worker_loop(N, FnATpl) ->
1110    {F,A} = element(rand:uniform(tuple_size(FnATpl)), FnATpl),
1111    ?MODULE:F(A),
1112    worker_loop(N-1, FnATpl).
1113
1114wait_pids([]) ->
1115    ok;
1116wait_pids(Pids) ->
1117    receive
1118	Pid ->
1119	    true = lists:member(Pid,Pids),
1120	    Others = lists:delete(Pid,Pids),
1121	    io:format("wait_pid got ~p, still waiting for ~p\n",[Pid,Others]),
1122	    wait_pids(Others)
1123    end.
1124
1125
1126%% Deflate/inflate data with size close to multiple of internal buffer size.
1127otp_7359(_Config) ->
1128    %% Find compressed size
1129    ZTry = zlib:open(),
1130    ok = zlib:deflateInit(ZTry),
1131    ISize = zlib:getBufSize(ZTry),
1132    IData = list_to_binary([Byte band 255 || Byte <- lists:seq(1,ISize)]),
1133    ISize = byte_size(IData),
1134
1135    DSize = iolist_size(zlib:deflate(ZTry, IData, sync)),
1136    zlib:close(ZTry),
1137
1138    io:format("Deflated try ~p -> ~p bytes~n", [ISize, DSize]),
1139
1140    %% Try deflate and inflate with different internal buffer sizes
1141    ISpan = 1,
1142    DSpan = 10, % use larger span around deflated size as it may vary depending on buf size
1143
1144    Cases = [{DS,IS} || DMul<-[1,2],
1145			DS <- lists:seq((DSize div DMul)-DSpan,
1146					(DSize div DMul)+DSpan),
1147			IMul<-[1,2],
1148			IS <- lists:seq((ISize div IMul)-ISpan,
1149					(ISize div IMul)+ISpan)],
1150
1151    lists:foreach(fun(Case) -> otp_7359_def_inf(IData,Case) end,
1152		  Cases).
1153
1154
1155otp_7359_def_inf(Data,{DefSize,InfSize}) ->
1156    %%io:format("Try: DefSize=~p InfSize=~p~n", [DefSize,InfSize]),
1157    ZDef = zlib:open(),
1158    ok = zlib:deflateInit(ZDef),
1159    ok = zlib:setBufSize(ZDef,DefSize),
1160    DefData = iolist_to_binary(zlib:deflate(ZDef, Data, sync)),
1161    %%io:format("Deflated ~p(~p) -> ~p(~p) bytes~n",
1162    %%          [byte_size(Data), InfSize, byte_size(DefData), DefSize]),
1163    ok = zlib:close(ZDef),
1164
1165    ZInf = zlib:open(),
1166    ok = zlib:inflateInit(ZInf),
1167    ok = zlib:setBufSize(ZInf,InfSize),
1168    Data = iolist_to_binary(zlib:inflate(ZInf, DefData)),
1169    ok = zlib:close(ZInf),
1170    ok.
1171
1172otp_9981(Config) when is_list(Config) ->
1173    Ports = lists:sort(erlang:ports()),
1174    Invalid = <<"My invalid data">>,
1175    catch zlib:compress(invalid),
1176    Ports = lists:sort(erlang:ports()),
1177    catch zlib:uncompress(Invalid),
1178    Ports = lists:sort(erlang:ports()),
1179    catch zlib:zip(invalid),
1180    Ports = lists:sort(erlang:ports()),
1181    catch zlib:unzip(Invalid),
1182    Ports = lists:sort(erlang:ports()),
1183    catch zlib:gzip(invalid),
1184    Ports = lists:sort(erlang:ports()),
1185    catch zlib:gunzip(Invalid),
1186    Ports = lists:sort(erlang:ports()),
1187    ok.
1188
1189-define(BENCH_SIZE, (16 bsl 20)).
1190
1191-define(DECOMPRESS_BENCH(Name, What, Data),
1192        Name(Config) when is_list(Config) ->
1193        Uncompressed = Data,
1194        Compressed = zlib:compress(Uncompressed),
1195        What(Compressed, byte_size(Uncompressed))).
1196
1197-define(COMPRESS_BENCH(Name, What, Data),
1198        Name(Config) when is_list(Config) ->
1199        Compressed = Data,
1200        What(Compressed, byte_size(Compressed))).
1201
1202?DECOMPRESS_BENCH(inflate_bench_zeroed, throughput_bench_inflate,
1203    <<0:(8 * ?BENCH_SIZE)>>).
1204?DECOMPRESS_BENCH(inflate_bench_rand, throughput_bench_inflate,
1205    gen_determ_rand_bytes(?BENCH_SIZE)).
1206
1207?DECOMPRESS_BENCH(chunk_bench_zeroed, throughput_bench_chunk,
1208    <<0:(8 * ?BENCH_SIZE)>>).
1209?DECOMPRESS_BENCH(chunk_bench_rand, throughput_bench_chunk,
1210    gen_determ_rand_bytes(?BENCH_SIZE)).
1211
1212?COMPRESS_BENCH(deflate_bench_zeroed, throughput_bench_deflate,
1213    <<0:(8 * ?BENCH_SIZE)>>).
1214?COMPRESS_BENCH(deflate_bench_rand, throughput_bench_deflate,
1215    gen_determ_rand_bytes(?BENCH_SIZE)).
1216
1217throughput_bench_inflate(Compressed, Size) ->
1218    Z = zlib:open(),
1219    zlib:inflateInit(Z),
1220
1221    submit_throughput_results(Size,
1222        fun() ->
1223            zlib:inflate(Z, Compressed)
1224        end).
1225
1226throughput_bench_deflate(Uncompressed, Size) ->
1227    Z = zlib:open(),
1228    zlib:deflateInit(Z),
1229
1230    submit_throughput_results(Size,
1231        fun() ->
1232            zlib:deflate(Z, Uncompressed, finish)
1233        end).
1234
1235throughput_bench_chunk(Compressed, Size) ->
1236    Z = zlib:open(),
1237    zlib:inflateInit(Z),
1238
1239    ChunkLoop =
1240        fun
1241            Loop({more, _}) -> Loop(zlib:inflateChunk(Z));
1242            Loop(_) -> ok
1243        end,
1244
1245    submit_throughput_results(Size,
1246        fun() ->
1247            ChunkLoop(zlib:inflateChunk(Z, Compressed))
1248        end).
1249
1250submit_throughput_results(Size, Fun) ->
1251    TimeTaken = measure_perf_counter(Fun, millisecond),
1252
1253    KBPS = trunc((Size bsr 10) / (TimeTaken / 1000)),
1254    ct_event:notify(#event{ name = benchmark_data, data = [{value,KBPS}] }),
1255    {comment, io_lib:format("~p ms, ~p KBPS", [TimeTaken, KBPS])}.
1256
1257measure_perf_counter(Fun, Unit) ->
1258    Start = os:perf_counter(Unit),
1259    Fun(),
1260    os:perf_counter(Unit) - Start.
1261
1262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1263%%% Helps with testing directly %%%%%%%%%%%%%
1264
1265get_data_dir(Config) ->
1266    try proplists:get_value(data_dir,Config) of
1267        undefined ->
1268            "./zlib_SUITE_data";
1269        Dir ->
1270            Dir
1271    catch
1272        _:_ -> "./zlib_SUITE_data"
1273    end.
1274
1275%% Generates a bunch of statistically random bytes using the size as seed.
1276gen_determ_rand_bytes(Size) ->
1277    gen_determ_rand_bytes(Size, erlang:md5_init(), <<>>).
1278gen_determ_rand_bytes(Size, _Context, Acc) when Size =< 0 ->
1279    Acc;
1280gen_determ_rand_bytes(Size, Context0, Acc) when Size > 0 ->
1281    Context = erlang:md5_update(Context0, <<Size/integer>>),
1282    Checksum = erlang:md5_final(Context),
1283    gen_determ_rand_bytes(Size - 16, Context, <<Acc/binary, Checksum/binary>>).
1284