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 : #include "zarr.h"
14 :
15 : #include <algorithm>
16 : #include <cassert>
17 : #include <limits>
18 : #include <map>
19 : #include <set>
20 :
21 : /************************************************************************/
22 : /* ~ZarrGroupBase() */
23 : /************************************************************************/
24 :
25 1518 : ZarrGroupBase::~ZarrGroupBase()
26 : {
27 1518 : CPL_IGNORE_RET_VAL(ZarrGroupBase::Close());
28 1518 : }
29 :
30 : /************************************************************************/
31 : /* Close() */
32 : /************************************************************************/
33 :
34 4782 : bool ZarrGroupBase::Close()
35 : {
36 4782 : bool ret = true;
37 :
38 5544 : for (auto &kv : m_oMapGroups)
39 : {
40 762 : ret = kv.second->Close() && ret;
41 : }
42 :
43 8835 : for (auto &kv : m_oMapMDArrays)
44 : {
45 4053 : ret = kv.second->Flush() && ret;
46 : }
47 4782 : return ret;
48 : }
49 :
50 : /************************************************************************/
51 : /* GetMDArrayNames() */
52 : /************************************************************************/
53 :
54 1145 : std::vector<std::string> ZarrGroupBase::GetMDArrayNames(CSLConstList) const
55 : {
56 1145 : if (!CheckValidAndErrorOutIfNot())
57 0 : return {};
58 :
59 1145 : if (!m_bDirectoryExplored)
60 269 : ExploreDirectory();
61 :
62 1145 : return m_aosArrays;
63 : }
64 :
65 : /************************************************************************/
66 : /* RegisterArray() */
67 : /************************************************************************/
68 :
69 1251 : void ZarrGroupBase::RegisterArray(const std::shared_ptr<ZarrArray> &array) const
70 : {
71 1251 : m_oMapMDArrays[array->GetName()] = array;
72 1251 : if (std::find(m_aosArrays.begin(), m_aosArrays.end(), array->GetName()) ==
73 2502 : m_aosArrays.end())
74 : {
75 1167 : m_aosArrays.emplace_back(array->GetName());
76 : }
77 2502 : array->RegisterGroup(
78 2502 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock()));
79 1251 : }
80 :
81 : /************************************************************************/
82 : /* GetGroupNames() */
83 : /************************************************************************/
84 :
85 297 : std::vector<std::string> ZarrGroupBase::GetGroupNames(CSLConstList) const
86 : {
87 297 : if (!CheckValidAndErrorOutIfNot())
88 0 : return {};
89 :
90 297 : if (!m_bDirectoryExplored)
91 38 : ExploreDirectory();
92 :
93 297 : return m_aosGroups;
94 : }
95 :
96 : /************************************************************************/
97 : /* DeleteGroup() */
98 : /************************************************************************/
99 :
100 18 : bool ZarrGroupBase::DeleteGroup(const std::string &osName,
101 : CSLConstList /*papszOptions*/)
102 : {
103 18 : if (!CheckValidAndErrorOutIfNot())
104 0 : return false;
105 :
106 18 : if (!m_bUpdatable)
107 : {
108 6 : CPLError(CE_Failure, CPLE_NotSupported,
109 : "Dataset not open in update mode");
110 6 : return false;
111 : }
112 :
113 12 : GetGroupNames();
114 :
115 12 : auto oIterNames = std::find(m_aosGroups.begin(), m_aosGroups.end(), osName);
116 12 : if (oIterNames == m_aosGroups.end())
117 : {
118 6 : CPLError(CE_Failure, CPLE_AppDefined,
119 : "Group %s is not a sub-group of this group", osName.c_str());
120 6 : return false;
121 : }
122 :
123 : const std::string osSubDirName =
124 12 : CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
125 6 : if (VSIRmdirRecursive(osSubDirName.c_str()) != 0)
126 : {
127 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot delete %s",
128 : osSubDirName.c_str());
129 0 : return false;
130 : }
131 :
132 6 : m_poSharedResource->DeleteZMetadataItemRecursive(osSubDirName);
133 :
134 6 : m_aosGroups.erase(oIterNames);
135 :
136 6 : auto oIter = m_oMapGroups.find(osName);
137 6 : if (oIter != m_oMapGroups.end())
138 : {
139 4 : oIter->second->Deleted();
140 4 : m_oMapGroups.erase(oIter);
141 : }
142 :
143 6 : return true;
144 : }
145 :
146 : /************************************************************************/
147 : /* NotifyChildrenOfDeletion() */
148 : /************************************************************************/
149 :
150 6 : void ZarrGroupBase::NotifyChildrenOfDeletion()
151 : {
152 8 : for (const auto &oIter : m_oMapGroups)
153 2 : oIter.second->ParentDeleted();
154 :
155 10 : for (const auto &oIter : m_oMapMDArrays)
156 4 : oIter.second->ParentDeleted();
157 :
158 6 : m_oAttrGroup.ParentDeleted();
159 :
160 10 : for (const auto &oIter : m_oMapDimensions)
161 4 : oIter.second->ParentDeleted();
162 6 : }
163 :
164 : /************************************************************************/
165 : /* ZarrGroupBase::CreateAttribute() */
166 : /************************************************************************/
167 :
168 78 : std::shared_ptr<GDALAttribute> ZarrGroupBase::CreateAttribute(
169 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
170 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
171 : {
172 78 : if (!CheckValidAndErrorOutIfNot())
173 0 : return nullptr;
174 :
175 78 : if (!m_bUpdatable)
176 : {
177 3 : CPLError(CE_Failure, CPLE_NotSupported,
178 : "Dataset not open in update mode");
179 3 : return nullptr;
180 : }
181 75 : if (anDimensions.size() >= 2)
182 : {
183 3 : CPLError(CE_Failure, CPLE_NotSupported,
184 : "Cannot create attributes of dimension >= 2");
185 3 : return nullptr;
186 : }
187 72 : LoadAttributes();
188 : return m_oAttrGroup.CreateAttribute(osName, anDimensions, oDataType,
189 72 : papszOptions);
190 : }
191 :
192 : /************************************************************************/
193 : /* ZarrGroupBase::DeleteAttribute() */
194 : /************************************************************************/
195 :
196 18 : bool ZarrGroupBase::DeleteAttribute(const std::string &osName, CSLConstList)
197 : {
198 18 : if (!CheckValidAndErrorOutIfNot())
199 0 : return false;
200 :
201 18 : if (!m_bUpdatable)
202 : {
203 6 : CPLError(CE_Failure, CPLE_NotSupported,
204 : "Dataset not open in update mode");
205 6 : return false;
206 : }
207 :
208 12 : LoadAttributes();
209 12 : return m_oAttrGroup.DeleteAttribute(osName);
210 : }
211 :
212 : /************************************************************************/
213 : /* GetDimensions() */
214 : /************************************************************************/
215 :
216 : std::vector<std::shared_ptr<GDALDimension>>
217 582 : ZarrGroupBase::GetDimensions(CSLConstList) const
218 : {
219 582 : if (!CheckValidAndErrorOutIfNot())
220 0 : return {};
221 :
222 582 : if (!m_bReadFromZMetadata && !m_bDimensionsInstantiated)
223 : {
224 290 : m_bDimensionsInstantiated = true;
225 : // We need to instantiate arrays to discover dimensions
226 580 : const auto aosArrays = GetMDArrayNames();
227 324 : for (const auto &osArray : aosArrays)
228 : {
229 34 : OpenMDArray(osArray);
230 : }
231 : }
232 :
233 1164 : std::vector<std::shared_ptr<GDALDimension>> oRes;
234 1025 : for (const auto &oIter : m_oMapDimensions)
235 : {
236 443 : oRes.push_back(oIter.second);
237 : }
238 582 : return oRes;
239 : }
240 :
241 : /************************************************************************/
242 : /* DeleteMDArray() */
243 : /************************************************************************/
244 :
245 18 : bool ZarrGroupBase::DeleteMDArray(const std::string &osName,
246 : CSLConstList /*papszOptions*/)
247 : {
248 18 : if (!CheckValidAndErrorOutIfNot())
249 0 : return false;
250 :
251 18 : if (!m_bUpdatable)
252 : {
253 6 : CPLError(CE_Failure, CPLE_NotSupported,
254 : "Dataset not open in update mode");
255 6 : return false;
256 : }
257 :
258 12 : GetMDArrayNames();
259 :
260 12 : auto oIterNames = std::find(m_aosArrays.begin(), m_aosArrays.end(), osName);
261 12 : if (oIterNames == m_aosArrays.end())
262 : {
263 6 : CPLError(CE_Failure, CPLE_AppDefined,
264 : "Array %s is not an array of this group", osName.c_str());
265 6 : return false;
266 : }
267 :
268 : const std::string osSubDirName =
269 12 : CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
270 6 : if (VSIRmdirRecursive(osSubDirName.c_str()) != 0)
271 : {
272 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot delete %s",
273 : osSubDirName.c_str());
274 0 : return false;
275 : }
276 :
277 6 : m_poSharedResource->DeleteZMetadataItemRecursive(osSubDirName);
278 :
279 6 : m_aosArrays.erase(oIterNames);
280 :
281 6 : auto oIter = m_oMapMDArrays.find(osName);
282 6 : if (oIter != m_oMapMDArrays.end())
283 : {
284 4 : oIter->second->Deleted();
285 4 : m_oMapMDArrays.erase(oIter);
286 : }
287 :
288 6 : return true;
289 : }
290 :
291 : /************************************************************************/
292 : /* CreateDimension() */
293 : /************************************************************************/
294 :
295 508 : std::shared_ptr<GDALDimension> ZarrGroupBase::CreateDimension(
296 : const std::string &osName, const std::string &osType,
297 : const std::string &osDirection, GUInt64 nSize, CSLConstList)
298 : {
299 508 : if (!CheckValidAndErrorOutIfNot())
300 0 : return nullptr;
301 :
302 508 : if (osName.empty())
303 : {
304 0 : CPLError(CE_Failure, CPLE_NotSupported,
305 : "Empty dimension name not supported");
306 0 : return nullptr;
307 : }
308 508 : GetDimensions(nullptr);
309 :
310 508 : if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
311 : {
312 0 : CPLError(CE_Failure, CPLE_AppDefined,
313 : "A dimension with same name already exists");
314 0 : return nullptr;
315 : }
316 : auto newDim(std::make_shared<ZarrDimension>(
317 508 : m_poSharedResource,
318 1016 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock()), GetFullName(),
319 1016 : osName, osType, osDirection, nSize));
320 508 : newDim->SetXArrayDimension();
321 508 : m_oMapDimensions[osName] = newDim;
322 508 : return newDim;
323 : }
324 :
325 : /************************************************************************/
326 : /* RenameDimension() */
327 : /************************************************************************/
328 :
329 12 : bool ZarrGroupBase::RenameDimension(const std::string &osOldName,
330 : const std::string &osNewName)
331 : {
332 12 : if (m_oMapDimensions.find(osNewName) != m_oMapDimensions.end())
333 : {
334 6 : CPLError(CE_Failure, CPLE_AppDefined,
335 : "A dimension with same name already exists");
336 6 : return false;
337 : }
338 6 : auto oIter = m_oMapDimensions.find(osOldName);
339 6 : if (oIter == m_oMapDimensions.end())
340 : {
341 0 : CPLAssert(false);
342 : return false;
343 : }
344 6 : auto poDim = std::move(oIter->second);
345 6 : m_oMapDimensions.erase(oIter);
346 6 : m_oMapDimensions[osNewName] = std::move(poDim);
347 6 : return true;
348 : }
349 :
350 : /************************************************************************/
351 : /* ZarrGroupBase::UpdateDimensionSize() */
352 : /************************************************************************/
353 :
354 7 : void ZarrGroupBase::UpdateDimensionSize(
355 : const std::shared_ptr<GDALDimension> &poUpdatedDim)
356 : {
357 14 : const auto aosGroupNames = GetGroupNames();
358 7 : for (const auto &osGroupName : aosGroupNames)
359 : {
360 0 : auto poSubGroup = OpenZarrGroup(osGroupName);
361 0 : if (poSubGroup)
362 : {
363 0 : poSubGroup->UpdateDimensionSize(poUpdatedDim);
364 : }
365 : }
366 14 : const auto aosArrayNames = GetMDArrayNames();
367 22 : for (const auto &osArrayName : aosArrayNames)
368 : {
369 : // Disable checks that size of variables referenced by _ARRAY_DIMENSIONS
370 : // are consistent with array shapes, as we are in the middle of updating
371 : // things
372 15 : m_bDimSizeInUpdate = true;
373 30 : auto poArray = OpenZarrArray(osArrayName);
374 15 : m_bDimSizeInUpdate = false;
375 15 : if (poArray)
376 : {
377 39 : for (auto &poDim : poArray->GetDimensions())
378 : {
379 24 : if (poDim->GetFullName() == poUpdatedDim->GetFullName())
380 : {
381 : auto poModifiableDim =
382 30 : std::dynamic_pointer_cast<ZarrDimension>(poDim);
383 15 : CPLAssert(poModifiableDim);
384 15 : poModifiableDim->SetSize(poUpdatedDim->GetSize());
385 15 : poArray->SetDefinitionModified(true);
386 : }
387 : }
388 : }
389 : }
390 7 : }
391 :
392 : /************************************************************************/
393 : /* ZarrGroupBase::NotifyArrayRenamed() */
394 : /************************************************************************/
395 :
396 6 : void ZarrGroupBase::NotifyArrayRenamed(const std::string &osOldName,
397 : const std::string &osNewName)
398 : {
399 6 : for (auto &osName : m_aosArrays)
400 : {
401 6 : if (osName == osOldName)
402 : {
403 6 : osName = osNewName;
404 6 : break;
405 : }
406 : }
407 :
408 6 : auto oIter = m_oMapMDArrays.find(osOldName);
409 6 : if (oIter != m_oMapMDArrays.end())
410 : {
411 6 : auto poArray = std::move(oIter->second);
412 6 : m_oMapMDArrays.erase(oIter);
413 6 : m_oMapMDArrays[osNewName] = std::move(poArray);
414 : }
415 6 : }
416 :
417 : /************************************************************************/
418 : /* IsValidObjectName() */
419 : /************************************************************************/
420 :
421 : /* static */
422 679 : bool ZarrGroupBase::IsValidObjectName(const std::string &osName)
423 : {
424 1951 : return !(osName.empty() || osName == "." || osName == ".." ||
425 1272 : osName.find('/') != std::string::npos ||
426 1256 : osName.find('\\') != std::string::npos ||
427 624 : osName.find(':') != std::string::npos ||
428 1295 : STARTS_WITH(osName.c_str(), ".z"));
429 : }
430 :
431 : /************************************************************************/
432 : /* CheckArrayOrGroupWithSameNameDoesNotExist() */
433 : /************************************************************************/
434 :
435 27 : bool ZarrGroupBase::CheckArrayOrGroupWithSameNameDoesNotExist(
436 : const std::string &osName) const
437 : {
438 54 : const auto groupNames = GetGroupNames();
439 27 : if (std::find(groupNames.begin(), groupNames.end(), osName) !=
440 54 : groupNames.end())
441 : {
442 9 : CPLError(CE_Failure, CPLE_AppDefined,
443 : "A group with same name already exists");
444 9 : return false;
445 : }
446 :
447 36 : const auto arrayNames = GetMDArrayNames();
448 18 : if (std::find(arrayNames.begin(), arrayNames.end(), osName) !=
449 36 : arrayNames.end())
450 : {
451 6 : CPLError(CE_Failure, CPLE_AppDefined,
452 : "An array with same name already exists");
453 6 : return false;
454 : }
455 :
456 12 : return true;
457 : }
458 :
459 : /************************************************************************/
460 : /* Rename() */
461 : /************************************************************************/
462 :
463 36 : bool ZarrGroupBase::Rename(const std::string &osNewName)
464 : {
465 36 : if (!CheckValidAndErrorOutIfNot())
466 3 : return false;
467 :
468 33 : if (!m_bUpdatable)
469 : {
470 6 : CPLError(CE_Failure, CPLE_NotSupported,
471 : "Dataset not open in update mode");
472 6 : return false;
473 : }
474 27 : if (!IsValidObjectName(osNewName))
475 : {
476 6 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid group name");
477 6 : return false;
478 : }
479 21 : if (m_osName == "/")
480 : {
481 6 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot rename root group");
482 6 : return false;
483 : }
484 :
485 30 : auto pParent = std::dynamic_pointer_cast<ZarrGroupBase>(m_poParent.lock());
486 15 : if (pParent)
487 : {
488 15 : if (!pParent->CheckArrayOrGroupWithSameNameDoesNotExist(osNewName))
489 9 : return false;
490 : }
491 :
492 12 : std::string osNewDirectoryName(m_osDirectoryName);
493 6 : osNewDirectoryName.resize(osNewDirectoryName.size() - m_osName.size());
494 6 : osNewDirectoryName += osNewName;
495 :
496 6 : if (VSIRename(m_osDirectoryName.c_str(), osNewDirectoryName.c_str()) != 0)
497 : {
498 0 : CPLError(CE_Failure, CPLE_AppDefined, "Renaming of %s to %s failed",
499 : m_osDirectoryName.c_str(), osNewDirectoryName.c_str());
500 0 : return false;
501 : }
502 :
503 6 : if (pParent)
504 : {
505 6 : auto oIter = pParent->m_oMapGroups.find(m_osName);
506 6 : if (oIter != pParent->m_oMapGroups.end())
507 : {
508 6 : pParent->m_oMapGroups.erase(oIter);
509 6 : CPLAssert(m_pSelf.lock());
510 12 : pParent->m_oMapGroups[osNewName] =
511 18 : std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
512 : }
513 :
514 6 : for (auto &osName : pParent->m_aosGroups)
515 : {
516 6 : if (osName == m_osName)
517 : {
518 6 : osName = osNewName;
519 6 : break;
520 : }
521 : }
522 : }
523 :
524 6 : m_poSharedResource->RenameZMetadataRecursive(m_osDirectoryName,
525 : osNewDirectoryName);
526 :
527 6 : m_osDirectoryName = std::move(osNewDirectoryName);
528 :
529 6 : BaseRename(osNewName);
530 :
531 6 : return true;
532 : }
533 :
534 : /************************************************************************/
535 : /* ParentRenamed() */
536 : /************************************************************************/
537 :
538 4 : void ZarrGroupBase::ParentRenamed(const std::string &osNewParentFullName)
539 : {
540 8 : auto pParent = std::dynamic_pointer_cast<ZarrGroupBase>(m_poParent.lock());
541 : // The parent necessarily exist, since it notified us
542 4 : CPLAssert(pParent);
543 :
544 8 : m_osDirectoryName = CPLFormFilenameSafe(pParent->m_osDirectoryName.c_str(),
545 4 : m_osName.c_str(), nullptr);
546 :
547 4 : GDALGroup::ParentRenamed(osNewParentFullName);
548 4 : }
549 :
550 : /************************************************************************/
551 : /* NotifyChildrenOfRenaming() */
552 : /************************************************************************/
553 :
554 10 : void ZarrGroupBase::NotifyChildrenOfRenaming()
555 : {
556 14 : for (const auto &oIter : m_oMapGroups)
557 4 : oIter.second->ParentRenamed(m_osFullName);
558 :
559 19 : for (const auto &oIter : m_oMapMDArrays)
560 9 : oIter.second->ParentRenamed(m_osFullName);
561 :
562 10 : m_oAttrGroup.ParentRenamed(m_osFullName);
563 :
564 16 : for (const auto &oIter : m_oMapDimensions)
565 6 : oIter.second->ParentRenamed(m_osFullName);
566 10 : }
|