1 /*
2 * Original version by Jean-loup Gailly. Modified for use by the
3 * Erlang run-time system and efile_driver; names of all external
4 * functions changed to avoid conflicts with the official gzio.c file.
5 *
6 * gzio.c -- IO on .gz files
7 * Copyright (C) 1995-1996 Jean-loup Gailly.
8 * For conditions of distribution and use, see copyright notice in zlib.h
9 */
10 /* %ExternalCopyright% */
11
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15 #include <stdio.h>
16 #include <string.h> /* ssize_t on Mac OS X */
17 #include <errno.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <ctype.h>
22
23 #include "erl_driver.h"
24 #include "sys.h"
25
26 #include "gzio_zutil.h"
27 #include "erl_zlib.h"
28 #include "gzio.h"
29
30 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
31
32 /* ===========================================================================
33 Uncompresses the buffer given and returns a pointer to a binary.
34 If the buffer was not compressed with gzip, the buffer contents
35 will be copied unchanged into the binary.
36
37 If a `gzip' header was found, but there were subsequent errors,
38 a NULL pointer is returned.
39 */
40
41 ErlDrvBinary*
erts_gzinflate_buffer(char * start,uLong size)42 erts_gzinflate_buffer(char* start, uLong size)
43 {
44 ErlDrvBinary* bin;
45 ErlDrvBinary* bin2;
46 z_stream zstr;
47 unsigned char* bptr;
48
49 /*
50 * Check for the magic bytes beginning a GZIP header.
51 */
52 bptr = (unsigned char *) start;
53 if (size < 2 || bptr[0] != gz_magic[0] || bptr[1] != gz_magic[1]) {
54 /* No GZIP header -- just copy the data into a new binary */
55 if ((bin = driver_alloc_binary(size)) == NULL) {
56 return NULL;
57 }
58 memcpy(bin->orig_bytes, start, size);
59 return bin;
60 }
61
62 /*
63 * The magic bytes for a GZIP header are there. Now try to decompress.
64 * It is an error if the GZIP header is not correct.
65 */
66
67 zstr.next_in = (unsigned char*) start;
68 zstr.avail_in = size;
69 erl_zlib_alloc_init(&zstr);
70 size *= 2;
71 if ((bin = driver_alloc_binary(size)) == NULL) {
72 return NULL;
73 }
74 if (inflateInit2(&zstr, 15+16) != Z_OK) { /* Decode GZIP format */
75 driver_free(bin);
76 return NULL;
77 }
78 for (;;) {
79 int status;
80
81 zstr.next_out = (unsigned char *) bin->orig_bytes + zstr.total_out;
82 zstr.avail_out = size - zstr.total_out;
83 status = inflate(&zstr, Z_NO_FLUSH);
84 if (status == Z_OK) {
85 size *= 2;
86 if ((bin2 = driver_realloc_binary(bin, size)) == NULL) {
87 error:
88 driver_free_binary(bin);
89 inflateEnd(&zstr);
90 return NULL;
91 }
92 bin = bin2;
93 } else if (status == Z_STREAM_END) {
94 if ((bin2 = driver_realloc_binary(bin, zstr.total_out)) == NULL) {
95 goto error;
96 }
97 inflateEnd(&zstr);
98 return bin2;
99 } else {
100 goto error;
101 }
102 }
103 }
104
105 /* ===========================================================================
106 Compresses the buffer given and returns a pointer to a binary.
107 A NULL pointer is returned if any error occurs.
108 Writes a gzip header as well.
109 */
110
111 #define GZIP_HD_SIZE 10
112 #define GZIP_TL_SIZE 8
113
114 #define GZIP_X_SIZE (GZIP_HD_SIZE+GZIP_TL_SIZE)
115
116 ErlDrvBinary*
erts_gzdeflate_buffer(char * start,uLong size)117 erts_gzdeflate_buffer(char* start, uLong size)
118 {
119 z_stream c_stream; /* compression stream */
120 ErlDrvBinary* bin;
121 ErlDrvBinary* bin2;
122 uLong crc; /* crc32 of uncompressed data */
123 uLong szIn;
124 Byte* ptr;
125 int comprLen = size + (size/1000) + 1 + 12; /* see zlib.h */
126
127 crc = crc32(0L, Z_NULL, 0);
128 erl_zlib_alloc_init(&c_stream);
129
130 if (deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION,
131 Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK)
132 return NULL;
133
134 if ((bin = driver_alloc_binary(comprLen+GZIP_X_SIZE)) == NULL)
135 return NULL;
136 sprintf(bin->orig_bytes, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
137 Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
138
139 c_stream.next_out = ((Byte*) bin->orig_bytes)+GZIP_HD_SIZE;
140 c_stream.avail_out = (uInt) bin->orig_size - GZIP_HD_SIZE;
141 c_stream.next_in = (Byte*) start;
142 c_stream.avail_in = (uInt) size;
143
144 if (deflate(&c_stream, Z_FINISH) != Z_STREAM_END) {
145 driver_free_binary(bin);
146 return NULL;
147 }
148 crc = crc32(crc, (unsigned char*)start, size);
149 ptr = c_stream.next_out;
150 szIn = c_stream.total_in;
151
152 *ptr++ = (crc & 0xff); crc >>= 8;
153 *ptr++ = (crc & 0xff); crc >>= 8;
154 *ptr++ = (crc & 0xff); crc >>= 8;
155 *ptr++ = (crc & 0xff); crc >>= 8;
156
157 *ptr++ = (szIn & 0xff); szIn >>= 8;
158 *ptr++ = (szIn & 0xff); szIn >>= 8;
159 *ptr++ = (szIn & 0xff); szIn >>= 8;
160 *ptr++ = (szIn & 0xff); szIn >>= 8;
161
162 if (deflateEnd(&c_stream) != Z_OK) {
163 driver_free_binary(bin);
164 return NULL;
165 }
166 size = ptr - (Byte*)bin->orig_bytes;
167
168 if ((bin2 = driver_realloc_binary(bin, size)) == NULL)
169 driver_free_binary(bin);
170 return bin2;
171 }
172
173