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 5 : 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 5 : const char *offset = CSLFetchNameValue(options, "OFFSET");
390 5 : 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 5 : const double dfOffset = CPLAtof(offset);
399 :
400 5 : const char *scale = CSLFetchNameValue(options, "SCALE");
401 5 : 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 5 : const double dfScale = CPLAtof(scale);
410 5 : 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 5 : const char *dtype = CSLFetchNameValue(options, "DTYPE");
420 5 : 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 5 : 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 5 : const GDALDataType eOutDT = EQUAL(dtype, "<f4") ? GDT_Float32 : GDT_Float64;
440 5 : const int outputEltSize = GDALGetDataTypeSizeBytes(eOutDT);
441 :
442 5 : const char *astype = CSLFetchNameValue(options, "ASTYPE");
443 5 : 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 5 : if (!EQUAL(astype, "|u1") && !EQUAL(astype, "<u2") &&
452 2 : !EQUAL(astype, "<u4") && !EQUAL(astype, "<f4"))
453 : {
454 0 : CPLError(
455 : CE_Failure, CPLE_AppDefined,
456 : "fixedscaleoffset: Only ASTYPE=|u1, <u2, <u4 or <f4 is supported. "
457 : "Not %s.",
458 : astype);
459 0 : if (output_size)
460 0 : *output_size = 0;
461 0 : return false;
462 : }
463 :
464 5 : const int inputEltSize = astype[2] - '0';
465 9 : const GDALDataType eInDT = EQUAL(astype, "<f4") ? GDT_Float32
466 6 : : inputEltSize == 1 ? GDT_UInt8
467 2 : : inputEltSize == 2 ? GDT_UInt16
468 : : GDT_UInt32;
469 :
470 5 : if ((input_size % inputEltSize) != 0)
471 : {
472 0 : CPLError(CE_Failure, CPLE_AppDefined,
473 : "input_size should be a multiple of sizeof(ASTYPE)");
474 0 : if (output_size)
475 0 : *output_size = 0;
476 0 : return false;
477 : }
478 :
479 5 : const size_t nElts = input_size / inputEltSize;
480 5 : const uint64_t required_output_size64 =
481 5 : static_cast<uint64_t>(nElts) * outputEltSize;
482 : if constexpr (SIZEOF_VOIDP < 8)
483 : {
484 : if (required_output_size64 >= std::numeric_limits<size_t>::max())
485 : {
486 : CPLError(CE_Failure, CPLE_AppDefined, "Too large input");
487 : if (output_size)
488 : *output_size = 0;
489 : return false;
490 : }
491 : }
492 5 : const size_t required_output_size =
493 : static_cast<size_t>(required_output_size64);
494 :
495 5 : if (output_data != nullptr && *output_data != nullptr &&
496 5 : output_size != nullptr && *output_size != 0)
497 : {
498 5 : if (*output_size < required_output_size)
499 : {
500 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too small output size");
501 0 : *output_size = required_output_size;
502 0 : return false;
503 : }
504 :
505 : #ifdef CPL_MSB
506 : std::vector<GByte> abyTmp;
507 : try
508 : {
509 : abyTmp.resize(input_size);
510 : }
511 : catch (const std::exception &)
512 : {
513 : CPLError(CE_Failure, CPLE_OutOfMemory,
514 : "ZarrFixedScaleOffsetDecompressor: Out of memory");
515 : return false;
516 : }
517 : memcpy(abyTmp.data(), input_data, input_size);
518 : GDALSwapWordsEx(abyTmp.data(), inputEltSize, nElts, inputEltSize);
519 : input_data = abyTmp.data();
520 : #endif
521 :
522 5 : GDALCopyWords64(input_data, eInDT, inputEltSize, *output_data, eOutDT,
523 : outputEltSize, nElts);
524 :
525 : // Cf https://numcodecs.readthedocs.io/en/v0.4.1/fixedscaleoffset.html
526 5 : if (eOutDT == GDT_Float32)
527 : {
528 1 : float *pafData = static_cast<float *>(*output_data);
529 11 : for (size_t i = 0; i < nElts; ++i)
530 : {
531 10 : pafData[i] =
532 10 : static_cast<float>(pafData[i] / dfScale + dfOffset);
533 : }
534 : }
535 : else
536 : {
537 4 : CPLAssert(eOutDT == GDT_Float64);
538 4 : double *padfData = static_cast<double *>(*output_data);
539 44 : for (size_t i = 0; i < nElts; ++i)
540 : {
541 40 : padfData[i] = padfData[i] / dfScale + dfOffset;
542 : }
543 : }
544 :
545 : #ifdef CPL_MSB
546 : GDALSwapWordsEx(*output_data, outputEltSize, nElts, outputEltSize);
547 : #endif
548 :
549 5 : *output_size = required_output_size;
550 5 : return true;
551 : }
552 :
553 : #ifdef not_needed
554 : if (output_data == nullptr && output_size != nullptr)
555 : {
556 : *output_size = required_output_size;
557 : return true;
558 : }
559 :
560 : if (output_data != nullptr && *output_data == nullptr &&
561 : output_size != nullptr)
562 : {
563 : *output_data = VSI_MALLOC_VERBOSE(required_output_size);
564 : *output_size = required_output_size;
565 : if (*output_data == nullptr)
566 : return false;
567 : bool ret = ZarrFixedScaleOffsetDecompressor(
568 : input_data, input_size, output_data, output_size, options, nullptr);
569 : if (!ret)
570 : {
571 : VSIFree(*output_data);
572 : *output_data = nullptr;
573 : }
574 : return ret;
575 : }
576 : #endif
577 :
578 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid use of API");
579 0 : return false;
580 : }
581 :
582 : /************************************************************************/
583 : /* ZarrGetFixedScaleOffsetDecompressor() */
584 : /************************************************************************/
585 :
586 5 : const CPLCompressor *ZarrGetFixedScaleOffsetDecompressor()
587 : {
588 : static const CPLCompressor gFixedScaleOffsetDecompressor = {
589 : /* nStructVersion = */ 1,
590 : /* pszId = */ "fixedscaleoffset",
591 : CCT_FILTER,
592 : /* papszMetadata = */ nullptr,
593 : ZarrFixedScaleOffsetDecompressor,
594 : /* user_data = */ nullptr};
595 :
596 5 : return &gFixedScaleOffsetDecompressor;
597 : }
|