1 /*
2   NrrdIO: stand-alone code for basic nrrd functionality
3   Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4   Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5   Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6 
7   This software is provided 'as-is', without any express or implied
8   warranty.  In no event will the authors be held liable for any
9   damages arising from the use of this software.
10 
11   Permission is granted to anyone to use this software for any
12   purpose, including commercial applications, and to alter it and
13   redistribute it freely, subject to the following restrictions:
14 
15   1. The origin of this software must not be misrepresented; you must
16      not claim that you wrote the original software. If you use this
17      software in a product, an acknowledgment in the product
18      documentation would be appreciated but is not required.
19 
20   2. Altered source versions must be plainly marked as such, and must
21      not be misrepresented as being the original software.
22 
23   3. This notice may not be removed or altered from any source distribution.
24 */
25 
26 #include "NrrdIO.h"
27 #include "privateNrrd.h"
28 
29 static int
_nrrdEncodingRaw_available(void)30 _nrrdEncodingRaw_available(void) {
31 
32   return AIR_TRUE;
33 }
34 
35 static int
_nrrdEncodingRaw_read(FILE * file,void * data,size_t elementNum,Nrrd * nrrd,NrrdIoState * nio)36 _nrrdEncodingRaw_read(FILE *file, void *data, size_t elementNum,
37                       Nrrd *nrrd, NrrdIoState *nio) {
38   static const char me[]="_nrrdEncodingRaw_read";
39   size_t ret, bsize;
40   int fd, dio, car;
41   long savePos;
42   char *data_c;
43   size_t elementSize, maxChunkSize, remainderValue, chunkSize;
44   size_t retTmp;
45   char stmp[3][AIR_STRLEN_SMALL];
46 
47   bsize = nrrdElementSize(nrrd)*elementNum;
48   if (nio->format->usesDIO) {
49     fd = fileno(file);
50     dio = airDioTest(fd, data, bsize);
51   } else {
52     fd = -1;
53     dio = airNoDio_format;
54   }
55   if (airNoDio_okay == dio) {
56     if (2 <= nrrdStateVerboseIO) {
57       fprintf(stderr, "with direct I/O ... ");
58     }
59     ret = airDioRead(fd, data, bsize);
60     if (ret != bsize) {
61       biffAddf(NRRD, "%s: airDioRead got read only %s of %sbytes "
62                "(%g%% of expected)", me,
63                airSprintSize_t(stmp[0], ret),
64                airSprintSize_t(stmp[1], bsize),
65                100.0*AIR_CAST(double, ret)/AIR_CAST(double, bsize));
66       return 1;
67     }
68   } else {
69     if (2 <= nrrdStateVerboseIO) {
70       if (AIR_DIO && nio->format->usesDIO) {
71         fprintf(stderr, "with fread(), not DIO: %s ...", airNoDioErr(dio));
72       }
73     }
74 
75     /* HEY: There's a bug in fread/fwrite in gcc 4.2.1 (with SnowLeopard).
76             When it reads/writes a >=2GB data array, it pretends to succeed
77             (i.e. the return value is the right number) but it hasn't
78             actually read/written the data.  The work-around is to loop
79             over the data, reading/writing 1GB (or smaller) chunks.         */
80     ret = 0;
81     data_c = (char *)data;
82     elementSize = nrrdElementSize(nrrd);
83     maxChunkSize = 1024 * 1024 * 1024 / elementSize;
84     while(ret < elementNum) {
85       remainderValue = elementNum-ret;
86       if (remainderValue < maxChunkSize) {
87         chunkSize = remainderValue;
88       } else {
89         chunkSize = maxChunkSize;
90       }
91       retTmp =
92         fread(&(data_c[ret*elementSize]), elementSize, chunkSize, file);
93       ret += retTmp;
94       if (retTmp != chunkSize) {
95         biffAddf(NRRD, "%s: fread got only %s %s-sized things, not %s "
96                  "(%g%% of expected)", me,
97                  airSprintSize_t(stmp[0], ret),
98                  airSprintSize_t(stmp[1], nrrdElementSize(nrrd)),
99                  airSprintSize_t(stmp[2], elementNum),
100                  100.0*AIR_CAST(double, ret)/AIR_CAST(double, elementNum));
101         return 1;
102       }
103     }
104 
105     car = fgetc(file);
106     if (EOF != car) {
107       if (1 <= nrrdStateVerboseIO) {
108         fprintf(stderr, "%s: WARNING: finished reading raw data, "
109                 "but file not at EOF\n", me);
110       }
111       ungetc(car, file);
112     }
113     if (2 <= nrrdStateVerboseIO && nio->byteSkip && stdin != file) {
114       savePos = ftell(file);
115       if (!fseek(file, 0, SEEK_END)) {
116         double frac = (AIR_CAST(double, bsize)
117                        /AIR_CAST(double, ftell(file) + 1));
118         fprintf(stderr, "(%s: used %g%% of file for nrrd data)\n", me,
119                 100.0*frac);
120         fseek(file, savePos, SEEK_SET);
121       }
122     }
123   }
124 
125   return 0;
126 }
127 
128 static int
_nrrdEncodingRaw_write(FILE * file,const void * data,size_t elementNum,const Nrrd * nrrd,NrrdIoState * nio)129 _nrrdEncodingRaw_write(FILE *file, const void *data, size_t elementNum,
130                        const Nrrd *nrrd, NrrdIoState *nio) {
131   static const char me[]="_nrrdEncodingRaw_write";
132   int fd, dio;
133   size_t ret, bsize;
134   const char *data_c;
135   size_t elementSize, maxChunkSize, remainderValue, chunkSize;
136   size_t retTmp;
137   char stmp[3][AIR_STRLEN_SMALL];
138 
139   bsize = nrrdElementSize(nrrd)*elementNum;
140   if (nio->format->usesDIO) {
141     fd = fileno(file);
142     dio = airDioTest(fd, data, bsize);
143   } else {
144     fd = -1;
145     dio = airNoDio_format;
146   }
147   if (airNoDio_okay == dio) {
148     if (2 <= nrrdStateVerboseIO) {
149       fprintf(stderr, "with direct I/O ... ");
150     }
151     ret = airDioWrite(fd, data, bsize);
152     if (ret != bsize) {
153       biffAddf(NRRD, "%s: airDioWrite wrote only %s of %s bytes "
154                "(%g%% of expected)", me,
155                airSprintSize_t(stmp[0], ret),
156                airSprintSize_t(stmp[1], bsize),
157                100.0*AIR_CAST(double, ret)/AIR_CAST(double, bsize));
158       return 1;
159     }
160   } else {
161     if (2 <= nrrdStateVerboseIO) {
162       if (AIR_DIO && nio->format->usesDIO) {
163         fprintf(stderr, "with fread(), not DIO: %s ...", airNoDioErr(dio));
164       }
165     }
166 
167     /* HEY: There's a bug in fread/fwrite in gcc 4.2.1 (with SnowLeopard).
168             When it reads/writes a >=2GB data array, it pretends to succeed
169             (i.e. the return value is the right number) but it hasn't
170             actually read/written the data.  The work-around is to loop
171             over the data, reading/writing 1GB (or smaller) chunks.         */
172     ret = 0;
173     data_c = AIR_CAST(const char *, data);
174     elementSize = nrrdElementSize(nrrd);
175     maxChunkSize = 1024 * 1024 * 1024 / elementSize;
176     while(ret < elementNum) {
177       remainderValue = elementNum-ret;
178       if (remainderValue < maxChunkSize) {
179         chunkSize = remainderValue;
180       } else {
181         chunkSize = maxChunkSize;
182       }
183       retTmp =
184         fwrite(&(data_c[ret*elementSize]), elementSize, chunkSize, file);
185       ret += retTmp;
186       if (retTmp != chunkSize) {
187         biffAddf(NRRD, "%s: fwrite wrote only %s %s-sized things, not %s "
188                  "(%g%% of expected)", me,
189                  airSprintSize_t(stmp[0], ret),
190                  airSprintSize_t(stmp[1], nrrdElementSize(nrrd)),
191                  airSprintSize_t(stmp[2], elementNum),
192                  100.0*AIR_CAST(double, ret)/AIR_CAST(double, elementNum));
193         return 1;
194       }
195     }
196 
197     fflush(file);
198     /*
199     if (ferror(file)) {
200       biffAddf(NRRD, "%s: ferror returned non-zero", me);
201       return 1;
202     }
203     */
204   }
205   return 0;
206 }
207 
208 const NrrdEncoding
209 _nrrdEncodingRaw = {
210   "raw",      /* name */
211   "raw",      /* suffix */
212   AIR_TRUE,   /* endianMatters */
213   AIR_FALSE,  /* isCompression */
214   _nrrdEncodingRaw_available,
215   _nrrdEncodingRaw_read,
216   _nrrdEncodingRaw_write
217 };
218 
219 const NrrdEncoding *const
220 nrrdEncodingRaw = &_nrrdEncodingRaw;
221