46 throw error(
"Group::create: null store");
48 detail::validate_path(
path);
49 write_group_chain(*store,
path, format);
50 return {std::move(store),
path, json::object(),
nullptr, format,
OpenOptions{}};
57 [[nodiscard]]
static Group open(std::shared_ptr<Store> store,
const std::string&
path =
"",
60 throw error(
"Group::open: null store");
62 detail::validate_path(
path);
65 const std::string v3_key = v3::meta_key(
path);
66 if (
const auto bytes = store->read(v3_key)) {
67 const json doc = detail::parse_json(*bytes, v3_key);
68 if (doc.is_object() && doc.value(
"node_type",
"") == std::string(
"array")) {
69 throw error(
"'" +
path +
"' is an array, not a group");
71 v3::GroupMeta meta = v3::parse_group_meta(doc, v3_key, options.lenient);
72 std::shared_ptr<const json> consolidated;
73 if (meta.consolidated) {
76 json by_key = json::object();
77 for (
const auto& item : meta.consolidated->items()) {
78 const std::string child =
path.empty() ? item.key() :
path +
"/" + item.key();
79 by_key[v3::meta_key(child)] = item.value();
82 consolidated = std::make_shared<const json>(std::move(by_key));
84 return {std::move(store),
path, std::move(meta.attributes),
85 std::move(consolidated), ZarrFormat::v3, options};
89 std::shared_ptr<const json> consolidated;
90 if (
const auto c = v2::read_consolidated(*store)) {
91 consolidated = std::make_shared<const json>(*c);
93 return open_with(std::move(store),
path, std::move(consolidated), options);
97 [[nodiscard]]
const std::string&
path()
const {
return path_; }
106 if (format_ == ZarrFormat::v3) {
107 const std::string key = v3::meta_key(path_);
108 const auto bytes = store_->read(key);
110 throw error(key +
": metadata disappeared");
112 json doc = detail::parse_json(*bytes, key);
113 if (attributes_.empty()) {
114 doc.erase(
"attributes");
116 doc[
"attributes"] = attributes_;
121 const std::string key = v2::meta_key(path_, v2::kAttrsSuffix);
122 if (attributes_.empty()) {
123 v2::erase_meta_key(*store_, key);
125 v2::write_meta_key(*store_, key, attributes_);
137 const std::string target = child_path(name);
138 const std::size_t slash = target.rfind(
'/');
139 if (slash != std::string::npos) {
140 write_group_chain(*store_, target.substr(0, slash), format_);
147 const std::string target = child_path(name);
148 if (format_ == ZarrFormat::v3) {
149 return open_v3_child_group(target);
151 return open_with(store_, target, consolidated_, options_);
156 return Array::open_impl(store_, child_path(name), options_, consolidated_);
162 const std::string prefix = path_.empty() ?
"" : path_ +
"/";
163 for (
const std::string& child : store_->list_dir(prefix).prefixes) {
164 const std::string child_full = prefix + child;
165 if (store_->exists(child_full +
"/" + v2::kArraySuffix)) {
166 out.
arrays.push_back(child);
167 }
else if (store_->exists(child_full +
"/" + v2::kGroupSuffix)) {
168 out.
groups.push_back(child);
169 }
else if (
const auto doc = read_doc(v3::meta_key(child_full))) {
171 const std::string node_type = doc->is_object() ? doc->value(
"node_type",
"") :
"";
172 if (node_type ==
"array") {
173 out.
arrays.push_back(child);
174 }
else if (node_type ==
"group") {
175 out.
groups.push_back(child);
185 std::shared_ptr<const json> consolidated,
ZarrFormat format = ZarrFormat::v2,
187 : store_(std::move(store)),
188 path_(std::move(
path)),
190 consolidated_(std::move(consolidated)),
194 [[nodiscard]] std::optional<json> read_doc(
const std::string& key)
const {
196 const auto it = consolidated_->find(key);
197 if (it == consolidated_->end()) {
202 const auto bytes = store_->read(key);
206 return detail::parse_json(*bytes, key);
209 [[nodiscard]] Group open_v3_child_group(
const std::string& target)
const {
210 const std::string key = v3::meta_key(target);
211 const auto doc = read_doc(key);
213 throw error(
"no group at '" + target +
"' (" + key +
" not found)");
215 if (doc->is_object() && doc->value(
"node_type",
"") == std::string(
"array")) {
216 throw error(
"'" + target +
"' is an array, not a group");
218 v3::GroupMeta meta = v3::parse_group_meta(*doc, key, options_.
lenient);
220 return {store_, target, std::move(meta.attributes), consolidated_, ZarrFormat::v3, options_};
223 static Group open_with(std::shared_ptr<Store> store,
const std::string&
path,
224 std::shared_ptr<const json> consolidated, OpenOptions options) {
225 const auto read_doc = [&](
const std::string& key) -> std::optional<json> {
227 const auto it = consolidated->find(key);
228 if (it == consolidated->end()) {
233 const auto bytes = store->read(key);
237 return detail::parse_json(*bytes, key);
240 const std::string group_key = v2::meta_key(
path, v2::kGroupSuffix);
241 const auto doc = read_doc(group_key);
243 if (store->exists(v2::meta_key(
path, v2::kArraySuffix))) {
244 throw error(
"'" +
path +
"' is an array, not a group");
246 throw error(
"no group at '" +
path +
"' (neither " + v3::meta_key(
path) +
" nor " +
247 group_key +
" found)");
249 v2::check_group_meta(*doc, group_key);
251 if (
const auto attrs = read_doc(v2::meta_key(
path, v2::kAttrsSuffix))) {
255 std::move(consolidated), ZarrFormat::v2, options};
259 static void write_group_chain(Store& store,
const std::string&
path, ZarrFormat format) {
260 std::vector<std::string> chain;
261 chain.emplace_back(
"");
262 std::size_t start = 0;
263 while (!
path.empty()) {
264 const std::size_t slash =
path.find(
'/', start);
265 if (slash == std::string::npos) {
266 chain.push_back(
path);
269 chain.push_back(
path.substr(0, slash));
272 for (
const std::string& node : chain) {
273 if (store.exists(v2::meta_key(node, v2::kArraySuffix))) {
274 throw error(
"'" + node +
"' is an array; cannot create a group inside it");
276 if (format == ZarrFormat::v3) {
277 const std::string key = v3::meta_key(node);
278 if (
const auto bytes = store.read(key)) {
279 const json doc = detail::parse_json(*bytes, key);
280 if (doc.is_object() && doc.value(
"node_type",
"") == std::string(
"array")) {
281 throw error(
"'" + node +
"' is an array; cannot create a group inside it");
287 const std::string key = v2::meta_key(node, v2::kGroupSuffix);
288 if (!store.exists(key)) {
289 v2::write_meta_key(store, key, v2::emit_group_meta());
295 [[nodiscard]] std::string child_path(
const std::string& name)
const {
296 detail::validate_path(name);
298 throw error(
"child name must not be empty");
300 return path_.empty() ? name : path_ +
"/" + name;
303 std::shared_ptr<Store> store_;
306 std::shared_ptr<const json> consolidated_;
308 OpenOptions options_;