Skip to content

Add dimension separator as a type parameter #182

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

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions src/Storage/Storage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ isemptysub(s::AbstractStore, p) = isempty(subkeys(s,p)) && isempty(subdirs(s,p))
#during auto-check of storage format when doing zopen
storageregexlist = Pair[]

include("versionedstore.jl")
include("directorystore.jl")
include("dictstore.jl")
include("s3store.jl")
Expand Down
18 changes: 16 additions & 2 deletions src/Storage/http.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
struct HTTPStore <: AbstractStore
url::String
allowed_codes::Set{Int}
HTTPStore(url, allowed_codes = Set((404,))) = new(url, allowed_codes)
end
HTTPStore(url) = HTTPStore(url,Set((404,)))

function Base.getindex(s::HTTPStore, k::String)
r = HTTP.request("GET",string(s.url,"/",k),status_exception = false,socket_type_tls=OpenSSL.SSLStream)
Expand All @@ -39,7 +39,21 @@

push!(storageregexlist,r"^https://"=>HTTPStore)
push!(storageregexlist,r"^http://"=>HTTPStore)
storefromstring(::Type{<:HTTPStore}, s,_) = ConsolidatedStore(HTTPStore(s),""),""
function storefromstring(::Type{<:HTTPStore}, s,_)
http_store = HTTPStore(s)
try
if http_store["", ".zmetadata"] !== nothing
http_store = ConsolidatedStore(http_store,"")
end
if is_zarray(http_store, "")
meta = getmetadata(http_store, "", false)
http_store = VersionedStore{meta.zarr_format, meta.dimension_separator}(http_store)

Check warning on line 50 in src/Storage/http.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/http.jl#L49-L50

Added lines #L49 - L50 were not covered by tests
end
catch err
@warn exception=err "Additional metadata was not available for HTTPStore."

Check warning on line 53 in src/Storage/http.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/http.jl#L53

Added line #L53 was not covered by tests
end
return http_store,""
end

"""
missing_chunk_return_code!(s::HTTPStore, code::Union{Int,AbstractVector{Int}})
Expand Down
135 changes: 135 additions & 0 deletions src/Storage/versionedstore.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Default Zarr version
const DV = 2

# Default Zarr separator

# Default Zarr v2 separator
const DS2 = '.'
# Default Zarr v3 separator
const DS3 = '/'

default_sep(version) = version == 2 ? DS2 :

Check warning on line 11 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L11

Added line #L11 was not covered by tests
version == 3 ? DS3 :
error("Unknown version: $version")
const DS = default_sep(DV)

# Chunk Key Encodings for Zarr v3
# A Char is the separator for the default chunk key encoding
struct V2ChunkKeyEncoding{SEP} end

# Version store differentiates between Zarr format versions
struct VersionedStore{V,SEP,STORE <: AbstractStore} <: AbstractStore
parent::STORE
end
VersionedStore(args...) = VersionedStore{DV,DS}(args...)
VersionedStore(s::VersionedStore) = s
VersionedStore{V}(args...) where V = VersionedStore{V, default_sep(V)}(args...)
VersionedStore{V}(s::VersionedStore{<:Any,S}) where {V,S} = VersionedStore{V, S}(s)
VersionedStore{<: Any, S}(args...) where S = VersionedStore{DV, S}(args...)
VersionedStore{<: Any, S}(s::VersionedStore{V}) where {V,S} = VersionedStore{V, S}(s)

Check warning on line 29 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L25-L29

Added lines #L25 - L29 were not covered by tests
function VersionedStore{V,S}(store::AbstractStore) where {V,S}
return VersionedStore{V,S,typeof(store)}(store)
end
function VersionedStore{V,S}(store::VersionedStore) where {V,S}
p = parent(store)
return VersionedStore{V,S,typeof(p)}(p)

Check warning on line 35 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L33-L35

Added lines #L33 - L35 were not covered by tests
end

Base.parent(store::VersionedStore) = store.parent

@inline citostring(i::CartesianIndex, version::Int, sep::Char=default_sep(version)) = (version == 3 ? "c$sep" : "" ) * join(reverse((i - oneunit(i)).I), sep)
@inline citostring(::CartesianIndex{0}, version::Int, sep::Char=default_sep(version)) = (version == 3 ? "c$(sep)0" : "0" )
@inline citostring(i::CartesianIndex, ::Int, ::Type{V2ChunkKeyEncoding{S}}) where S = citostring(i, 2, S)
citostring(i::CartesianIndex, s::VersionedStore{V, S}) where {V,S} = citostring(i, V, S)

Base.getindex(s::VersionedStore, p, i::CartesianIndex) = s[p, citostring(i,s)]
Base.delete!(s::VersionedStore, p, i::CartesianIndex) = delete!(s, p, citostring(i,s))
Base.setindex!(s::VersionedStore, v, p, i::CartesianIndex) = s[p, citostring(i,s)]=v

isinitialized(s::VersionedStore, p, i::CartesianIndex) = isinitialized(s,p,citostring(i, s))

"""
- [`storagesize(d::AbstractStore, p::AbstractString)`](@ref storagesize)
- [`subdirs(d::AbstractStore, p::AbstractString)`](@ref subdirs)
- [`subkeys(d::AbstractStore, p::AbstractString)`](@ref subkeys)
- [`isinitialized(d::AbstractStore, p::AbstractString)`](@ref isinitialized)
- [`storefromstring(::Type{<: AbstractStore}, s, _)`](@ref storefromstring)
- `Base.getindex(d::AbstractStore, i::AbstractString)`: return the data stored in key `i` as a Vector{UInt8}
- `Base.setindex!(d::AbstractStore, v, i::AbstractString)`: write the values in `v` to the key `i` of the given store `d`
"""

storagesize(d::VersionedStore, p::AbstractString) = storagesize(parent(d), p)
subdirs(d::VersionedStore, p::AbstractString) = subdirs(parent(d), p)
subkeys(d::VersionedStore, p::AbstractString) = subkeys(parent(d), p)
isinitialized(d::VersionedStore, p::AbstractString) = isinitialized(parent(d), p)
storefromstring(::Type{VersionedStore{<: Any, <: Any, STORE}}, s, _) where STORE = VersionedStore{DV,DS}(storefromstring(STORE, s))
storefromstring(::Type{VersionedStore{V,S}}, s, _) where {V,S} = VersionedStore{DV,DS}(storefromstring(s))
storefromstring(::Type{VersionedStore{V,S,STORE}}, s, _) where {V,S,STORE} = VersionedStore{V,S,STORE}(storefromstring(STORE, s))

Check warning on line 67 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L65-L67

Added lines #L65 - L67 were not covered by tests
Base.getindex(d::VersionedStore, i::AbstractString) = getindex(parent(d), i)
Base.setindex!(d::VersionedStore, v, i::AbstractString) = setindex!(parent(d), v, i)
Base.delete!(d::VersionedStore, i::AbstractString) = delete!(parent(d), i)


function Base.getproperty(store::VersionedStore{V,S}, sym::Symbol) where {V,S}
if sym == :dimension_separator
return S

Check warning on line 75 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L75

Added line #L75 was not covered by tests
elseif sym == :zarr_format
return V

Check warning on line 77 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L77

Added line #L77 was not covered by tests
elseif sym ∈ propertynames(getfield(store, :parent))
# Support forwarding of properties to parent
return getproperty(store.parent, sym)
else
getfield(store, sym)
end
end
function Base.propertynames(store::VersionedStore)
return (:dimension_separator, :zarr_format, fieldnames(typeof(store))..., propertynames(store.parent)...)
end


"""
Zarr.set_dimension_separator(store::VersionedStore{V}, sep::Char)::VersionedStore{V,sep}

