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 69598 : template <class T> inline T MultiplyElements(const std::vector<T> &vector)
52 : {
53 69598 : return std::reduce(vector.begin(), vector.end(), T{1},
54 69598 : 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 9047 : std::shared_ptr<GDALAttribute> GetAttribute(const std::string &osName) const
184 : {
185 9047 : return m_poGroup->GetAttribute(osName);
186 : }
187 :
188 : std::vector<std::shared_ptr<GDALAttribute>>
189 254 : GetAttributes(CSLConstList papszOptions = nullptr) const
190 : {
191 254 : 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 4057 : void SetUpdatable(bool bUpdatable)
220 : {
221 8114 : auto attrs = m_poGroup->GetAttributes(nullptr);
222 4991 : 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 4057 : }
229 :
230 403 : void UnsetModified()
231 : {
232 403 : m_bModified = false;
233 806 : auto attrs = m_poGroup->GetAttributes(nullptr);
234 557 : 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 403 : }
241 :
242 16088 : bool IsModified() const
243 : {
244 16088 : if (m_bModified)
245 261 : return true;
246 31654 : const auto attrs = m_poGroup->GetAttributes(nullptr);
247 18126 : 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 15796 : 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 :
294 : explicit ZarrSharedResource(const std::string &osRootDirectoryName,
295 : bool bUpdatable);
296 :
297 : std::shared_ptr<ZarrGroupBase> OpenRootGroup();
298 : void InitConsolidatedMetadataIfNeeded();
299 :
300 : public:
301 : static std::shared_ptr<ZarrSharedResource>
302 : Create(const std::string &osRootDirectoryName, bool bUpdatable);
303 :
304 : ~ZarrSharedResource();
305 :
306 4971 : bool IsUpdatable() const
307 : {
308 4971 : return m_bUpdatable;
309 : }
310 :
311 37 : const CPLJSONObject &GetConsolidatedMetadataObj() const
312 : {
313 37 : return m_oObjConsolidatedMetadata;
314 : }
315 :
316 299 : bool IsConsolidatedMetadataEnabled() const
317 : {
318 299 : return m_eConsolidatedMetadataKind != ConsolidatedMetadataKind::NONE;
319 : }
320 :
321 325 : void EnableConsolidatedMetadata(ConsolidatedMetadataKind kind)
322 : {
323 325 : m_eConsolidatedMetadataKind = kind;
324 325 : }
325 :
326 : void SetZMetadataItem(const std::string &osFilename,
327 : const CPLJSONObject &obj);
328 :
329 : void DeleteZMetadataItemRecursive(const std::string &osFilename);
330 :
331 : void RenameZMetadataRecursive(const std::string &osOldFilename,
332 : const std::string &osNewFilename);
333 :
334 2377 : const std::shared_ptr<GDALPamMultiDim> &GetPAM()
335 : {
336 2377 : return m_poPAM;
337 : }
338 :
339 13 : const std::string &GetRootDirectoryName() const
340 : {
341 13 : return m_osRootDirectoryName;
342 : }
343 :
344 2835 : const CPLStringList &GetOpenOptions() const
345 : {
346 2835 : return m_aosOpenOptions;
347 : }
348 :
349 1594 : void SetOpenOptions(CSLConstList papszOpenOptions)
350 : {
351 1594 : m_aosOpenOptions = papszOpenOptions;
352 1594 : }
353 :
354 : void
355 : UpdateDimensionSize(const std::shared_ptr<GDALDimension> &poUpdatedDim);
356 :
357 : std::shared_ptr<ZarrGroupBase> GetRootGroup();
358 :
359 90 : void SetRootGroup(const std::shared_ptr<ZarrGroupBase> &poRootGroup)
360 : {
361 90 : m_poWeakRootGroup = poRootGroup;
362 90 : }
363 :
364 : bool AddArrayInLoading(const std::string &osZarrayFilename);
365 : void RemoveArrayInLoading(const std::string &osZarrayFilename);
366 :
367 : struct SetFilenameAdder
368 : {
369 : std::shared_ptr<ZarrSharedResource> m_poSharedResource;
370 : const std::string m_osFilename;
371 : const bool m_bOK;
372 :
373 1967 : SetFilenameAdder(
374 : const std::shared_ptr<ZarrSharedResource> &poSharedResource,
375 : const std::string &osFilename)
376 1967 : : m_poSharedResource(poSharedResource), m_osFilename(osFilename),
377 1967 : m_bOK(m_poSharedResource->AddArrayInLoading(m_osFilename))
378 : {
379 1967 : }
380 :
381 1967 : ~SetFilenameAdder()
382 1967 : {
383 1967 : if (m_bOK)
384 1965 : m_poSharedResource->RemoveArrayInLoading(m_osFilename);
385 1967 : }
386 :
387 1967 : bool ok() const
388 : {
389 1967 : return m_bOK;
390 : }
391 : };
392 :
393 8 : void RegisterIndexingVariable(const std::string &osDimName,
394 : const std::shared_ptr<GDALMDArray> &poVar)
395 : {
396 8 : m_oCacheIndexingVar[osDimName] = poVar;
397 8 : }
398 : };
399 :
400 : /************************************************************************/
401 : /* ZarrGroup */
402 : /************************************************************************/
403 :
404 : class ZarrArray;
405 : class ZarrDimension;
406 :
407 : class ZarrGroupBase CPL_NON_FINAL : public GDALGroup
408 : {
409 : protected:
410 : friend class ZarrV2Group;
411 : friend class ZarrV3Group;
412 :
413 : // For ZarrV2, this is the directory of the group
414 : // For ZarrV3, this is the root directory of the dataset
415 : std::shared_ptr<ZarrSharedResource> m_poSharedResource;
416 : std::string m_osDirectoryName{};
417 : std::weak_ptr<ZarrGroupBase>
418 : m_poParent{}; // weak reference to owning parent
419 : std::shared_ptr<ZarrGroupBase>
420 : m_poParentStrongRef{}; // strong reference, used only when opening from
421 : // a subgroup
422 : mutable std::map<CPLString, std::shared_ptr<ZarrGroupBase>> m_oMapGroups{};
423 : mutable std::map<CPLString, std::shared_ptr<ZarrArray>> m_oMapMDArrays{};
424 : mutable std::map<CPLString, std::shared_ptr<ZarrDimension>>
425 : m_oMapDimensions{};
426 : mutable bool m_bDirectoryExplored = false;
427 : mutable std::set<std::string> m_oSetGroupNames{};
428 : mutable std::vector<std::string> m_aosGroups{};
429 : mutable std::set<std::string> m_oSetArrayNames{};
430 : mutable std::vector<std::string> m_aosArrays{};
431 : mutable ZarrAttributeGroup m_oAttrGroup;
432 : mutable bool m_bAttributesLoaded = false;
433 : bool m_bReadFromConsolidatedMetadata = false;
434 : mutable bool m_bDimensionsInstantiated = false;
435 : bool m_bUpdatable = false;
436 : bool m_bDimSizeInUpdate = false;
437 :
438 : virtual void ExploreDirectory() const = 0;
439 : virtual void LoadAttributes() const = 0;
440 :
441 2383 : ZarrGroupBase(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
442 : const std::string &osParentName, const std::string &osName)
443 2383 : : GDALGroup(osParentName, osName), m_poSharedResource(poSharedResource),
444 2383 : m_oAttrGroup(m_osFullName, /*bContainerIsGroup=*/true)
445 : {
446 2383 : }
447 :
448 : protected:
449 : friend class ZarrDimension;
450 : bool RenameDimension(const std::string &osOldName,
451 : const std::string &osNewName);
452 :
453 : void NotifyChildrenOfRenaming() override;
454 :
455 : void NotifyChildrenOfDeletion() override;
456 :
457 : public:
458 : ~ZarrGroupBase() override;
459 :
460 : virtual bool Close();
461 :
462 : bool Flush();
463 :
464 : std::shared_ptr<GDALAttribute>
465 408 : GetAttribute(const std::string &osName) const override
466 : {
467 408 : LoadAttributes();
468 408 : return m_oAttrGroup.GetAttribute(osName);
469 : }
470 :
471 : std::vector<std::shared_ptr<GDALAttribute>>
472 47 : GetAttributes(CSLConstList papszOptions = nullptr) const override
473 : {
474 47 : LoadAttributes();
475 47 : return m_oAttrGroup.GetAttributes(papszOptions);
476 : }
477 :
478 : std::shared_ptr<GDALAttribute>
479 : CreateAttribute(const std::string &osName,
480 : const std::vector<GUInt64> &anDimensions,
481 : const GDALExtendedDataType &oDataType,
482 : CSLConstList papszOptions = nullptr) override;
483 :
484 : bool DeleteAttribute(const std::string &osName,
485 : CSLConstList papszOptions = nullptr) override;
486 :
487 : std::vector<std::shared_ptr<GDALDimension>>
488 : GetDimensions(CSLConstList papszOptions = nullptr) const override;
489 :
490 : std::shared_ptr<GDALDimension>
491 : CreateDimension(const std::string &osName, const std::string &osType,
492 : const std::string &osDirection, GUInt64 nSize,
493 : CSLConstList papszOptions = nullptr) override;
494 :
495 : std::vector<std::string>
496 : GetMDArrayNames(CSLConstList papszOptions = nullptr) const override;
497 :
498 : std::vector<std::string>
499 : GetGroupNames(CSLConstList papszOptions = nullptr) const override;
500 :
501 : virtual std::shared_ptr<ZarrGroupBase>
502 : OpenZarrGroup(const std::string &osName,
503 : CSLConstList papszOptions = nullptr) const = 0;
504 :
505 : std::shared_ptr<GDALGroup>
506 1432 : OpenGroup(const std::string &osName,
507 : CSLConstList papszOptions = nullptr) const override
508 : {
509 : return std::static_pointer_cast<GDALGroup>(
510 1432 : OpenZarrGroup(osName, papszOptions));
511 : }
512 :
513 : bool DeleteGroup(const std::string &osName,
514 : CSLConstList papszOptions = nullptr) override;
515 :
516 : std::shared_ptr<GDALMDArray>
517 2136 : OpenMDArray(const std::string &osName,
518 : CSLConstList papszOptions = nullptr) const override
519 : {
520 : return std::static_pointer_cast<GDALMDArray>(
521 2136 : OpenZarrArray(osName, papszOptions));
522 : }
523 :
524 : bool DeleteMDArray(const std::string &osName,
525 : CSLConstList papszOptions = nullptr) override;
526 :
527 : virtual std::shared_ptr<ZarrArray>
528 : OpenZarrArray(const std::string &osName,
529 : CSLConstList papszOptions = nullptr) const = 0;
530 :
531 1129 : void SetDirectoryName(const std::string &osDirectoryName)
532 : {
533 1129 : m_osDirectoryName = osDirectoryName;
534 1129 : }
535 :
536 9 : const std::string &GetDirectoryName() const
537 : {
538 9 : return m_osDirectoryName;
539 : }
540 :
541 : void RegisterArray(const std::shared_ptr<ZarrArray> &array) const;
542 :
543 2377 : void SetUpdatable(bool bUpdatable)
544 : {
545 2377 : m_bUpdatable = bUpdatable;
546 2377 : }
547 :
548 : void UpdateDimensionSize(const std::shared_ptr<GDALDimension> &poDim);
549 :
550 : static bool IsValidObjectName(const std::string &osName);
551 :
552 : bool Rename(const std::string &osNewName) override;
553 :
554 : //! Returns false in case of error
555 : bool
556 : CheckArrayOrGroupWithSameNameDoesNotExist(const std::string &osName) const;
557 :
558 : void ParentRenamed(const std::string &osNewParentFullName) override;
559 :
560 : void NotifyArrayRenamed(const std::string &osOldName,
561 : const std::string &osNewName);
562 :
563 : //! Return the group owning the array. Might be nullptr
564 : std::shared_ptr<ZarrGroupBase> GetParentGroup() const;
565 :
566 4252 : std::shared_ptr<ZarrGroupBase> Self() const
567 : {
568 4252 : return std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
569 : }
570 :
571 2765 : const ZarrAttributeGroup &GetAttributeGroup() const
572 : {
573 2765 : return m_oAttrGroup;
574 : }
575 : };
576 :
577 : /************************************************************************/
578 : /* ZarrV2Group */
579 : /************************************************************************/
580 :
581 : class ZarrV2Group final : public ZarrGroupBase
582 : {
583 : void ExploreDirectory() const override;
584 : void LoadAttributes() const override;
585 :
586 : std::shared_ptr<ZarrV2Group>
587 : GetOrCreateSubGroup(const std::string &osSubGroupFullname);
588 :
589 991 : ZarrV2Group(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
590 : const std::string &osParentName, const std::string &osName)
591 991 : : ZarrGroupBase(poSharedResource, osParentName, osName)
592 : {
593 991 : }
594 :
595 : bool Close() override;
596 :
597 : public:
598 : static std::shared_ptr<ZarrV2Group>
599 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
600 : const std::string &osParentName, const std::string &osName);
601 :
602 : ~ZarrV2Group() override;
603 :
604 : static std::shared_ptr<ZarrV2Group>
605 : CreateOnDisk(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
606 : const std::string &osParentName, const std::string &osName,
607 : const std::string &osDirectoryName);
608 :
609 : std::shared_ptr<ZarrArray>
610 : OpenZarrArray(const std::string &osName,
611 : CSLConstList papszOptions = nullptr) const override;
612 :
613 : std::shared_ptr<ZarrGroupBase>
614 : OpenZarrGroup(const std::string &osName,
615 : CSLConstList papszOptions = nullptr) const override;
616 :
617 : std::shared_ptr<GDALGroup>
618 : CreateGroup(const std::string &osName,
619 : CSLConstList papszOptions = nullptr) override;
620 :
621 : std::shared_ptr<ZarrArray>
622 : LoadArray(const std::string &osArrayName,
623 : const std::string &osZarrayFilename, const CPLJSONObject &oRoot,
624 : bool bLoadedFromZMetadata,
625 : const CPLJSONObject &oAttributes) const;
626 :
627 : std::shared_ptr<GDALMDArray> CreateMDArray(
628 : const std::string &osName,
629 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
630 : const GDALExtendedDataType &oDataType,
631 : CSLConstList papszOptions = nullptr) override;
632 :
633 : void InitFromConsolidatedMetadata(const CPLJSONObject &oRoot);
634 :
635 : bool InitFromZGroup(const CPLJSONObject &oRoot);
636 : };
637 :
638 : /************************************************************************/
639 : /* ZarrV3Group */
640 : /************************************************************************/
641 :
642 : class ZarrV3Group final : public ZarrGroupBase
643 : {
644 : bool m_bFileHasBeenWritten = false;
645 :
646 : void ExploreDirectory() const override;
647 : void LoadAttributes() const override;
648 :
649 : ZarrV3Group(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
650 : const std::string &osParentName, const std::string &osName,
651 : const std::string &osDirectoryName);
652 :
653 : std::shared_ptr<ZarrV3Group>
654 : GetOrCreateSubGroup(const std::string &osSubGroupFullname);
655 :
656 : bool Close() override;
657 :
658 : public:
659 : ~ZarrV3Group() override;
660 :
661 : static std::shared_ptr<ZarrV3Group>
662 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
663 : const std::string &osParentName, const std::string &osName,
664 : const std::string &osDirectoryName);
665 :
666 : std::shared_ptr<ZarrArray>
667 : OpenZarrArray(const std::string &osName,
668 : CSLConstList papszOptions = nullptr) const override;
669 :
670 : std::shared_ptr<ZarrGroupBase>
671 : OpenZarrGroup(const std::string &osName,
672 : CSLConstList papszOptions = nullptr) const override;
673 :
674 : static std::shared_ptr<ZarrV3Group>
675 : CreateOnDisk(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
676 : const std::string &osParentFullName, const std::string &osName,
677 : const std::string &osDirectoryName);
678 :
679 : std::shared_ptr<GDALGroup>
680 : CreateGroup(const std::string &osName,
681 : CSLConstList papszOptions = nullptr) override;
682 :
683 : std::shared_ptr<ZarrArray> LoadArray(const std::string &osArrayName,
684 : const std::string &osZarrayFilename,
685 : const CPLJSONObject &oRoot) const;
686 :
687 : void GenerateMultiscalesMetadata(const char *pszResampling = nullptr);
688 :
689 : std::shared_ptr<GDALMDArray> CreateMDArray(
690 : const std::string &osName,
691 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
692 : const GDALExtendedDataType &oDataType,
693 : CSLConstList papszOptions = nullptr) override;
694 :
695 723 : void SetExplored()
696 : {
697 723 : m_bDirectoryExplored = true;
698 723 : }
699 :
700 : void
701 : InitFromConsolidatedMetadata(const CPLJSONObject &oConsolidatedMetadata,
702 : const CPLJSONObject &oRootAttributes);
703 : };
704 :
705 : /************************************************************************/
706 : /* ZarrDimension */
707 : /************************************************************************/
708 :
709 : class ZarrDimension final : public GDALDimensionWeakIndexingVar
710 : {
711 : const bool m_bUpdatable;
712 : std::weak_ptr<ZarrGroupBase> m_poParentGroup;
713 : bool m_bModified = false;
714 : bool m_bXArrayDim = false;
715 :
716 : public:
717 4971 : ZarrDimension(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
718 : const std::weak_ptr<ZarrGroupBase> &poParentGroup,
719 : const std::string &osParentName, const std::string &osName,
720 : const std::string &osType, const std::string &osDirection,
721 : GUInt64 nSize)
722 4971 : : GDALDimensionWeakIndexingVar(osParentName, osName, osType,
723 : osDirection, nSize),
724 4971 : m_bUpdatable(poSharedResource->IsUpdatable()),
725 9942 : m_poParentGroup(poParentGroup)
726 : {
727 4971 : }
728 :
729 : bool Rename(const std::string &osNewName) override;
730 :
731 9528 : bool IsModified() const
732 : {
733 9528 : return m_bModified;
734 : }
735 :
736 1585 : void SetXArrayDimension()
737 : {
738 1585 : m_bXArrayDim = true;
739 1585 : }
740 :
741 14178 : bool IsXArrayDimension() const
742 : {
743 14178 : return m_bXArrayDim;
744 : }
745 : };
746 :
747 : /************************************************************************/
748 : /* DtypeElt() */
749 : /************************************************************************/
750 :
751 : struct DtypeElt
752 : {
753 : enum class NativeType
754 : {
755 : BOOLEAN,
756 : UNSIGNED_INT,
757 : SIGNED_INT,
758 : IEEEFP,
759 : COMPLEX_IEEEFP,
760 : STRING_ASCII,
761 : STRING_UNICODE
762 : };
763 :
764 : NativeType nativeType = NativeType::BOOLEAN;
765 : size_t nativeOffset = 0;
766 : size_t nativeSize = 0;
767 : bool needByteSwapping = false;
768 : bool gdalTypeIsApproxOfNative = false;
769 : GDALExtendedDataType gdalType = GDALExtendedDataType::Create(GDT_Unknown);
770 : size_t gdalOffset = 0;
771 : size_t gdalSize = 0;
772 : };
773 :
774 : /************************************************************************/
775 : /* ZarrByteVectorQuickResize */
776 : /************************************************************************/
777 :
778 : /* std::vector<GByte> with quick resizing (ie that doesn't zero out when
779 : * growing back to a previously reached greater size).
780 : */
781 185411 : class ZarrByteVectorQuickResize
782 : {
783 : std::vector<GByte> m_oVec{};
784 : size_t m_nSize = 0;
785 :
786 : public:
787 115086 : ZarrByteVectorQuickResize() = default;
788 :
789 : ZarrByteVectorQuickResize(const ZarrByteVectorQuickResize &) = delete;
790 : ZarrByteVectorQuickResize &
791 : operator=(const ZarrByteVectorQuickResize &) = delete;
792 :
793 68583 : ZarrByteVectorQuickResize(ZarrByteVectorQuickResize &&) = default;
794 : ZarrByteVectorQuickResize &
795 : operator=(ZarrByteVectorQuickResize &&) = default;
796 :
797 221704 : void resize(size_t nNewSize)
798 : {
799 221704 : if (nNewSize > m_oVec.size())
800 68573 : m_oVec.resize(nNewSize);
801 221704 : m_nSize = nNewSize;
802 221704 : }
803 :
804 1603 : inline void clear()
805 : {
806 1603 : m_nSize = 0;
807 1603 : }
808 :
809 154 : inline std::vector<GByte>::iterator begin()
810 : {
811 154 : return m_oVec.begin();
812 : }
813 :
814 1188 : inline std::vector<GByte>::const_iterator begin() const
815 : {
816 1188 : return m_oVec.begin();
817 : }
818 :
819 1031 : inline std::vector<GByte>::iterator end()
820 : {
821 1031 : return m_oVec.begin() + m_nSize;
822 : }
823 :
824 184 : inline std::vector<GByte>::const_iterator end() const
825 : {
826 184 : return m_oVec.begin() + m_nSize;
827 : }
828 :
829 : template <class InputIt>
830 : inline std::vector<GByte>::iterator
831 877 : insert(std::vector<GByte>::const_iterator pos, InputIt first, InputIt last)
832 : {
833 877 : const size_t nCount = std::distance(first, last);
834 877 : const auto &oVec = m_oVec;
835 877 : const size_t nStart = std::distance(oVec.begin(), pos);
836 877 : if (nStart == m_nSize && nStart + nCount <= m_oVec.size())
837 : {
838 : // Insert at end of user-visible vector, but fully inside the
839 : // container vector. We can just copy
840 591 : std::copy(first, last, m_oVec.begin() + nStart);
841 591 : m_nSize += nCount;
842 591 : return m_oVec.begin() + nStart;
843 : }
844 : else
845 : {
846 : // Generic case
847 286 : auto ret = m_oVec.insert(pos, first, last);
848 286 : m_nSize += nCount;
849 286 : return ret;
850 : }
851 : }
852 :
853 293184 : inline bool empty() const
854 : {
855 293184 : return m_nSize == 0;
856 : }
857 :
858 256117 : inline size_t size() const
859 : {
860 256117 : return m_nSize;
861 : }
862 :
863 46947 : inline size_t capacity() const
864 : {
865 : // Not a typo: the capacity of this object is the size
866 : // of the underlying std::vector
867 46947 : return m_oVec.size();
868 : }
869 :
870 162677 : inline GByte *data()
871 : {
872 162677 : return m_oVec.data();
873 : }
874 :
875 64898 : inline const GByte *data() const
876 : {
877 64898 : return m_oVec.data();
878 : }
879 :
880 1898 : inline GByte operator[](size_t idx) const
881 : {
882 1898 : return m_oVec[idx];
883 : }
884 :
885 60841 : inline GByte &operator[](size_t idx)
886 : {
887 60841 : return m_oVec[idx];
888 : }
889 : };
890 :
891 : /************************************************************************/
892 : /* ZarrArray */
893 : /************************************************************************/
894 :
895 : class ZarrArray CPL_NON_FINAL : public GDALPamMDArray
896 : {
897 : protected:
898 : std::shared_ptr<ZarrSharedResource> m_poSharedResource;
899 :
900 : //! weak reference to owning parent
901 : std::weak_ptr<ZarrGroupBase> m_poParent{};
902 :
903 : const std::vector<std::shared_ptr<GDALDimension>> m_aoDims;
904 : const GDALExtendedDataType m_oType;
905 :
906 : //! Array (several in case of compound data type) of native Zarr data types
907 : const std::vector<DtypeElt> m_aoDtypeElts;
908 :
909 : /** m_anOuterBlockSize is the chunk_size at the Zarr array level, which
910 : * determines the files/objects
911 : */
912 : const std::vector<GUInt64> m_anOuterBlockSize;
913 :
914 : /** m_anInnerBlockSize is the inner most block size of sharding, which
915 : * is the one exposed to the user with GetBlockSize()
916 : * When no sharding is involved m_anOuterBlockSize == m_anInnerBlockSize
917 : * Note that m_anOuterBlockSize might be equal to m_anInnerBlockSize, even
918 : * when sharding is involved, and it is actually a common use case.
919 : */
920 : const std::vector<GUInt64> m_anInnerBlockSize;
921 :
922 : /** m_anCountInnerBlockInOuter[i] = m_anOuterBlockSize[i] / m_anInnerBlockSize[i]
923 : * That is the number of inner blocks in one outer block
924 : */
925 : const std::vector<GUInt64> m_anCountInnerBlockInOuter;
926 :
927 : //! Total number of inner chunks in the array
928 : const uint64_t m_nTotalInnerChunkCount;
929 :
930 : //! Size in bytes of a inner chunk using the Zarr native data type
931 : const size_t m_nInnerBlockSizeBytes;
932 :
933 : mutable ZarrAttributeGroup m_oAttrGroup;
934 :
935 : const bool m_bUseOptimizedCodePaths;
936 :
937 : CPLStringList m_aosStructuralInfo{};
938 : CPLJSONObject m_dtype{};
939 : GByte *m_pabyNoData = nullptr;
940 : std::string m_osDimSeparator{"."};
941 : std::string m_osFilename{};
942 : mutable ZarrByteVectorQuickResize m_abyRawBlockData{};
943 : mutable ZarrByteVectorQuickResize m_abyDecodedBlockData{};
944 :
945 : /** Inner block index of the cached block
946 : * i.e. m_anCachedBlockIndices[i] < cpl::round_up(m_aoDims[i]->GetSize, m_anInnerBlockSize[i])
947 : */
948 : mutable std::vector<uint64_t> m_anCachedBlockIndices{};
949 :
950 : mutable bool m_bCachedBlockValid = false;
951 : mutable bool m_bCachedBlockEmpty = false;
952 : mutable bool m_bDirtyBlock = false;
953 : mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
954 : mutable bool m_bAllocateWorkingBuffersDone = false;
955 : mutable bool m_bWorkingBuffersOK = false;
956 : bool m_bUpdatable = false;
957 : bool m_bDefinitionModified = false;
958 : bool m_bSRSModified = false;
959 : bool m_bNew = false;
960 : std::string m_osUnit{};
961 : bool m_bUnitModified = false;
962 : double m_dfOffset = 0.0;
963 : bool m_bHasOffset = false;
964 : bool m_bOffsetModified = false;
965 : double m_dfScale = 1.0;
966 : bool m_bHasScale = false;
967 : bool m_bScaleModified = false;
968 : std::weak_ptr<ZarrGroupBase> m_poGroupWeak{};
969 : mutable bool m_bHasTriedBlockCachePresenceArray = false;
970 : mutable std::shared_ptr<GDALMDArray> m_poBlockCachePresenceArray{};
971 : mutable std::mutex m_oMutex{};
972 : CPLStringList m_aosCreationOptions{};
973 :
974 : struct CachedBlock
975 : {
976 : ZarrByteVectorQuickResize abyDecoded{};
977 : };
978 :
979 : mutable std::map<std::vector<uint64_t>, CachedBlock> m_oChunkCache{};
980 :
981 : static uint64_t
982 : ComputeBlockCount(const std::string &osName,
983 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
984 : const std::vector<GUInt64> &anBlockSize);
985 :
986 : ZarrArray(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
987 : const std::shared_ptr<ZarrGroupBase> &poParent,
988 : const std::string &osName,
989 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
990 : const GDALExtendedDataType &oType,
991 : const std::vector<DtypeElt> &aoDtypeElts,
992 : const std::vector<GUInt64> &anOuterBlockSize,
993 : const std::vector<GUInt64> &anInnerBlockSize);
994 :
995 : virtual bool LoadBlockData(const uint64_t *blockIndices,
996 : bool &bMissingBlockOut) const = 0;
997 :
998 : virtual bool AllocateWorkingBuffers() const = 0;
999 :
1000 : void SerializeNumericNoData(CPLJSONObject &oRoot) const;
1001 :
1002 : void DeallocateDecodedBlockData();
1003 :
1004 : virtual std::string GetDataDirectory() const = 0;
1005 :
1006 : virtual CPLStringList
1007 : GetChunkIndicesFromFilename(const char *pszFilename) const = 0;
1008 :
1009 : virtual bool FlushDirtyBlock() const = 0;
1010 :
1011 : std::shared_ptr<GDALMDArray> OpenBlockPresenceCache(bool bCanCreate) const;
1012 :
1013 : void NotifyChildrenOfRenaming() override;
1014 :
1015 : void NotifyChildrenOfDeletion() override;
1016 :
1017 : static void EncodeElt(const std::vector<DtypeElt> &elts, const GByte *pSrc,
1018 : GByte *pDst);
1019 :
1020 : // Disable copy constructor and assignment operator
1021 : ZarrArray(const ZarrArray &) = delete;
1022 : ZarrArray &operator=(const ZarrArray &) = delete;
1023 :
1024 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
1025 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1026 : const GDALExtendedDataType &bufferDataType,
1027 : void *pDstBuffer) const override;
1028 :
1029 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
1030 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1031 : const GDALExtendedDataType &bufferDataType,
1032 : const void *pSrcBuffer) override;
1033 :
1034 : bool IsEmptyBlock(const ZarrByteVectorQuickResize &abyBlock) const;
1035 :
1036 : bool IAdviseReadCommon(const GUInt64 *arrayStartIdx, const size_t *count,
1037 : CSLConstList papszOptions,
1038 : std::vector<uint64_t> &anIndicesCur,
1039 : int &nThreadsMax,
1040 : std::vector<uint64_t> &anReqBlocksIndices,
1041 : size_t &nReqBlocks) const;
1042 :
1043 : CPLJSONObject SerializeSpecialAttributes();
1044 :
1045 : virtual std::string
1046 : BuildChunkFilename(const uint64_t *blockIndices) const = 0;
1047 :
1048 : bool SetStatistics(bool bApproxStats, double dfMin, double dfMax,
1049 : double dfMean, double dfStdDev, GUInt64 nValidCount,
1050 : CSLConstList papszOptions) override;
1051 :
1052 : bool IsBlockMissingFromCacheInfo(const std::string &osFilename,
1053 : const uint64_t *blockIndices) const;
1054 :
1055 : virtual CPLStringList GetRawBlockInfoInfo() const = 0;
1056 :
1057 : public:
1058 : ~ZarrArray() override;
1059 :
1060 : static bool ParseChunkSize(const CPLJSONArray &oChunks,
1061 : const GDALExtendedDataType &oType,
1062 : std::vector<GUInt64> &anBlockSize);
1063 :
1064 : static bool FillBlockSize(
1065 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
1066 : const GDALExtendedDataType &oDataType,
1067 : std::vector<GUInt64> &anBlockSize, CSLConstList papszOptions);
1068 :
1069 179 : bool IsWritable() const override
1070 : {
1071 179 : return m_bUpdatable;
1072 : }
1073 :
1074 4499 : const std::string &GetFilename() const override
1075 : {
1076 4499 : return m_osFilename;
1077 : }
1078 :
1079 : const std::vector<std::shared_ptr<GDALDimension>> &
1080 33874 : GetDimensions() const override
1081 : {
1082 33874 : return m_aoDims;
1083 : }
1084 :
1085 32272 : const GDALExtendedDataType &GetDataType() const override
1086 : {
1087 32272 : return m_oType;
1088 : }
1089 :
1090 699 : std::vector<GUInt64> GetBlockSize() const override
1091 : {
1092 699 : return m_anInnerBlockSize;
1093 : }
1094 :
1095 23 : CSLConstList GetStructuralInfo() const override
1096 : {
1097 23 : return m_aosStructuralInfo.List();
1098 : }
1099 :
1100 22781 : const void *GetRawNoDataValue() const override
1101 : {
1102 22781 : return m_pabyNoData;
1103 : }
1104 :
1105 85 : const std::string &GetUnit() const override
1106 : {
1107 85 : return m_osUnit;
1108 : }
1109 :
1110 : bool SetUnit(const std::string &osUnit) override;
1111 :
1112 55 : void RegisterUnit(const std::string &osUnit)
1113 : {
1114 55 : m_osUnit = osUnit;
1115 55 : }
1116 :
1117 2375 : void RegisterGroup(const std::weak_ptr<ZarrGroupBase> &group)
1118 : {
1119 2375 : m_poGroupWeak = group;
1120 2375 : }
1121 :
1122 : double GetOffset(bool *pbHasOffset,
1123 : GDALDataType *peStorageType) const override;
1124 :
1125 : double GetScale(bool *pbHasScale,
1126 : GDALDataType *peStorageType) const override;
1127 :
1128 : bool SetOffset(double dfOffset, GDALDataType eStorageType) override;
1129 :
1130 : bool SetScale(double dfScale, GDALDataType eStorageType) override;
1131 :
1132 : std::vector<std::shared_ptr<GDALMDArray>>
1133 : GetCoordinateVariables() const override;
1134 :
1135 : bool IsRegularlySpaced(double &dfStart, double &dfIncrement) const override;
1136 :
1137 : bool Resize(const std::vector<GUInt64> &anNewDimSizes,
1138 : CSLConstList) override;
1139 :
1140 3 : void RegisterOffset(double dfOffset)
1141 : {
1142 3 : m_bHasOffset = true;
1143 3 : m_dfOffset = dfOffset;
1144 3 : }
1145 :
1146 3 : void RegisterScale(double dfScale)
1147 : {
1148 3 : m_bHasScale = true;
1149 3 : m_dfScale = dfScale;
1150 3 : }
1151 :
1152 : bool SetRawNoDataValue(const void *pRawNoData) override;
1153 :
1154 : void RegisterNoDataValue(const void *);
1155 :
1156 2375 : void SetFilename(const std::string &osFilename)
1157 : {
1158 2375 : m_osFilename = osFilename;
1159 2375 : }
1160 :
1161 2375 : void SetDimSeparator(const std::string &osDimSeparator)
1162 : {
1163 2375 : m_osDimSeparator = osDimSeparator;
1164 2375 : }
1165 :
1166 : void SetAttributes(const std::shared_ptr<ZarrGroupBase> &poGroup,
1167 : CPLJSONObject &oAttributes);
1168 :
1169 51 : void SetSRS(const std::shared_ptr<OGRSpatialReference> &srs)
1170 : {
1171 51 : m_poSRS = srs;
1172 51 : }
1173 :
1174 : std::shared_ptr<GDALAttribute>
1175 77 : GetAttribute(const std::string &osName) const override
1176 : {
1177 77 : return m_oAttrGroup.GetAttribute(osName);
1178 : }
1179 :
1180 : std::vector<std::shared_ptr<GDALAttribute>>
1181 207 : GetAttributes(CSLConstList papszOptions) const override
1182 : {
1183 207 : return m_oAttrGroup.GetAttributes(papszOptions);
1184 : }
1185 :
1186 : std::shared_ptr<GDALAttribute>
1187 : CreateAttribute(const std::string &osName,
1188 : const std::vector<GUInt64> &anDimensions,
1189 : const GDALExtendedDataType &oDataType,
1190 : CSLConstList papszOptions = nullptr) override;
1191 :
1192 : bool DeleteAttribute(const std::string &osName,
1193 : CSLConstList papszOptions = nullptr) override;
1194 :
1195 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override;
1196 :
1197 : bool SetSpatialRef(const OGRSpatialReference *poSRS) override;
1198 :
1199 2375 : void SetUpdatable(bool bUpdatable)
1200 : {
1201 2375 : m_bUpdatable = bUpdatable;
1202 2375 : }
1203 :
1204 2375 : void SetDtype(const CPLJSONObject &dtype)
1205 : {
1206 2375 : m_dtype = dtype;
1207 2375 : }
1208 :
1209 515 : void SetDefinitionModified(bool bModified)
1210 : {
1211 515 : m_bDefinitionModified = bModified;
1212 515 : }
1213 :
1214 500 : void SetNew(bool bNew)
1215 : {
1216 500 : m_bNew = bNew;
1217 500 : }
1218 :
1219 : bool Rename(const std::string &osNewName) override;
1220 :
1221 : void ParentRenamed(const std::string &osNewParentFullName) override;
1222 :
1223 : virtual bool Flush() = 0;
1224 :
1225 : //! Return the group owning the array. Might be nullptr
1226 : std::shared_ptr<ZarrGroupBase> GetParentGroup() const;
1227 :
1228 : //! Return the root group. Might be nullptr
1229 1937 : std::shared_ptr<GDALGroup> GetRootGroup() const override
1230 : {
1231 1937 : return m_poSharedResource->GetRootGroup();
1232 : }
1233 :
1234 : bool GetRawBlockInfo(const uint64_t *panBlockCoordinates,
1235 : GDALMDArrayRawBlockInfo &info) const override;
1236 :
1237 : bool BlockCachePresence();
1238 :
1239 754 : void SetStructuralInfo(const char *pszKey, const char *pszValue)
1240 : {
1241 754 : m_aosStructuralInfo.SetNameValue(pszKey, pszValue);
1242 754 : }
1243 :
1244 500 : void SetCreationOptions(CSLConstList papszOptions)
1245 : {
1246 500 : m_aosCreationOptions = papszOptions;
1247 500 : }
1248 :
1249 : static void DecodeSourceElt(const std::vector<DtypeElt> &elts,
1250 : const GByte *pSrc, GByte *pDst);
1251 :
1252 : static void GetDimensionTypeDirection(CPLJSONObject &oAttributes,
1253 : std::string &osType,
1254 : std::string &osDirection);
1255 : };
1256 :
1257 : /************************************************************************/
1258 : /* ZarrV2Array */
1259 : /************************************************************************/
1260 :
1261 : class ZarrV2Array final : public ZarrArray
1262 : {
1263 : CPLJSONObject m_oCompressorJSon{};
1264 : const CPLCompressor *m_psCompressor = nullptr;
1265 : std::string m_osDecompressorId{};
1266 : const CPLCompressor *m_psDecompressor = nullptr;
1267 : CPLJSONArray m_oFiltersArray{}; // ZarrV2 specific
1268 : bool m_bFortranOrder = false;
1269 : mutable ZarrByteVectorQuickResize
1270 : m_abyTmpRawBlockData{}; // used for Fortran order
1271 :
1272 : ZarrV2Array(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1273 : const std::shared_ptr<ZarrGroupBase> &poParent,
1274 : const std::string &osName,
1275 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1276 : const GDALExtendedDataType &oType,
1277 : const std::vector<DtypeElt> &aoDtypeElts,
1278 : const std::vector<GUInt64> &anOuterBlockSize,
1279 : bool bFortranOrder);
1280 :
1281 : bool Serialize();
1282 :
1283 : bool LoadBlockData(const uint64_t *blockIndices, bool bUseMutex,
1284 : const CPLCompressor *psDecompressor,
1285 : ZarrByteVectorQuickResize &abyRawBlockData,
1286 : ZarrByteVectorQuickResize &abyTmpRawBlockData,
1287 : ZarrByteVectorQuickResize &abyDecodedBlockData,
1288 : bool &bMissingBlockOut) const;
1289 :
1290 : bool NeedDecodedBuffer() const;
1291 :
1292 : bool AllocateWorkingBuffers(
1293 : ZarrByteVectorQuickResize &abyRawBlockData,
1294 : ZarrByteVectorQuickResize &abyTmpRawBlockData,
1295 : ZarrByteVectorQuickResize &abyDecodedBlockData) const;
1296 :
1297 : void BlockTranspose(const ZarrByteVectorQuickResize &abySrc,
1298 : ZarrByteVectorQuickResize &abyDst, bool bDecode) const;
1299 :
1300 : // Disable copy constructor and assignment operator
1301 : ZarrV2Array(const ZarrV2Array &) = delete;
1302 : ZarrV2Array &operator=(const ZarrV2Array &) = delete;
1303 :
1304 : public:
1305 : ~ZarrV2Array() override;
1306 :
1307 : static std::shared_ptr<ZarrV2Array>
1308 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1309 : const std::shared_ptr<ZarrGroupBase> &poParent,
1310 : const std::string &osName,
1311 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1312 : const GDALExtendedDataType &oType,
1313 : const std::vector<DtypeElt> &aoDtypeElts,
1314 : const std::vector<GUInt64> &anBlockSize, bool bFortranOrder);
1315 :
1316 : void SetCompressorJson(const CPLJSONObject &oCompressor);
1317 :
1318 1071 : void SetCompressorDecompressor(const std::string &osDecompressorId,
1319 : const CPLCompressor *psComp,
1320 : const CPLCompressor *psDecomp)
1321 : {
1322 1071 : m_psCompressor = psComp;
1323 1071 : m_osDecompressorId = osDecompressorId;
1324 1071 : m_psDecompressor = psDecomp;
1325 1071 : }
1326 :
1327 : void SetFilters(const CPLJSONArray &oFiltersArray);
1328 :
1329 : bool Flush() override;
1330 :
1331 : protected:
1332 : std::string GetDataDirectory() const override;
1333 :
1334 : CPLStringList
1335 : GetChunkIndicesFromFilename(const char *pszFilename) const override;
1336 :
1337 : bool FlushDirtyBlock() const override;
1338 :
1339 : std::string BuildChunkFilename(const uint64_t *blockIndices) const override;
1340 :
1341 : bool AllocateWorkingBuffers() const override;
1342 :
1343 : bool LoadBlockData(const uint64_t *blockIndices,
1344 : bool &bMissingBlockOut) const override;
1345 :
1346 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
1347 : CSLConstList papszOptions) const override;
1348 :
1349 : CPLStringList GetRawBlockInfoInfo() const override;
1350 : };
1351 :
1352 : /************************************************************************/
1353 : /* ZarrV3Array */
1354 : /************************************************************************/
1355 :
1356 : class ZarrV3CodecSequence;
1357 :
1358 : class ZarrV3Array final : public ZarrArray
1359 : {
1360 : bool m_bV2ChunkKeyEncoding = false;
1361 : std::unique_ptr<ZarrV3CodecSequence> m_poCodecs{};
1362 : CPLJSONArray m_oJSONCodecs{};
1363 : mutable bool m_bOverviewsLoaded = false;
1364 : mutable std::vector<std::shared_ptr<GDALMDArray>> m_apoOverviews{};
1365 :
1366 : /** Shard write cache: accumulates dirty inner chunks per shard, encodes
1367 : * each shard exactly once on FlushShardCache() (called from Flush()).
1368 : * Without this cache, FlushDirtyBlockSharded() would re-read, decode,
1369 : * overlay, re-encode, and write the entire shard for every inner chunk,
1370 : * resulting in O(N) encode cycles per shard where N = inner chunks/shard.
1371 : */
1372 : struct ShardWriteEntry
1373 : {
1374 : ZarrByteVectorQuickResize abyShardBuffer{};
1375 : std::vector<bool> abDirtyInnerChunks{};
1376 : };
1377 :
1378 : // Note: cache is unbounded - one entry per shard written. For very large
1379 : // rasters, consider adding LRU eviction in a follow-up.
1380 : mutable std::map<std::string, ShardWriteEntry> m_oShardWriteCache{};
1381 :
1382 : ZarrV3Array(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1383 : const std::shared_ptr<ZarrGroupBase> &poParent,
1384 : const std::string &osName,
1385 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1386 : const GDALExtendedDataType &oType,
1387 : const std::vector<DtypeElt> &aoDtypeElts,
1388 : const std::vector<GUInt64> &anOuterBlockSize,
1389 : const std::vector<GUInt64> &anInnerBlockSize);
1390 :
1391 : bool Serialize(const CPLJSONObject &oAttrs);
1392 :
1393 : bool NeedDecodedBuffer() const;
1394 :
1395 : bool AllocateWorkingBuffers(
1396 : ZarrByteVectorQuickResize &abyRawBlockData,
1397 : ZarrByteVectorQuickResize &abyDecodedBlockData) const;
1398 :
1399 : bool LoadBlockData(const uint64_t *blockIndices, bool bUseMutex,
1400 : ZarrV3CodecSequence *poCodecs,
1401 : ZarrByteVectorQuickResize &abyRawBlockData,
1402 : ZarrByteVectorQuickResize &abyDecodedBlockData,
1403 : bool &bMissingBlockOut) const;
1404 :
1405 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
1406 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1407 : const GDALExtendedDataType &bufferDataType,
1408 : void *pDstBuffer) const override;
1409 :
1410 : void PreloadShardedBlocks(const GUInt64 *arrayStartIdx,
1411 : const size_t *count) const;
1412 :
1413 : bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
1414 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1415 : const GDALExtendedDataType &bufferDataType,
1416 : const void *pSrcBuffer) override;
1417 :
1418 : bool WriteChunksThreadSafe(const GUInt64 *arrayStartIdx,
1419 : const size_t *count, const GInt64 *arrayStep,
1420 : const GPtrDiff_t *bufferStride,
1421 : const GDALExtendedDataType &bufferDataType,
1422 : const void *pSrcBuffer, const int iThread,
1423 : const int nThreads,
1424 : std::string &osErrorMsg) const;
1425 :
1426 : void LoadOverviews() const;
1427 :
1428 : void ReconstructCreationOptionsFromCodecs();
1429 :
1430 : public:
1431 : ~ZarrV3Array() override;
1432 :
1433 : static std::shared_ptr<ZarrV3Array>
1434 : Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
1435 : const std::shared_ptr<ZarrGroupBase> &poParent,
1436 : const std::string &osName,
1437 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
1438 : const GDALExtendedDataType &oType,
1439 : const std::vector<DtypeElt> &aoDtypeElts,
1440 : const std::vector<GUInt64> &anOuterBlockSize,
1441 : const std::vector<GUInt64> &anInnerBlockSize);
1442 :
1443 1118 : void SetIsV2ChunkKeyEncoding(bool b)
1444 : {
1445 1118 : m_bV2ChunkKeyEncoding = b;
1446 1118 : }
1447 :
1448 : void SetCodecs(const CPLJSONArray &oJSONCodecs,
1449 : std::unique_ptr<ZarrV3CodecSequence> &&poCodecs);
1450 :
1451 : bool Flush() override;
1452 :
1453 : static std::unique_ptr<ZarrV3CodecSequence>
1454 : SetupCodecs(const CPLJSONArray &oCodecs,
1455 : const std::vector<GUInt64> &anOuterBlockSize,
1456 : std::vector<GUInt64> &anInnerBlockSize, DtypeElt &zarrDataType,
1457 : const std::vector<GByte> &abyNoData);
1458 : int GetOverviewCount() const override;
1459 :
1460 : std::shared_ptr<GDALMDArray> GetOverview(int idx) const override;
1461 :
1462 : CPLErr BuildOverviews(const char *pszResampling, int nOverviews,
1463 : const int *panOverviewList,
1464 : GDALProgressFunc pfnProgress, void *pProgressData,
1465 : CSLConstList papszOptions) override;
1466 :
1467 : static void
1468 : ExtractSubArrayFromLargerOne(const ZarrByteVectorQuickResize &abySrc,
1469 : const std::vector<size_t> &anSrcBlockSize,
1470 : const std::vector<size_t> &anInnerBlockSize,
1471 : const std::vector<size_t> &anInnerBlockIndices,
1472 : ZarrByteVectorQuickResize &abyChunk,
1473 : const size_t nDTSize);
1474 :
1475 : protected:
1476 : std::string GetDataDirectory() const override;
1477 :
1478 : CPLStringList
1479 : GetChunkIndicesFromFilename(const char *pszFilename) const override;
1480 :
1481 : bool AllocateWorkingBuffers() const override;
1482 :
1483 : bool FlushDirtyBlock() const override;
1484 : bool FlushDirtyBlockSharded() const;
1485 : bool FlushSingleShard(const std::string &osFilename,
1486 : ShardWriteEntry &entry) const;
1487 : bool FlushShardCache() const;
1488 :
1489 : std::string BuildChunkFilename(const uint64_t *blockIndices) const override;
1490 :
1491 : bool LoadBlockData(const uint64_t *blockIndices,
1492 : bool &bMissingBlockOut) const override;
1493 :
1494 : bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
1495 : CSLConstList papszOptions) const override;
1496 :
1497 : CPLStringList GetRawBlockInfoInfo() const override;
1498 : };
1499 :
1500 : void ZarrClearCoordinateCache();
1501 : void ZarrClearShardIndexCache();
1502 : void ZarrEraseShardIndexFromCache(const std::string &osFilename);
1503 :
1504 : #endif // ZARR_H
|