Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Zarr driver
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "zarr.h"
14 :
15 : #include "cpl_conv.h"
16 :
17 : #include <limits>
18 :
19 : /************************************************************************/
20 : /* ZarrShuffleCompressor() */
21 : /************************************************************************/
22 :
23 1 : static bool ZarrShuffleCompressor(const void *input_data, size_t input_size,
24 : void **output_data, size_t *output_size,
25 : CSLConstList options,
26 : void * /* compressor_user_data */)
27 : {
28 : // 4 is the default of the shuffle numcodecs:
29 : // https://numcodecs.readthedocs.io/en/v0.10.0/shuffle.html
30 1 : const int eltSize = atoi(CSLFetchNameValueDef(options, "ELEMENTSIZE", "4"));
31 1 : if (eltSize != 1 && eltSize != 2 && eltSize != 4 && eltSize != 8)
32 : {
33 0 : CPLError(CE_Failure, CPLE_AppDefined,
34 : "Only ELEMENTSIZE=1,2,4,8 is supported");
35 0 : if (output_size)
36 0 : *output_size = 0;
37 0 : return false;
38 : }
39 1 : if ((input_size % eltSize) != 0)
40 : {
41 0 : CPLError(CE_Failure, CPLE_AppDefined,
42 : "input_size should be a multiple of ELEMENTSIZE");
43 0 : if (output_size)
44 0 : *output_size = 0;
45 0 : return false;
46 : }
47 1 : if (output_data != nullptr && *output_data != nullptr &&
48 1 : output_size != nullptr && *output_size != 0)
49 : {
50 1 : if (*output_size < input_size)
51 : {
52 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
53 0 : *output_size = input_size;
54 0 : return false;
55 : }
56 :
57 1 : const size_t nElts = input_size / eltSize;
58 : // Put at the front of the output buffer all the least significant
59 : // bytes of each word, then then 2nd least significant byte, etc.
60 3 : for (size_t i = 0; i < nElts; ++i)
61 : {
62 6 : for (int j = 0; j < eltSize; j++)
63 : {
64 4 : (static_cast<uint8_t *>(*output_data))[j * nElts + i] =
65 4 : (static_cast<const uint8_t *>(input_data))[i * eltSize + j];
66 : }
67 : }
68 :
69 1 : *output_size = input_size;
70 1 : return true;
71 : }
72 :
73 : #ifdef not_needed
74 : if (output_data == nullptr && output_size != nullptr)
75 : {
76 : *output_size = input_size;
77 : return true;
78 : }
79 :
80 : if (output_data != nullptr && *output_data == nullptr &&
81 : output_size != nullptr)
82 : {
83 : *output_data = VSI_MALLOC_VERBOSE(input_size);
84 : *output_size = input_size;
85 : if (*output_data == nullptr)
86 : return false;
87 : bool ret = ZarrShuffleCompressor(input_data, input_size, output_data,
88 : output_size, options, nullptr);
89 : if (!ret)
90 : {
91 : VSIFree(*output_data);
92 : *output_data = nullptr;
93 : }
94 : return ret;
95 : }
96 : #endif
97 :
98 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
99 0 : return false;
100 : }
101 :
102 : /************************************************************************/
103 : /* ZarrShuffleDecompressor() */
104 : /************************************************************************/
105 :
106 2 : static bool ZarrShuffleDecompressor(const void *input_data, size_t input_size,
107 : void **output_data, size_t *output_size,
108 : CSLConstList options,
109 : void * /* compressor_user_data */)
110 : {
111 : // 4 is the default of the shuffle numcodecs:
112 : // https://numcodecs.readthedocs.io/en/v0.10.0/shuffle.html
113 2 : const int eltSize = atoi(CSLFetchNameValueDef(options, "ELEMENTSIZE", "4"));
114 2 : if (eltSize != 1 && eltSize != 2 && eltSize != 4 && eltSize != 8)
115 : {
116 0 : CPLError(CE_Failure, CPLE_AppDefined,
117 : "Only ELEMENTSIZE=1,2,4,8 is supported");
118 0 : if (output_size)
119 0 : *output_size = 0;
120 0 : return false;
121 : }
122 2 : if ((input_size % eltSize) != 0)
123 : {
124 0 : CPLError(CE_Failure, CPLE_AppDefined,
125 : "input_size should be a multiple of ELEMENTSIZE");
126 0 : if (output_size)
127 0 : *output_size = 0;
128 0 : return false;
129 : }
130 2 : if (output_data != nullptr && *output_data != nullptr &&
131 2 : output_size != nullptr && *output_size != 0)
132 : {
133 2 : if (*output_size < input_size)
134 : {
135 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
136 0 : *output_size = input_size;
137 0 : return false;
138 : }
139 :
140 : // Reverse of what is done in the compressor function.
141 2 : const size_t nElts = input_size / eltSize;
142 6 : for (size_t i = 0; i < nElts; ++i)
143 : {
144 12 : for (int j = 0; j < eltSize; j++)
145 : {
146 8 : (static_cast<uint8_t *>(*output_data))[i * eltSize + j] =
147 8 : (static_cast<const uint8_t *>(input_data))[j * nElts + i];
148 : }
149 : }
150 :
151 2 : *output_size = input_size;
152 2 : return true;
153 : }
154 :
155 : #ifdef not_needed
156 : if (output_data == nullptr && output_size != nullptr)
157 : {
158 : *output_size = input_size;
159 : return true;
160 : }
161 :
162 : if (output_data != nullptr && *output_data == nullptr &&
163 : output_size != nullptr)
164 : {
165 : *output_data = VSI_MALLOC_VERBOSE(input_size);
166 : *output_size = input_size;
167 : if (*output_data == nullptr)
168 : return false;
169 : bool ret = ZarrShuffleDecompressor(input_data, input_size, output_data,
170 : output_size, options, nullptr);
171 : if (!ret)
172 : {
173 : VSIFree(*output_data);
174 : *output_data = nullptr;
175 : }
176 : return ret;
177 : }
178 : #endif
179 :
180 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
181 0 : return false;
182 : }
183 :
184 : /************************************************************************/
185 : /* ZarrGetShuffleCompressor() */
186 : /************************************************************************/
187 :
188 1 : const CPLCompressor *ZarrGetShuffleCompressor()
189 : {
190 : static const CPLCompressor gShuffleCompressor = {
191 : /* nStructVersion = */ 1,
192 : /* pszId = */ "shuffle",
193 : CCT_FILTER,
194 : /* papszMetadata = */ nullptr,
195 : ZarrShuffleCompressor,
196 : /* user_data = */ nullptr};
197 :
198 1 : return &gShuffleCompressor;
199 : }
200 :
201 : /************************************************************************/
202 : /* ZarrGetShuffleDecompressor() */
203 : /************************************************************************/
204 :
205 2 : const CPLCompressor *ZarrGetShuffleDecompressor()
206 : {
207 : static const CPLCompressor gShuffleDecompressor = {
208 : /* nStructVersion = */ 1,
209 : /* pszId = */ "shuffle",
210 : CCT_FILTER,
211 : /* papszMetadata = */ nullptr,
212 : ZarrShuffleDecompressor,
213 : /* user_data = */ nullptr};
214 :
215 2 : return &gShuffleDecompressor;
216 : }
217 :
218 : /************************************************************************/
219 : /* ZarrQuantizeDecompressor() */
220 : /************************************************************************/
221 :
222 2 : static bool ZarrQuantizeDecompressor(const void *input_data, size_t input_size,
223 : void **output_data, size_t *output_size,
224 : CSLConstList options,
225 : void * /* compressor_user_data */)
226 : {
227 2 : const char *dtype = CSLFetchNameValue(options, "DTYPE");
228 2 : if (!dtype)
229 : {
230 0 : CPLError(CE_Failure, CPLE_AppDefined, "quantize: DTYPE missing");
231 0 : if (output_size)
232 0 : *output_size = 0;
233 0 : return false;
234 : }
235 2 : if (!EQUAL(dtype, "<f4") && !EQUAL(dtype, "<f8"))
236 : {
237 0 : CPLError(CE_Failure, CPLE_AppDefined,
238 : "quantize: Only DTYPE=<f4 or <f8 is supported. Not %s.",
239 : dtype);
240 0 : if (output_size)
241 0 : *output_size = 0;
242 0 : return false;
243 : }
244 :
245 2 : const int outputEltSize = EQUAL(dtype, "<f4") ? 4 : 8;
246 2 : const GDALDataType eOutDT = EQUAL(dtype, "<f4") ? GDT_Float32 : GDT_Float64;
247 :
248 2 : const char *astype = CSLFetchNameValue(options, "ASTYPE");
249 2 : if (!astype)
250 : {
251 0 : CPLError(CE_Failure, CPLE_AppDefined, "quantize: ASTYPE missing");
252 0 : if (output_size)
253 0 : *output_size = 0;
254 0 : return false;
255 : }
256 2 : if (!EQUAL(astype, "<f4") && !EQUAL(astype, "<f8"))
257 : {
258 0 : CPLError(CE_Failure, CPLE_AppDefined,
259 : "quantize: Only ASTYPE=<f4 or <f8 is supported. Not %s.",
260 : astype);
261 0 : if (output_size)
262 0 : *output_size = 0;
263 0 : return false;
264 : }
265 :
266 2 : const int inputEltSize = EQUAL(astype, "<f4") ? 4 : 8;
267 2 : const GDALDataType eInDT = EQUAL(astype, "<f4") ? GDT_Float32 : GDT_Float64;
268 :
269 2 : if ((input_size % inputEltSize) != 0)
270 : {
271 0 : CPLError(CE_Failure, CPLE_AppDefined,
272 : "input_size should be a multiple of sizeof(ASTYPE)");
273 0 : if (output_size)
274 0 : *output_size = 0;
275 0 : return false;
276 : }
277 :
278 2 : const size_t nElts = input_size / inputEltSize;
279 2 : const uint64_t required_output_size64 =
280 2 : static_cast<uint64_t>(nElts) * outputEltSize;
281 : if constexpr (SIZEOF_VOIDP < 8)
282 : {
283 : if (required_output_size64 >= std::numeric_limits<size_t>::max())
284 : {
285 : CPLError(CE_Failure, CPLE_AppDefined, "Too large input");
286 : if (output_size)
287 : *output_size = 0;
288 : return false;
289 : }
290 : }
291 2 : const size_t required_output_size =
292 : static_cast<size_t>(required_output_size64);
293 :
294 2 : if (output_data != nullptr && *output_data != nullptr &&
295 2 : output_size != nullptr && *output_size != 0)
296 : {
297 2 : if (*output_size < required_output_size)
298 : {
299 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
300 0 : *output_size = required_output_size;
301 0 : return false;
302 : }
303 :
304 : #ifdef CPL_MSB
305 : std::vector<GByte> abyTmp;
306 : try
307 : {
308 : abyTmp.resize(input_size);
309 : }
310 : catch (const std::exception &)
311 : {
312 : CPLError(CE_Failure, CPLE_OutOfMemory,
313 : "ZarrQuantizeDecompressor: Out of memory");
314 : return false;
315 : }
316 : memcpy(abyTmp.data(), input_data, input_size);
317 : GDALSwapWordsEx(abyTmp.data(), inputEltSize, nElts, inputEltSize);
318 : input_data = abyTmp.data();
319 : #endif
320 :
321 2 : GDALCopyWords64(input_data, eInDT, inputEltSize, *output_data, eOutDT,
322 : outputEltSize, nElts);
323 :
324 : #ifdef CPL_MSB
325 : GDALSwapWordsEx(*output_data, outputEltSize, nElts, outputEltSize);
326 : #endif
327 :
328 2 : *output_size = required_output_size;
329 2 : return true;
330 : }
331 :
332 : #ifdef not_needed
333 : if (output_data == nullptr && output_size != nullptr)
334 : {
335 : *output_size = required_output_size;
336 : return true;
337 : }
338 :
339 : if (output_data != nullptr && *output_data == nullptr &&
340 : output_size != nullptr)
341 : {
342 : *output_data = VSI_MALLOC_VERBOSE(required_output_size);
343 : *output_size = required_output_size;
344 : if (*output_data == nullptr)
345 : return false;
346 : bool ret = ZarrQuantizeDecompressor(input_data, input_size, output_data,
347 : output_size, options, nullptr);
348 : if (!ret)
349 : {
350 : VSIFree(*output_data);
351 : *output_data = nullptr;
352 : }
353 : return ret;
354 : }
355 : #endif
356 :
357 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
358 0 : return false;
359 : }
360 :
361 : /************************************************************************/
362 : /* ZarrGetQuantizeDecompressor() */
363 : /************************************************************************/
364 :
365 2 : const CPLCompressor *ZarrGetQuantizeDecompressor()
366 : {
367 : static const CPLCompressor gQuantizeDecompressor = {
368 : /* nStructVersion = */ 1,
369 : /* pszId = */ "quantize",
370 : CCT_FILTER,
371 : /* papszMetadata = */ nullptr,
372 : ZarrQuantizeDecompressor,
373 : /* user_data = */ nullptr};
374 :
375 2 : return &gQuantizeDecompressor;
376 : }
377 :
378 : /************************************************************************/
379 : /* ZarrFixedScaleOffsetDecompressor() */
380 : /************************************************************************/
381 :
382 4 : static bool ZarrFixedScaleOffsetDecompressor(const void *input_data,
383 : size_t input_size,
384 : void **output_data,
385 : size_t *output_size,
386 : CSLConstList options,
387 : void * /* compressor_user_data */)
388 : {
389 4 : const char *offset = CSLFetchNameValue(options, "OFFSET");
390 4 : if (!offset)
391 : {
392 0 : CPLError(CE_Failure, CPLE_AppDefined,
393 : "fixedscaleoffset: OFFSET missing");
394 0 : if (output_size)
395 0 : *output_size = 0;
396 0 : return false;
397 : }
398 4 : const double dfOffset = CPLAtof(offset);
399 :
400 4 : const char *scale = CSLFetchNameValue(options, "SCALE");
401 4 : if (!scale)
402 : {
403 0 : CPLError(CE_Failure, CPLE_AppDefined,
404 : "fixedscaleoffset: SCALE missing");
405 0 : if (output_size)
406 0 : *output_size = 0;
407 0 : return false;
408 : }
409 4 : const double dfScale = CPLAtof(scale);
410 4 : if (dfScale == 0)
411 : {
412 0 : CPLError(CE_Failure, CPLE_AppDefined,
413 : "fixedscaleoffset: SCALE = 0 is invalid");
414 0 : if (output_size)
415 0 : *output_size = 0;
416 0 : return false;
417 : }
418 :
419 4 : const char *dtype = CSLFetchNameValue(options, "DTYPE");
420 4 : if (!dtype)
421 : {
422 0 : CPLError(CE_Failure, CPLE_AppDefined,
423 : "fixedscaleoffset: DTYPE missing");
424 0 : if (output_size)
425 0 : *output_size = 0;
426 0 : return false;
427 : }
428 4 : if (!EQUAL(dtype, "<f4") && !EQUAL(dtype, "<f8"))
429 : {
430 0 : CPLError(
431 : CE_Failure, CPLE_AppDefined,
432 : "fixedscaleoffset: Only DTYPE=<f4 or <f8 is supported. Not %s.",
433 : dtype);
434 0 : if (output_size)
435 0 : *output_size = 0;
436 0 : return false;
437 : }
438 :
439 4 : const GDALDataType eOutDT = EQUAL(dtype, "<f4") ? GDT_Float32 : GDT_Float64;
440 4 : const int outputEltSize = GDALGetDataTypeSizeBytes(eOutDT);
441 :
442 4 : const char *astype = CSLFetchNameValue(options, "ASTYPE");
443 4 : if (!astype)
444 : {
445 0 : CPLError(CE_Failure, CPLE_AppDefined,
446 : "fixedscaleoffset: ASTYPE missing");
447 0 : if (output_size)
448 0 : *output_size = 0;
449 0 : return false;
450 : }
451 4 : if (!EQUAL(astype, "|u1") && !EQUAL(astype, "<u2") && !EQUAL(astype, "<u4"))
452 : {
453 0 : CPLError(CE_Failure, CPLE_AppDefined,
454 : "fixedscaleoffset: Only ASTYPE=|u1, <u2 or <f4 is supported. "
455 : "Not %s.",
456 : astype);
457 0 : if (output_size)
458 0 : *output_size = 0;
459 0 : return false;
460 : }
461 :
462 4 : const int inputEltSize = astype[2] - '0';
463 6 : const GDALDataType eInDT = inputEltSize == 1 ? GDT_Byte
464 2 : : inputEltSize == 2 ? GDT_UInt16
465 : : GDT_UInt32;
466 :
467 4 : if ((input_size % inputEltSize) != 0)
468 : {
469 0 : CPLError(CE_Failure, CPLE_AppDefined,
470 : "input_size should be a multiple of sizeof(ASTYPE)");
471 0 : if (output_size)
472 0 : *output_size = 0;
473 0 : return false;
474 : }
475 :
476 4 : const size_t nElts = input_size / inputEltSize;
477 4 : const uint64_t required_output_size64 =
478 4 : static_cast<uint64_t>(nElts) * outputEltSize;
479 : if constexpr (SIZEOF_VOIDP < 8)
480 : {
481 : if (required_output_size64 >= std::numeric_limits<size_t>::max())
482 : {
483 : CPLError(CE_Failure, CPLE_AppDefined, "Too large input");
484 : if (output_size)
485 : *output_size = 0;
486 : return false;
487 : }
488 : }
489 4 : const size_t required_output_size =
490 : static_cast<size_t>(required_output_size64);
491 :
492 4 : if (output_data != nullptr && *output_data != nullptr &&
493 4 : output_size != nullptr && *output_size != 0)
494 : {
495 4 : if (*output_size < required_output_size)
496 : {
497 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
498 0 : *output_size = required_output_size;
499 0 : return false;
500 : }
501 :
502 : #ifdef CPL_MSB
503 : std::vector<GByte> abyTmp;
504 : try
505 : {
506 : abyTmp.resize(input_size);
507 : }
508 : catch (const std::exception &)
509 : {
510 : CPLError(CE_Failure, CPLE_OutOfMemory,
511 : "ZarrFixedScaleOffsetDecompressor: Out of memory");
512 : return false;
513 : }
514 : memcpy(abyTmp.data(), input_data, input_size);
515 : GDALSwapWordsEx(abyTmp.data(), inputEltSize, nElts, inputEltSize);
516 : input_data = abyTmp.data();
517 : #endif
518 :
519 4 : GDALCopyWords64(input_data, eInDT, inputEltSize, *output_data, eOutDT,
520 : outputEltSize, nElts);
521 :
522 : // Cf https://numcodecs.readthedocs.io/en/v0.4.1/fixedscaleoffset.html
523 4 : if (eOutDT == GDT_Float32)
524 : {
525 1 : float *pafData = static_cast<float *>(*output_data);
526 11 : for (size_t i = 0; i < nElts; ++i)
527 : {
528 10 : pafData[i] =
529 10 : static_cast<float>(pafData[i] / dfScale + dfOffset);
530 : }
531 : }
532 : else
533 : {
534 3 : CPLAssert(eOutDT == GDT_Float64);
535 3 : double *padfData = static_cast<double *>(*output_data);
536 33 : for (size_t i = 0; i < nElts; ++i)
537 : {
538 30 : padfData[i] = padfData[i] / dfScale + dfOffset;
539 : }
540 : }
541 :
542 : #ifdef CPL_MSB
543 : GDALSwapWordsEx(*output_data, outputEltSize, nElts, outputEltSize);
544 : #endif
545 :
546 4 : *output_size = required_output_size;
547 4 : return true;
548 : }
549 :
550 : #ifdef not_needed
551 : if (output_data == nullptr && output_size != nullptr)
552 : {
553 : *output_size = required_output_size;
554 : return true;
555 : }
556 :
557 : if (output_data != nullptr && *output_data == nullptr &&
558 : output_size != nullptr)
559 : {
560 : *output_data = VSI_MALLOC_VERBOSE(required_output_size);
561 : *output_size = required_output_size;
562 : if (*output_data == nullptr)
563 : return false;
564 : bool ret = ZarrFixedScaleOffsetDecompressor(
565 : input_data, input_size, output_data, output_size, options, nullptr);
566 : if (!ret)
567 : {
568 : VSIFree(*output_data);
569 : *output_data = nullptr;
570 : }
571 : return ret;
572 : }
573 : #endif
574 :
575 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
576 0 : return false;
577 : }
578 :
579 : /************************************************************************/
580 : /* ZarrGetFixedScaleOffsetDecompressor() */
581 : /************************************************************************/
582 :
583 4 : const CPLCompressor *ZarrGetFixedScaleOffsetDecompressor()
584 : {
585 : static const CPLCompressor gFixedScaleOffsetDecompressor = {
586 : /* nStructVersion = */ 1,
587 : /* pszId = */ "fixedscaleoffset",
588 : CCT_FILTER,
589 : /* papszMetadata = */ nullptr,
590 : ZarrFixedScaleOffsetDecompressor,
591 : /* user_data = */ nullptr};
592 :
593 4 : return &gFixedScaleOffsetDecompressor;
594 : }
|