Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 144
Add support for opening structured dtypes as void for zarr driver#272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
59a68712aedabf46d9902ccc4bd75d4a68fc410f5ed886c2fa42b6f5e9c15da7fb91d7389d6a9101011b62fd8f99735318a0efd695775f0ceb169a0c3fb8c023bff857d187e55b90443File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -29,6 +29,10 @@ | ||
| #include "absl/status/status.h" | ||
| #include "absl/strings/cord.h" | ||
| #include <nlohmann/json_fwd.hpp> | ||
| #include "riegeli/bytes/cord_reader.h" | ||
| #include "riegeli/bytes/cord_writer.h" | ||
| #include "riegeli/bytes/read_all.h" | ||
| #include "riegeli/bytes/write.h" | ||
| #include "tensorstore/array.h" | ||
| #include "tensorstore/array_storage_statistics.h" | ||
| #include "tensorstore/box.h" | ||
| @@ -55,6 +59,7 @@ | ||
| #include "tensorstore/internal/chunk_grid_specification.h" | ||
| #include "tensorstore/internal/grid_storage_statistics.h" | ||
| #include "tensorstore/internal/intrusive_ptr.h" | ||
| #include "tensorstore/internal/riegeli/array_endian_codec.h" | ||
| #include "tensorstore/internal/json_binding/bindable.h" | ||
| #include "tensorstore/internal/json_binding/json_binding.h" | ||
| #include "tensorstore/internal/uri_utils.h" | ||
| @@ -137,7 +142,8 @@ absl::Status ZarrDriverSpec::ApplyOptions(SpecOptions&& options) { | ||
| } | ||
| Result<SpecRankAndFieldInfo> ZarrDriverSpec::GetSpecInfo() const { | ||
| return GetSpecRankAndFieldInfo(partial_metadata, selected_field, schema); | ||
| return GetSpecRankAndFieldInfo(partial_metadata, selected_field, schema, | ||
| open_as_void); | ||
| } | ||
| TENSORSTORE_DEFINE_JSON_DEFAULT_BINDER( | ||
| @@ -171,7 +177,16 @@ TENSORSTORE_DEFINE_JSON_DEFAULT_BINDER( | ||
| jb::Member("field", jb::Projection<&ZarrDriverSpec::selected_field>( | ||
| jb::DefaultValue<jb::kNeverIncludeDefaults>( | ||
| [](auto* obj) { *obj = std::string{}; }))), | ||
| jb::Member("open_as_void", | ||
| jb::Projection<&ZarrDriverSpec::open_as_void>( | ||
| jb::DefaultValue<jb::kNeverIncludeDefaults>( | ||
| [](auto* v) { *v = false; }))), | ||
| jb::Initialize([](auto* obj) { | ||
| // Validate that field and open_as_void are mutually exclusive | ||
laramiel marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (obj->open_as_void && !obj->selected_field.empty()) { | ||
| return absl::InvalidArgumentError( | ||
| "\"field\" and \"open_as_void\" are mutually exclusive"); | ||
| } | ||
| TENSORSTORE_ASSIGN_OR_RETURN(auto info, obj->GetSpecInfo()); | ||
| if (info.full_rank != dynamic_rank) { | ||
| TENSORSTORE_RETURN_IF_ERROR( | ||
| @@ -209,8 +224,11 @@ Result<SharedArray<const void>> ZarrDriverSpec::GetFillValue( | ||
| const auto& metadata = partial_metadata; | ||
| if (metadata.dtype && metadata.fill_value) { | ||
| TENSORSTORE_ASSIGN_OR_RETURN( | ||
| size_t field_index, GetFieldIndex(*metadata.dtype, selected_field)); | ||
| size_t field_index = 0; // open_as_void has a single field. | ||
| if (!open_as_void) { | ||
| TENSORSTORE_ASSIGN_OR_RETURN( | ||
| field_index, GetFieldIndex(*metadata.dtype, selected_field)); | ||
| } | ||
| fill_value = (*metadata.fill_value)[field_index]; | ||
| } | ||
| @@ -356,6 +374,7 @@ absl::Status DataCache::GetBoundSpecData( | ||
| const auto& metadata = *static_cast<const ZarrMetadata*>(metadata_ptr); | ||
| spec.selected_field = EncodeSelectedField(component_index, metadata.dtype); | ||
| spec.metadata_key = metadata_key_; | ||
| spec.open_as_void = false; | ||
| auto& pm = spec.partial_metadata; | ||
| pm.rank = metadata.rank; | ||
| pm.zarr_format = metadata.zarr_format; | ||
| @@ -382,6 +401,36 @@ Result<ChunkLayout> DataCache::GetChunkLayoutFromMetadata( | ||
| } | ||
| std::string DataCache::GetBaseKvstorePath() { return key_prefix_; } | ||
| // VoidDataCache implementation | ||
BrianMichell marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // Uses inherited DataCache constructor and encode/decode methods. | ||
| // The void metadata (with dtype containing only the void field) is created | ||
| // in GetDataCache and passed via the initializer, so standard encode/decode | ||
| // paths work correctly. | ||
| absl::Status VoidDataCache::ValidateMetadataCompatibility( | ||
| const void* existing_metadata_ptr, const void* new_metadata_ptr) { | ||
| // The existing metadata is already void metadata (from cache initialization). | ||
| // Convert the new metadata to void metadata so both have the same synthesized | ||
| // void dtype, then use normal validation which compares all fields except | ||
| // shape (via IsMetadataCompatible). | ||
| assert(new_metadata_ptr); | ||
| const auto& new_metadata = | ||
| *static_cast<const ZarrMetadata*>(new_metadata_ptr); | ||
| return DataCache::ValidateMetadataCompatibility( | ||
| existing_metadata_ptr, new_metadata.GetVoidMetadata().get()); | ||
| } | ||
| absl::Status VoidDataCache::GetBoundSpecData( | ||
| internal_kvs_backed_chunk_driver::KvsDriverSpec& spec_base, | ||
| const void* metadata_ptr, size_t component_index) { | ||
| TENSORSTORE_RETURN_IF_ERROR( | ||
| DataCache::GetBoundSpecData(spec_base, metadata_ptr, component_index)); | ||
| auto& spec = static_cast<ZarrDriverSpec&>(spec_base); | ||
BrianMichell marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| spec.open_as_void = true; | ||
| return absl::OkStatus(); | ||
| } | ||
| Result<CodecSpec> ZarrDriver::GetCodec() { | ||
| return internal_zarr::GetCodecSpecFromMetadata(metadata()); | ||
| } | ||
| @@ -416,6 +465,10 @@ Result<std::string> ZarrDriverSpec::ToUrl() const { | ||
| return absl::InvalidArgumentError( | ||
| "zarr2 URL syntax not supported with selected_field specified"); | ||
| } | ||
| if (open_as_void) { | ||
| return absl::InvalidArgumentError( | ||
| "zarr2 URL syntax not supported with open_as_void specified"); | ||
| } | ||
| TENSORSTORE_ASSIGN_OR_RETURN(auto base_url, store.ToUrl()); | ||
| return tensorstore::StrCat(base_url, "|", kUrlScheme, ":"); | ||
| } | ||
| @@ -451,7 +504,7 @@ Future<ArrayStorageStatistics> ZarrDriver::GetStorageStatistics( | ||
| /*chunk_shape=*/grid.chunk_shape, | ||
| /*shape=*/metadata->shape, | ||
| /*dimension_separator=*/ | ||
| GetDimensionSeparatorChar(cache->dimension_separator_), | ||
| GetDimensionSeparatorChar(cache->dimension_separator()), | ||
| staleness_bound, request.options)); | ||
| }), | ||
| std::move(promise), std::move(metadata_future)); | ||
| @@ -483,7 +536,8 @@ class ZarrDriver::OpenState : public ZarrDriver::OpenStateBase { | ||
| TENSORSTORE_ASSIGN_OR_RETURN( | ||
| auto metadata, | ||
| internal_zarr::GetNewMetadata(spec().partial_metadata, | ||
| spec().selected_field, spec().schema), | ||
| spec().selected_field, spec().schema, | ||
| spec().open_as_void), | ||
| tensorstore::MaybeAnnotateStatus( | ||
| _, "Cannot create using specified \"metadata\" and schema")); | ||
| return metadata; | ||
| @@ -496,29 +550,52 @@ class ZarrDriver::OpenState : public ZarrDriver::OpenStateBase { | ||
| internal::EncodeCacheKey( | ||
| &result, spec.store.path, | ||
| GetDimensionSeparator(spec.partial_metadata, zarr_metadata), | ||
| zarr_metadata, spec.metadata_key); | ||
| zarr_metadata, spec.metadata_key, | ||
| spec.open_as_void ? "void" : "normal"); | ||
| return result; | ||
| } | ||
| std::unique_ptr<internal_kvs_backed_chunk_driver::DataCacheBase> GetDataCache( | ||
| DataCache::Initializer&& initializer) override { | ||
| const auto& metadata = | ||
| const auto& original_metadata = | ||
| *static_cast<const ZarrMetadata*>(initializer.metadata.get()); | ||
| auto dim_sep = GetDimensionSeparator(spec().partial_metadata, original_metadata); | ||
| if (spec().open_as_void) { | ||
| // Use the cached void metadata from the original. The void metadata has | ||
| // dtype.fields containing only the void field, allowing standard | ||
| // encode/decode to work. | ||
| initializer.metadata = original_metadata.GetVoidMetadata(); | ||
| return std::make_unique<VoidDataCache>( | ||
| std::move(initializer), spec().store.path, dim_sep, | ||
| spec().metadata_key); | ||
| } | ||
| return std::make_unique<DataCache>( | ||
| std::move(initializer), spec().store.path, | ||
| GetDimensionSeparator(spec().partial_metadata, metadata), | ||
| std::move(initializer), spec().store.path, dim_sep, | ||
| spec().metadata_key); | ||
| } | ||
| Result<size_t> GetComponentIndex(const void* metadata_ptr, | ||
| OpenMode open_mode) override { | ||
| const auto& metadata = *static_cast<const ZarrMetadata*>(metadata_ptr); | ||
| // Validate partial_metadata against regular metadata | ||
| TENSORSTORE_RETURN_IF_ERROR( | ||
| ValidateMetadata(metadata, spec().partial_metadata)); | ||
| TENSORSTORE_ASSIGN_OR_RETURN( | ||
| auto field_index, GetFieldIndex(metadata.dtype, spec().selected_field)); | ||
| TENSORSTORE_RETURN_IF_ERROR( | ||
| ValidateMetadataSchema(metadata, field_index, spec().schema)); | ||
| // For void access, use component index 0 since we create a special | ||
| // component for raw byte access | ||
| size_t field_index; | ||
| if (spec().open_as_void) { | ||
| field_index = 0; | ||
| // Validate schema against void metadata, which has the synthesized void | ||
| // field that matches how the data will actually be accessed | ||
| TENSORSTORE_RETURN_IF_ERROR(ValidateMetadataSchema( | ||
| *metadata.GetVoidMetadata(), field_index, spec().schema)); | ||
| } else { | ||
| TENSORSTORE_ASSIGN_OR_RETURN( | ||
| field_index, | ||
| GetFieldIndex(metadata.dtype, spec().selected_field)); | ||
| TENSORSTORE_RETURN_IF_ERROR( | ||
| ValidateMetadataSchema(metadata, field_index, spec().schema)); | ||
| } | ||
| return field_index; | ||
| } | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.