1 /*
2 * Copyright (c) 2017, Planet Labs
3 * Author: <even.rouault at spatialys.com>
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and
6 * its documentation for any purpose is hereby granted without fee, provided
7 * that (i) the above copyright notices and this permission notice appear in
8 * all copies of the software and related documentation, and (ii) the names of
9 * Sam Leffler and Silicon Graphics may not be used in any advertising or
10 * publicity relating to the software without the specific, prior written
11 * permission of Sam Leffler and Silicon Graphics.
12 *
13 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16 *
17 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 * OF THIS SOFTWARE.
23 */
24 
25 #include "tiffiop.h"
26 #ifdef ZSTD_SUPPORT
27 /*
28 * TIFF Library.
29 *
30 * ZSTD Compression Support
31 *
32 */
33 
34 #include "tif_predict.h"
35 #include "zstd.h"
36 
37 #include <stdio.h>
38 
39 /*
40 * State block for each open TIFF file using ZSTD compression/decompression.
41 */
42 typedef struct {
43         TIFFPredictorState predict;
44         ZSTD_DStream*   dstream;
45         ZSTD_CStream*   cstream;
46         int             compression_level;      /* compression level */
47         ZSTD_outBuffer  out_buffer;
48         int             state;                  /* state flags */
49 #define LSTATE_INIT_DECODE 0x01
50 #define LSTATE_INIT_ENCODE 0x02
51 
52         TIFFVGetMethod  vgetparent;            /* super-class method */
53         TIFFVSetMethod  vsetparent;            /* super-class method */
54 } ZSTDState;
55 
56 #define LState(tif)             ((ZSTDState*) (tif)->tif_data)
57 #define DecoderState(tif)       LState(tif)
58 #define EncoderState(tif)       LState(tif)
59 
60 static int ZSTDEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s);
61 static int ZSTDDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s);
62 
63 static int
ZSTDFixupTags(TIFF * tif)64 ZSTDFixupTags(TIFF* tif)
65 {
66         (void) tif;
67         return 1;
68 }
69 
70 static int
ZSTDSetupDecode(TIFF * tif)71 ZSTDSetupDecode(TIFF* tif)
72 {
73         ZSTDState* sp = DecoderState(tif);
74 
75         assert(sp != NULL);
76 
77         /* if we were last encoding, terminate this mode */
78         if (sp->state & LSTATE_INIT_ENCODE) {
79             ZSTD_freeCStream(sp->cstream);
80             sp->cstream = NULL;
81             sp->state = 0;
82         }
83 
84         sp->state |= LSTATE_INIT_DECODE;
85         return 1;
86 }
87 
88 /*
89 * Setup state for decoding a strip.
90 */
91 static int
ZSTDPreDecode(TIFF * tif,uint16 s)92 ZSTDPreDecode(TIFF* tif, uint16 s)
93 {
94         static const char module[] = "ZSTDPreDecode";
95         ZSTDState* sp = DecoderState(tif);
96         size_t zstd_ret;
97 
98         (void) s;
99         assert(sp != NULL);
100 
101         if( (sp->state & LSTATE_INIT_DECODE) == 0 )
102             tif->tif_setupdecode(tif);
103 
104         if( sp->dstream )
105         {
106             ZSTD_freeDStream(sp->dstream);
107             sp->dstream = NULL;
108         }
109 
110         sp->dstream = ZSTD_createDStream();
111         if( sp->dstream == NULL ) {
112             TIFFErrorExt(tif->tif_clientdata, module,
113                          "Cannot allocate decompression stream");
114             return 0;
115         }
116         zstd_ret = ZSTD_initDStream(sp->dstream);
117         if( ZSTD_isError(zstd_ret) ) {
118             TIFFErrorExt(tif->tif_clientdata, module,
119                          "Error in ZSTD_initDStream(): %s",
120                          ZSTD_getErrorName(zstd_ret));
121             return 0;
122         }
123 
124         return 1;
125 }
126 
127 static int
ZSTDDecode(TIFF * tif,uint8 * op,tmsize_t occ,uint16 s)128 ZSTDDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s)
129 {
130         static const char module[] = "ZSTDDecode";
131         ZSTDState* sp = DecoderState(tif);
132         ZSTD_inBuffer   in_buffer;
133         ZSTD_outBuffer  out_buffer;
134         size_t          zstd_ret;
135 
136         (void) s;
137         assert(sp != NULL);
138         assert(sp->state == LSTATE_INIT_DECODE);
139 
140         in_buffer.src = tif->tif_rawcp;
141         in_buffer.size = (size_t) tif->tif_rawcc;
142         in_buffer.pos = 0;
143 
144         out_buffer.dst = op;
145         out_buffer.size = (size_t) occ;
146         out_buffer.pos = 0;
147 
148         do {
149                 zstd_ret = ZSTD_decompressStream(sp->dstream, &out_buffer,
150                                                  &in_buffer);
151                 if( ZSTD_isError(zstd_ret) ) {
152                     TIFFErrorExt(tif->tif_clientdata, module,
153                                 "Error in ZSTD_decompressStream(): %s",
154                                 ZSTD_getErrorName(zstd_ret));
155                     return 0;
156                 }
157         } while( zstd_ret != 0 &&
158                  in_buffer.pos < in_buffer.size &&
159                  out_buffer.pos < out_buffer.size );
160 
161         if (out_buffer.pos < (size_t)occ) {
162                 TIFFErrorExt(tif->tif_clientdata, module,
163                     "Not enough data at scanline %lu (short %lu bytes)",
164                     (unsigned long) tif->tif_row,
165                     (unsigned long) (size_t)occ - out_buffer.pos);
166                 return 0;
167         }
168 
169         tif->tif_rawcp += in_buffer.pos;
170         tif->tif_rawcc -= in_buffer.pos;
171 
172         return 1;
173 }
174 
175 static int
ZSTDSetupEncode(TIFF * tif)176 ZSTDSetupEncode(TIFF* tif)
177 {
178         ZSTDState* sp = EncoderState(tif);
179 
180         assert(sp != NULL);
181         if (sp->state & LSTATE_INIT_DECODE) {
182                 ZSTD_freeDStream(sp->dstream);
183                 sp->dstream = NULL;
184                 sp->state = 0;
185         }
186 
187         sp->state |= LSTATE_INIT_ENCODE;
188         return 1;
189 }
190 
191 /*
192 * Reset encoding state at the start of a strip.
193 */
194 static int
ZSTDPreEncode(TIFF * tif,uint16 s)195 ZSTDPreEncode(TIFF* tif, uint16 s)
196 {
197         static const char module[] = "ZSTDPreEncode";
198         ZSTDState *sp = EncoderState(tif);
199         size_t zstd_ret;
200 
201         (void) s;
202         assert(sp != NULL);
203         if( sp->state != LSTATE_INIT_ENCODE )
204             tif->tif_setupencode(tif);
205 
206         if (sp->cstream) {
207             ZSTD_freeCStream(sp->cstream);
208             sp->cstream = NULL;
209         }
210         sp->cstream = ZSTD_createCStream();
211         if( sp->cstream == NULL ) {
212             TIFFErrorExt(tif->tif_clientdata, module,
213                          "Cannot allocate compression stream");
214             return 0;
215         }
216 
217         zstd_ret = ZSTD_initCStream(sp->cstream, sp->compression_level);
218         if( ZSTD_isError(zstd_ret) ) {
219             TIFFErrorExt(tif->tif_clientdata, module,
220                          "Error in ZSTD_initCStream(): %s",
221                          ZSTD_getErrorName(zstd_ret));
222             return 0;
223         }
224 
225         sp->out_buffer.dst = tif->tif_rawdata;
226         sp->out_buffer.size = (size_t)tif->tif_rawdatasize;
227         sp->out_buffer.pos = 0;
228 
229         return 1;
230 }
231 
232 /*
233 * Encode a chunk of pixels.
234 */
235 static int
ZSTDEncode(TIFF * tif,uint8 * bp,tmsize_t cc,uint16 s)236 ZSTDEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s)
237 {
238         static const char module[] = "ZSTDEncode";
239         ZSTDState *sp = EncoderState(tif);
240         ZSTD_inBuffer   in_buffer;
241         size_t          zstd_ret;
242 
243         assert(sp != NULL);
244         assert(sp->state == LSTATE_INIT_ENCODE);
245 
246         (void) s;
247 
248         in_buffer.src = bp;
249         in_buffer.size = (size_t)cc;
250         in_buffer.pos = 0;
251 
252         do {
253                 zstd_ret = ZSTD_compressStream(sp->cstream, &sp->out_buffer,
254                                                &in_buffer);
255                 if( ZSTD_isError(zstd_ret) ) {
256                     TIFFErrorExt(tif->tif_clientdata, module,
257                                 "Error in ZSTD_compressStream(): %s",
258                                 ZSTD_getErrorName(zstd_ret));
259                     return 0;
260                 }
261                 if( sp->out_buffer.pos == sp->out_buffer.size ) {
262                         tif->tif_rawcc = tif->tif_rawdatasize;
263                         TIFFFlushData1(tif);
264                         sp->out_buffer.dst = tif->tif_rawcp;
265                         sp->out_buffer.pos = 0;
266                 }
267         } while( in_buffer.pos < in_buffer.size );
268 
269         return 1;
270 }
271 
272 /*
273 * Finish off an encoded strip by flushing it.
274 */
275 static int
ZSTDPostEncode(TIFF * tif)276 ZSTDPostEncode(TIFF* tif)
277 {
278         static const char module[] = "ZSTDPostEncode";
279         ZSTDState *sp = EncoderState(tif);
280         size_t          zstd_ret;
281 
282         do {
283                 zstd_ret = ZSTD_endStream(sp->cstream, &sp->out_buffer);
284                 if( ZSTD_isError(zstd_ret) ) {
285                     TIFFErrorExt(tif->tif_clientdata, module,
286                                 "Error in ZSTD_endStream(): %s",
287                                 ZSTD_getErrorName(zstd_ret));
288                     return 0;
289                 }
290                 if( sp->out_buffer.pos > 0 ) {
291                         tif->tif_rawcc = sp->out_buffer.pos;
292                         TIFFFlushData1(tif);
293                         sp->out_buffer.dst = tif->tif_rawcp;
294                         sp->out_buffer.pos = 0;
295                 }
296         } while (zstd_ret != 0);
297         return 1;
298 }
299 
300 static void
ZSTDCleanup(TIFF * tif)301 ZSTDCleanup(TIFF* tif)
302 {
303         ZSTDState* sp = LState(tif);
304 
305         assert(sp != 0);
306 
307         (void)TIFFPredictorCleanup(tif);
308 
309         tif->tif_tagmethods.vgetfield = sp->vgetparent;
310         tif->tif_tagmethods.vsetfield = sp->vsetparent;
311 
312         if (sp->dstream) {
313             ZSTD_freeDStream(sp->dstream);
314             sp->dstream = NULL;
315         }
316         if (sp->cstream) {
317             ZSTD_freeCStream(sp->cstream);
318             sp->cstream = NULL;
319         }
320         _TIFFfree(sp);
321         tif->tif_data = NULL;
322 
323         _TIFFSetDefaultCompressionState(tif);
324 }
325 
326 static int
ZSTDVSetField(TIFF * tif,uint32 tag,va_list ap)327 ZSTDVSetField(TIFF* tif, uint32 tag, va_list ap)
328 {
329 	static const char module[] = "ZSTDVSetField";
330         ZSTDState* sp = LState(tif);
331 
332         switch (tag) {
333         case TIFFTAG_ZSTD_LEVEL:
334                 sp->compression_level = (int) va_arg(ap, int);
335                 if( sp->compression_level <= 0 ||
336                     sp->compression_level > ZSTD_maxCLevel() )
337                 {
338                     TIFFWarningExt(tif->tif_clientdata, module,
339                                    "ZSTD_LEVEL should be between 1 and %d",
340                                    ZSTD_maxCLevel());
341                 }
342                 return 1;
343         default:
344                 return (*sp->vsetparent)(tif, tag, ap);
345         }
346         /*NOTREACHED*/
347 }
348 
349 static int
ZSTDVGetField(TIFF * tif,uint32 tag,va_list ap)350 ZSTDVGetField(TIFF* tif, uint32 tag, va_list ap)
351 {
352         ZSTDState* sp = LState(tif);
353 
354         switch (tag) {
355         case TIFFTAG_ZSTD_LEVEL:
356                 *va_arg(ap, int*) = sp->compression_level;
357                 break;
358         default:
359                 return (*sp->vgetparent)(tif, tag, ap);
360         }
361         return 1;
362 }
363 
364 static const TIFFField ZSTDFields[] = {
365         { TIFFTAG_ZSTD_LEVEL, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT,
366           TIFF_SETGET_UNDEFINED,
367           FIELD_PSEUDO, TRUE, FALSE, "ZSTD compression_level", NULL },
368 };
369 
370 int
TIFFInitZSTD(TIFF * tif,int scheme)371 TIFFInitZSTD(TIFF* tif, int scheme)
372 {
373         static const char module[] = "TIFFInitZSTD";
374         ZSTDState* sp;
375 
376         assert( scheme == COMPRESSION_ZSTD );
377 
378         /*
379         * Merge codec-specific tag information.
380         */
381         if (!_TIFFMergeFields(tif, ZSTDFields, TIFFArrayCount(ZSTDFields))) {
382                 TIFFErrorExt(tif->tif_clientdata, module,
383                             "Merging ZSTD codec-specific tags failed");
384                 return 0;
385         }
386 
387         /*
388         * Allocate state block so tag methods have storage to record values.
389         */
390         tif->tif_data = (uint8*) _TIFFmalloc(sizeof(ZSTDState));
391         if (tif->tif_data == NULL)
392                 goto bad;
393         sp = LState(tif);
394 
395         /*
396         * Override parent get/set field methods.
397         */
398         sp->vgetparent = tif->tif_tagmethods.vgetfield;
399         tif->tif_tagmethods.vgetfield = ZSTDVGetField;	/* hook for codec tags */
400         sp->vsetparent = tif->tif_tagmethods.vsetfield;
401         tif->tif_tagmethods.vsetfield = ZSTDVSetField;	/* hook for codec tags */
402 
403         /* Default values for codec-specific fields */
404         sp->compression_level = 9;		/* default comp. level */
405         sp->state = 0;
406         sp->dstream = 0;
407         sp->cstream = 0;
408         sp->out_buffer.dst = NULL;
409         sp->out_buffer.size = 0;
410         sp->out_buffer.pos = 0;
411 
412         /*
413         * Install codec methods.
414         */
415         tif->tif_fixuptags = ZSTDFixupTags;
416         tif->tif_setupdecode = ZSTDSetupDecode;
417         tif->tif_predecode = ZSTDPreDecode;
418         tif->tif_decoderow = ZSTDDecode;
419         tif->tif_decodestrip = ZSTDDecode;
420         tif->tif_decodetile = ZSTDDecode;
421         tif->tif_setupencode = ZSTDSetupEncode;
422         tif->tif_preencode = ZSTDPreEncode;
423         tif->tif_postencode = ZSTDPostEncode;
424         tif->tif_encoderow = ZSTDEncode;
425         tif->tif_encodestrip = ZSTDEncode;
426         tif->tif_encodetile = ZSTDEncode;
427         tif->tif_cleanup = ZSTDCleanup;
428         /*
429         * Setup predictor setup.
430         */
431         (void) TIFFPredictorInit(tif);
432         return 1;
433 bad:
434         TIFFErrorExt(tif->tif_clientdata, module,
435                     "No space for ZSTD state block");
436         return 0;
437 }
438 #endif /* ZSTD_SUPPORT */
439 
440 /* vim: set ts=8 sts=8 sw=8 noet: */
441