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) 2021, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef ZARR_H
14 : #define ZARR_H
15 :
16 : #include "cpl_compressor.h"
17 : #include "cpl_json.h"
18 : #include "gdal_priv.h"
19 : #include "gdal_pam_multidim.h"
20 : #include "memmultidim.h"
21 :
22 : #include <array>
23 : #include <iterator>
24 : #include <map>
25 : #include <memory>
26 : #include <mutex>
27 : #include <numeric>
28 : #include <set>
29 :
30 : #define ZARR_DEBUG_KEY "ZARR"
31 :
32 : #define CRS_ATTRIBUTE_NAME "_CRS"
33 :
34 : // UUID identifying the multiscales zarr convention
35 : // (https://github.com/zarr-conventions/multiscales)
36 : constexpr const char *ZARR_MULTISCALES_UUID =
37 : "d35379db-88df-4056-af3a-620245f8e347";
38 :
39 : const CPLCompressor *ZarrGetShuffleCompressor();
40 : const CPLCompressor *ZarrGetShuffleDecompressor();
41 : const CPLCompressor *ZarrGetQuantizeDecompressor();
42 : const CPLCompressor *ZarrGetTIFFDecompressor();
43 : const CPLCompressor *ZarrGetFixedScaleOffsetDecompressor();
44 :
45 : /************************************************************************/
46 : /* MultiplyElements() */
47 : /************************************************************************/
48 :
49 : /** Return the product of elements in the vector
50 : */
51 69762 : template <class T> inline T MultiplyElements(const std::vector<T> &vector)
52 : {
53 69762 : return std::reduce(vector.begin(), vector.end(), T{1},
54 69762 : std::multiplies<T>{});
55 : }
56 :
57 : /************************************************************************/
58 : /* ZarrDataset */
59 : /************************************************************************/
60 :
61 : class ZarrArray;
62 : class ZarrGroupBase;
63 :
64 : class ZarrDataset final : public GDALDataset
65 : {
66 : friend class ZarrRasterBand;
67 :
68 : std::shared_ptr<ZarrGroupBase> m_poRootGroup{};
69 : CPLStringList m_aosSubdatasets{};
70 : GDALGeoTransform m_gt{};
71 : bool m_bHasGT = false;
72 : bool m_bSpatialProjConvention = false;
73 : std::shared_ptr<GDALDimension> m_poDimX{};
74 : std::shared_ptr<GDALDimension> m_poDimY{};
75 : std::shared_ptr<ZarrArray> m_poSingleArray{};
76 :
77 : static GDALDataset *OpenMultidim(const char *pszFilename, bool bUpdateMode,
78 : CSLConstList papszOpenOptions);
79 :
80 : public:
81 : explicit ZarrDataset(const std::shared_ptr<ZarrGroupBase> &poRootGroup);
82 : ~ZarrDataset() override;
83 :
84 : CPLErr FlushCache(bool bAtClosing = false) override;
85 :
86 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
87 : static GDALDataset *
88 : CreateMultiDimensional(const char *pszFilename,
89 : CSLConstList /*papszRootGroupOptions*/,
90 : CSLConstList /*papszOptions*/);
91 :
92 : static GDALDataset *Create(const char *pszName, int nXSize, int nYSize,
93 : int nBands, GDALDataType eType,
94 : CSLConstList papszOptions);
95 :
96 : static GDALDataset *CreateCopy(const char *, GDALDataset *, int,
97 : CSLConstList papszOptions,
98 : GDALProgressFunc pfnProgress,
99 : void *pProgressData);
100 :
101 : const char *GetMetadataItem(const char *pszName,
102 : const char *pszDomain) override;
103 : CSLConstList GetMetadata(const char *pszDomain) override;
104 :
105 : CPLErr SetMetadata(CSLConstList papszMetadata,
106 : const char *pszDomain) override;
107 :
108 : const OGRSpatialReference *GetSpatialRef() const override;
109 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
110 :
111 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
112 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
113 :
114 : std::shared_ptr<GDALGroup> GetRootGroup() const override;
115 :
116 : protected:
117 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
118 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
119 : GDALDataType eBufType, int nBandCount,
120 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
121 : GSpacing nLineSpace, GSpacing nBandSpace,
122 : GDALRasterIOExtraArg *psExtraArg) override;
123 : };
124 :
125 : /************************************************************************/
126 : /* ZarrRasterBand */
127 : /************************************************************************/
128 :
129 : class ZarrRasterBand final : public GDALRasterBand
130 : {
131 : friend class ZarrDataset;
132 :
133 : std::shared_ptr<GDALMDArray> m_poArray;
134 : GDALColorInterp m_eColorInterp = GCI_Undefined;
135 :
136 : protected:
137 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
138 : CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
139 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
140 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
141 : GDALDataType eBufType, GSpacing nPixelSpaceBuf,
142 : GSpacing nLineSpaceBuf,
143 : GDALRasterIOExtraArg *psExtraArg) override;
144 :
145 : public:
146 : explicit ZarrRasterBand(const std::shared_ptr<GDALMDArray> &poArray);
147 :
148 : double GetNoDataValue(int *pbHasNoData) override;
149 : int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
150 : uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
151 : CPLErr SetNoDataValue(double dfNoData) override;
152 : CPLErr SetNoDataValueAsInt64(int64_t nNoData) override;
153 : CPLErr SetNoDataValueAsUInt64(uint64_t nNoData) override;
154 : double GetOffset(int *pbSuccess = nullptr) override;
155 : CPLErr SetOffset(double dfNewOffset) override;
156 : double GetScale(int *pbSuccess = nullptr) override;
157 : CPLErr SetScale(double dfNewScale) override;
158 : const char *GetUnitType() override;
159 : CPLErr SetUnitType(const char *pszNewValue) override;
160 : GDALColorInterp GetColorInterpretation() override;
161 : CPLErr SetColorInterpretation(GDALColorInterp eColorInterp) override;
162 : };
163 :
164 : /************************************************************************/
165 : /* ZarrAttributeGroup() */
166 : /************************************************************************/
167 :
168 : class ZarrAttributeGroup
169 : {
170 : // Use a MEMGroup as a convenient container for attributes.
171 : const bool m_bContainerIsGroup;
172 : std::shared_ptr<MEMGroup> m_poGroup;
173 : bool m_bModified = false;
174 :
175 : public:
176 : explicit ZarrAttributeGroup(const std::string &osParentName,
177 : bool bContainerIsGroup);
178 :
179 : bool Close();
180 :
181 : void Init(const CPLJSONObject &obj, bool bUpdatable);
182 :
183 9112 : std::shared_ptr<GDALAttribute> GetAttribute(const std::string &osName) const
184 : {
185 9112 : return m_poGroup->GetAttribute(osName);
186 : }
187 :
188 : std::vector<std::shared_ptr<GDALAttribute>>
189 262 : GetAttributes(CSLConstList papszOptions = nullptr) const
190 : {
191 262 : return m_poGroup->GetAttributes(papszOptions);
192 : }
193 :
194 : std::shared_ptr<GDALAttribute>
195 229 : CreateAttribute(const std::string &osName,
196 : const std::vector<GUInt64> &anDimensions,
197 : const GDALExtendedDataType &oDataType,
198 : CSLConstList /* papszOptions */ = nullptr)
199 : {
200 229 : auto poAttr = m_poGroup->CreateAttribute(osName, anDimensions,
201 229 : oDataType, nullptr);
202 229 : if (poAttr)
203 : {
204 229 : m_bModified = true;
205 : }
206 229 : return poAttr;
207 : }
208 :
209 28 : bool DeleteAttribute(const std::string &osName)
210 : {
211 28 : const bool bOK = m_poGroup->DeleteAttribute(osName, nullptr);
212 28 : if (bOK)
213 : {
214 16 : m_bModified = true;
215 : }
216 28 : return bOK;
217 : }
218 :
219 4087 : void SetUpdatable(bool bUpdatable)
220 : {
221 8174 : auto attrs = m_poGroup->GetAttributes(nullptr);
222 5021 : for (auto &attr : attrs)
223 : {
224 1868 : auto memAttr = std::dynamic_pointer_cast<MEMAttribute>(attr);
225 934 : if (memAttr)
226 934 : memAttr->SetWritable(bUpdatable);
227 : }
228 4087 : }
229 :
230 405 : void UnsetModified()
231 : {
232 405 : m_bModified = false;
233 810 : auto attrs = m_poGroup->GetAttributes(nullptr);
234 559 : for (auto &attr : attrs)
235 : {
236 308 : auto memAttr = std::dynamic_pointer_cast<MEMAttribute>(attr);
237 154 : if (memAttr)
238 154 : memAttr->SetModified(false);
239 : }
240 405 : }
241 :
242 16238 : bool IsModified() const
243 : {
244 16238 : if (m_bModified)
245 261 : return true;
246 31954 : const auto attrs = m_poGroup->GetAttributes(nullptr);
247 18276 : for (const auto &attr : attrs)
248 : {
249 2330 : const auto memAttr = std::dynamic_pointer_cast<MEMAttribute>(attr);
250 2330 : if (memAttr && memAttr->IsModified())
251 31 : return true;
252 : }
253 15946 : return false;
254 : }
255 :
256 : CPLJSONObject Serialize() const;
257 :
258 : void ParentRenamed(const std::string &osNewParentFullName);
259 :
260 : void ParentDeleted();
261 : };
262 :
263 : /************************************************************************/
264 : /* ZarrSharedResource */
265 : /************************************************************************/
266 :
267 : class ZarrSharedResource
268 : : public std::enable_shared_from_this<ZarrSharedResource>
269 : {
270 : public:
271 : enum class ConsolidatedMetadataKind
272 : {
273 : NONE,
274 : EXTERNAL, // Zarr V2 .zmetadata
275 : INTERNAL, // Zarr V3 consolidated_metadata
276 : };
277 :
278 : private:
279 : bool m_bUpdatable = false;
280 : std::string m_osRootDirectoryName{};
281 :
282 : ConsolidatedMetadataKind m_eConsolidatedMetadataKind =
283 : ConsolidatedMetadataKind::NONE;
284 : CPLJSONObject m_oObjConsolidatedMetadata{};
285 : CPLJSONObject m_oRootAttributes{};
286 : bool m_bConsolidatedMetadataModified = false;
287 :
288 : std::shared_ptr<GDALPamMultiDim> m_poPAM{};
289 : CPLStringList m_aosOpenOptions{};
290 : std::weak_ptr<ZarrGroupBase> m_poWeakRootGroup{};
291 : std::set<std::string> m_oSetArrayInLoading{};
292 : std::map<std::string, std::shared_ptr<GDALMDArray>> m_oCacheIndexingVar{};
293 : std::string m_osKerchunkParquetPath{};
294 :
295 : explicit ZarrSharedResource(const std::string &osRootDirectoryName,
296 : bool bUpdatable);
297 :
298 : std::shared_ptr<ZarrGroupBase> OpenRootGroup();
299 : void InitConsolidatedMetadataIfNeeded();
300 :
301 : public:
302 : static std::shared_ptr<ZarrSharedResource>
303 : Create(const std::string &osRootDirectoryName, bool bUpdatable);
304 :
305 : ~ZarrSharedResource();
306 :
307 5042 : bool IsUpdatable() const
308 : {
309 5042 : return m_bUpdatable;
310 : }
311 :
312 37 : const CPLJSONObject &GetConsolidatedMetadataObj() const
313 : {
314 37 : return m_oObjConsolidatedMetadata;
315 : }
316 :
317 305 : bool IsConsolidatedMetadataEnabled() const
318 : {
319 305 : return m_eConsolidatedMetadataKind != ConsolidatedMetadataKind::NONE;
320 : }
321 :
322 333 : void EnableConsolidatedMetadata(ConsolidatedMetadataKind kind)
323 : {
324 333 : m_eConsolidatedMetadataKind = kind;
325 333 : }
326 :
327 : void SetZMetadataItem(const std::string &osFilename,
328 : const CPLJSONObject &obj);
329 :
330 : void DeleteZMetadataItemRecursive(const std::string &osFilename);
331 :
332 : void RenameZMetadataRecursive(const std::string &osOldFilename,
333 : const std::string &osNewFilename);
334 :
335 2402 : const std::shared_ptr<GDALPamMultiDim> &GetPAM()
336 : {
337 2402 : return m_poPAM;
338 : }
339 :
340 13 : const std::string &GetRootDirectoryName() const
341 : {
342 13 : return m_osRootDirectoryName;
343 : }
344 :
345 2862 : const CPLStringList &GetOpenOptions() const
346 : {
347 2862 : return m_aosOpenOptions;
348 : }
349 :
350 1614 : void SetOpenOptions(CSLConstList papszOpenOptions)
351 : {
352 1614 : m_aosOpenOptions = papszOpenOptions;
353 1614 : }
354 :
355 : void
356 : UpdateDimensionSize(const std::shared_ptr<GDALDimension> &poUpdatedDim);
357 :
358 : std::shared_ptr<ZarrGroupBase> GetRootGroup();
359 :
360 108 : const std::string &GetKerchunkParquetPath() const
361 : {
362 108 : return m_osKerchunkParquetPath;
363 : }
364 :
365 90 : void SetRootGroup(const std::shared_ptr<ZarrGroupBase> &poRootGroup)
366 : {
367 90 : m_poWeakRootGroup = poRootGroup;
368 90 : }
369 :
370 : bool AddArrayInLoading(const std::string &osZarrayFilename);
371 : void RemoveArrayInLoading(const std::string &osZarrayFilename);
372 :
373 : struct SetFilenameAdder
374 : {
375 : std::shared_ptr<ZarrSharedResource> m_poSharedResource;
376 : const std::string m_osFilename;
377 : const bool m_bOK;
378 :
379 1982 : SetFilenameAdder(
380 : const std::shared_ptr<ZarrSharedResource> &poSharedResource,
381 : const std::string &osFilename)
382 1982 : : m_poSharedResource(poSharedResource), m_osFilename(osFilename),
383 1982 : m_bOK(m_poSharedResource->AddArrayInLoading(m_osFilename))
384 : {
385 1982 : }
386 :
387 1982 : ~SetFilenameAdder()
388 1982 : {
389 1982 : if (m_bOK)
390 1980 : m_poSharedResource->RemoveArrayInLoading(m_osFilename);
391 1982 : }
392 :
393 1982 : bool ok() const
394 : {
395 1982 : return m_bOK;
396 : }
397 : };
398 :
399 8 : void RegisterIndexingVariable(const std::string &osDimName,
400 : const std::shared_ptr<GDALMDArray> &poVar)
401 : {
402 8 : m_oCacheIndexingVar[osDimName] = poVar;
403 8 : }
404 : };
405 :
406 : /************************************************************************/
407 : /* ZarrGroup */
408 : /************************************************************************/
409 :
410 : class ZarrArray;
411 : class ZarrDimension;
412 :
413 : class ZarrGroupBase CPL_NON_FINAL : public GDALGroup
414 : {
415 : protected:
416 : friend class ZarrV2Group;
417 : friend class ZarrV3Group;
418 :
419 : // For ZarrV2, this is the directory of the group
420 : // For ZarrV3, this is the root directory of the dataset
421 : std::shared_ptr<ZarrSharedResource> m_poSharedResource;
422 : std::string m_osDirectoryName{};
423 : std::weak_ptr<ZarrGroupBase>
424 : m_poParent{}; // weak reference to owning parent
425 : std::shared_ptr<ZarrGroupBase>
426 : m_poParentStrongRef{}; // strong reference, used only when opening from
427 : // a subgroup
428 : mutable std::map<CPLString, std::shared_ptr<ZarrGroupBase>> m_oMapGroups{};
429 : mutable std::map<CPLString, std::shared_ptr<ZarrArray>> m_oMapMDArrays{};
430 : mutable std::map<CPLString, std::shared_ptr<ZarrDimension>>
431 : m_oMapDimensions{};
432 : mutable bool m_bDirectoryExplored = false;
433 : mutable std::set<std::string> m_oSetGroupNames{};
434 : mutable std::vector<std::string> m_aosGroups{};
435 : mutable std::set<std::string> m_oSetArrayNames{};
436 : mutable std::vector<std::string> m_aosArrays{};
437 : mutable ZarrAttributeGroup m_oAttrGroup;
438 : mutable bool m_bAttributesLoaded = false;
439 : bool m_bReadFromConsolidatedMetadata = false;
440 : mutable bool m_bDimensionsInstantiated = false;
441 : bool m_bUpdatable = false;
442 : bool m_bDimSizeInUpdate = false;
443 :
444 : virtual void ExploreDirectory() const = 0;
445 : virtual void LoadAttributes() const = 0;
446 :
447 2406 : ZarrGroupBase(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
448 : const std::string &osParentName, const std::string &osName)
449 2406 : : GDALGroup(osParentName, osName), m_poSharedResource(poSharedResource),
450 2406 : m_oAttrGroup(m_osFullName, /*bContainerIsGroup=*/true)
451 : {
452 2406 : }
453 :
454 : protected:
455 : friend class ZarrDimension;
456 : bool RenameDimension(const std::string &osOldName,
457 : const std::string &osNewName);
458 :
459 : void NotifyChildrenOfRenaming() override;
460 :
461 : void NotifyChildrenOfDeletion() override;
462 :
463 : public:
464 : ~ZarrGroupBase() override;
465 :
466 : virtual bool Close();
467 :
468 : bool Flush();
469 :
470 : std::shared_ptr<GDALAttribute>
471 408 : GetAttribute(const std::string &osName) const override
472 : {
473 408 : LoadAttributes();
474 408 : return m_oAttrGroup.GetAttribute(osName);
475 : }
476 :
477 : std::vector<std::shared_ptr<GDALAttribute>>
478 47 : GetAttributes(CSLConstList papszOptions = nullptr) const override
479 : {
480 47 : LoadAttributes();
481 47 : return m_oAttrGroup.GetAttributes(papszOptions);
482 : }
483 :
484 : std::shared_ptr<GDALAttribute>
485 : CreateAttribute(const std::string &osName,
486 : const std::vector<GUInt64> &anDimensions,
487 : const GDALExtendedDataType &oDataType,
488 : CSLConstList papszOptions = nullptr) override;
489 :
490 : bool DeleteAttribute(const std::string &osName,
491 : CSLConstList papszOptions = nullptr) override;
492 :
493 : std::vector<std::shared_ptr<GDALDimension>>
494 : GetDimensions(CSLConstList papszOptions = nullptr) const override;
495 :
496 : std::shared_ptr<GDALDimension>
497 : CreateDimension(const std::string &osName, const std::string &osType,
498 : const std::string &osDirection, GUInt64 nSize,
499 : CSLConstList papszOptions = nullptr) override;
500 :
501 : std::vector<std::string>
502 : GetMDArrayNames(CSLConstList papszOptions = nullptr) const override;
503 :
504 : std::vector<std::string>
505 : GetGroupNames(CSLConstList papszOptions = nullptr) const override;
506 :
507 : virtual std::shared_ptr<ZarrGroupBase>
508 : OpenZarrGroup(const std::string &osName,
509 : CSLConstList papszOptions = nullptr) const = 0;
510 :
511 : std::shared_ptr<GDALGroup>
512 1432 : OpenGroup(const std::string &osName,
513 : CSLConstList papszOptions = nullptr) const override
514 : {
515 : return std::static_pointer_cast<GDALGroup>(
516 1432 : OpenZarrGroup(osName, papszOptions));
517 : }
518 :
519 : bool DeleteGroup(const std::string &osName,
520 : CSLConstList papszOptions = nullptr) override;
521 :
522 : std::shared_ptr<GDALMDArray>
523 2151 : OpenMDArray(const std::string &osName,
524 : CSLConstList papszOptions = nullptr) const override
525 : {
526 : return std::static_pointer_cast<GDALMDArray>(
527 2151 : OpenZarrArray(osName, papszOptions));
528 : }
529 :
530 : bool DeleteMDArray(const std::string &osName,
531 : CSLConstList papszOptions = nullptr) override;
532 :
533 : virtual std::shared_ptr<ZarrArray>
534 : OpenZarrArray(const std::string &osName,
535 : CSLConstList papszOptions = nullptr) const = 0;
536 :
537 1135 : void SetDirectoryName(const std::string &osDirectoryName)
538 : {
539 1135 : m_osDirectoryName = osDirectoryName;
540 1135 : }
541 :
542 9 : const std::string &GetDirectoryName() const
543 : {
544 9 : return m_osDirectoryName;
545 : }
546 :
547 : void RegisterArray(const std::shared_ptr<ZarrArray> &array) const;
548 :
549 2400 : void SetUpdatable(bool bUpdatable)
550 : {
551 2400 : m_bUpdatable = bUpdatable;
552 2400 : }
553 :
554 : void UpdateDimensionSize(const std::shared_ptr<GDALDimension> &poDim);
555 :
556 : static bool IsValidObjectName(const std::string &osName);
557 :
558 : bool Rename(const std::string &osNewName) override;
559 :
560 : //! Returns false in case of error
561 : bool
562 : CheckArrayOrGroupWithSameNameDoesNotExist(const std::string &osName) const;
563 :
564 : void ParentRenamed(const std::string &osNewParentFullName) override;
565 :
566 : void NotifyArrayRenamed(const std::string &osOldName,
567 : const std::string &osNewName);
568 :
569 : //! Return the group owning the array. Might be nullptr
570 : std::shared_ptr<ZarrGroupBase> GetParentGroup() const;
571 :
572 4292 : std::shared_ptr<ZarrGroupBase> Self() const
573 : {
574 4292 : return std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
575 : }
576 :
577 2785 : const ZarrAttributeGroup &GetAttributeGroup() const
578 : {
579 2785 : return m_oAttrGroup;
580 : }
581 : };
582 :
583 : /************************************************************************/
584 : /* ZarrV2Group */
585 : /************************************************************************/
586 :
587 : class ZarrV2Group final : public ZarrGroupBase
588 : {
589 : void ExploreDirectory() const override;
590 : void LoadAttributes() const override;
591 :
592 : std::shared_ptr<ZarrV2Group>
593 : GetOrCreateSubGroup(const std::string &osSubGroupFullname);
594 :
595 997 : ZarrV2Group(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
596 : const std::string &osParentName, const std::string &osName)
597 997 : : ZarrGroupBase(poSharedResource, osParentName, osName)
598 : {
599 997 : }
600 :
601 : bool Close() override;
602 :
603 : public:
604 : static std::shared_ptr<ZarrV2Group>
605 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
606 : const std::string &osParentName, const std::string &osName);
607 :
608 : ~ZarrV2Group() override;
609 :
610 : static std::shared_ptr<ZarrV2Group>
611 : CreateOnDisk(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
612 : const std::string &osParentName, const std::string &osName,
613 : const std::string &osDirectoryName);
614 :
615 : std::shared_ptr<ZarrArray>
616 : OpenZarrArray(const std::string &osName,
617 : CSLConstList papszOptions = nullptr) const override;
618 :
619 : std::shared_ptr<ZarrGroupBase>
620 : OpenZarrGroup(const std::string &osName,
621 : CSLConstList papszOptions = nullptr) const override;
622 :
623 : std::shared_ptr<GDALGroup>
624 : CreateGroup(const std::string &osName,
625 : CSLConstList papszOptions = nullptr) override;
626 :
627 : std::shared_ptr<ZarrArray>
628 : LoadArray(const std::string &osArrayName,
629 : const std::string &osZarrayFilename, const CPLJSONObject &oRoot,
630 : bool bLoadedFromZMetadata,
631 : const CPLJSONObject &oAttributes) const;
632 :
633 : std::shared_ptr<GDALMDArray> CreateMDArray(
634 : const std::string &osName,
635 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
636 : const GDALExtendedDataType &oDataType,
637 : CSLConstList papszOptions = nullptr) override;
638 :
639 : void InitFromConsolidatedMetadata(const CPLJSONObject &oRoot);
640 :
641 : bool InitFromZGroup(const CPLJSONObject &oRoot);
642 : };
643 :
644 : /************************************************************************/
645 : /* ZarrV3Group */
646 : /************************************************************************/
647 :
648 : class ZarrV3Group final : public ZarrGroupBase
649 : {
650 : bool m_bFileHasBeenWritten = false;
651 :
652 : void ExploreDirectory() const override;
653 : void LoadAttributes() const override;
654 :
655 : ZarrV3Group(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
656 : const std::string &osParentName, const std::string &osName,
657 : const std::string &osDirectoryName);
658 :
659 : std::shared_ptr<ZarrV3Group>
660 : GetOrCreateSubGroup(const std::string &osSubGroupFullname);
661 :
662 : bool Close() override;
663 :
664 : public:
665 : ~ZarrV3Group() override;
666 :
667 : static std::shared_ptr<ZarrV3Group>
668 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
669 : const std::string &osParentName, const std::string &osName,
670 : const std::string &osDirectoryName);
671 :
672 : std::shared_ptr<ZarrArray>
673 : OpenZarrArray(const std::string &osName,
674 : CSLConstList papszOptions = nullptr) const override;
675 :
676 : std::shared_ptr<ZarrGroupBase>
677 : OpenZarrGroup(const std::string &osName,
678 : CSLConstList papszOptions = nullptr) const override;
679 :
680 : static std::shared_ptr<ZarrV3Group>
681 : CreateOnDisk(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
682 : const std::string &osParentFullName, const std::string &osName,
683 : const std::string &osDirectoryName);
684 :
685 : std::shared_ptr<GDALGroup>
686 : CreateGroup(const std::string &osName,
687 : CSLConstList papszOptions = nullptr) override;
688 :
689 : std::shared_ptr<ZarrArray> LoadArray(const std::string &osArrayName,
690 : const std::string &osZarrayFilename,
691 : const CPLJSONObject &oRoot) const;
692 :
693 : void GenerateMultiscalesMetadata(const char *pszResampling = nullptr);
694 :
695 : std::shared_ptr<GDALMDArray> CreateMDArray(
696 : const std::string &osName,
697 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
698 : const GDALExtendedDataType &oDataType,
699 : CSLConstList papszOptions = nullptr) override;
700 :
701 723 : void SetExplored()
702 : {
703 723 : m_bDirectoryExplored = true;
704 723 : }
705 :
706 : void
707 : InitFromConsolidatedMetadata(const CPLJSONObject &oConsolidatedMetadata,
708 : const CPLJSONObject &oRootAttributes);
709 : };
710 :
711 : /************************************************************************/
712 : /* ZarrDimension */
713 : /************************************************************************/
714 :
715 : class ZarrDimension final : public GDALDimensionWeakIndexingVar
716 : {
717 : const bool m_bUpdatable;
718 : std::weak_ptr<ZarrGroupBase> m_poParentGroup;
719 : bool m_bModified = false;
720 : bool m_bXArrayDim = false;
721 :
722 : public:
723 5042 : ZarrDimension(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
724 : const std::weak_ptr<ZarrGroupBase> &poParentGroup,
725 : const std::string &osParentName, const std::string &osName,
726 : const std::string &osType, const std::string &osDirection,
727 : GUInt64 nSize)
728 5042 : : GDALDimensionWeakIndexingVar(osParentName, osName, osType,
729 : osDirection, nSize),
730 5042 : m_bUpdatable(poSharedResource->IsUpdatable()),
731 10084 : m_poParentGroup(poParentGroup)
732 : {
733 5042 : }
734 :
735 : bool Rename(const std::string &osNewName) override;
736 :
737 9722 : bool IsModified() const
738 : {
739 9722 : return m_bModified;
740 : }
741 :
742 1627 : void SetXArrayDimension()
743 : {
744 1627 : m_bXArrayDim = true;
745 1627 : }
746 :
747 14389 : bool IsXArrayDimension() const
748 : {
749 14389 : return m_bXArrayDim;
750 : }
751 : };
752 :
753 : /************************************************************************/
754 : /* DtypeElt() */
755 : /************************************************************************/
756 :
757 : struct DtypeElt
758 : {
759 : enum class NativeType
760 : {
761 : BOOLEAN,
762 : UNSIGNED_INT,
763 : SIGNED_INT,
764 : IEEEFP,
765 : COMPLEX_IEEEFP,
766 : STRING_ASCII,
767 : STRING_UNICODE
768 : };
769 :
770 : NativeType nativeType = NativeType::BOOLEAN;
771 : size_t nativeOffset = 0;
772 : size_t nativeSize = 0;
773 : bool needByteSwapping = false;
774 : bool gdalTypeIsApproxOfNative = false;
775 : GDALExtendedDataType gdalType = GDALExtendedDataType::Create(GDT_Unknown);
776 : size_t gdalOffset = 0;
777 : size_t gdalSize = 0;
778 : };
779 :
780 : /************************************************************************/
781 : /* ZarrByteVectorQuickResize */
782 : /************************************************************************/
783 :
784 : /* std::vector<GByte> with quick resizing (ie that doesn't zero out when
785 : * growing back to a previously reached greater size).
786 : */
787 186477 : class ZarrByteVectorQuickResize
788 : {
789 : std::vector<GByte> m_oVec{};
790 : size_t m_nSize = 0;
791 :
792 : public:
793 117033 : ZarrByteVectorQuickResize() = default;
794 :
795 : ZarrByteVectorQuickResize(const ZarrByteVectorQuickResize &) = delete;
796 : ZarrByteVectorQuickResize &
797 : operator=(const ZarrByteVectorQuickResize &) = delete;
798 :
799 68870 : ZarrByteVectorQuickResize(ZarrByteVectorQuickResize &&) = default;
800 : ZarrByteVectorQuickResize &
801 : operator=(ZarrByteVectorQuickResize &&) = default;
802 :
803 222636 : void resize(size_t nNewSize)
804 : {
805 222636 : if (nNewSize > m_oVec.size())
806 69095 : m_oVec.resize(nNewSize);
807 222636 : m_nSize = nNewSize;
808 222636 : }
809 :
810 1603 : inline void clear()
811 : {
812 1603 : m_nSize = 0;
813 1603 : }
814 :
815 154 : inline std::vector<GByte>::iterator begin()
816 : {
817 154 : return m_oVec.begin();
818 : }
819 :
820 1188 : inline std::vector<GByte>::const_iterator begin() const
821 : {
822 1188 : return m_oVec.begin();
823 : }
824 :
825 1031 : inline std::vector<GByte>::iterator end()
826 : {
827 1031 : return m_oVec.begin() + m_nSize;
828 : }
829 :
830 184 : inline std::vector<GByte>::const_iterator end() const
831 : {
832 184 : return m_oVec.begin() + m_nSize;
833 : }
834 :
835 : template <class InputIt>
836 : inline std::vector<GByte>::iterator
837 877 : insert(std::vector<GByte>::const_iterator pos, InputIt first, InputIt last)
838 : {
839 877 : const size_t nCount = std::distance(first, last);
840 877 : const auto &oVec = m_oVec;
841 877 : const size_t nStart = std::distance(oVec.begin(), pos);
842 877 : if (nStart == m_nSize && nStart + nCount <= m_oVec.size())
843 : {
844 : // Insert at end of user-visible vector, but fully inside the
845 : // container vector. We can just copy
846 591 : std::copy(first, last, m_oVec.begin() + nStart);
847 591 : m_nSize += nCount;
848 591 : return m_oVec.begin() + nStart;
849 : }
850 : else
851 : {
852 : // Generic case
853 286 : auto ret = m_oVec.insert(pos, first, last);
854 286 : m_nSize += nCount;
855 286 : return ret;
856 : }
857 : }
858 :
859 294869 : inline bool empty() const
860 : {
861 294869 : return m_nSize == 0;
862 : }
863 :
864 257188 : inline size_t size() const
865 : {
866 257188 : return m_nSize;
867 : }
868 :
869 47055 : inline size_t capacity() const
870 : {
871 : // Not a typo: the capacity of this object is the size
872 : // of the underlying std::vector
873 47055 : return m_oVec.size();
874 : }
875 :
876 163038 : inline GByte *data()
877 : {
878 163038 : return m_oVec.data();
879 : }
880 :
881 65011 : inline const GByte *data() const
882 : {
883 65011 : return m_oVec.data();
884 : }
885 :
886 1898 : inline GByte operator[](size_t idx) const
887 : {
888 1898 : return m_oVec[idx];
889 : }
890 :
891 61251 : inline GByte &operator[](size_t idx)
892 : {
893 61251 : return m_oVec[idx];
894 : }
895 : };
896 :
897 : /************************************************************************/
898 : /* ZarrArray */
899 : /************************************************************************/
900 :
901 : class ZarrArray CPL_NON_FINAL : public GDALPamMDArray
902 : {
903 : protected:
904 : std::shared_ptr<ZarrSharedResource> m_poSharedResource;
905 :
906 : //! weak reference to owning parent
907 : std::weak_ptr<ZarrGroupBase> m_poParent{};
908 :
909 : const std::vector<std::shared_ptr<GDALDimension>> m_aoDims;
910 : const GDALExtendedDataType m_oType;
911 :
912 : //! Array (several in case of compound data type) of native Zarr data types
913 : const std::vector<DtypeElt> m_aoDtypeElts;
914 :
915 : /** m_anOuterBlockSize is the chunk_size at the Zarr array level, which
916 : * determines the files/objects
917 : */
918 : const std::vector<GUInt64> m_anOuterBlockSize;
919 :
920 : /** m_anInnerBlockSize is the inner most block size of sharding, which
921 : * is the one exposed to the user with GetBlockSize()
922 : * When no sharding is involved m_anOuterBlockSize == m_anInnerBlockSize
923 : * Note that m_anOuterBlockSize might be equal to m_anInnerBlockSize, even
924 : * when sharding is involved, and it is actually a common use case.
925 : */
926 : const std::vector<GUInt64> m_anInnerBlockSize;
927 :
928 : /** m_anCountInnerBlockInOuter[i] = m_anOuterBlockSize[i] / m_anInnerBlockSize[i]
929 : * That is the number of inner blocks in one outer block
930 : */
931 : const std::vector<GUInt64> m_anCountInnerBlockInOuter;
932 :
933 : //! Total number of inner chunks in the array
934 : const uint64_t m_nTotalInnerChunkCount;
935 :
936 : //! Size in bytes of a inner chunk using the Zarr native data type
937 : const size_t m_nInnerBlockSizeBytes;
938 :
939 : mutable ZarrAttributeGroup m_oAttrGroup;
940 :
941 : const bool m_bUseOptimizedCodePaths;
942 :
943 : CPLStringList m_aosStructuralInfo{};
944 : CPLJSONObject m_dtype{};
945 : GByte *m_pabyNoData = nullptr;
946 : std::string m_osDimSeparator{"."};
947 : std::string m_osFilename{};
948 : mutable ZarrByteVectorQuickResize m_abyRawBlockData{};
949 : mutable ZarrByteVectorQuickResize m_abyDecodedBlockData{};
950 :
951 : /** Inner block index of the cached block
952 : * i.e. m_anCachedBlockIndices[i] < cpl::round_up(m_aoDims[i]->GetSize, m_anInnerBlockSize[i])
953 : */
954 : mutable std::vector<uint64_t> m_anCachedBlockIndices{};
955 :
956 : mutable bool m_bCachedBlockValid = false;
957 : mutable bool m_bCachedBlockEmpty = false;
958 : mutable bool m_bDirtyBlock = false;
959 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
960 : mutable bool m_bAllocateWorkingBuffersDone = false;
961 : mutable bool m_bWorkingBuffersOK = false;
962 : bool m_bUpdatable = false;
963 : bool m_bDefinitionModified = false;
964 : bool m_bSRSModified = false;
965 : bool m_bNew = false;
966 : std::string m_osUnit{};
967 : bool m_bUnitModified = false;
968 : double m_dfOffset = 0.0;
969 : bool m_bHasOffset = false;
970 : bool m_bOffsetModified = false;
971 : double m_dfScale = 1.0;
972 : bool m_bHasScale = false;
973 : bool m_bScaleModified = false;
974 : std::weak_ptr<ZarrGroupBase> m_poGroupWeak{};
975 : mutable bool m_bHasTriedBlockCachePresenceArray = false;
976 : mutable std::shared_ptr<GDALMDArray> m_poBlockCachePresenceArray{};
977 : mutable std::mutex m_oMutex{};
978 : CPLStringList m_aosCreationOptions{};
979 :
980 : struct CachedBlock
981 : {
982 : ZarrByteVectorQuickResize abyDecoded{};
983 : };
984 :
985 : mutable std::map<std::vector<uint64_t>, CachedBlock> m_oChunkCache{};
986 :
987 : //! Region covered by the last IAdviseRead (for subset check in IRead)
988 : mutable std::vector<GUInt64> m_anCachedAdviseReadStart{};
989 : mutable std::vector<size_t> m_anCachedAdviseReadCount{};
990 :
991 : static uint64_t
992 : ComputeBlockCount(const std::string &osName,
993 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
994 : const std::vector<GUInt64> &anBlockSize);
995 :
996 : ZarrArray(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
997 : const std::shared_ptr<ZarrGroupBase> &poParent,
998 : const std::string &osName,
999 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1000 : const GDALExtendedDataType &oType,
1001 : const std::vector<DtypeElt> &aoDtypeElts,
1002 : const std::vector<GUInt64> &anOuterBlockSize,
1003 : const std::vector<GUInt64> &anInnerBlockSize);
1004 :
1005 : virtual bool LoadBlockData(const uint64_t *blockIndices,
1006 : bool &bMissingBlockOut) const = 0;
1007 :
1008 : virtual bool AllocateWorkingBuffers() const = 0;
1009 :
1010 : void SerializeNumericNoData(CPLJSONObject &oRoot) const;
1011 :
1012 : void DeallocateDecodedBlockData();
1013 :
1014 : virtual std::string GetDataDirectory() const = 0;
1015 :
1016 : virtual CPLStringList
1017 : GetChunkIndicesFromFilename(const char *pszFilename) const = 0;
1018 :
1019 : virtual bool FlushDirtyBlock() const = 0;
1020 :
1021 : std::shared_ptr<GDALMDArray> OpenBlockPresenceCache(bool bCanCreate) const;
1022 :
1023 : void NotifyChildrenOfRenaming() override;
1024 :
1025 : void NotifyChildrenOfDeletion() override;
1026 :
1027 : static void EncodeElt(const std::vector<DtypeElt> &elts, const GByte *pSrc,
1028 : GByte *pDst);
1029 :
1030 : // Disable copy constructor and assignment operator
1031 : ZarrArray(const ZarrArray &) = delete;
1032 : ZarrArray &operator=(const ZarrArray &) = delete;
1033 :
1034 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
1035 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1036 : const GDALExtendedDataType &bufferDataType,
1037 : void *pDstBuffer) const override;
1038 :
1039 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
1040 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1041 : const GDALExtendedDataType &bufferDataType,
1042 : const void *pSrcBuffer) override;
1043 :
1044 : bool IsEmptyBlock(const ZarrByteVectorQuickResize &abyBlock) const;
1045 :
1046 : bool IAdviseReadCommon(const GUInt64 *arrayStartIdx, const size_t *count,
1047 : CSLConstList papszOptions,
1048 : std::vector<uint64_t> &anIndicesCur,
1049 : int &nThreadsMax,
1050 : std::vector<uint64_t> &anReqBlocksIndices,
1051 : size_t &nReqBlocks) const;
1052 :
1053 : CPLJSONObject SerializeSpecialAttributes();
1054 :
1055 : virtual std::string
1056 : BuildChunkFilename(const uint64_t *blockIndices) const = 0;
1057 :
1058 : bool SetStatistics(bool bApproxStats, double dfMin, double dfMax,
1059 : double dfMean, double dfStdDev, GUInt64 nValidCount,
1060 : CSLConstList papszOptions) override;
1061 :
1062 : bool IsBlockMissingFromCacheInfo(const std::string &osFilename,
1063 : const uint64_t *blockIndices) const;
1064 :
1065 : virtual CPLStringList GetRawBlockInfoInfo() const = 0;
1066 :
1067 : public:
1068 : ~ZarrArray() override;
1069 :
1070 : static bool ParseChunkSize(const CPLJSONArray &oChunks,
1071 : const GDALExtendedDataType &oType,
1072 : std::vector<GUInt64> &anBlockSize);
1073 :
1074 : static bool FillBlockSize(
1075 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
1076 : const GDALExtendedDataType &oDataType,
1077 : std::vector<GUInt64> &anBlockSize, CSLConstList papszOptions);
1078 :
1079 187 : bool IsWritable() const override
1080 : {
1081 187 : return m_bUpdatable;
1082 : }
1083 :
1084 4601 : const std::string &GetFilename() const override
1085 : {
1086 4601 : return m_osFilename;
1087 : }
1088 :
1089 : const std::vector<std::shared_ptr<GDALDimension>> &
1090 34716 : GetDimensions() const override
1091 : {
1092 34716 : return m_aoDims;
1093 : }
1094 :
1095 32369 : const GDALExtendedDataType &GetDataType() const override
1096 : {
1097 32369 : return m_oType;
1098 : }
1099 :
1100 715 : std::vector<GUInt64> GetBlockSize() const override
1101 : {
1102 715 : return m_anInnerBlockSize;
1103 : }
1104 :
1105 23 : CSLConstList GetStructuralInfo() const override
1106 : {
1107 23 : return m_aosStructuralInfo.List();
1108 : }
1109 :
1110 22783 : const void *GetRawNoDataValue() const override
1111 : {
1112 22783 : return m_pabyNoData;
1113 : }
1114 :
1115 85 : const std::string &GetUnit() const override
1116 : {
1117 85 : return m_osUnit;
1118 : }
1119 :
1120 : bool SetUnit(const std::string &osUnit) override;
1121 :
1122 55 : void RegisterUnit(const std::string &osUnit)
1123 : {
1124 55 : m_osUnit = osUnit;
1125 55 : }
1126 :
1127 2400 : void RegisterGroup(const std::weak_ptr<ZarrGroupBase> &group)
1128 : {
1129 2400 : m_poGroupWeak = group;
1130 2400 : }
1131 :
1132 : double GetOffset(bool *pbHasOffset,
1133 : GDALDataType *peStorageType) const override;
1134 :
1135 : double GetScale(bool *pbHasScale,
1136 : GDALDataType *peStorageType) const override;
1137 :
1138 : bool SetOffset(double dfOffset, GDALDataType eStorageType) override;
1139 :
1140 : bool SetScale(double dfScale, GDALDataType eStorageType) override;
1141 :
1142 : std::vector<std::shared_ptr<GDALMDArray>>
1143 : GetCoordinateVariables() const override;
1144 :
1145 : bool IsRegularlySpaced(double &dfStart, double &dfIncrement) const override;
1146 :
1147 : bool Resize(const std::vector<GUInt64> &anNewDimSizes,
1148 : CSLConstList) override;
1149 :
1150 3 : void RegisterOffset(double dfOffset)
1151 : {
1152 3 : m_bHasOffset = true;
1153 3 : m_dfOffset = dfOffset;
1154 3 : }
1155 :
1156 3 : void RegisterScale(double dfScale)
1157 : {
1158 3 : m_bHasScale = true;
1159 3 : m_dfScale = dfScale;
1160 3 : }
1161 :
1162 : bool SetRawNoDataValue(const void *pRawNoData) override;
1163 :
1164 : void RegisterNoDataValue(const void *);
1165 :
1166 2400 : void SetFilename(const std::string &osFilename)
1167 : {
1168 2400 : m_osFilename = osFilename;
1169 2400 : }
1170 :
1171 2400 : void SetDimSeparator(const std::string &osDimSeparator)
1172 : {
1173 2400 : m_osDimSeparator = osDimSeparator;
1174 2400 : }
1175 :
1176 : void SetAttributes(const std::shared_ptr<ZarrGroupBase> &poGroup,
1177 : CPLJSONObject &oAttributes);
1178 :
1179 51 : void SetSRS(const std::shared_ptr<OGRSpatialReference> &srs)
1180 : {
1181 51 : m_poSRS = srs;
1182 51 : }
1183 :
1184 : std::shared_ptr<GDALAttribute>
1185 77 : GetAttribute(const std::string &osName) const override
1186 : {
1187 77 : return m_oAttrGroup.GetAttribute(osName);
1188 : }
1189 :
1190 : std::vector<std::shared_ptr<GDALAttribute>>
1191 215 : GetAttributes(CSLConstList papszOptions) const override
1192 : {
1193 215 : return m_oAttrGroup.GetAttributes(papszOptions);
1194 : }
1195 :
1196 : std::shared_ptr<GDALAttribute>
1197 : CreateAttribute(const std::string &osName,
1198 : const std::vector<GUInt64> &anDimensions,
1199 : const GDALExtendedDataType &oDataType,
1200 : CSLConstList papszOptions = nullptr) override;
1201 :
1202 : bool DeleteAttribute(const std::string &osName,
1203 : CSLConstList papszOptions = nullptr) override;
1204 :
1205 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override;
1206 :
1207 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override;
1208 :
1209 2400 : void SetUpdatable(bool bUpdatable)
1210 : {
1211 2400 : m_bUpdatable = bUpdatable;
1212 2400 : }
1213 :
1214 2400 : void SetDtype(const CPLJSONObject &dtype)
1215 : {
1216 2400 : m_dtype = dtype;
1217 2400 : }
1218 :
1219 525 : void SetDefinitionModified(bool bModified)
1220 : {
1221 525 : m_bDefinitionModified = bModified;
1222 525 : }
1223 :
1224 510 : void SetNew(bool bNew)
1225 : {
1226 510 : m_bNew = bNew;
1227 510 : }
1228 :
1229 : bool Rename(const std::string &osNewName) override;
1230 :
1231 : void ParentRenamed(const std::string &osNewParentFullName) override;
1232 :
1233 : virtual bool Flush() = 0;
1234 :
1235 : //! Return the group owning the array. Might be nullptr
1236 : std::shared_ptr<ZarrGroupBase> GetParentGroup() const;
1237 :
1238 : //! Return the root group. Might be nullptr
1239 1952 : std::shared_ptr<GDALGroup> GetRootGroup() const override
1240 : {
1241 1952 : return m_poSharedResource->GetRootGroup();
1242 : }
1243 :
1244 : bool GetRawBlockInfo(const uint64_t *panBlockCoordinates,
1245 : GDALMDArrayRawBlockInfo &info) const override;
1246 :
1247 : bool BlockCachePresence();
1248 :
1249 762 : void SetStructuralInfo(const char *pszKey, const char *pszValue)
1250 : {
1251 762 : m_aosStructuralInfo.SetNameValue(pszKey, pszValue);
1252 762 : }
1253 :
1254 510 : void SetCreationOptions(CSLConstList papszOptions)
1255 : {
1256 510 : m_aosCreationOptions = papszOptions;
1257 510 : }
1258 :
1259 : static void DecodeSourceElt(const std::vector<DtypeElt> &elts,
1260 : const GByte *pSrc, GByte *pDst);
1261 :
1262 : static void GetDimensionTypeDirection(CPLJSONObject &oAttributes,
1263 : std::string &osType,
1264 : std::string &osDirection);
1265 : };
1266 :
1267 : /************************************************************************/
1268 : /* ZarrV2Array */
1269 : /************************************************************************/
1270 :
1271 : class ZarrV2Array final : public ZarrArray
1272 : {
1273 : CPLJSONObject m_oCompressorJSon{};
1274 : const CPLCompressor *m_psCompressor = nullptr;
1275 : std::string m_osDecompressorId{};
1276 : const CPLCompressor *m_psDecompressor = nullptr;
1277 : CPLJSONArray m_oFiltersArray{}; // ZarrV2 specific
1278 : bool m_bFortranOrder = false;
1279 : mutable ZarrByteVectorQuickResize
1280 : m_abyTmpRawBlockData{}; // used for Fortran order
1281 :
1282 : ZarrV2Array(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1283 : const std::shared_ptr<ZarrGroupBase> &poParent,
1284 : const std::string &osName,
1285 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1286 : const GDALExtendedDataType &oType,
1287 : const std::vector<DtypeElt> &aoDtypeElts,
1288 : const std::vector<GUInt64> &anOuterBlockSize,
1289 : bool bFortranOrder);
1290 :
1291 : bool Serialize();
1292 :
1293 : bool LoadBlockData(const uint64_t *blockIndices, bool bUseMutex,
1294 : const CPLCompressor *psDecompressor,
1295 : ZarrByteVectorQuickResize &abyRawBlockData,
1296 : ZarrByteVectorQuickResize &abyTmpRawBlockData,
1297 : ZarrByteVectorQuickResize &abyDecodedBlockData,
1298 : bool &bMissingBlockOut) const;
1299 :
1300 : bool NeedDecodedBuffer() const;
1301 :
1302 : bool AllocateWorkingBuffers(
1303 : ZarrByteVectorQuickResize &abyRawBlockData,
1304 : ZarrByteVectorQuickResize &abyTmpRawBlockData,
1305 : ZarrByteVectorQuickResize &abyDecodedBlockData) const;
1306 :
1307 : void BlockTranspose(const ZarrByteVectorQuickResize &abySrc,
1308 : ZarrByteVectorQuickResize &abyDst, bool bDecode) const;
1309 :
1310 : // Disable copy constructor and assignment operator
1311 : ZarrV2Array(const ZarrV2Array &) = delete;
1312 : ZarrV2Array &operator=(const ZarrV2Array &) = delete;
1313 :
1314 : public:
1315 : ~ZarrV2Array() override;
1316 :
1317 : static std::shared_ptr<ZarrV2Array>
1318 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1319 : const std::shared_ptr<ZarrGroupBase> &poParent,
1320 : const std::string &osName,
1321 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1322 : const GDALExtendedDataType &oType,
1323 : const std::vector<DtypeElt> &aoDtypeElts,
1324 : const std::vector<GUInt64> &anBlockSize, bool bFortranOrder);
1325 :
1326 : void SetCompressorJson(const CPLJSONObject &oCompressor);
1327 :
1328 1077 : void SetCompressorDecompressor(const std::string &osDecompressorId,
1329 : const CPLCompressor *psComp,
1330 : const CPLCompressor *psDecomp)
1331 : {
1332 1077 : m_psCompressor = psComp;
1333 1077 : m_osDecompressorId = osDecompressorId;
1334 1077 : m_psDecompressor = psDecomp;
1335 1077 : }
1336 :
1337 : void SetFilters(const CPLJSONArray &oFiltersArray);
1338 :
1339 : bool Flush() override;
1340 :
1341 : protected:
1342 : std::string GetDataDirectory() const override;
1343 :
1344 : CPLStringList
1345 : GetChunkIndicesFromFilename(const char *pszFilename) const override;
1346 :
1347 : bool FlushDirtyBlock() const override;
1348 :
1349 : std::string BuildChunkFilename(const uint64_t *blockIndices) const override;
1350 :
1351 : bool AllocateWorkingBuffers() const override;
1352 :
1353 : bool LoadBlockData(const uint64_t *blockIndices,
1354 : bool &bMissingBlockOut) const override;
1355 :
1356 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
1357 : CSLConstList papszOptions) const override;
1358 :
1359 : CPLStringList GetRawBlockInfoInfo() const override;
1360 : };
1361 :
1362 : /************************************************************************/
1363 : /* ZarrV3Array */
1364 : /************************************************************************/
1365 :
1366 : class ZarrV3CodecSequence;
1367 :
1368 : class ZarrV3Array final : public ZarrArray
1369 : {
1370 : bool m_bV2ChunkKeyEncoding = false;
1371 : std::unique_ptr<ZarrV3CodecSequence> m_poCodecs{};
1372 : CPLJSONArray m_oJSONCodecs{};
1373 : mutable bool m_bOverviewsLoaded = false;
1374 : mutable std::vector<std::shared_ptr<GDALMDArray>> m_apoOverviews{};
1375 :
1376 : /** Shard write cache: accumulates dirty inner chunks per shard, encodes
1377 : * each shard exactly once on FlushShardCache() (called from Flush()).
1378 : * Without this cache, FlushDirtyBlockSharded() would re-read, decode,
1379 : * overlay, re-encode, and write the entire shard for every inner chunk,
1380 : * resulting in O(N) encode cycles per shard where N = inner chunks/shard.
1381 : */
1382 : struct ShardWriteEntry
1383 : {
1384 : ZarrByteVectorQuickResize abyShardBuffer{};
1385 : std::vector<bool> abDirtyInnerChunks{};
1386 : };
1387 :
1388 : // Note: cache is unbounded - one entry per shard written. For very large
1389 : // rasters, consider adding LRU eviction in a follow-up.
1390 : mutable std::map<std::string, ShardWriteEntry> m_oShardWriteCache{};
1391 :
1392 : ZarrV3Array(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1393 : const std::shared_ptr<ZarrGroupBase> &poParent,
1394 : const std::string &osName,
1395 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1396 : const GDALExtendedDataType &oType,
1397 : const std::vector<DtypeElt> &aoDtypeElts,
1398 : const std::vector<GUInt64> &anOuterBlockSize,
1399 : const std::vector<GUInt64> &anInnerBlockSize);
1400 :
1401 : bool Serialize(const CPLJSONObject &oAttrs);
1402 :
1403 : bool NeedDecodedBuffer() const;
1404 :
1405 : bool AllocateWorkingBuffers(
1406 : ZarrByteVectorQuickResize &abyRawBlockData,
1407 : ZarrByteVectorQuickResize &abyDecodedBlockData) const;
1408 :
1409 : bool LoadBlockData(const uint64_t *blockIndices, bool bUseMutex,
1410 : ZarrV3CodecSequence *poCodecs,
1411 : ZarrByteVectorQuickResize &abyRawBlockData,
1412 : ZarrByteVectorQuickResize &abyDecodedBlockData,
1413 : bool &bMissingBlockOut) const;
1414 :
1415 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
1416 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1417 : const GDALExtendedDataType &bufferDataType,
1418 : void *pDstBuffer) const override;
1419 :
1420 : void PreloadShardedBlocks(const GUInt64 *arrayStartIdx,
1421 : const size_t *count) const;
1422 :
1423 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
1424 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1425 : const GDALExtendedDataType &bufferDataType,
1426 : const void *pSrcBuffer) override;
1427 :
1428 : bool WriteChunksThreadSafe(const GUInt64 *arrayStartIdx,
1429 : const size_t *count, const GInt64 *arrayStep,
1430 : const GPtrDiff_t *bufferStride,
1431 : const GDALExtendedDataType &bufferDataType,
1432 : const void *pSrcBuffer, const int iThread,
1433 : const int nThreads,
1434 : std::string &osErrorMsg) const;
1435 :
1436 : void LoadOverviews() const;
1437 :
1438 : void ReconstructCreationOptionsFromCodecs();
1439 :
1440 : public:
1441 : ~ZarrV3Array() override;
1442 :
1443 : static std::shared_ptr<ZarrV3Array>
1444 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1445 : const std::shared_ptr<ZarrGroupBase> &poParent,
1446 : const std::string &osName,
1447 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1448 : const GDALExtendedDataType &oType,
1449 : const std::vector<DtypeElt> &aoDtypeElts,
1450 : const std::vector<GUInt64> &anOuterBlockSize,
1451 : const std::vector<GUInt64> &anInnerBlockSize);
1452 :
1453 1129 : void SetIsV2ChunkKeyEncoding(bool b)
1454 : {
1455 1129 : m_bV2ChunkKeyEncoding = b;
1456 1129 : }
1457 :
1458 : void SetCodecs(const CPLJSONArray &oJSONCodecs,
1459 : std::unique_ptr<ZarrV3CodecSequence> &&poCodecs);
1460 :
1461 : bool Flush() override;
1462 :
1463 : static std::unique_ptr<ZarrV3CodecSequence>
1464 : SetupCodecs(const CPLJSONArray &oCodecs,
1465 : const std::vector<GUInt64> &anOuterBlockSize,
1466 : std::vector<GUInt64> &anInnerBlockSize, DtypeElt &zarrDataType,
1467 : const std::vector<GByte> &abyNoData);
1468 : int GetOverviewCount() const override;
1469 :
1470 : std::shared_ptr<GDALMDArray> GetOverview(int idx) const override;
1471 :
1472 : CPLErr BuildOverviews(const char *pszResampling, int nOverviews,
1473 : const int *panOverviewList,
1474 : GDALProgressFunc pfnProgress, void *pProgressData,
1475 : CSLConstList papszOptions) override;
1476 :
1477 : static void
1478 : ExtractSubArrayFromLargerOne(const ZarrByteVectorQuickResize &abySrc,
1479 : const std::vector<size_t> &anSrcBlockSize,
1480 : const std::vector<size_t> &anInnerBlockSize,
1481 : const std::vector<size_t> &anInnerBlockIndices,
1482 : ZarrByteVectorQuickResize &abyChunk,
1483 : const size_t nDTSize);
1484 :
1485 : protected:
1486 : std::string GetDataDirectory() const override;
1487 :
1488 : CPLStringList
1489 : GetChunkIndicesFromFilename(const char *pszFilename) const override;
1490 :
1491 : bool AllocateWorkingBuffers() const override;
1492 :
1493 : bool FlushDirtyBlock() const override;
1494 : bool FlushDirtyBlockSharded() const;
1495 : bool FlushSingleShard(const std::string &osFilename,
1496 : ShardWriteEntry &entry) const;
1497 : bool FlushShardCache() const;
1498 :
1499 : std::string BuildChunkFilename(const uint64_t *blockIndices) const override;
1500 :
1501 : bool LoadBlockData(const uint64_t *blockIndices,
1502 : bool &bMissingBlockOut) const override;
1503 :
1504 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
1505 : CSLConstList papszOptions) const override;
1506 :
1507 : CPLStringList GetRawBlockInfoInfo() const override;
1508 : };
1509 :
1510 : void ZarrClearCoordinateCache();
1511 : void ZarrClearShardIndexCache();
1512 : void ZarrEraseShardIndexFromCache(const std::string &osFilename);
1513 :
1514 : #endif // ZARR_H
|