1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkDeflate.h"
9#include "SkRandom.h"
10#include "Test.h"
11
12namespace {
13
14#ifdef ZLIB_INCLUDE
15    #include ZLIB_INCLUDE
16#else
17    #include "zlib.h"
18#endif
19
20// Different zlib implementations use different T.
21// We've seen size_t and unsigned.
22template <typename T> void* skia_alloc_func(void*, T items, T size) {
23    return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size));
24}
25
26void skia_free_func(void*, void* address) { sk_free(address); }
27
28/**
29 *  Use the un-deflate compression algorithm to decompress the data in src,
30 *  returning the result.  Returns nullptr if an error occurs.
31 */
32SkStreamAsset* stream_inflate(SkStream* src) {
33    SkDynamicMemoryWStream decompressedDynamicMemoryWStream;
34    SkWStream* dst = &decompressedDynamicMemoryWStream;
35
36    static const size_t kBufferSize = 1024;
37    uint8_t inputBuffer[kBufferSize];
38    uint8_t outputBuffer[kBufferSize];
39    z_stream flateData;
40    flateData.zalloc = &skia_alloc_func;
41    flateData.zfree = &skia_free_func;
42    flateData.opaque = nullptr;
43    flateData.next_in = nullptr;
44    flateData.avail_in = 0;
45    flateData.next_out = outputBuffer;
46    flateData.avail_out = kBufferSize;
47    int rc;
48    rc = inflateInit(&flateData);
49    if (rc != Z_OK)
50        return nullptr;
51
52    uint8_t* input = (uint8_t*)src->getMemoryBase();
53    size_t inputLength = src->getLength();
54    if (input == nullptr || inputLength == 0) {
55        input = nullptr;
56        flateData.next_in = inputBuffer;
57        flateData.avail_in = 0;
58    } else {
59        flateData.next_in = input;
60        flateData.avail_in = SkToUInt(inputLength);
61    }
62
63    rc = Z_OK;
64    while (true) {
65        if (flateData.avail_out < kBufferSize) {
66            if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) {
67                rc = Z_BUF_ERROR;
68                break;
69            }
70            flateData.next_out = outputBuffer;
71            flateData.avail_out = kBufferSize;
72        }
73        if (rc != Z_OK)
74            break;
75        if (flateData.avail_in == 0) {
76            if (input != nullptr)
77                break;
78            size_t read = src->read(&inputBuffer, kBufferSize);
79            if (read == 0)
80                break;
81            flateData.next_in = inputBuffer;
82            flateData.avail_in = SkToUInt(read);
83        }
84        rc = inflate(&flateData, Z_NO_FLUSH);
85    }
86    while (rc == Z_OK) {
87        rc = inflate(&flateData, Z_FINISH);
88        if (flateData.avail_out < kBufferSize) {
89            if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out))
90                return nullptr;
91            flateData.next_out = outputBuffer;
92            flateData.avail_out = kBufferSize;
93        }
94    }
95
96    inflateEnd(&flateData);
97    if (rc != Z_STREAM_END) {
98        return nullptr;
99    }
100    return decompressedDynamicMemoryWStream.detachAsStream();
101}
102}  // namespace
103
104DEF_TEST(SkDeflateWStream, r) {
105    SkRandom random(123456);
106    for (int i = 0; i < 50; ++i) {
107        uint32_t size = random.nextULessThan(10000);
108        SkAutoTMalloc<uint8_t> buffer(size);
109        for (uint32_t j = 0; j < size; ++j) {
110            buffer[j] = random.nextU() & 0xff;
111        }
112
113        SkDynamicMemoryWStream dynamicMemoryWStream;
114        {
115            SkDeflateWStream deflateWStream(&dynamicMemoryWStream);
116            uint32_t j = 0;
117            while (j < size) {
118                uint32_t writeSize =
119                        SkTMin(size - j, random.nextRangeU(1, 400));
120                if (!deflateWStream.write(&buffer[j], writeSize)) {
121                    ERRORF(r, "something went wrong.");
122                    return;
123                }
124                j += writeSize;
125            }
126        }
127        SkAutoTDelete<SkStreamAsset> compressed(
128                dynamicMemoryWStream.detachAsStream());
129        SkAutoTDelete<SkStreamAsset> decompressed(stream_inflate(compressed));
130
131        if (decompressed->getLength() != size) {
132            ERRORF(r, "Decompression failed to get right size [%d]."
133                   " %u != %u", i,  (unsigned)(decompressed->getLength()),
134                   (unsigned)size);
135            SkString s = SkStringPrintf("/tmp/deftst_compressed_%d", i);
136            SkFILEWStream o(s.c_str());
137            o.writeStream(compressed.get(), compressed->getLength());
138            compressed->rewind();
139
140            s = SkStringPrintf("/tmp/deftst_input_%d", i);
141            SkFILEWStream o2(s.c_str());
142            o2.write(&buffer[0], size);
143
144            continue;
145        }
146        uint32_t minLength = SkTMin(size,
147                                    (uint32_t)(decompressed->getLength()));
148        for (uint32_t i = 0; i < minLength; ++i) {
149            uint8_t c;
150            SkDEBUGCODE(size_t rb =)decompressed->read(&c, sizeof(uint8_t));
151            SkASSERT(sizeof(uint8_t) == rb);
152            if (buffer[i] != c) {
153                ERRORF(r, "Decompression failed at byte %u.", (unsigned)i);
154                break;
155            }
156        }
157    }
158}
159