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