|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "vm/Compression.h" |
|
8 |
|
9 #include "js/Utility.h" |
|
10 |
|
11 using namespace js; |
|
12 |
|
13 #if USE_ZLIB |
|
14 static void * |
|
15 zlib_alloc(void *cx, uInt items, uInt size) |
|
16 { |
|
17 return js_calloc(items, size); |
|
18 } |
|
19 |
|
20 static void |
|
21 zlib_free(void *cx, void *addr) |
|
22 { |
|
23 js_free(addr); |
|
24 } |
|
25 |
|
26 Compressor::Compressor(const unsigned char *inp, size_t inplen) |
|
27 : inp(inp), |
|
28 inplen(inplen), |
|
29 outbytes(0), |
|
30 initialized(false) |
|
31 { |
|
32 JS_ASSERT(inplen > 0); |
|
33 zs.opaque = nullptr; |
|
34 zs.next_in = (Bytef *)inp; |
|
35 zs.avail_in = 0; |
|
36 zs.next_out = nullptr; |
|
37 zs.avail_out = 0; |
|
38 zs.zalloc = zlib_alloc; |
|
39 zs.zfree = zlib_free; |
|
40 } |
|
41 |
|
42 |
|
43 Compressor::~Compressor() |
|
44 { |
|
45 if (initialized) { |
|
46 int ret = deflateEnd(&zs); |
|
47 if (ret != Z_OK) { |
|
48 // If we finished early, we can get a Z_DATA_ERROR. |
|
49 JS_ASSERT(ret == Z_DATA_ERROR); |
|
50 JS_ASSERT(uInt(zs.next_in - inp) < inplen || !zs.avail_out); |
|
51 } |
|
52 } |
|
53 } |
|
54 |
|
55 bool |
|
56 Compressor::init() |
|
57 { |
|
58 if (inplen >= UINT32_MAX) |
|
59 return false; |
|
60 // zlib is slow and we'd rather be done compression sooner |
|
61 // even if it means decompression is slower which penalizes |
|
62 // Function.toString() |
|
63 int ret = deflateInit(&zs, Z_BEST_SPEED); |
|
64 if (ret != Z_OK) { |
|
65 JS_ASSERT(ret == Z_MEM_ERROR); |
|
66 return false; |
|
67 } |
|
68 initialized = true; |
|
69 return true; |
|
70 } |
|
71 |
|
72 void |
|
73 Compressor::setOutput(unsigned char *out, size_t outlen) |
|
74 { |
|
75 JS_ASSERT(outlen > outbytes); |
|
76 zs.next_out = out + outbytes; |
|
77 zs.avail_out = outlen - outbytes; |
|
78 } |
|
79 |
|
80 Compressor::Status |
|
81 Compressor::compressMore() |
|
82 { |
|
83 JS_ASSERT(zs.next_out); |
|
84 uInt left = inplen - (zs.next_in - inp); |
|
85 bool done = left <= CHUNKSIZE; |
|
86 if (done) |
|
87 zs.avail_in = left; |
|
88 else if (zs.avail_in == 0) |
|
89 zs.avail_in = CHUNKSIZE; |
|
90 Bytef *oldout = zs.next_out; |
|
91 int ret = deflate(&zs, done ? Z_FINISH : Z_NO_FLUSH); |
|
92 outbytes += zs.next_out - oldout; |
|
93 if (ret == Z_MEM_ERROR) { |
|
94 zs.avail_out = 0; |
|
95 return OOM; |
|
96 } |
|
97 if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) { |
|
98 JS_ASSERT(zs.avail_out == 0); |
|
99 return MOREOUTPUT; |
|
100 } |
|
101 JS_ASSERT_IF(!done, ret == Z_OK); |
|
102 JS_ASSERT_IF(done, ret == Z_STREAM_END); |
|
103 return done ? DONE : CONTINUE; |
|
104 } |
|
105 |
|
106 bool |
|
107 js::DecompressString(const unsigned char *inp, size_t inplen, unsigned char *out, size_t outlen) |
|
108 { |
|
109 JS_ASSERT(inplen <= UINT32_MAX); |
|
110 z_stream zs; |
|
111 zs.zalloc = zlib_alloc; |
|
112 zs.zfree = zlib_free; |
|
113 zs.opaque = nullptr; |
|
114 zs.next_in = (Bytef *)inp; |
|
115 zs.avail_in = inplen; |
|
116 zs.next_out = out; |
|
117 JS_ASSERT(outlen); |
|
118 zs.avail_out = outlen; |
|
119 int ret = inflateInit(&zs); |
|
120 if (ret != Z_OK) { |
|
121 JS_ASSERT(ret == Z_MEM_ERROR); |
|
122 return false; |
|
123 } |
|
124 ret = inflate(&zs, Z_FINISH); |
|
125 JS_ASSERT(ret == Z_STREAM_END); |
|
126 ret = inflateEnd(&zs); |
|
127 JS_ASSERT(ret == Z_OK); |
|
128 return true; |
|
129 } |
|
130 #endif /* USE_ZLIB */ |
|
131 |