11import re
22import warnings
33from abc import ABC , abstractmethod
4+ from collections .abc import Iterable
45from dataclasses import dataclass
56from functools import lru_cache
67from os .path import relpath
1112 Dict ,
1213 Iterator ,
1314 List ,
15+ Literal ,
1416 Optional ,
1517 Tuple ,
1618 Type ,
@@ -223,7 +225,7 @@ def _list_files(self) -> Iterator[Path]:
223225 for filename in self ._wkw_dataset .list_files ()
224226 )
225227
226- def list_bounding_boxes (self ) -> Iterator [NDBoundingBox ]:
228+ def list_bounding_boxes (self ) -> Iterator [BoundingBox ]:
227229 def _extract_num (s : str ) -> int :
228230 match = re .search ("[0-9]+" , s )
229231 assert match is not None
@@ -592,9 +594,62 @@ def write(self, bbox: NDBoundingBox, data: np.ndarray) -> None:
592594 )
593595 array [requested_domain ].write (data ).result ()
594596
595- def list_bounding_boxes (self ) -> Iterator [ BoundingBox ]:
597+ def _chunk_key_encoding (self ) -> tuple [ Literal [ "default" , "v2" ], Literal [ "/" , "." ] ]:
596598 raise NotImplementedError
597599
600+ def _list_bounding_boxes (
601+ self , kvstore : Any , shard_shape : Vec3Int , shape : VecInt
602+ ) -> Iterator [BoundingBox ]:
603+ _type , separator = self ._chunk_key_encoding ()
604+
605+ def _try_parse_ints (vec : Iterable [Any ]) -> Optional [list [int ]]:
606+ output = []
607+ for value in vec :
608+ try :
609+ output .append (int (value ))
610+ except ValueError : # noqa: PERF203
611+ return None
612+ return output
613+
614+ keys = kvstore .list ().result ()
615+ for key in keys :
616+ key_parts = key .decode ("utf-8" ).split (separator )
617+ if _type == "default" :
618+ if key_parts [0 ] != "c" :
619+ continue
620+ key_parts = key_parts [1 :]
621+ if len (key_parts ) != self ._array .ndim :
622+ continue
623+ chunk_coords_list = _try_parse_ints (key_parts )
624+ if chunk_coords_list is None :
625+ continue
626+
627+ if shape .axes [0 ] == "c" :
628+ chunk_coords = Vec3Int (chunk_coords_list [1 :])
629+ else :
630+ chunk_coords = Vec3Int (chunk_coords_list )
631+
632+ yield BoundingBox (chunk_coords * shard_shape , shard_shape )
633+
634+ def list_bounding_boxes (self ) -> Iterator [BoundingBox ]:
635+ kvstore = self ._array .kvstore
636+
637+ if kvstore .spec ().to_json ()["driver" ] == "s3" :
638+ raise NotImplementedError (
639+ "list_bounding_boxes() is not supported for s3 arrays."
640+ )
641+
642+ _ , _ , shard_shape , _ , shape = self ._get_array_dimensions (self ._array )
643+
644+ if shape .axes != ("c" , "x" , "y" , "z" ) and shape .axes != ("x" , "y" , "z" ):
645+ raise NotImplementedError (
646+ "list_bounding_boxes() is not supported for non 3-D arrays."
647+ )
648+
649+ # This needs to be a separate function because we need the NotImplementedError
650+ # to be raised immediately and not part of the iterator.
651+ return self ._list_bounding_boxes (kvstore , shard_shape , shape )
652+
598653 def close (self ) -> None :
599654 if self ._cached_array is not None :
600655 self ._cached_array = None
@@ -724,12 +779,10 @@ def create(cls, path: Path, array_info: ArrayInfo) -> "Zarr3Array":
724779 "configuration" : {"endian" : "little" },
725780 },
726781 {
727- "name" : "blosc " ,
782+ "name" : "zstd " ,
728783 "configuration" : {
729- "cname" : "zstd" ,
730- "clevel" : 5 ,
731- "shuffle" : "shuffle" ,
732- "typesize" : array_info .voxel_type .itemsize ,
784+ "level" : 5 ,
785+ "checksum" : True ,
733786 },
734787 },
735788 ]
@@ -761,6 +814,17 @@ def create(cls, path: Path, array_info: ArrayInfo) -> "Zarr3Array":
761814 ).result ()
762815 return cls (path , _array )
763816
817+ def _chunk_key_encoding (self ) -> tuple [Literal ["default" , "v2" ], Literal ["/" , "." ]]:
818+ metadata = self ._array .spec ().to_json ()["metadata" ]
819+ chunk_key_encoding = metadata ["chunk_key_encoding" ]
820+ _type = chunk_key_encoding ["name" ]
821+ separator = chunk_key_encoding .get ("configuration" , {}).get (
822+ "separator" , "/" if _type == "default" else "."
823+ )
824+ assert _type in ["default" , "v2" ]
825+ assert separator in ["/" , "." ]
826+ return _type , separator
827+
764828
765829class Zarr2Array (TensorStoreArray ):
766830 data_format = DataFormat .Zarr
@@ -812,10 +876,8 @@ def create(cls, path: Path, array_info: ArrayInfo) -> "Zarr2Array":
812876 "order" : "F" ,
813877 "compressor" : (
814878 {
815- "id" : "blosc" ,
816- "cname" : "zstd" ,
817- "clevel" : 5 ,
818- "shuffle" : 1 ,
879+ "id" : "zstd" ,
880+ "level" : 5 ,
819881 }
820882 if array_info .compression_mode
821883 else None
@@ -827,3 +889,9 @@ def create(cls, path: Path, array_info: ArrayInfo) -> "Zarr2Array":
827889 }
828890 ).result ()
829891 return cls (path , _array )
892+
893+ def _chunk_key_encoding (self ) -> tuple [Literal ["default" , "v2" ], Literal ["/" , "." ]]:
894+ metadata = self ._array .spec ().to_json ()["metadata" ]
895+ separator = metadata .get ("dimension_separator" , "." )
896+ assert separator in ["/" , "." ]
897+ return "v2" , separator
0 commit comments