@@ -54,6 +54,7 @@ private class DelayedVarMaker {
5454 private RandomAccessDirectoryItem var ;
5555 private ZArray zarray ;
5656 private Map <Integer , Long > initializedChunks ; // track any uninitialized chunks for var
57+ private Map <Integer , Long > chunkStarts ; // byte offset of each chunk within the store, keyed by chunk index
5758 private List <Attribute > attrs ; // list of variable attributes
5859 private long dataOffset ; // byte position where data starts
5960
@@ -65,6 +66,7 @@ void setVar(RandomAccessDirectoryItem var) {
6566 this .var = var ;
6667 this .attrs = null ;
6768 this .initializedChunks = new HashMap <>();
69+ this .chunkStarts = new HashMap <>();
6870 this .dataOffset = -1 ;
6971 if (var != null ) {
7072 try {
@@ -104,6 +106,11 @@ void processItem(RandomAccessDirectoryItem item) {
104106 this .var = null ; // skip rest of var is unrecognized files found
105107 }
106108 this .initializedChunks .put (index , item .length ());
109+ // Record the actual byte offset of this chunk within the store, keyed by its numeric chunk index.
110+ // This avoids any dependency on the order in which the store lists files (which is lexicographic
111+ // and would otherwise place e.g. chunk 0.10 before chunk 0.2, which is the root cause of
112+ // https://github.com/Unidata/netcdf-java/issues/1542)
113+ this .chunkStarts .put (index , item .startIndex ());
107114 // if data offset is uninitialized, set here
108115 if (this .dataOffset < 0 ) {
109116 this .dataOffset = item .startIndex ();
@@ -115,7 +122,7 @@ void makeVar() {
115122 return ; // do nothing if no variable is in progress
116123 }
117124 try {
118- makeVariable (var , dataOffset , zarray , initializedChunks , attrs );
125+ makeVariable (var , dataOffset , zarray , initializedChunks , chunkStarts , attrs );
119126 } catch (ZarrFormatException ex ) {
120127 logger .error (ex .getMessage ());
121128 }
@@ -200,7 +207,8 @@ private void makeGroup(RandomAccessDirectoryItem item, List<Attribute> attrs) {
200207 }
201208
202209 private void makeVariable (RandomAccessDirectoryItem item , long dataOffset , ZArray zarray ,
203- Map <Integer , Long > initializedChunks , List <Attribute > attrs ) throws ZarrFormatException {
210+ Map <Integer , Long > initializedChunks , Map <Integer , Long > chunkStarts , List <Attribute > attrs )
211+ throws ZarrFormatException {
204212 // make new Variable
205213 Variable .Builder <?> var = Variable .builder ();
206214 String location = ZarrUtils .trimLocation (item .getLocation ());
@@ -303,7 +311,7 @@ private void makeVariable(RandomAccessDirectoryItem item, long dataOffset, ZArra
303311
304312 // create VInfo
305313 VInfo vinfo = new VInfo (chunks , zarray .getFillValue (), zarray .getCompressor (), zarray .getByteOrder (),
306- zarray .getOrder (), zarray .getSeparator (), zarray .getFilters (), dataOffset , initializedChunks ,
314+ zarray .getOrder (), zarray .getSeparator (), zarray .getFilters (), dataOffset , initializedChunks , chunkStarts ,
307315 zarray .getElementSize (), zarray .isUnicodeString ());
308316 var .setSPobject (vinfo );
309317
@@ -389,7 +397,7 @@ private static int getChunkIndex(RandomAccessDirectoryItem item, ZArray zarray)
389397 int [] shape = zarray .getShape ();
390398 int [] chunkSize = zarray .getChunks ();
391399 for (int i = 0 ; i < nDims ; i ++) {
392- nChunks [i ] = (int ) Math .ceil (shape [i ] / chunkSize [i ]);
400+ nChunks [i ] = (int ) Math .ceil (( double ) shape [i ] / chunkSize [i ]);
393401 }
394402 return ZarrUtils .subscriptsToIndex (subs , nChunks );
395403 } else {
@@ -422,12 +430,13 @@ class VInfo {
422430 private final List <Filter > filters ;
423431 private final long offset ;
424432 private final Map <Integer , Long > initializedChunks ;
433+ private final Map <Integer , Long > chunkStarts ;
425434 private final int elementSize ;
426435 private final boolean unicodeString ;
427436
428437 VInfo (int [] chunks , Object fillValue , Filter compressor , ByteOrder byteOrder , ZArray .Order order , String separator ,
429- List <Filter > filters , long offset , Map <Integer , Long > initializedChunks , int elementSize ,
430- boolean unicodeString ) {
438+ List <Filter > filters , long offset , Map <Integer , Long > initializedChunks , Map < Integer , Long > chunkStarts ,
439+ int elementSize , boolean unicodeString ) {
431440 this .chunks = chunks ;
432441 this .fillValue = fillValue ;
433442 this .byteOrder = byteOrder ;
@@ -437,6 +446,7 @@ class VInfo {
437446 this .filters = filters ;
438447 this .offset = offset ;
439448 this .initializedChunks = initializedChunks ;
449+ this .chunkStarts = chunkStarts ;
440450 this .elementSize = elementSize ;
441451 this .unicodeString = unicodeString ;
442452 }
@@ -477,6 +487,10 @@ public Map<Integer, Long> getInitializedChunks() {
477487 return this .initializedChunks ;
478488 }
479489
490+ public Map <Integer , Long > getChunkStarts () {
491+ return this .chunkStarts ;
492+ }
493+
480494 int getElementSize () {
481495 return this .elementSize ;
482496 }
0 commit comments