Returns a VersionedStore of the same type with the same `zarr_format` parameter, `V`,
but with a dimension separator of `sep`. Note that this does not mutate the original store.

# Examples

```
julia> Zarr.set_dimension_separator(Zarr.VersionedStore{2, '.'}(Zarr.DictStore(), '/')) |> typeof
Zarr.VersionedStore{2, '/',Zarr.DictStore}
```

"""
function set_dimension_separator(store::VersionedStore{V}, sep::Char) where V
return VersionedStore{V,sep}(store)

Check warning on line 105 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L104-L105

Added lines #L104 - L105 were not covered by tests
end
function set_dimension_separator(store::AbstractStore, sep::Char)
return VersionedStore{<: Any,sep}(store)

Check warning on line 108 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L107-L108

Added lines #L107 - L108 were not covered by tests
end

"""
set_zarr_format(::VersionedStore{<: Any, S}, zarr_format::Int)::VersionedStore{zarr_format,S}

Returns a VersionedStore of the same type with the same `dimension_separator` parameter, `S`,
but with the specified `zarr_format` parameter. Note that this does not mutate the original store.

# Examples

```
julia> Zarr.set_zarr_format(Zarr.VersionedStore{2, '.'}(Zarr.DictStore(), 3)) |> typeof
Zarr.VersionedStore{3, '.', DictStore}
```

