/* ******** airArrayLenIncr() ** ** Like airArrayLenSet, but works with an increment instead of an ** absolute length. Return value is different: ** got NULL: return 0 ** allocation error: return 0, and a->data set to NULL ** no error, delta > 0: return index of 1st element in newly allocated ** segment (a->len before length was increased) ** no error, delta <= 0: return 0, and a->data unchanged ** ** HEY: it is apparently not clear how to do error checking (aside from ** looking at a->data) when there was NO data previously allocated, and the ** first index of the newly allocated data is zero. */ unsigned int airArrayLenIncr(airArray *a, int delta) { /* char me[]="airArrayLenIncr"; */ unsigned int oldlen, ret, negdel; if (!a) { return 0; } negdel = (delta < 0 ? AIR_UINT(-delta) : 0); if (delta < 0 && negdel > a->len) { /* error: asked for newlength to be negative */ airArrayLenSet(a, 0); return 0; } oldlen = a->len; airArrayLenSet(a, (delta >= 0 ? oldlen + AIR_UINT(delta) : oldlen - negdel)); if (!a->data) { /* allocation error */ ret = 0; } else { ret = (delta <= 0 ? 0 : oldlen); } return ret; }
/* ******** nrrdCommentClear() ** ** blows away comments, but does not blow away the comment airArray */ void nrrdCommentClear(Nrrd *nrrd) { if (nrrd) { airArrayLenSet(nrrd->cmtArr, 0); } }
/* ******** airArrayNuke() ** ** free both the memory pointed to by the struct and the struct itself */ airArray * airArrayNuke(airArray *a) { if (a) { airArrayLenSet(a, 0); free(a); } return NULL; }
/* ** _biffNukeEntry() ** ** deletes given entry, and all info contained therein */ void _biffNukeEntry(_biffEntry *ent) { if (ent) { airArrayLenSet(ent->AA, 0); airArrayNuke(ent->AA); free(ent); } return; }
void biffMsgClear(biffMsg *msg) { if (biffMsgNoop == msg) { return; } airArrayLenSet(msg->errArr, 0); /* frees all msg->err[i] */ /* but msg->key stays allocated */ return; }
biffMsg * biffMsgNix(biffMsg *msg) { if (msg && msg != biffMsgNoop) { airFree(msg->key); airArrayLenSet(msg->errArr, 0); /* frees all msg->err[i] */ airArrayNuke(msg->errArr); airFree(msg); } return NULL; }
/* Tries to increase or decrease the length of h. returns 0 upon success. */ static int heapLenIncr (airHeap *h, int delta) { unsigned int oldlen=h->key_a->len; unsigned int newlen=oldlen+delta; if (delta==0) return 0; airArrayLenIncr(h->key_a, delta); if (h->data_a!=NULL) airArrayLenIncr(h->data_a, delta); airArrayLenIncr(h->idx_a, delta); airArrayLenIncr(h->invidx_a, delta); if (h->key_a->len<newlen || (h->data_a!=NULL && h->data_a->len<newlen) || h->idx_a->len<newlen || h->invidx_a->len<newlen) { /* Error. Try to undo changes and return error code. */ if (h->key_a->len>oldlen) airArrayLenSet(h->key_a, oldlen); if (h->data_a!=NULL && h->data_a->len>oldlen) airArrayLenSet(h->data_a, oldlen); if (h->idx_a->len>oldlen) airArrayLenSet(h->idx_a, oldlen); if (h->invidx_a->len>oldlen) airArrayLenSet(h->invidx_a, oldlen); return 1; } return 0; }
/* ******** airArrayLenIncr() ** ** Like airArrayLenSet, but works with an increment instead of an ** absolute length. Return value is different: ** got NULL: return 0 ** allocation error: return 0, and a->data set to NULL ** no error, delta > 0: return index of 1st element in newly allocated ** segment (a->len before length was increased) ** no error, delta <= 0: return 0, and a->data unchanged */ unsigned int airArrayLenIncr(airArray *a, int delta) { unsigned int oldlen, ret; if (!a) { return 0; } if (delta < 0 && (unsigned int)(-delta) > a->len) { /* error: asked for newlength to be negative */ airArrayLenSet(a, 0); return 0; } oldlen = a->len; airArrayLenSet(a, oldlen + delta); if (!a->data) { /* allocation error */ ret = 0; } else { ret = (delta <= 0 ? 0 : oldlen); } return ret; }
void nrrdIoStateInit(NrrdIoState *nio) { if (nio) { nio->path = (char *)airFree(nio->path); nio->base = (char *)airFree(nio->base); nio->line = (char *)airFree(nio->line); nio->dataFNFormat = (char *)airFree(nio->dataFNFormat); /* the way IO to/from strings works, I don't think this should be freed */ nio->headerStringRead = NULL; nio->headerStringWrite = NULL; airArrayLenSet(nio->dataFNArr, 0); /* closing this is always someone else's responsibility */ nio->headerFile = NULL; nio->dataFile = NULL; nio->dataFileDim = 0; nio->dataFNMin = 0; nio->dataFNMax = 0; nio->dataFNStep = 0; nio->dataFNIndex = -1; nio->lineLen = 0; nio->pos = 0; nio->endian = airEndianUnknown; nio->lineSkip = 0; nio->headerStrlen = 0; nio->headerStrpos = 0; nio->byteSkip = 0; memset(nio->seen, 0, (NRRD_FIELD_MAX+1)*sizeof(int)); nio->detachedHeader = AIR_FALSE; nio->bareText = nrrdDefaultWriteBareText; nio->charsPerLine = nrrdDefaultWriteCharsPerLine; nio->valsPerLine = nrrdDefaultWriteValsPerLine; nio->skipData = AIR_FALSE; nio->keepNrrdDataFileOpen = AIR_FALSE; nio->zlibLevel = -1; nio->zlibStrategy = nrrdZlibStrategyDefault; nio->bzip2BlockSize = -1; nio->learningHeaderStrlen = AIR_FALSE; nio->oldData = NULL; nio->oldDataSize = 0; nio->format = nrrdFormatUnknown; nio->encoding = nrrdEncodingUnknown; } return; }
void nrrdKeyValueClear(Nrrd *nrrd) { unsigned int nk, ki; if (!nrrd) { return; } nk = nrrd->kvpArr->len; for (ki=0; ki<nk; ki++) { nrrd->kvp[0 + 2*ki] = (char *)airFree(nrrd->kvp[0 + 2*ki]); nrrd->kvp[1 + 2*ki] = (char *)airFree(nrrd->kvp[1 + 2*ki]); } airArrayLenSet(nrrd->kvpArr, 0); return; }
void makeSceneBVH(limnCamera *cam, echoRTParm *parm, echoObject **sceneP) { echoObject *sphere; int i, N; float r, g, b; echoObject *scene; double time0, time1; *sceneP = scene = echoObjectNew(echoList); ELL_3V_SET(cam->from, 9, 6, 0); ELL_3V_SET(cam->at, 0, 0, 0); ELL_3V_SET(cam->up, 0, 0, 1); cam->uRange[0] = -3; cam->uRange[1] = 3; cam->vRange[0] = -3; cam->vRange[1] = 3; parm->jitterType = echoJitterNone; parm->numSamples = 1; parm->imgResU = 500; parm->imgResV = 500; parm->aperture = 0.0; parm->renderLights = AIR_TRUE; parm->renderBoxes = AIR_FALSE; parm->seedRand = AIR_FALSE; parm->maxRecDepth = 10; parm->shadow = 0.0; N = 1000000; airArrayLenSet(LIST(scene)->objArr, N); for (i=0; i<N; i++) { sphere = echoObjectNew(echoSphere); echoSphereSet(sphere, 4*airDrandMT()-2, 4*airDrandMT()-2, 4*airDrandMT()-2, 0.005); _dyeHSVtoRGB(&r, &g, &b, AIR_AFFINE(0, i, N, 0.0, 1.0), 1.0, 1.0); echoMatterPhongSet(sphere, r, g, b, 1.0, 1.0, 0.0, 0.0, 50); LIST(scene)->obj[i] = sphere; } time0 = airTime(); *sceneP = scene = echoListSplit3(scene, 8); time1 = airTime(); printf("BVH build time = %g seconds\n", time1 - time0); }
/* ******** echoListSplit() ** ** returns a echoObjectSplit to point to the same things as pointed ** to by the given echoObjectList */ echoObject * echoListSplit(echoScene *scene, echoObject *list, int axis) { echoPos_t lo[3], hi[3], loest0[3], hiest0[3], loest1[3], hiest1[3]; double *mids; echoObject *o, *split, *list0, *list1; int i, splitIdx, len; if (!( echoTypeList == list->type || echoTypeAABBox == list->type )) { return list; } len = LIST(list)->objArr->len; if (len <= ECHO_LEN_SMALL_ENOUGH) { /* there is nothing or only one object */ return list; } split = echoObjectNew(scene, echoTypeSplit); list0 = echoObjectNew(scene, echoTypeList); list1 = echoObjectNew(scene, echoTypeList); SPLIT(split)->axis = axis; SPLIT(split)->obj0 = list0; SPLIT(split)->obj1 = list1; mids = (double *)malloc(2 * len * sizeof(double)); for (i=0; i<len; i++) { o = LIST(list)->obj[i]; echoBoundsGet(lo, hi, o); mids[0 + 2*i] = (lo[axis] + hi[axis])/2; *((unsigned int *)(mids + 1 + 2*i)) = i; } /* overkill, I know, I know */ qsort(mids, len, 2*sizeof(double), (int (*)(const void *, const void *))_echoPosCompare); /* for (i=0; i<len; i++) { printf("%d -> %g\n", i, mids[0 + 2*i]); } */ splitIdx = len/2; /* printf("splitIdx = %d\n", splitIdx); */ ELL_3V_SET(loest0, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(loest1, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX); ELL_3V_SET(hiest0, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); ELL_3V_SET(hiest1, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN); airArrayLenSet(LIST(list0)->objArr, splitIdx); for (i=0; i<splitIdx; i++) { o = LIST(list)->obj[*((unsigned int *)(mids + 1 + 2*i))]; LIST(list0)->obj[i] = o; echoBoundsGet(lo, hi, o); /* printf("000 lo = (%g,%g,%g), hi = (%g,%g,%g)\n", lo[0], lo[1], lo[2], hi[0], hi[1], hi[2]); */ ELL_3V_MIN(loest0, loest0, lo); ELL_3V_MAX(hiest0, hiest0, hi); } airArrayLenSet(LIST(list1)->objArr, len-splitIdx); for (i=splitIdx; i<len; i++) { o = LIST(list)->obj[*((unsigned int *)(mids + 1 + 2*i))]; LIST(list1)->obj[i-splitIdx] = o; echoBoundsGet(lo, hi, o); /* printf("111 lo = (%g,%g,%g), hi = (%g,%g,%g)\n", lo[0], lo[1], lo[2], hi[0], hi[1], hi[2]); */ ELL_3V_MIN(loest1, loest1, lo); ELL_3V_MAX(hiest1, hiest1, hi); } /* printf("0: loest = (%g,%g,%g); hiest = (%g,%g,%g)\n", loest0[0], loest0[1], loest0[2], hiest0[0], hiest0[1], hiest0[2]); printf("1: loest = (%g,%g,%g); hiest = (%g,%g,%g)\n", loest1[0], loest1[1], loest1[2], hiest1[0], hiest1[1], hiest1[2]); */ ELL_3V_COPY(SPLIT(split)->min0, loest0); ELL_3V_COPY(SPLIT(split)->max0, hiest0); ELL_3V_COPY(SPLIT(split)->min1, loest1); ELL_3V_COPY(SPLIT(split)->max1, hiest1); /* we can't delete the list object here, we just gut it so that there's nothing substantial left of it */ airArrayLenSet(LIST(list)->objArr, 0); mids = (double *)airFree(mids); return split; }
/* ** nio->byteSkip < 0 functionality contributed by Katharina Quintus */ static int _nrrdEncodingGzip_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { static const char me[]="_nrrdEncodingGzip_read"; #if TEEM_ZLIB size_t sizeData, sizeRed; int error; long int bi; unsigned int didread, sizeChunk, maxChunk; char *data; gzFile gzfin; airPtrPtrUnion appu; sizeData = nrrdElementSize(nrrd)*elNum; /* Create the gzFile for reading in the gzipped data. */ if ((gzfin = _nrrdGzOpen(file, "rb")) == Z_NULL) { /* there was a problem */ biffAddf(NRRD, "%s: error opening gzFile", me); return 1; } /* keeps track of how many bytes have been successfully read in */ sizeRed = 0; /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to read in, we read in in chunks. However, we wrap a value _nrrdZlibMaxChunk around UINT_MAX for testing purposes. Given how sizeChunk is used below, we also cap chunk size at _nrrdZlibMaxChunk/2 to prevent overflow. */ maxChunk = _nrrdZlibMaxChunk/2; sizeChunk = AIR_CAST(unsigned int, AIR_MIN(sizeData, maxChunk)); if (nio->byteSkip < 0) { /* We don't know the size of the size to skip before the data, so decompress the data first into a temporary memory buffer. Then the byteskipping is then just memcpy-ing the appropriate region of memory from "buff" into the given "_data" pointer */ char *buff; airArray *buffArr; long backwards; /* setting the airArray increment to twice the chunk size means that for headers that are small compared to the data, the airArray never actually has to reallocate. The unit is 1 because we are managing the reading in terms of bytes (sizeof(char)==1 by definition) */ buff = NULL; appu.c = &buff; buffArr = airArrayNew(appu.v, NULL, 1, 2*sizeChunk); airArrayLenSet(buffArr, sizeChunk); if (!( buffArr && buffArr->data )) { biffAddf(NRRD, "%s: couldn't initialize airArray\n", me); return 1; } /* we keep reading in chunks as long as there hasn't been an error, and we haven't hit EOF (EOF signified by read == 0). Unlike the code below (for positive byteskip), we are obligated to read until the bitter end, and can't update sizeChunk to encompass only the required data. */ while (!(error = _nrrdGzRead(gzfin, buff + sizeRed, sizeChunk, &didread)) && didread > 0) { sizeRed += didread; if (didread >= sizeChunk) { /* we were able to read as much data as we requested, maybe there is more, so we need to make our temp buffer bigger */ unsigned int newlen = buffArr->len + sizeChunk; if (newlen < buffArr->len) { biffAddf(NRRD, "%s: array size will exceed uint capacity", me); return 1; } airArrayLenSet(buffArr, newlen); if (!buffArr->data) { biffAddf(NRRD, "%s: couldn't re-allocate data buffer", me); return 1; } } } if (error) { biffAddf(NRRD, "%s: error reading from gzFile", me); return 1; } /* backwards is (positive) number of bytes AFTER data that we ignore */ backwards = -nio->byteSkip - 1; if (sizeRed < sizeData + AIR_CAST(size_t, backwards)) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received only %s", me, airSprintSize_t(stmp1, sizeData + AIR_CAST(size_t, backwards)), airSprintSize_t(stmp2, sizeRed)); return 1; } /* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */ memcpy(_data, buff + sizeRed - sizeData - backwards, sizeData); airArrayNuke(buffArr); } else { /* no negative byteskip: after byteskipping, we can read directly into given data buffer */ if (nio->byteSkip > 0) { for (bi=0; bi<nio->byteSkip; bi++) { unsigned char b; /* Check to see if a single byte was able to be read. */ if (_nrrdGzRead(gzfin, &b, 1, &didread) != 0 || didread != 1) { biffAddf(NRRD, "%s: hit an error skipping byte %ld of %ld", me, bi, nio->byteSkip); return 1; } } } /* Pointer to chunks as we read them. */ data = AIR_CAST(char *, _data); while (!(error = _nrrdGzRead(gzfin, data, sizeChunk, &didread)) && didread > 0) { /* Increment the data pointer to the next available chunk. */ data += didread; sizeRed += didread; /* We only want to read as much data as we need, so we need to check to make sure that we don't request data that might be there but that we don't want. This will reduce sizeChunk when we get to the last block (which may be smaller than the original sizeChunk). */ if (sizeData >= sizeRed && sizeData - sizeRed < sizeChunk) { sizeChunk = AIR_CAST(unsigned int, sizeData - sizeRed); } } if (error) { biffAddf(NRRD, "%s: error reading from gzFile", me); return 1; } /* Check to see if we got out as much as we thought we should. */ if (sizeRed != sizeData) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(NRRD, "%s: expected %s bytes but received %s", me, airSprintSize_t(stmp1, sizeData), airSprintSize_t(stmp2, sizeRed)); return 1; } }
int main(int argc, char *argv[]) { char *me, *fname, *incrS; airArray *mop, *dataArr; FILE *file; unsigned int incr, numRed; unsigned char *data; int datum; /* must be int, so it can store EOF */ airPtrPtrUnion appu; me = argv[0]; if (3 != argc) { /* 0 1 2 (3) */ fprintf(stderr, "usage: %s <filename> <incr>\n", me); return 1; } fname = argv[1]; incrS = argv[2]; /* the "mop" is for management of dynamically allocated resources cleanly in combination with error handling, its not important for understanding how airArrays work (although as you can tell from the declaration, the "mop" is built on an airArray) */ mop = airMopNew(); if (!(file = fopen(fname, "rb"))) { fprintf(stderr, "%s: couldn't open %s for reading\n", me, fname); airMopError(mop); return 1; } airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (1 != sscanf(incrS, "%ud", &incr)) { fprintf(stderr, "%s: couln't parse incr \"%s\" as unsigned int\n", me, incrS); airMopError(mop); return 1; } /* now "file" is the open file that we read from, and "incr is the size increment (the granularity) for re-allocating the data */ /* the arguments here are as follows: 1) &data: the address of the array (itself a pointer) into which we'll copy the data. Whenever the airArray re-allocates the array, it will update the value of the array variable to the new location. So, while it seems a little weird at first, the value of the "data" variable can change as a side-effect of calling the airArray functions. 2) NULL: we could pass the address of a variable to record the current allocated length of the array, and this might be useful, but isn't necessary 3) sizeof(unsigned char): this is the size of the individual elements that we are saving in the array. Because memory is allocated and addressed at the level of individual bytes (and files can be read one byte at-a-time), we manage the buffer as an array of unsigned chars. 4) incr: when the array length is a multiple of incr, the memory segment is re-allocated, so this determines how often the re-allocation happens (we want it to happen fairly infrequently) */ /* dataArr = airArrayNew(&data, NULL, sizeof(unsigned char), incr); */ /* but wait: to play well with type checking, we have to use a stupid union to pass in the address of the array. So, appu.v == &data, but the types are right. We don't do a cast because recent versions of gcc will complain about breaking "strict-aliasing rules". */ appu.uc = &data; dataArr = airArrayNew(appu.v, NULL, sizeof(unsigned char), incr); if (!dataArr) { fprintf(stderr, "%s: couldn't allocate airArray\n", me); airMopError(mop); return 1; } /* numRed will keep track of the number of bytes that have been successfully read from the file AND stored in data[] */ numRed = 0; /* try to get the first byte of data */ datum = fgetc(file); if (EOF == datum) { fprintf(stderr, "%s: hit EOF trying to get first byte\n", me); airMopError(mop); return 1; } while (EOF != datum) { airArrayLenSet(dataArr, numRed+1); if (!data) { fprintf(stderr, "%s: couldn't re-allocated data buffer\n", me); airMopError(mop); return 1; } /* now "data" is the address of a sufficiently large array */ data[numRed++] = datum; datum = fgetc(file); } /* loop finishes when there's nothing more to read from file */ printf("%s: read %u bytes into memory\n", me, numRed); /* destroy the airArray, but keep the data allocated */ airArrayNix(dataArr); printf("%s: first value was %u\n", me, data[0]); /* free up the data array itself */ free(data); airMopOkay(mop); return 0; }
double _energyFromPoints(pullTask *task, pullBin *bin, pullPoint *point, /* output */ double egradSum[4]) { /* char me[]="_energyFromPoints"; */ double energySum, distSqSum, spaDistSqMax, wghtSum; int nopt, /* optimiziation: we sometimes re-use neighbor lists */ ntrue; /* we search all possible neighbors, stored in the bins (either because !nopt, or, this iter we learn true subset of interacting neighbors). This could also be called "dontreuse" or something like that */ unsigned int nidx, nnum; /* how much of task->neigh[] we use */ /* set nopt and ntrue */ if (task->pctx->neighborTrueProb < 1) { nopt = AIR_TRUE; if (egradSum) { /* We allow the neighbor list optimization only when we're also asked to compute the energy gradient. When we're not getting the energy gradient, we're being called to test the waters at possible new locations, in which case we can't be changing the effective particle neighborhood */ ntrue = (0 == task->pctx->iter || airDrandMT_r(task->rng) < task->pctx->neighborTrueProb); } else { ntrue = AIR_FALSE; } } else { nopt = AIR_FALSE; ntrue = AIR_TRUE; } /* fprintf(stderr, "!%s(%u), nopt = %d, ntrue = %d\n", me, point->idtag, nopt, ntrue); */ /* set nnum and task->neigh[] */ if (ntrue) { nnum = _neighBinPoints(task, bin, point); if (nopt) { airArrayLenSet(point->neighArr, 0); } } else { /* (nopt true) this iter we re-use existing neighbor list */ nnum = point->neighNum; for (nidx=0; nidx<nnum; nidx++) { task->neighPoint[nidx] = point->neighPoint[nidx]; } } /* loop through neighbor points */ spaDistSqMax = 4*task->pctx->radiusSpace*task->pctx->radiusSpace; /* fprintf(stderr, "%s: radiusSpace = %g -> spaDistSqMax = %g\n", me, task->pctx->radiusSpace, spaDistSqMax); */ wghtSum = 0; energySum = 0; distSqSum = 0; point->neighInterNum = 0; point->neighDist = 0.0; point->neighMode = 0.0; if (egradSum) { ELL_4V_SET(egradSum, 0, 0, 0, 0); } for (nidx=0; nidx<nnum; nidx++) { double diff[4], spaDistSq, enr, egrad[4]; pullPoint *herPoint; herPoint = task->neighPoint[nidx]; ELL_4V_SUB(diff, herPoint->pos, point->pos); spaDistSq = ELL_3V_DOT(diff, diff); /* fprintf(stderr, "!%s: %u:%g,%g,%g <-- %u:%g,%g,%g = sqd %g %s %g\n", me, point->idtag, point->pos[0], point->pos[1], point->pos[2], herPoint->idtag, herPoint->pos[0], herPoint->pos[1], herPoint->pos[2], spaDistSq, spaDistSq > spaDistSqMax ? ">" : "<=", spaDistSqMax); */ if (spaDistSq > spaDistSqMax) { continue; } if (AIR_ABS(diff[3] > task->pctx->radiusScale)) { continue; } enr = _energyInterParticle(task, point, herPoint, egrad); /* fprintf(stderr, "!%s: energySum = %g + %g = %g\n", me, energySum, enr, energySum + enr); */ energySum += enr; if (egradSum) { ELL_4V_INCR(egradSum, egrad); if (ELL_4V_DOT(egrad, egrad)) { point->neighInterNum++; point->neighDist = spaDistSq; if (task->pctx->ispec[pullInfoTangentMode]) { double w, m; m = _pullPointScalar(task->pctx, herPoint, pullInfoTangentMode, NULL, NULL); w = 1.0/spaDistSq; point->neighMode += w*m; wghtSum += w; } if (nopt && ntrue) { unsigned int ii; ii = airArrayLenIncr(point->neighArr, 1); point->neighPoint[ii] = herPoint; } } } } /* finish computing things averaged over neighbors */ if (point->neighInterNum) { point->neighDist = sqrt(point->neighDist/point->neighInterNum); point->neighMode /= wghtSum; } else { point->neighDist = -1; point->neighMode = AIR_NAN; } return energySum; }
/* ** nio->byteSkip < 0 functionality contributed by Katharina Quintus */ int _nrrdEncodingGzip_read(FILE *file, void *_data, size_t elNum, Nrrd *nrrd, NrrdIoState *nio) { char me[]="_nrrdEncodingGzip_read", err[BIFF_STRLEN]; #if TEEM_ZLIB size_t sizeData, sizeRed, sizeChunk; int error; long int bi; unsigned int read; char *data; gzFile gzfin; ptrHack hack; sizeData = nrrdElementSize(nrrd)*elNum; /* Create the gzFile for reading in the gzipped data. */ if ((gzfin = _nrrdGzOpen(file, "rb")) == Z_NULL) { /* there was a problem */ sprintf(err, "%s: error opening gzFile", me); biffAdd(NRRD, err); return 1; } /* keeps track of how many bytes have been successfully read in */ sizeRed = 0; /* zlib can only handle data sizes up to UINT_MAX ==> if there's more than UINT_MAX bytes to read in, we read in in chunks */ sizeChunk = AIR_MIN(sizeData, UINT_MAX); if (nio->byteSkip < 0) { /* We don't know the size of the size to skip before the data, so decompress the data first into a temporary memory buffer. Then the byteskipping is then just memcpy-ing the appropriate region of memory from "buff" into the given "_data" pointer */ char *buff; airArray *buffArr; /* setting the airArray increment to twice the chunk size means that for headers that are small compared to the data, the airArray never actually has to reallocate. The unit is 1 because we are managing the reading in terms of bytes (sizeof(char)==1 by definition) */ buff = NULL; hack.c = &buff; buffArr = airArrayNew(hack.v, NULL, 1, 2*sizeChunk); airArrayLenSet(buffArr, sizeChunk); if (!( buffArr && buffArr->data )) { sprintf(err, "%s: couldn't initialize airArray\n", me); biffAdd(NRRD, err); return 1; } /* we keep reading in chunks as long as there hasn't been an error, and we haven't hit EOF (EOF signified by read == 0). Unlike the code below (for positive byteskip), we are obligated to read until the bitter end, and can't update sizeChunk to encompass only the required data. Cast on third arg ok because of AIR_MIN use above */ while (!(error = _nrrdGzRead(gzfin, buff + sizeRed, AIR_CAST(unsigned int, sizeChunk), &read)) && read > 0) { sizeRed += read; if (read >= sizeChunk) { /* we were able to read as much data as we requested, maybe there is more, so we need to make our temp buffer bigger */ airArrayLenIncr(buffArr, sizeChunk); if (!buffArr->data) { sprintf(err, "%s: couldn't re-allocate data buffer", me); biffAdd(NRRD, err); return 1; } } } if (error) { sprintf(err, "%s: error reading from gzFile", me); biffAdd(NRRD, err); return 1; } if (sizeRed < sizeData + (-nio->byteSkip - 1)) { sprintf(err, "%s: expected " _AIR_SIZE_T_CNV " bytes and received only " _AIR_SIZE_T_CNV " bytes", me, AIR_CAST(size_t, sizeData + (-nio->byteSkip - 1)), sizeRed); biffAdd(NRRD, err); return 1; } /* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */ memcpy(_data, buff + sizeRed - sizeData - (-nio->byteSkip - 1), sizeData); airArrayNuke(buffArr); } else {