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