"""
function set_zarr_format(store::VersionedStore{<: Any, S}, zarr_format::Int) where S
return VersionedStore{zarr_format,S}(store)

Check warning on line 126 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L125-L126

Added lines #L125 - L126 were not covered by tests
end
function set_zarr_format(store::AbstractStore, zarr_format::Int)
return VersionedStore{zarr_format}(store)

Check warning on line 129 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L128-L129

Added lines #L128 - L129 were not covered by tests
end

dimension_separator(::AbstractStore) = DS

Check warning on line 132 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L132

Added line #L132 was not covered by tests
dimension_separator(::VersionedStore{<: Any,S}) where S = S
zarr_format(::AbstractStore) = DV
zarr_format(::VersionedStore{V}) where V = V

Check warning on line 135 in src/Storage/versionedstore.jl

View check run for this annotation

Codecov / codecov/patch

src/Storage/versionedstore.jl#L134-L135

Added lines #L134 - L135 were not covered by tests
26 changes: 21 additions & 5 deletions src/ZArray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,16 +311,24 @@
* `attrs=Dict()` a dict containing key-value pairs with metadata attributes associated to the array
* `writeable=true` determines if the array is opened in read-only or write mode
* `indent_json=false` determines if indents are added to format the json files `.zarray` and `.zattrs`. This makes them more readable, but increases file size.
* `dimension_separator='.'` sets how chunks are encoded. The Zarr v2 default is '.' such that the first 3D chunk would be `0.0.0`. The Zarr v3 default is `/`.
"""
function zcreate(::Type{T}, dims::Integer...;
name="",
path=nothing,
dimension_separator='.',
kwargs...
) where T

if dimension_separator isa AbstractString
# Convert AbstractString to Char
dimension_separator = only(dimension_separator)

Check warning on line 325 in src/ZArray.jl

View check run for this annotation

Codecov / codecov/patch

src/ZArray.jl#L325

Added line #L325 was not covered by tests
end

if path===nothing
store = DictStore()
store = VersionedStore{DV, dimension_separator}(DictStore())
else
store = DirectoryStore(joinpath(path,name))
store = VersionedStore{DV, dimension_separator}(DirectoryStore(joinpath(path,name)))
end
zcreate(T, store, dims...; kwargs...)
end
Expand All @@ -335,14 +343,22 @@
filters = filterfromtype(T),
attrs=Dict(),
writeable=true,
indent_json=false
) where T
indent_json=false,
dimension_separator=nothing
) where {T}

if isnothing(dimension_separator)
dimension_separator = Zarr.dimension_separator(storage)
elseif dimension_separator != Zarr.dimension_separator(storage)
error("The dimension separator keyword value, $dimension_separator,

Check warning on line 353 in src/ZArray.jl

View check run for this annotation

Codecov / codecov/patch

src/ZArray.jl#L352-L353

Added lines #L352 - L353 were not covered by tests
must agree with the dimension separator type parameter, $(Zarr.dimension_separator(storage))")
end

length(dims) == length(chunks) || throw(DimensionMismatch("Dims must have the same length as chunks"))
N = length(dims)
C = typeof(compressor)
T2 = (fill_value === nothing || !fill_as_missing) ? T : Union{T,Missing}
metadata = Metadata{T2, N, C, typeof(filters)}(
metadata = Metadata{T2, N, C, typeof(filters), dimension_separator}(
2,
dims,
chunks,
Expand Down
28 changes: 22 additions & 6 deletions src/ZGroup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@

for d in subdirs(s,path)
dshort = split(d,'/')[end]
m = zopen_noerr(s,mode,path=_concatpath(path,dshort),fill_as_missing=fill_as_missing)
if isa(m, ZArray)
subpath = _concatpath(path,dshort)
if is_zarray(s, subpath)
meta = getmetadata(s, subpath, false)
if dimension_separator(s) != meta.dimension_separator
s = set_dimension_separator(s, meta.dimension_separator)

Check warning on line 27 in src/ZGroup.jl

View check run for this annotation

Codecov / codecov/patch

src/ZGroup.jl#L27

Added line #L27 was not covered by tests
end
m = zopen_noerr(s,mode,path=_concatpath(path,dshort),fill_as_missing=fill_as_missing)
arrays[dshort] = m
elseif isa(m, ZGroup)
elseif is_zgroup(s, subpath)
m = zopen_noerr(s,mode,path=_concatpath(path,dshort),fill_as_missing=fill_as_missing)

Check warning on line 32 in src/ZGroup.jl

View check run for this annotation

Codecov / codecov/patch

src/ZGroup.jl#L31-L32

Added lines #L31 - L32 were not covered by tests
groups[dshort] = m
end
end
Expand All @@ -39,7 +45,7 @@
the path or store does not point to a valid zarr array or group, but nothing
is returned instead.
"""
function zopen_noerr(s::AbstractStore, mode="r";
function zopen_noerr(s::AbstractStore, mode="r";
consolidated = false,
path="",
lru = 0,
Expand Down Expand Up @@ -116,8 +122,18 @@
return storefromstring(t,s,create)
end
end
if create || isdir(s)
return DirectoryStore(s), ""
if create
return VersionedStore(DirectoryStore(s)), ""
elseif isdir(s)
# parse metadata to determine store kind
temp_store = DirectoryStore(s)
if is_zarray(temp_store, "")
meta = getmetadata(temp_store, "", false)
store = VersionedStore{meta.zarr_format, meta.dimension_separator}(temp_store)
else
store = VersionedStore(temp_store)
end
return store, ""
else
throw(ArgumentError("Path $s is not a directory."))
end
Expand Down
Loading
Loading