1 /*
2 cmap_data.c: read and write map sections.
3 Copyright (C) 2001 CCLRC, Charles Ballard
4
5 This library is free software: you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License
7 version 3, modified in accordance with the provisions of the
8 license to address the requirements of UK law.
9
10 You should have received a copy of the modified GNU Lesser General
11 Public License along with this library. If not, copies may be
12 downloaded from http://www.ccp4.ac.uk/ccp4license.php
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18 */
19 #include <math.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include "cmaplib.h"
23 #include "cmap_data.h"
24 #include "cmap_stats.h"
25 #include "cmap_errno.h"
26
27 /*! Internal: return the an estimate of the number of sections in the map
28 file based upon the length.
29 Update mfile->data.number as a side effect.
30 \param mfile (CMMFile *)
31 \return number of sections according to length-data/section_size */
number_sections(CMMFile * mfile)32 int number_sections(CMMFile *mfile)
33 {
34 div_t sections;
35
36 sections = div(ccp4_file_length(mfile->stream)-mfile->data.offset,
37 mfile->data.block_size);
38
39 return mfile->data.number = sections.quot;
40 }
41
42 /*! seek among the map sections. The units are of size block_size.
43 \param mfile (CMMFile *)
44 \param sec (int) section number
45 \param whence (unsigned int) SEEK_SET, SEEK_CUR or SEEK_END
46 \return offset in file, or EOF */
ccp4_cmap_seek_section(CMMFile * mfile,int sec,unsigned int whence)47 int ccp4_cmap_seek_section(CMMFile *mfile, int sec, unsigned int whence)
48 {
49 size_t curr_posn;
50 div_t secs;
51 int result = EOF;
52
53 if ( mfile == NULL ) {
54 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_NoChannel),
55 "ccp4_cmap_seekdata",NULL);
56 return EOF; }
57
58 switch (whence) {
59 case SEEK_SET:
60 if ( ccp4_file_is_read(mfile->stream) &&
61 ( sec < 0 || sec > mfile->data.number) )
62 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ParamError),
63 "ccp4_cmap_seek_section",NULL);
64 else
65 result = ccp4_file_raw_seek(mfile->stream, mfile->data.offset +
66 sec * mfile->data.block_size, SEEK_SET);
67 break;
68 case SEEK_END:
69 if ( ccp4_file_is_read(mfile->stream) &&
70 ( sec > 0 || abs(sec) > mfile->data.number) )
71 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ParamError),
72 "ccp4_cmap_seek_section",NULL);
73 else
74 result = ccp4_file_raw_seek(mfile->stream, sec * mfile->data.block_size,
75 SEEK_END);
76 break;
77 case SEEK_CUR:
78 curr_posn = ccp4_file_tell(mfile->stream);
79 secs = div(curr_posn - mfile->data.offset,mfile->data.block_size);
80 if ( ccp4_file_is_read(mfile->stream) &&
81 ( (secs.quot + sec) < 0 || (secs.quot + sec) >= mfile->data.number) )
82 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ParamError),
83 "ccp4_cmap_seek_section",NULL);
84 else
85 result = ccp4_file_raw_seek(mfile->stream,
86 (sec > 0) ? (mfile->data.block_size - secs.rem +
87 (sec - 1)*mfile->data.block_size) :
88 (sec*mfile->data.block_size - secs.rem),
89 SEEK_CUR);
90 }
91 return (result == EOF) ? EOF :
92 ((result - mfile->data.offset)/mfile->data.block_size);
93 }
94
95 /*! write map section to file.
96 Note: this wraps a raw write, with no location checking. It is
97 therefore the responsibility of the calling program to ensure that
98 everything is correct. Effectively assume appending to file.
99 \param mfile (CMMFile *)
100 \param section (const void *)
101 \return 1 on success, 0 on failure */
ccp4_cmap_write_section(CMMFile * mfile,const void * section)102 int ccp4_cmap_write_section(CMMFile *mfile, const void *section)
103 {
104 int result=0;
105 size_t write_dim;
106
107 if (mfile == NULL || section == NULL) {
108 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
109 "ccp4_cmap_write_section",NULL);
110 return 0; }
111
112 if (!ccp4_file_is_write(mfile->stream)) {
113 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_WriteFail),
114 "ccp4_cmap_write_section",NULL);
115 return 0; }
116
117 write_dim = mfile->map_dim[0] * mfile->map_dim[1];
118 result = ccp4_file_write(mfile->stream, section, write_dim);
119
120 /* note that we have started writing */
121 mfile->data.number++;
122
123 if (result != write_dim)
124 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_WriteFail),
125 "ccp4_cmap_write_section",NULL);
126 else
127 if (mfile->data_mode == FLOAT32)
128 stats_update(&mfile->stats, (float *)section,
129 (float *)section+write_dim);
130
131 return (result == write_dim) ? 1 : 0;
132 }
133
134 /*! read current map section from file to section.
135 Some checking is performed to ensure we are at the start of a
136 legitimate map section.
137 \param mfile (CMMFile *)
138 \param section (void *) array large enough to hold the map section
139 \return 1 on success, 0 on failure */
ccp4_cmap_read_section(CMMFile * mfile,void * section)140 int ccp4_cmap_read_section(CMMFile *mfile, void *section)
141 {
142 int result = 0;
143 div_t secs;
144 off_t curr_posn;
145 const off_t data_offset = 0;
146 size_t read_dim;
147
148 if (mfile == NULL ) {
149 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
150 "ccp4_cmap_read_section",NULL);
151 return 0; }
152
153 if (!ccp4_file_is_read(mfile->stream)) {
154 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ReadFail),
155 "ccp4_cmap_read_section",NULL);
156 return 0; }
157
158 curr_posn = ccp4_file_tell(mfile->stream);
159
160 secs = div(curr_posn - mfile->data.offset,
161 mfile->data.block_size);
162
163 /* ensure legit section (although rely upon EOF ) */
164 if (secs.quot < 0 || secs.rem < 0) {
165 ccp4_file_raw_seek(mfile->stream, mfile->data.offset, SEEK_SET);
166 secs.quot = 0;
167 } else if( secs.rem > data_offset && secs.rem < mfile->data.section_size )
168 ccp4_file_raw_seek(mfile->stream, - secs.rem, SEEK_CUR);
169 else if ( secs.rem >= mfile->data.section_size ) {
170 ccp4_file_raw_seek(mfile->stream, (mfile->data.block_size-secs.rem), SEEK_CUR);
171 secs.quot++; }
172
173 read_dim = mfile->map_dim[0] * mfile->map_dim[1];
174 /* do not read if at end */
175 if (secs.quot < 0 || secs.quot < mfile->data.number)
176 result = ccp4_file_read(mfile->stream, section, read_dim);
177
178 if (result != read_dim)
179 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_ReadFail),
180 "ccp4_cmap_read_section",NULL);
181
182 return (result == read_dim) ? 1 : 0;
183 }
184
185 /*! read current section header (character array)
186 After reading we are at the end of the local header
187 \param mfile (CMMFile *)
188 \param header (char *) character array large enough to hold
189 the local header (raw read so not string)
190 \return 1 on success, 0 on failure */
ccp4_cmap_read_section_header(const CMMFile * mfile,char * header)191 int ccp4_cmap_read_section_header(const CMMFile *mfile, char *header)
192 {
193 int result;
194 div_t secs;
195
196 if (mfile == NULL || header == NULL) {
197 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_NoChannel),
198 "ccp4_cmap_read_section_header",NULL);
199 return EOF; }
200
201 if (!ccp4_file_is_read(mfile->stream)) {
202 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_ReadFail),
203 "ccp4_cmap_read_section header",NULL);
204 return EOF; }
205
206 if ( mfile->data.header_size == 0) return (0);
207
208 result = ccp4_file_tell(mfile->stream);
209 secs = div(result - mfile->data.offset, mfile->data.block_size);
210
211 if ( secs.quot < 0 || secs.quot >= mfile->data.number ) return (0);
212
213 /* navigate to nearest header */
214 if ( secs.rem != mfile->data.section_size)
215 ccp4_file_raw_seek(mfile->stream,(mfile->data.section_size
216 - secs.rem), SEEK_CUR);
217
218 if ( (result = ccp4_file_readchar( mfile->stream, (uint8 *) header,
219 mfile->data.header_size)) != mfile->data.header_size)
220 ccp4_signal(ccp4_errno,
221 "ccp4_cmap_read_section_header",
222 NULL);
223
224 return (result == mfile->data.header_size) ? 1 : 0;
225 }
226
227 /*! write the local section header to the file. This must be of
228 size mfile->data.header.size.
229 Note: no checking is done so it is up to the calling program
230 to ensure that the file is in the correct location. As seeking
231 is turned off, this assumes we are appending to the file.
232 \param mfile (CMMFile *)
233 \param header (const char *) the local header character array
234 (not necessarily a string)
235 \return number of bytes written or EOF */
ccp4_cmap_write_section_header(CMMFile * mfile,const char * header)236 int ccp4_cmap_write_section_header(CMMFile *mfile, const char *header)
237 {
238 char *output;
239 int result;
240
241 if (mfile == NULL ) {
242 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_NoChannel),
243 "ccp4_cmap_write_section_header",NULL);
244 return EOF; }
245
246 if (!ccp4_file_is_write(mfile->stream)) {
247 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_NoChannel),
248 "ccp4_cmap_write_section_header",NULL);
249 return EOF; }
250
251 if ( mfile->data.header_size == 0) return (0);
252
253 output = (char *) malloc(mfile->data.header_size);
254 memset(output,' ', mfile->data.header_size);
255 if (header) memcpy(output, header,mfile->data.header_size);
256
257 if ( (result = ccp4_file_writechar( mfile->stream, (uint8 *) output,
258 mfile->data.header_size))
259 != mfile->data.header_size)
260 ccp4_signal(ccp4_errno,
261 "ccp4_cmap_write_section_header",
262 NULL);
263
264 return (result == mfile->data.header_size) ? 1 : 0;
265 }
266
267 /*! seek a row within a map section
268 \param mfile (CMMFile *)
269 \param row (int)
270 \param whence (unsigned int) SEEK_SET, SEEK_END, SEEK_CUR
271 \return offset in file or EOF */
ccp4_cmap_seek_row(CMMFile * mfile,int row,unsigned int whence)272 int ccp4_cmap_seek_row(CMMFile *mfile, int row, unsigned int whence)
273 {
274 size_t curr_posn;
275 div_t secs, rows;
276 int result = EOF;
277 size_t item_size;
278
279 if ( mfile == NULL ) {
280 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
281 "ccp4_cmap_seek_row",NULL);
282 return EOF; }
283
284 item_size = ccp4_file_itemsize(mfile->stream);
285 curr_posn = ccp4_file_tell(mfile->stream);
286 secs = div(curr_posn - mfile->data.offset,mfile->data.block_size);
287
288 switch (whence) {
289 case SEEK_SET:
290 if ( row < 0 || row >= mfile->map_dim[1])
291 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ParamError),
292 "ccp4_cmap_seek_row",NULL);
293 else
294 result = ccp4_file_raw_seek(mfile->stream, mfile->data.offset +
295 (secs.quot * mfile->data.block_size +
296 row * mfile->map_dim[0]*item_size),
297 SEEK_SET);
298 break;
299 case SEEK_END:
300 if ( row >= 0 || abs(row) > mfile->map_dim[1])
301 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ParamError),
302 "ccp4_cmap_seek_row",NULL);
303 else
304 result = ccp4_file_raw_seek(mfile->stream, mfile->data.offset +
305 (secs.quot * mfile->data.block_size +
306 mfile->data.section_size +
307 row * mfile->map_dim[0]*item_size),
308 SEEK_SET);
309 break;
310 case SEEK_CUR:
311 rows = div(secs.rem,mfile->map_dim[0]*item_size);
312 if ( (rows.quot + row) < 0 || (rows.quot + row) >= mfile->data.number)
313 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ParamError),
314 "ccp4_cmap_seek_row",NULL);
315 else
316 result = ccp4_file_raw_seek(mfile->stream,
317 ( row > 0) ? (mfile->map_dim[0]*item_size - rows.rem
318 + (row-1)*mfile->map_dim[0]*item_size) :
319 ( row*mfile->map_dim[0]*item_size - rows.rem),
320 SEEK_CUR);
321 }
322 return (result);
323 }
324
325 /*! write map row to file.
326 Note: this wraps a raw write, with no location checking. It is
327 therefore the responsibility of the calling program to ensure that
328 everything is correct. Effectively assume appending to file.
329 \param mfile (CMMFile *)
330 \param row (const void *) data to be written
331 \return 1 on success, 0 on failure */
ccp4_cmap_write_row(CMMFile * mfile,const void * row)332 int ccp4_cmap_write_row(CMMFile *mfile, const void *row)
333 {
334 int result=0;
335
336 if (mfile == NULL || row == NULL) {
337 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
338 "ccp4_cmap_write_row",NULL);
339 return EOF; }
340
341 if (!ccp4_file_is_write(mfile->stream)) {
342 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_WriteFail),
343 "ccp4_cmap_write_row",NULL);
344 return EOF; }
345
346 result = ccp4_file_write(mfile->stream, row, mfile->map_dim[0]);
347
348 /* note that we have started writing */
349 mfile->data.number++;
350
351 if (result != mfile->map_dim[0])
352 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_WriteFail),
353 "ccp4_cmap_write_row",NULL);
354 else
355 if (mfile->data_mode == FLOAT32)
356 stats_update(&mfile->stats, (float *)row, (float *)row+mfile->map_dim[0]);
357
358 return (result == mfile->map_dim[0]) ? 1 : 0;
359 }
360
361 /*! read current map section from file to section.
362 Some checking is performed to ensure we are at the start of a
363 legitimate map row.
364 \param mfile (CMMFile *)
365 \param row (void *) array large enough to hold the map row
366 \return 1 on success, 0 on failure */
ccp4_cmap_read_row(CMMFile * mfile,void * row)367 int ccp4_cmap_read_row(CMMFile *mfile, void *row)
368 {
369 int result = 0, item_size;
370 div_t secs, rows;
371 off_t curr_posn;
372
373 if (mfile == NULL) {
374 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
375 "ccp4_cmap_read_row",NULL);
376 return EOF; }
377
378 if (!ccp4_file_is_read(mfile->stream) || row == NULL) {
379 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_ReadFail),
380 "ccp4_cmap_read_row",NULL);
381 return EOF; }
382
383 item_size = ccp4_file_itemsize(mfile->stream);
384 curr_posn = ccp4_file_tell(mfile->stream);
385
386 secs = div(curr_posn - mfile->data.offset,
387 mfile->data.block_size);
388 rows = div(secs.rem, mfile->map_dim[0]*item_size);
389
390 if (secs.quot < 0 || secs.rem < 0)
391 ccp4_file_raw_seek(mfile->stream, mfile->data.offset, SEEK_SET);
392 else if(rows.quot >= mfile->map_dim[1] )
393 ccp4_file_raw_seek(mfile->stream, (mfile->data.block_size
394 - secs.rem), SEEK_CUR);
395 else if( rows.rem != 0)
396 ccp4_file_raw_seek(mfile->stream, ( - secs.rem), SEEK_CUR);
397
398 result = ccp4_file_read(mfile->stream, row, mfile->map_dim[0]);
399
400 if (result != mfile->map_dim[0])
401 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_ReadFail),
402 "ccp4_cmap_read_row",NULL);
403
404 return (result == mfile->map_dim[0]) ? 1 : 0;
405 }
406
407 /*! raw seek in items
408 \param mfile (CMMFile *)
409 \param offset (int) number of items
410 \param whence (unsigned int) SEEK_SET, SEEK_CUR, SEEK_END;
411 \return 0 on success, EOF on failure */
ccp4_cmap_seek_data(CMMFile * mfile,int offset,unsigned int whence)412 int ccp4_cmap_seek_data(CMMFile *mfile, int offset, unsigned int whence)
413 {
414 int result = EOF;
415
416 if ( mfile == NULL ) {
417 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
418 "ccp4_cmap_seekdata",NULL);
419 return (result); }
420
421 if ((result = ccp4_file_seek( mfile->stream, offset, whence)) == -1)
422 ccp4_signal(ccp4_errno, "ccp4_cmap_seek_data",NULL);
423
424 return (result);
425 }
426
427 /*! raw write of nelements items to file, according to the datamode,
428 at current location
429 \param mfile (const CMMFile *)
430 \param section (void *) values written, should contain at least
431 nelements items
432 \param n_items (int) number of items to be written
433 \return number of items written or EOF */
ccp4_cmap_write_data(CMMFile * mfile,const void * items,int n_items)434 int ccp4_cmap_write_data(CMMFile *mfile, const void *items, int n_items)
435 {
436 int result=0;
437
438 if (mfile == NULL || items == NULL) {
439 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
440 "ccp4_cmap_write_data",NULL);
441 return EOF; }
442
443 if (ccp4_file_is_write(mfile->stream)) {
444 result = ccp4_file_write(mfile->stream, (uint8 *) items, n_items);
445 if (result != n_items)
446 ccp4_signal( CCP4_ERRLEVEL(3) | CMAP_ERRNO(CMERR_WriteFail),
447 "ccp4_cmap_write_data",NULL);
448 else if (mfile->data_mode == FLOAT32)
449 stats_update(&mfile->stats, (float *)items, (float *)items+result);
450 }
451
452 return (result);
453 }
454
455 /*! raw read of nelements items from file according to the datamode
456 at current location
457 \param mfile (const CMMFile *)
458 \param items (void *) values read to here, so should have enough space
459 for nelements items
460 \param n_items (int) number of items to be read
461 \return number of items read or EOF */
ccp4_cmap_read_data(const CMMFile * mfile,void * items,int n_items)462 int ccp4_cmap_read_data(const CMMFile *mfile, void *items, int n_items)
463 {
464 int result=0;
465
466 if (mfile == NULL || items == NULL) {
467 ccp4_signal( CCP4_ERRLEVEL(2) | CMAP_ERRNO(CMERR_NoChannel),
468 "ccp4_cmap_read_data",NULL);
469 return EOF; }
470
471 if (ccp4_file_is_read(mfile->stream))
472 result = ccp4_file_read(mfile->stream, (uint8 *) items, n_items);
473
474 return (result);
475 }
476
477