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