/src/netcdf-c/libnczarr/zwalk.c
Line | Count | Source |
1 | | /********************************************************************* |
2 | | * Copyright 2018, UCAR/Unidata |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | *********************************************************************/ |
5 | | #include "zincludes.h" |
6 | | |
7 | | #define WDEBUG |
8 | | #undef DFALTOPTIMIZE |
9 | | |
10 | | #define TRANSFERN |
11 | | |
12 | | static int initialized = 0; |
13 | | |
14 | | static unsigned int optimize = 0; |
15 | | |
16 | | extern int NCZ_buildchunkkey(size_t R, const size64_t* chunkindices, char** keyp); |
17 | | |
18 | | /* 0 => no debug */ |
19 | | static unsigned int wdebug = 1; |
20 | | |
21 | | /* Forward */ |
22 | | static int NCZ_walk(NCZProjection** projv, NCZOdometer* chunkodom, NCZOdometer* slpodom, NCZOdometer* memodom, const struct Common* common, void* chunkdata); |
23 | | static int rangecount(NCZChunkRange range); |
24 | | static int readfromcache(void* source, size64_t* chunkindices, void** chunkdata); |
25 | | static int iswholechunk(struct Common* common,NCZSlice*); |
26 | | static int wholechunk_indices(struct Common* common, NCZSlice* slices, size64_t* chunkindices); |
27 | | #ifdef TRANSFERN |
28 | | static int transfern(const struct Common* common, unsigned char* slpptr, unsigned char* memptr, size_t avail, size_t slpstride, void* chunkdata); |
29 | | #endif |
30 | | |
31 | | #if 0 |
32 | | static const char* |
33 | | astype(int typesize, void* ptr) |
34 | | { |
35 | | switch(typesize) { |
36 | | case 4: { |
37 | | static char is[8]; |
38 | | snprintf(is,sizeof(is),"%u",*((unsigned int*)ptr)); |
39 | | return is; |
40 | | } break; |
41 | | default: break; |
42 | | } |
43 | | return "?"; |
44 | | } |
45 | | #endif |
46 | | |
47 | | /**************************************************/ |
48 | | int |
49 | | ncz_chunking_init(void) |
50 | 0 | { |
51 | 0 | const char* val = NULL; |
52 | | #ifdef DFALTOPTIMIZE |
53 | | val = getenv("NCZ_NOOPTIMIZATION"); |
54 | | optimize = (val == NULL ? 1 : 0); |
55 | | #else |
56 | 0 | optimize = 0; |
57 | 0 | #endif |
58 | 0 | val = getenv("NCZ_WDEBUG"); |
59 | 0 | wdebug = (val == NULL ? 0 : atoi(val)); |
60 | 0 | #ifdef WDEBUG |
61 | 0 | if(wdebug > 0) fprintf(stderr,"wdebug=%u\n",wdebug); |
62 | 0 | #endif |
63 | 0 | initialized = 1; |
64 | 0 | return NC_NOERR; |
65 | 0 | } |
66 | | |
67 | | /**************************************************/ |
68 | | |
69 | | /** |
70 | | Goal: Given the slices being applied to the variable, create |
71 | | and walk all possible combinations of projection vectors that |
72 | | can be evaluated to provide the output data. |
73 | | Note that we do not actually pass NCZSlice but rather |
74 | | (start,count,stride) vectors. |
75 | | |
76 | | @param var Controlling variable |
77 | | @param usreading reading vs writing |
78 | | @param start start vector |
79 | | @param stop stop vector |
80 | | @param stride stride vector |
81 | | @param memory target or source of data |
82 | | @param typecode nc_type of type being written |
83 | | @param walkfcn fcn parameter to actually transfer data |
84 | | */ |
85 | | |
86 | | int |
87 | | NCZ_transferslice(NC_VAR_INFO_T* var, int reading, |
88 | | size64_t* start, size64_t* count, size64_t* stride, |
89 | | void* memory, nc_type typecode) |
90 | 0 | { |
91 | 0 | int r,stat = NC_NOERR; |
92 | 0 | size64_t dimlens[NC_MAX_VAR_DIMS]; |
93 | 0 | unsigned char isunlimited[NC_MAX_VAR_DIMS]; |
94 | 0 | size64_t chunklens[NC_MAX_VAR_DIMS]; |
95 | 0 | size64_t memshape[NC_MAX_VAR_DIMS]; |
96 | 0 | NCZSlice slices[NC_MAX_VAR_DIMS]; |
97 | 0 | struct Common common; |
98 | 0 | NCZ_FILE_INFO_T* zfile = NULL; |
99 | 0 | NCZ_VAR_INFO_T* zvar = NULL; |
100 | 0 | size_t typesize; |
101 | |
|
102 | 0 | if(!initialized) ncz_chunking_init(); |
103 | |
|
104 | 0 | if((stat = NC4_inq_atomic_type(typecode, NULL, &typesize))) goto done; |
105 | | |
106 | 0 | if(wdebug >= 1) { |
107 | 0 | size64_t stopvec[NC_MAX_VAR_DIMS]; |
108 | 0 | for(r=0;r<var->ndims;r++) stopvec[r] = start[r]+(count[r]*stride[r]); |
109 | 0 | fprintf(stderr,"var: name=%s",var->hdr.name); |
110 | 0 | fprintf(stderr," start=%s",nczprint_vector(var->ndims,start)); |
111 | 0 | fprintf(stderr," count=%s",nczprint_vector(var->ndims,count)); |
112 | 0 | fprintf(stderr," stop=%s",nczprint_vector(var->ndims,stopvec)); |
113 | 0 | fprintf(stderr," stride=%s\n",nczprint_vector(var->ndims,stride)); |
114 | 0 | } |
115 | | |
116 | | /* Fill in common */ |
117 | 0 | memset(&common,0,sizeof(common)); |
118 | 0 | common.var = var; |
119 | 0 | common.file = (var->container)->nc4_info; |
120 | 0 | zfile = common.file->format_file_info; |
121 | 0 | zvar = common.var->format_var_info; |
122 | |
|
123 | 0 | common.reading = reading; |
124 | 0 | common.memory = memory; |
125 | 0 | common.typesize = typesize; |
126 | 0 | common.cache = zvar->cache; |
127 | | |
128 | | /* We need to take scalar into account */ |
129 | 0 | common.rank = var->ndims; |
130 | 0 | common.scalar = zvar->scalar; |
131 | 0 | common.swap = (zfile->native_endianness == var->endianness ? 0 : 1); |
132 | |
|
133 | 0 | common.chunkcount = 1; |
134 | 0 | if(common.scalar) { |
135 | 0 | dimlens[0] = 1; |
136 | 0 | isunlimited[0] = 0; |
137 | 0 | chunklens[0] = 1; |
138 | 0 | slices[0].start = 0; |
139 | 0 | slices[0].stride = 1; |
140 | 0 | slices[0].stop = 0; |
141 | 0 | slices[0].len = 1; |
142 | 0 | common.chunkcount = 1; |
143 | 0 | memshape[0] = 1; |
144 | 0 | } else for(r=0;r<common.rank;r++) { |
145 | 0 | dimlens[r] = var->dim[r]->len; |
146 | 0 | isunlimited[r] = var->dim[r]->unlimited; |
147 | 0 | chunklens[r] = var->chunksizes[r]; |
148 | 0 | slices[r].start = start[r]; |
149 | 0 | slices[r].stride = stride[r]; |
150 | 0 | slices[r].stop = start[r]+(count[r]*stride[r]); |
151 | 0 | if(!isunlimited[r]) |
152 | 0 | slices[r].stop = minimum(slices[r].stop,dimlens[r]); |
153 | 0 | slices[r].len = var->dim[r]->len; |
154 | 0 | common.chunkcount *= chunklens[r]; |
155 | 0 | memshape[r] = count[r]; |
156 | 0 | } |
157 | |
|
158 | 0 | if(wdebug >= 1) { |
159 | 0 | fprintf(stderr,"\trank=%d",common.rank); |
160 | 0 | if(!common.scalar) { |
161 | 0 | fprintf(stderr," dimlens=%s",nczprint_vector(common.rank,dimlens)); |
162 | 0 | fprintf(stderr," chunklens=%s",nczprint_vector(common.rank,chunklens)); |
163 | 0 | fprintf(stderr," memshape=%s",nczprint_vector(common.rank,memshape)); |
164 | 0 | } |
165 | 0 | fprintf(stderr,"\n"); |
166 | 0 | } |
167 | | |
168 | | /* Transfer data */ |
169 | 0 | memcpy(common.dimlens,dimlens,sizeof(size64_t)*common.rank); |
170 | 0 | memcpy(common.isunlimited,isunlimited,sizeof(unsigned char)*common.rank); |
171 | 0 | memcpy(common.chunklens,chunklens,sizeof(size64_t)*common.rank); |
172 | 0 | memcpy(common.memshape,memshape,sizeof(size64_t)*common.rank); |
173 | |
|
174 | 0 | common.reader.source = ((NCZ_VAR_INFO_T*)(var->format_var_info))->cache; |
175 | 0 | common.reader.read = readfromcache; |
176 | |
|
177 | 0 | if(common.scalar) { |
178 | 0 | if((stat = NCZ_transferscalar(&common))) goto done; |
179 | 0 | } |
180 | 0 | else { |
181 | 0 | if((stat = NCZ_transfer(&common, slices))) goto done; |
182 | 0 | } |
183 | 0 | done: |
184 | 0 | NCZ_clearcommon(&common); |
185 | 0 | return stat; |
186 | 0 | } |
187 | | |
188 | | /* |
189 | | Walk the possible projections. |
190 | | Broken out so we can use it for unit testing |
191 | | @param common common parameters |
192 | | @param slices |
193 | | */ |
194 | | int |
195 | | NCZ_transfer(struct Common* common, NCZSlice* slices) |
196 | 0 | { |
197 | 0 | int stat = NC_NOERR; |
198 | 0 | NCZOdometer* chunkodom = NULL; |
199 | 0 | NCZOdometer* slpodom = NULL; |
200 | 0 | NCZOdometer* memodom = NULL; |
201 | 0 | void* chunkdata = NULL; |
202 | 0 | int wholechunk = 0; |
203 | | |
204 | | /* |
205 | | We will need three sets of odometers. |
206 | | 1. Chunk odometer to walk the chunk ranges to get all possible |
207 | | combinations of chunkranges over all dimensions. |
208 | | 2. For each chunk odometer set of indices, we need a projection |
209 | | odometer that walks the set of projection slices for a given |
210 | | set of chunk ranges over all dimensions. Note that this is where |
211 | | we detect unlimited extensions. |
212 | | 3. A memory odometer that walks the memory data to specify |
213 | | the locations in memory for read/write |
214 | | */ |
215 | |
|
216 | 0 | if(wdebug >= 2) |
217 | 0 | fprintf(stderr,"slices=%s\n",nczprint_slices(common->rank,slices)); |
218 | |
|
219 | 0 | if((stat = NCZ_projectslices(common, slices, &chunkodom))) |
220 | 0 | goto done; |
221 | | |
222 | 0 | if(wdebug >= 4) { |
223 | 0 | fprintf(stderr,"allprojections:\n%s",nczprint_allsliceprojections(common->rank,common->allprojections)); fflush(stderr); |
224 | 0 | } |
225 | |
|
226 | 0 | wholechunk = iswholechunk(common,slices); |
227 | |
|
228 | 0 | if(wholechunk) { |
229 | | /* Implement a whole chunk read optimization; this is a rare occurrence |
230 | | where the the slices cover all of a single chunk. |
231 | | */ |
232 | 0 | size64_t chunkindices[NC_MAX_VAR_DIMS]; |
233 | 0 | unsigned char* memptr; |
234 | 0 | unsigned char* slpptr; |
235 | | |
236 | | /* Which chunk are we getting? */ |
237 | 0 | if((stat=wholechunk_indices(common,slices,chunkindices))) goto done; |
238 | 0 | if(wdebug >= 1) |
239 | 0 | fprintf(stderr,"case: wholechunk: chunkindices: %s\n",nczprint_vector(common->rank,chunkindices)); |
240 | | /* Read the chunk; handles fixed vs char* strings*/ |
241 | 0 | switch ((stat = common->reader.read(common->reader.source, chunkindices, &chunkdata))) { |
242 | 0 | case NC_EEMPTY: /* cache created the chunk */ |
243 | 0 | break; |
244 | 0 | case NC_NOERR: break; |
245 | 0 | default: goto done; |
246 | 0 | } |
247 | | /* Figure out memory address */ |
248 | 0 | memptr = ((unsigned char*)common->memory); |
249 | 0 | slpptr = ((unsigned char*)chunkdata); |
250 | 0 | #ifdef TRANSFERN |
251 | 0 | transfern(common,slpptr,memptr,common->chunkcount,1,chunkdata); |
252 | 0 | if(!common->reading) { |
253 | 0 | if((stat=NCZ_chunk_cache_modify(common->cache, chunkindices))) goto done; |
254 | 0 | } |
255 | | #else |
256 | | if(common->reading) { |
257 | | if((stat=NCZ_copy_data(common->file,common->var,slpptr,common->chunkcount,!ZCLEAR,memptr))) goto done; |
258 | | } else { |
259 | | if((stat=NCZ_copy_data(common->file,common->var,memptr,common->chunkcount,ZCLEAR,slpptr))) goto done; |
260 | | |
261 | | } |
262 | | #endif |
263 | | |
264 | | #ifdef UTTEST |
265 | | if(zutest && zutest->tests & UTEST_WHOLECHUNK) |
266 | | zutest->print(UTEST_WHOLECHUNK, common, chunkindices); |
267 | | #endif |
268 | 0 | goto done; |
269 | 0 | } |
270 | | |
271 | | /* iterate over the odometer: all combination of chunk |
272 | | indices in the projections */ |
273 | 0 | for(;nczodom_more(chunkodom);) { |
274 | 0 | int r; |
275 | 0 | size64_t* chunkindices = NULL; |
276 | 0 | NCZSlice slpslices[NC_MAX_VAR_DIMS]; |
277 | 0 | NCZSlice memslices[NC_MAX_VAR_DIMS]; |
278 | 0 | NCZProjection* proj[NC_MAX_VAR_DIMS]; |
279 | 0 | size64_t shape[NC_MAX_VAR_DIMS]; |
280 | |
|
281 | 0 | chunkindices = nczodom_indices(chunkodom); |
282 | 0 | if(wdebug >= 1) |
283 | 0 | fprintf(stderr,"chunkindices: %s\n",nczprint_vector(common->rank,chunkindices)); |
284 | |
|
285 | 0 | for(r=0;r<common->rank;r++) { |
286 | 0 | NCZSliceProjections* slp = &common->allprojections[r]; |
287 | 0 | NCZProjection* projlist = slp->projections; |
288 | 0 | size64_t indexr = chunkindices[r]; |
289 | | /* use chunkindices[r] to find the corresponding projection slice */ |
290 | | /* We must take into account that the chunkindex of projlist[r] |
291 | | may be greater than zero */ |
292 | | /* note the 2 level indexing */ |
293 | 0 | indexr -= slp->range.start; |
294 | 0 | NCZProjection* pr = &projlist[indexr]; |
295 | 0 | proj[r] = pr; |
296 | 0 | } |
297 | |
|
298 | 0 | if(wdebug > 0) { |
299 | 0 | fprintf(stderr,"Selected projections:\n"); |
300 | 0 | for(r=0;r<common->rank;r++) { |
301 | 0 | fprintf(stderr,"\t[%d] %s\n",r,nczprint_projection(*proj[r])); |
302 | 0 | shape[r] = proj[r]->iocount; |
303 | 0 | } |
304 | 0 | fprintf(stderr,"\tshape=%s\n",nczprint_vector(common->rank,shape)); |
305 | 0 | } |
306 | | |
307 | | /* See if any of the projections is a skip; if so, then move to the next chunk indices */ |
308 | 0 | for(r=0;r<common->rank;r++) { |
309 | 0 | if(proj[r]->skip) goto next; |
310 | 0 | } |
311 | | |
312 | 0 | for(r=0;r<common->rank;r++) { |
313 | 0 | slpslices[r] = proj[r]->chunkslice; |
314 | 0 | memslices[r] = proj[r]->memslice; |
315 | 0 | } |
316 | | #ifdef UTTEST |
317 | | if(zutest && zutest->tests & UTEST_TRANSFER) |
318 | | zutest->print(UTEST_TRANSFER, common, chunkodom, slpslices, memslices); |
319 | | #endif |
320 | | |
321 | | /* Read from cache */ |
322 | 0 | stat = common->reader.read(common->reader.source, chunkindices, &chunkdata); |
323 | 0 | switch (stat) { |
324 | 0 | case NC_EEMPTY: /* cache created the chunk */ |
325 | 0 | break; |
326 | 0 | case NC_NOERR: break; |
327 | 0 | default: goto done; |
328 | 0 | } |
329 | | |
330 | 0 | slpodom = nczodom_fromslices(common->rank,slpslices); |
331 | 0 | memodom = nczodom_fromslices(common->rank,memslices); |
332 | |
|
333 | 0 | { /* walk with odometer */ |
334 | 0 | if(wdebug >= 1) |
335 | 0 | fprintf(stderr,"case: odometer:\n"); |
336 | | /* This is the key action: walk this set of slices and transfer data */ |
337 | 0 | if((stat = NCZ_walk(proj,chunkodom,slpodom,memodom,common,chunkdata))) goto done; |
338 | 0 | if(!common->reading) { |
339 | 0 | if((stat=NCZ_chunk_cache_modify(common->cache, chunkindices))) goto done; |
340 | 0 | } |
341 | 0 | } |
342 | 0 | next: |
343 | 0 | nczodom_free(slpodom); slpodom = NULL; |
344 | 0 | nczodom_free(memodom); memodom = NULL; |
345 | 0 | nczodom_next(chunkodom); |
346 | 0 | } |
347 | 0 | done: |
348 | 0 | nczodom_free(slpodom); |
349 | 0 | nczodom_free(memodom); |
350 | 0 | nczodom_free(chunkodom); |
351 | 0 | return stat; |
352 | 0 | } |
353 | | |
354 | | #ifdef WDEBUG |
355 | | static void |
356 | | wdebug2(const struct Common* common, unsigned char* slpptr, unsigned char* memptr, size_t avail, size_t stride, void* chunkdata) |
357 | 0 | { |
358 | 0 | unsigned char* slpbase = chunkdata; |
359 | 0 | unsigned char* membase = common->memory; |
360 | 0 | unsigned slpoff = (unsigned)(slpptr - slpbase); |
361 | 0 | unsigned memoff = (unsigned)(memptr - membase); |
362 | 0 | unsigned slpidx = slpoff / common->typesize; |
363 | 0 | unsigned memidx = memoff / common->typesize; |
364 | 0 | unsigned value; |
365 | |
|
366 | 0 | fprintf(stderr,"wdebug2: %s: [%u/%d] %u->%u", |
367 | 0 | common->reading?"read":"write", |
368 | 0 | (unsigned)avail, |
369 | 0 | (unsigned)stride, |
370 | 0 | (unsigned)(common->reading?slpidx:memidx), |
371 | 0 | (unsigned)(common->reading?memidx:slpidx) |
372 | 0 | ); |
373 | 0 | if(common->reading) |
374 | 0 | value = ((unsigned*)slpptr)[0]; |
375 | 0 | else |
376 | 0 | value = ((unsigned*)memptr)[0]; |
377 | 0 | fprintf(stderr,"; [%u]=%u",(unsigned)(common->reading?slpidx:memidx),value); |
378 | |
|
379 | 0 | fprintf(stderr,"\n"); |
380 | 0 | } |
381 | | #else |
382 | | #define wdebug2(common,slpptr,memptr,avail,stride,chunkdata) |
383 | | #endif |
384 | | |
385 | | /* |
386 | | Walk a set of slices and transfer data. |
387 | | |
388 | | @param projv |
389 | | @param chunkodom |
390 | | @param slpodom |
391 | | @param memodom |
392 | | @param common |
393 | | @param chunkdata |
394 | | @return NC_NOERR |
395 | | */ |
396 | | static int |
397 | | NCZ_walk(NCZProjection** projv, NCZOdometer* chunkodom, NCZOdometer* slpodom, NCZOdometer* memodom, const struct Common* common, void* chunkdata) |
398 | 0 | { |
399 | 0 | int stat = NC_NOERR; |
400 | |
|
401 | 0 | for(;;) { |
402 | 0 | size64_t slpoffset = 0; |
403 | 0 | size64_t memoffset = 0; |
404 | 0 | size64_t slpavail = 0; |
405 | 0 | size64_t memavail = 0; |
406 | 0 | size64_t laststride = 0; |
407 | 0 | unsigned char* memptr0 = NULL; |
408 | 0 | unsigned char* slpptr0 = NULL; |
409 | | |
410 | 0 | if(!nczodom_more(slpodom)) break; |
411 | | |
412 | 0 | if(wdebug >= 3) { |
413 | 0 | fprintf(stderr,"xx.slp: odom: %s\n",nczprint_odom(slpodom)); |
414 | 0 | fprintf(stderr,"xx.mem: odom: %s\n",nczprint_odom(memodom)); |
415 | 0 | } |
416 | | |
417 | | /* Convert the indices to a linear offset WRT to chunk indices */ |
418 | 0 | slpoffset = nczodom_offset(slpodom); |
419 | 0 | memoffset = nczodom_offset(memodom); |
420 | | |
421 | | /* transfer data between these addresses */ |
422 | 0 | memptr0 = ((unsigned char*)common->memory)+(memoffset * common->typesize); |
423 | 0 | slpptr0 = ((unsigned char*)chunkdata)+(slpoffset * common->typesize); |
424 | |
|
425 | 0 | LOG((1,"%s: slpptr0=%p memptr0=%p slpoffset=%llu memoffset=%lld",__func__,slpptr0,memptr0,slpoffset,memoffset)); |
426 | | #ifdef UTTEST |
427 | | if(zutest && zutest->tests & UTEST_WALK) |
428 | | zutest->print(UTEST_WALK, common, chunkodom, slpodom, memodom); |
429 | | #endif |
430 | | /* See if we can transfer multiple values at one shot */ |
431 | 0 | laststride = slpodom->stride[common->rank-1]; |
432 | 0 | if(laststride == 1) { |
433 | 0 | slpavail = nczodom_avail(slpodom); /* How much can we read? */ |
434 | 0 | memavail = nczodom_avail(memodom); |
435 | 0 | assert(memavail == slpavail); |
436 | 0 | nczodom_skipavail(slpodom); |
437 | 0 | nczodom_skipavail(memodom); |
438 | 0 | } else { |
439 | 0 | slpavail = 1; |
440 | 0 | } |
441 | 0 | if(slpavail > 0) { |
442 | 0 | if(wdebug > 0) {wdebug2(common,slpptr0,memptr0,slpavail,laststride,chunkdata);} |
443 | 0 | #ifdef TRANSFERN |
444 | 0 | if((stat = transfern(common,slpptr0,memptr0,slpavail,nczodom_laststride(slpodom),chunkdata))) goto done; |
445 | | #else |
446 | | if(common->reading) { |
447 | | if((stat=NCZ_copy_data(common->file,common->var,slpptr0,slpavail,!ZCLEAR,memptr0))) goto done; |
448 | | } else { |
449 | | if((stat=NCZ_copy_data(common->file,common->var,memptr0,slpavail,ZCLEAR,slpptr0))) goto done; |
450 | | } |
451 | | #endif |
452 | 0 | } |
453 | 0 | nczodom_next(memodom); |
454 | 0 | nczodom_next(slpodom); |
455 | 0 | } |
456 | 0 | done: |
457 | 0 | return stat; |
458 | 0 | } |
459 | | |
460 | | #if 0 |
461 | | #ifdef WDEBUG |
462 | | static void |
463 | | wdebug1(const struct Common* common, unsigned char* srcptr, unsigned char* dstptr, size_t count, size_t stride, void* chunkdata, const char* tag) |
464 | | { |
465 | | unsigned char* dstbase = (common->reading?common->memory:chunkdata); |
466 | | unsigned char* srcbase = (common->reading?chunkdata:common->memory); |
467 | | unsigned dstoff = (unsigned)(dstptr - dstbase); |
468 | | unsigned srcoff = (unsigned)(srcptr - srcbase); |
469 | | // unsigned srcidx = srcoff / sizeof(unsigned); |
470 | | |
471 | | fprintf(stderr,"%s: %s: [%u/%d] %u->%u", |
472 | | tag, |
473 | | common->reading?"read":"write", |
474 | | (unsigned)count, |
475 | | (unsigned)stride, |
476 | | (unsigned)(srcoff/common->typesize), |
477 | | (unsigned)(dstoff/common->typesize) |
478 | | ); |
479 | | #if 0 |
480 | | fprintf(stderr,"\t%s[%u]=%u\n",(common->reading?"chunkdata":"memdata"), |
481 | | // 0,((unsigned*)srcptr)[0] |
482 | | srcidx,((unsigned*)srcbase)[srcidx] |
483 | | ); |
484 | | #endif |
485 | | #if 0 |
486 | | { size_t len = common->typesize*count; |
487 | | fprintf(stderr," | [%u] %u->%u\n",(unsigned)len,(unsigned)srcoff,(unsigned)dstoff); |
488 | | } |
489 | | #endif |
490 | | fprintf(stderr,"\n"); |
491 | | } |
492 | | #else |
493 | | #define wdebug1(common,srcptr,dstptr,count,srcstride,dststride,chunkdata,tag) |
494 | | #endif |
495 | | #endif /*0*/ |
496 | | |
497 | | #ifdef TRANSFERN |
498 | | static int |
499 | | transfern(const struct Common* common, unsigned char* slpptr, unsigned char* memptr, size_t avail, size_t slpstride, void* chunkdata) |
500 | 0 | { |
501 | 0 | int stat = NC_NOERR; |
502 | 0 | nc_type xtype = common->var->type_info->hdr.id; |
503 | 0 | size_t typesize = common->typesize; |
504 | 0 | size_t len = typesize*avail; |
505 | 0 | size_t m,s; |
506 | |
|
507 | 0 | if(common->reading) { |
508 | 0 | if(slpstride == 1) { |
509 | 0 | if((stat=NCZ_copy_data(common->file,common->var,slpptr,avail,common->reading,memptr))) goto done; |
510 | | /// memcpy(memptr,slpptr,len); /* straight copy */ |
511 | 0 | } else { |
512 | 0 | for(m=0,s=0;s<avail;s+=slpstride,m++) { |
513 | 0 | size_t soffset = s*typesize; |
514 | 0 | size_t moffset = m*typesize; |
515 | 0 | if((stat=NCZ_copy_data(common->file,common->var,slpptr+soffset,1,common->reading,memptr+moffset))) goto done; |
516 | | /// memcpy(memptr+moffset,slpptr+soffset,typesize); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | if(common->swap && xtype < NC_STRING) |
520 | 0 | NCZ_swapatomicdata(len,memptr,common->typesize); |
521 | 0 | } else { /*writing*/ |
522 | 0 | unsigned char* srcbase = (common->reading?chunkdata:common->memory); |
523 | 0 | unsigned srcoff = (unsigned)(memptr - srcbase); |
524 | 0 | unsigned srcidx = srcoff / sizeof(unsigned); (void)srcidx; |
525 | 0 | if(slpstride == 1) { |
526 | 0 | if((stat=NCZ_copy_data(common->file,common->var,memptr,avail,common->reading,slpptr))) goto done; |
527 | | /// memcpy(slpptr,memptr,len); /* straight copy */ |
528 | 0 | } else { |
529 | 0 | for(m=0,s=0;s<avail;s+=slpstride,m++) { |
530 | 0 | size_t soffset = s*typesize; |
531 | 0 | size_t moffset = m*typesize; |
532 | 0 | if((stat=NCZ_copy_data(common->file,common->var,memptr+moffset,1,common->reading,slpptr+soffset))) goto done; |
533 | | /// memcpy(slpptr+soffset,memptr+moffset,typesize); |
534 | 0 | } |
535 | 0 | } |
536 | 0 | if(common->swap && xtype < NC_STRING) |
537 | 0 | NCZ_swapatomicdata(len,slpptr,common->typesize); |
538 | 0 | } |
539 | 0 | done: |
540 | 0 | return THROW(stat); |
541 | 0 | } |
542 | | #endif /*TRANSFERN*/ |
543 | | |
544 | | #if 0 |
545 | | /* This function may not be necessary if code in zvar does it instead */ |
546 | | static int |
547 | | NCZ_fillchunk(void* chunkdata, struct Common* common) |
548 | | { |
549 | | int stat = NC_NOERR; |
550 | | |
551 | | if(common->fillvalue == NULL) { |
552 | | memset(chunkdata,0,common->chunkcount*common->typesize); |
553 | | goto done; |
554 | | } |
555 | | |
556 | | if(common->cache->fillchunk == NULL) { |
557 | | /* Get fill chunk*/ |
558 | | if((stat = NCZ_create_fill_chunk(common->cache->chunksize, common->typesize, common->fillvalue, &common->cache->fillchunk))) |
559 | | goto done; |
560 | | } |
561 | | memcpy(chunkdata,common->cache->fillchunk,common->cache->chunksize); |
562 | | done: |
563 | | return stat; |
564 | | } |
565 | | #endif |
566 | | |
567 | | /* Break out this piece so we can use it for unit testing */ |
568 | | /** |
569 | | @param slices |
570 | | @param common |
571 | | @param odomp |
572 | | @return err code |
573 | | */ |
574 | | int |
575 | | NCZ_projectslices(struct Common* common, |
576 | | NCZSlice* slices, |
577 | | NCZOdometer** odomp) |
578 | 0 | { |
579 | 0 | int stat = NC_NOERR; |
580 | 0 | int r; |
581 | 0 | NCZOdometer* odom = NULL; |
582 | 0 | NCZSliceProjections* allprojections = NULL; |
583 | 0 | NCZChunkRange ranges[NC_MAX_VAR_DIMS]; |
584 | 0 | size64_t start[NC_MAX_VAR_DIMS]; |
585 | 0 | size64_t stop[NC_MAX_VAR_DIMS]; |
586 | 0 | size64_t stride[NC_MAX_VAR_DIMS]; |
587 | 0 | size64_t len[NC_MAX_VAR_DIMS]; |
588 | |
|
589 | 0 | if((allprojections = calloc((size_t)common->rank, sizeof(NCZSliceProjections))) == NULL) |
590 | 0 | {stat = NC_ENOMEM; goto done;} |
591 | 0 | memset(ranges,0,sizeof(ranges)); |
592 | | |
593 | | /* Compute the chunk ranges for each slice in a given dim */ |
594 | 0 | if((stat = NCZ_compute_chunk_ranges(common,slices,ranges))) |
595 | 0 | goto done; |
596 | | |
597 | | /* Compute the slice index vector */ |
598 | 0 | if((stat=NCZ_compute_all_slice_projections(common,slices,ranges,allprojections))) |
599 | 0 | goto done; |
600 | | |
601 | | /* Verify */ |
602 | 0 | for(r=0;r<common->rank;r++) { |
603 | 0 | assert(rangecount(ranges[r]) == allprojections[r].count); |
604 | 0 | } |
605 | | |
606 | | /* Compute the shape vector */ |
607 | 0 | for(r=0;r<common->rank;r++) { |
608 | 0 | int j; |
609 | 0 | size64_t iocount = 0; |
610 | 0 | NCZProjection* projections = allprojections[r].projections; |
611 | 0 | for(j=0;j<allprojections[r].count;j++) { |
612 | 0 | NCZProjection* proj = &projections[j]; |
613 | 0 | iocount += proj->iocount; |
614 | 0 | } |
615 | 0 | common->shape[r] = iocount; |
616 | 0 | } |
617 | 0 | common->allprojections = allprojections; |
618 | 0 | allprojections = NULL; |
619 | | |
620 | | /* Create an odometer to walk all the range combinations */ |
621 | 0 | for(r=0;r<common->rank;r++) { |
622 | 0 | start[r] = ranges[r].start; |
623 | 0 | stop[r] = ranges[r].stop; |
624 | 0 | stride[r] = 1; |
625 | 0 | len[r] = ceildiv(common->dimlens[r],common->chunklens[r]); |
626 | 0 | } |
627 | |
|
628 | 0 | if((odom = nczodom_new(common->rank,start,stop,stride,len)) == NULL) |
629 | 0 | {stat = NC_ENOMEM; goto done;} |
630 | 0 | if(odomp) *odomp = odom; |
631 | |
|
632 | 0 | done: |
633 | | /* reclaim allprojections if !NULL */ |
634 | 0 | if(allprojections != NULL) { |
635 | 0 | NCZ_clearsliceprojections(common->rank,allprojections); |
636 | 0 | nullfree(allprojections); |
637 | 0 | } |
638 | 0 | return stat; |
639 | 0 | } |
640 | | |
641 | | /***************************************************/ |
642 | | /* Utilities */ |
643 | | |
644 | | static int |
645 | | rangecount(NCZChunkRange range) |
646 | 0 | { |
647 | 0 | return (range.stop - range.start); |
648 | 0 | } |
649 | | |
650 | | /* Goal: Given a set of per-dimension indices, |
651 | | compute the corresponding linear position. |
652 | | */ |
653 | | size64_t |
654 | | NCZ_computelinearoffset(size_t R, const size64_t* indices, const size64_t* dimlens) |
655 | 0 | { |
656 | 0 | size64_t offset; |
657 | 0 | int i; |
658 | |
|
659 | 0 | offset = 0; |
660 | 0 | for(i=0;i<R;i++) { |
661 | 0 | offset *= dimlens[i]; |
662 | 0 | offset += indices[i]; |
663 | 0 | } |
664 | 0 | return offset; |
665 | 0 | } |
666 | | |
667 | | #if 0 |
668 | | /* Goal: Given a linear position |
669 | | compute the corresponding set of R indices |
670 | | */ |
671 | | void |
672 | | NCZ_offset2indices(size_t R, size64_t offset, const size64_t* dimlens, size64_t* indices) |
673 | | { |
674 | | int i; |
675 | | |
676 | | for(i=0;i<R;i++) { |
677 | | indices[i] = offset % dimlens[i]; |
678 | | offset = offset / dimlens[i]; |
679 | | } |
680 | | } |
681 | | #endif |
682 | | |
683 | | /**************************************************/ |
684 | | /* Unit test entry points */ |
685 | | |
686 | | int |
687 | | NCZ_chunkindexodom(int rank, const NCZChunkRange* ranges, size64_t* chunkcounts, NCZOdometer** odomp) |
688 | 0 | { |
689 | 0 | int stat = NC_NOERR; |
690 | 0 | int r; |
691 | 0 | NCZOdometer* odom = NULL; |
692 | 0 | size64_t start[NC_MAX_VAR_DIMS]; |
693 | 0 | size64_t stop[NC_MAX_VAR_DIMS]; |
694 | 0 | size64_t stride[NC_MAX_VAR_DIMS]; |
695 | 0 | size64_t len[NC_MAX_VAR_DIMS]; |
696 | |
|
697 | 0 | for(r=0;r<rank;r++) { |
698 | 0 | start[r] = ranges[r].start; |
699 | 0 | stop[r] = ranges[r].stop; |
700 | 0 | stride[r] = 1; |
701 | 0 | len[r] = chunkcounts[r]; |
702 | 0 | } |
703 | |
|
704 | 0 | if((odom = nczodom_new(rank, start, stop, stride, len))==NULL) |
705 | 0 | {stat = NC_ENOMEM; goto done;} |
706 | | |
707 | 0 | if(odomp) {*odomp = odom; odom = NULL;} |
708 | |
|
709 | 0 | done: |
710 | 0 | nczodom_free(odom); |
711 | 0 | return stat; |
712 | 0 | } |
713 | | |
714 | | static int |
715 | | readfromcache(void* source, size64_t* chunkindices, void** chunkdatap) |
716 | 0 | { |
717 | 0 | return NCZ_read_cache_chunk((struct NCZChunkCache*)source, chunkindices, chunkdatap); |
718 | 0 | } |
719 | | |
720 | | void |
721 | | NCZ_clearcommon(struct Common* common) |
722 | 0 | { |
723 | 0 | NCZ_clearsliceprojections(common->rank,common->allprojections); |
724 | 0 | nullfree(common->allprojections); |
725 | 0 | } |
726 | | |
727 | | /* Does the User want all of one and only chunk? */ |
728 | | static int |
729 | | iswholechunk(struct Common* common, NCZSlice* slices) |
730 | 0 | { |
731 | 0 | int i; |
732 | | |
733 | | /* Check that slices cover a whole chunk */ |
734 | 0 | for(i=0;i<common->rank;i++) { |
735 | 0 | if(!(slices[i].stride == 1 /* no point skipping */ |
736 | 0 | && (slices[i].start % common->chunklens[i]) == 0 /* starting at beginning of chunk */ |
737 | 0 | && (slices[i].stop - slices[i].start) /* stop-start = edge length */ |
738 | 0 | == common->chunklens[i] /* edge length == chunk length */ |
739 | 0 | )) |
740 | 0 | return 0; /* slices do not cover a whole chunk */ |
741 | 0 | } |
742 | 0 | return 1; |
743 | 0 | } |
744 | | |
745 | | static int |
746 | | wholechunk_indices(struct Common* common, NCZSlice* slices, size64_t* chunkindices) |
747 | 0 | { |
748 | 0 | int i; |
749 | 0 | for(i=0;i<common->rank;i++) |
750 | 0 | chunkindices[i] = (slices[i].start / common->chunklens[i]); |
751 | 0 | return NC_NOERR; |
752 | 0 | } |
753 | | |
754 | | /**************************************************/ |
755 | | /* Scalar variable support */ |
756 | | |
757 | | /* |
758 | | @param common common parameters |
759 | | */ |
760 | | |
761 | | int |
762 | | NCZ_transferscalar(struct Common* common) |
763 | 0 | { |
764 | 0 | int stat = NC_NOERR; |
765 | 0 | void* chunkdata = NULL; |
766 | 0 | size64_t chunkindices[NC_MAX_VAR_DIMS]; |
767 | 0 | unsigned char* memptr, *slpptr; |
768 | | |
769 | | /* Read from single chunk from cache */ |
770 | 0 | chunkindices[0] = 0; |
771 | 0 | switch ((stat = common->reader.read(common->reader.source, chunkindices, &chunkdata))) { |
772 | 0 | case NC_EEMPTY: /* cache created the chunk */ |
773 | 0 | break; |
774 | 0 | case NC_NOERR: break; |
775 | 0 | default: goto done; |
776 | 0 | } |
777 | | |
778 | | /* Figure out memory address */ |
779 | 0 | memptr = ((unsigned char*)common->memory); |
780 | 0 | slpptr = ((unsigned char*)chunkdata); |
781 | 0 | #ifdef TRANSFERN |
782 | 0 | if((stat = transfern(common,slpptr,memptr,1,1,chunkdata))) goto done; |
783 | | #else |
784 | | if(common->reading) { |
785 | | if((stat=NCZ_copy_data(common->file,common->var,slpptr,common->chunkcount,!ZCLEAR,memptr))) goto done; |
786 | | } else { |
787 | | if((stat=NCZ_copy_data(common->file,common->var,memptr,common->chunkcount,ZCLEAR,slpptr))) goto done; |
788 | | } |
789 | | #endif |
790 | | |
791 | 0 | done: |
792 | 0 | return stat; |
793 | 0 | } |
794 | | |
795 | | /* Debugging Interface: return the contents of a specified chunk */ |
796 | | EXTERNL int |
797 | | NCZ_read_chunk(int ncid, int varid, size64_t* zindices, void* chunkdata) |
798 | 0 | { |
799 | 0 | int stat = NC_NOERR; |
800 | 0 | NC_FILE_INFO_T* h5 = NULL; |
801 | 0 | NC_VAR_INFO_T* var = NULL; |
802 | 0 | NCZ_VAR_INFO_T* zvar = NULL; |
803 | 0 | struct NCZChunkCache* cache = NULL; |
804 | 0 | void* cachedata = NULL; |
805 | |
|
806 | 0 | if ((stat = nc4_find_grp_h5_var(ncid, varid, &h5, NULL, &var))) |
807 | 0 | return THROW(stat); |
808 | 0 | zvar = (NCZ_VAR_INFO_T*)var->format_var_info; |
809 | 0 | cache = zvar->cache; |
810 | |
|
811 | 0 | if((stat = NCZ_read_cache_chunk(cache,zindices,&cachedata))) goto done; |
812 | 0 | if(chunkdata) { |
813 | 0 | if((stat = NC_copy_data(h5->controller,var->type_info->hdr.id,cachedata,cache->chunkcount,chunkdata))) goto done; |
814 | 0 | } |
815 | | |
816 | 0 | done: |
817 | 0 | return stat; |
818 | 0 | } |