1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Copyright by The HDF Group. *
3 * Copyright by the Board of Trustees of the University of Illinois. *
4 * All rights reserved. *
5 * *
6 * This file is part of HDF. The full HDF copyright notice, including *
7 * terms governing use, modification, and redistribution, is contained in *
8 * the COPYING file, which can be found at the root of the source code *
9 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/. *
10 * If you do not have access to either file, you may request a copy from *
11 * help@hdfgroup.org. *
12 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13
14 /* $Id$ */
15 /*LINTLIBRARY */
16 /* ------------------------------ hbuffer.c -------------------------------
17
18 Routines for buffered elements, i.e., data elements that reside in memory
19 until they are closed, and are then flushed out to the file. Buffered
20 elements are held in memory while they are being accessed and are only
21 written back to the file if they are modified.
22
23 File Organization
24 ******************
25 These special elements are invoked at run-time only, information about
26 whether an element was cached is not stored in the file. Unless specificly
27 asked for by an API routine or required for a particular kind of access by
28 the library, these routines aren't called.
29
30 LOCAL ROUTINES
31
32 EXPORTED BUT LIBRARY PRIVATE ROUTINES
33 HBPcloseAID -- close file but keep AID active
34 HBPendacess -- close file, free AID
35 HBPinfo -- return info about an buffered element
36 HBPinquire -- retrieve information about an buffered element
37 HBPread -- read some data out of an buffered element
38 HBPreset -- replace the current buffered info with new info (NOP)
39 HBPseek -- set the seek position
40 HBPsetaccesstype -- set the I/O access type of the buffered element
41 HBPstread -- open an access record for reading
42 HBPstwrite -- open an access record for reading
43 HBPwrite -- write some data out to a buffered element
44
45 EXPORTED ROUTINES
46 HBconvert -- start buffering an AID
47
48 ------------------------------------------------------------------------- */
49
50 #include "hdf.h"
51 #include "hfile.h"
52 #include <assert.h>
53
54 /* extinfo_t -- external elt information structure */
55
56 typedef struct
57 {
58 intn attached; /* number of access records attached
59 to this information structure */
60 intn modified; /* has the buffered element been modified? */
61 int32 length; /* length of this element */
62 uint8 *buf; /* pointer to the buffered data */
63 int32 buf_aid; /* AID for buffered access record (below) */
64 accrec_t *buf_access_rec; /* "Real" access record for buffered data */
65 }
66 bufinfo_t;
67
68 /* forward declaration of the functions provided in this module */
69
70 /* buf_funcs -- table of the accessing functions of the buffered
71 data element function modules. The position of each function in
72 the table is standard */
73 funclist_t buf_funcs =
74 {
75 HBPstread,
76 HBPstwrite,
77 HBPseek,
78 HBPinquire,
79 HBPread,
80 HBPwrite,
81 HBPendaccess,
82 HBPinfo,
83 NULL /* no routine registered */
84 };
85
86 /*------------------------------------------------------------------------
87 NAME
88 HBconvert -- cause an existing AID to be buffered.
89 USAGE
90 intn HBcreate(aid)
91 int32 aid; IN: AID of data element to buffer
92 RETURNS
93 SUCCEED/FAIL
94 DESCRIPTION
95 Buffers an existing data element (referred to with the AID passed in) in
96 memory for faster access. This is especially useful when repeatedly
97 accessing a compressed special element object which would otherwise have
98 to be repeatedly decompressed over many I/O accesses.
99
100 If the ALLOW_BUFFER_GROWING flag is defined during compile time,
101 the buffered object is allowed to grow, it is assumed that a higher-level
102 API will prevent this if it is not allowed through that API.
103
104 FORTRAN
105 None
106
107 --------------------------------------------------------------------------*/
108 intn
HBconvert(int32 aid)109 HBconvert(int32 aid)
110 {
111 CONSTR(FUNC, "HBconvert"); /* for HERROR */
112 accrec_t *access_rec=NULL;/* access element record */
113 accrec_t *new_access_rec; /* newly created access record */
114 accrec_t *tmp_access_rec; /* temp. access record */
115 bufinfo_t *info; /* information for the buffered element */
116 uint16 data_tag,data_ref; /* tag/ref of the data we are checking */
117 int32 data_off; /* offset of the data we are checking */
118 int32 data_len; /* length of the data we are checking */
119 intn ret_value = SUCCEED;
120
121 HEclear();
122 if ((access_rec = HAatom_object(aid)) == NULL) /* get the access_rec pointer */
123 HGOTO_ERROR(DFE_ARGS, FAIL);
124
125 /* get the info for the dataset */
126 if (HTPis_special(access_rec->ddid) || access_rec->special!=0) {
127 if((*access_rec->special_func->inquire) (access_rec, NULL,
128 &data_tag, &data_ref, &data_len, &data_off, NULL, NULL, NULL)==FAIL)
129 HGOTO_ERROR(DFE_INTERNAL, FAIL);
130 } /* end if */
131 else
132 if(HTPinquire(access_rec->ddid,&data_tag,&data_ref,&data_off,&data_len)==FAIL)
133 HGOTO_ERROR(DFE_INTERNAL, FAIL);
134
135 /* is data defined but does not exist in the file? */
136 if(data_off==INVALID_OFFSET && data_len==INVALID_LENGTH)
137 { /* catch the case where the data doesn't exist yet */
138
139 /* set length to zero */
140 if(Hsetlength(aid,0)==FAIL)
141 HGOTO_ERROR(DFE_INTERNAL, FAIL);
142
143 /* get back new offset and length */
144 if(HTPinquire(access_rec->ddid,&data_tag,&data_ref,&data_off,&data_len)==FAIL)
145 HGOTO_ERROR(DFE_INTERNAL, FAIL);
146 } /* end if */
147
148 /* allocate special info struct for buffered element */
149 if ((info = HDmalloc((uint32) sizeof(bufinfo_t)))==NULL)
150 HGOTO_ERROR(DFE_NOSPACE, FAIL);
151
152 /* fill in special info struct */
153 info->attached = 1;
154 info->modified = 0; /* Data starts out not modified */
155 info->length = data_len; /* initial buffer size */
156
157 /* Get space for buffer */
158 if(data_len>0) {
159 if((info->buf = HDmalloc((uint32) data_len))==NULL)
160 HGOTO_ERROR(DFE_NOSPACE, FAIL);
161 } /* end if */
162 else
163 info->buf=NULL;
164
165 /* Read in existing data into buffer */
166 if(data_len>0) {
167 if (Hseek(aid, 0, DF_START) == FAIL)
168 HGOTO_ERROR(DFE_SEEKERROR, FAIL);
169 if (Hread(aid, data_len, info->buf) == FAIL)
170 HGOTO_ERROR(DFE_READERROR, FAIL);
171 } /* end if */
172
173 /* get empty access record */
174 new_access_rec = HIget_access_rec();
175 if (new_access_rec == NULL)
176 HGOTO_ERROR(DFE_TOOMANY, FAIL);
177
178 /* Copy the old access record information to the new access record */
179 /*
180 * Don't get a new copy of the DD id or increment the number of attached
181 * elements, buffered elements are supposed to be "transparent".
182 * We "inherit" the appendable flag if it's set and ALLOW_BUFFER_GROW is
183 * defined to support it.
184 */
185 tmp_access_rec=new_access_rec->next; /* preserve free list pointer */
186 HDmemcpy(new_access_rec,access_rec,sizeof(accrec_t));
187 new_access_rec->next=tmp_access_rec; /* restore free list pointer */
188
189 /* Preserve the actual access record for the buffered element */
190 info->buf_access_rec = new_access_rec; /* Access record of actual data on disk */
191
192 /* Create AID for actual access record */
193 info->buf_aid = HAregister_atom(AIDGROUP,new_access_rec);
194
195 /* Modify access record to point to buffered element functions */
196 access_rec->special_info = (void *)info;
197 access_rec->special_func = &buf_funcs;
198 access_rec->special = SPECIAL_BUFFERED;
199
200 done:
201 if(ret_value == FAIL)
202 { /* Error condition cleanup */
203 } /* end if */
204
205 return ret_value;
206 } /* HBconvert */
207
208 /* ------------------------------ HBPstread ------------------------------- */
209 /*
210 NAME
211 HBPstread -- open an access record for reading
212 USAGE
213 int32 HBPstread(access_rec)
214 access_t * access_rec; IN: access record to fill in
215 RETURNS
216 The AID of the access record on success FAIL on error.
217 DESCRIPTION
218 This is a stub routine and should never be called
219
220 ---------------------------------------------------------------------------*/
221 int32
HBPstread(accrec_t * rec)222 HBPstread(accrec_t * rec)
223 {
224 /* shut compilers up*/
225 rec=rec;
226
227 assert(0 && "Should never be called");
228 return (FAIL);
229 } /* HBPstread */
230
231 /* ------------------------------ HBPstwrite ------------------------------- */
232 /*
233 NAME
234 HBPstwrite -- open an access record for reading
235 USAGE
236 int32 HBPstwrite(access_rec)
237 access_t * access_rec; IN: access record to fill in
238 RETURNS
239 The AID of the access record on success FAIL on error.
240 DESCRIPTION
241 This is a stub routine and should never be called.
242
243 ---------------------------------------------------------------------------*/
244 int32
HBPstwrite(accrec_t * rec)245 HBPstwrite(accrec_t * rec)
246 {
247 /* shut compilers up*/
248 rec=rec;
249
250 assert(0 && "Should never be called");
251 return (FAIL);
252 } /* HBPstwrite */
253
254 /* ------------------------------ HBPseek ------------------------------- */
255 /*
256 NAME
257 HBPseek -- set the seek posn
258 USAGE
259 int32 HXPseek(access_rec, offset, origin)
260 access_t * access_rec; IN: access record to mess with
261 int32 offset; IN: seek offset
262 int32 origin; IN: where we should calc the offset from
263 RETURNS
264 SUCCEED / FAIL
265 DESCRIPTION
266 Set the seek posn in the given buffered element
267
268 ---------------------------------------------------------------------------*/
269 int32
HBPseek(accrec_t * access_rec,int32 offset,int origin)270 HBPseek(accrec_t * access_rec, int32 offset, int origin)
271 {
272 int32 ret_value = SUCCEED;
273 CONSTR(FUNC, "HBPseek"); /* for HERROR */
274
275 /* Adjust offset according to origin. There is no upper bound to posn */
276 if (origin == DF_CURRENT)
277 offset += access_rec->posn;
278 if (origin == DF_END)
279 offset += ((bufinfo_t *) (access_rec->special_info))->length;
280 if (offset < 0)
281 HGOTO_ERROR(DFE_RANGE, FAIL);
282
283 /* set the offset */
284 access_rec->posn = offset;
285
286 done:
287 if(ret_value == FAIL)
288 { /* Error condition cleanup */
289
290 } /* end if */
291
292 /* Normal function cleanup */
293
294 return ret_value;
295 } /* HBPseek */
296
297 /* ------------------------------ HBPread ------------------------------- */
298 /*
299 NAME
300 HBPread -- read some data out of buffered element
301 USAGE
302 int32 HBPread(access_rec, length, data)
303 access_t * access_rec; IN: access record to mess with
304 int32 length; IN: number of bytes to read
305 void * data; IN: buffer for data
306 RETURNS
307 The number of bytes read or FAIL on error
308 DESCRIPTION
309 Read in some data from a buffered element. If length is zero
310 read until the end of the element. It is assumed that the
311 data buffer is big enough to store the data.
312
313 ---------------------------------------------------------------------------*/
314 int32
HBPread(accrec_t * access_rec,int32 length,void * data)315 HBPread(accrec_t * access_rec, int32 length, void * data)
316 {
317 CONSTR(FUNC, "HBPread"); /* for HERROR */
318 bufinfo_t *info = /* information on the special element */
319 (bufinfo_t *) access_rec->special_info;
320 int32 ret_value = SUCCEED;
321
322 /* validate length */
323 if (length < 0)
324 HGOTO_ERROR(DFE_RANGE, FAIL);
325
326 /* adjust length if it falls off the end of the element */
327 if ((length == 0) || (access_rec->posn + length > info->length))
328 length = info->length - access_rec->posn;
329 else if (length < 0)
330 HGOTO_ERROR(DFE_RANGE, FAIL);
331
332 /* Copy data from buffer */
333 HDmemcpy(data,info->buf+access_rec->posn,length);
334
335 /* adjust access position */
336 access_rec->posn += length;
337
338 ret_value = length;
339
340 done:
341 if(ret_value == FAIL)
342 { /* Error condition cleanup */
343
344 } /* end if */
345
346 /* Normal function cleanup */
347
348 return(ret_value);
349 } /* HBPread */
350
351 /* ------------------------------ HBPwrite ------------------------------- */
352 /*
353 NAME
354 HBPwrite -- write some data out to a buffered element
355 USAGE
356 int32 HBPwrite(access_rec, length, data)
357 access_t * access_rec; IN: access record to mess with
358 int32 length; IN: number of bytes to read
359 void * data; IN: buffer of data
360 RETURNS
361 The number of bytes written or FAIL on error
362 DESCRIPTION
363 Write out some data to a buffered element.
364
365 ---------------------------------------------------------------------------*/
366 int32
HBPwrite(accrec_t * access_rec,int32 length,const void * data)367 HBPwrite(accrec_t * access_rec, int32 length, const void * data)
368 {
369 CONSTR(FUNC, "HBPwrite"); /* for HERROR */
370 bufinfo_t *info = /* information on the special element */
371 (bufinfo_t *) (access_rec->special_info);
372 int32 new_len; /* new length of object */
373 int32 ret_value = SUCCEED;
374
375 /* validate length */
376 if (length < 0)
377 HGOTO_ERROR(DFE_RANGE, FAIL);
378
379 /* Check if the data to write will overrun the buffer and realloc it if so */
380 if(access_rec->posn+length>info->length) {
381 /* Calc. the new size of the object */
382 new_len=access_rec->posn+length;
383
384 /* Resize buffer in safe manner */
385 /* Realloc should handle this, but the Sun is whining about it... -QAK */
386 if(info->buf==NULL) {
387 if((info->buf = HDmalloc((uint32)new_len))==NULL)
388 HGOTO_ERROR(DFE_NOSPACE, FAIL);
389 }
390 else {
391 uint8 *temp_buf=info->buf; /* temporary buffer pointer in case realloc fails */
392
393 if((info->buf = HDrealloc(info->buf, (uint32)new_len))==NULL) {
394 info->buf=temp_buf;
395 HGOTO_ERROR(DFE_NOSPACE, FAIL);
396 } /* end if */
397 }
398
399 /* update length */
400 info->length=new_len;
401 } /* end if */
402
403 /* Copy data to buffer */
404 HDmemcpy(info->buf+access_rec->posn,data,length);
405
406 /* Mark the buffer as modified */
407 info->modified=TRUE;
408
409 /* update access record */
410 access_rec->posn += length;
411
412 ret_value = length; /* return length of bytes written */
413
414 done:
415 if(ret_value == FAIL)
416 { /* Error condition cleanup */
417
418 } /* end if */
419
420 /* Normal function cleanup */
421
422 return(ret_value);
423 } /* HBPwrite */
424
425 /* ------------------------------ HBPinquire ------------------------------ */
426 /*
427 NAME
428 HBPinquire -- retreive information about a buffered element
429 USAGE
430 int32 HBPinquire(access_rec, file, tag, ref, len, off, pos, acc, sp)
431 access_t * access_rec; IN: access record to return info about
432 uint16 * file; OUT: file ID;
433 uint16 * tag; OUT: tag of info record;
434 uint16 * ref; OUT: ref of info record;
435 int32 * len; OUT: length of element;
436 int32 * off; OUT: offset of element (NOT correct);
437 int32 * pos; OUT: current position in element;
438 int16 * acc; OUT: access mode;
439 int16 * sp; OUT: special code;
440 RETURNS
441 SUCCEED
442 DESCRIPTION
443 Return interesting information about a buffered element.
444 NULL can be passed for any of the OUT parameters if their
445 value is not needed.
446
447 ---------------------------------------------------------------------------*/
448 int32
HBPinquire(accrec_t * access_rec,int32 * pfile_id,uint16 * ptag,uint16 * pref,int32 * plength,int32 * poffset,int32 * pposn,int16 * paccess,int16 * pspecial)449 HBPinquire(accrec_t * access_rec, int32 *pfile_id, uint16 *ptag,
450 uint16 *pref, int32 *plength, int32 *poffset,
451 int32 *pposn, int16 *paccess, int16 *pspecial)
452 {
453 CONSTR(FUNC, "HBPinquire"); /* for HERROR */
454 bufinfo_t *info = /* special information record */
455 (bufinfo_t *) access_rec->special_info;
456 uint16 data_tag,data_ref; /* tag/ref of the data we are checking */
457 int32 data_off; /* offset of the data we are checking */
458 int32 ret_value = SUCCEED;
459
460 /* Get the data's offset & length */
461 if(HTPinquire(info->buf_access_rec->ddid,&data_tag,&data_ref,&data_off,NULL)==FAIL)
462 HGOTO_ERROR(DFE_INTERNAL, FAIL);
463
464 /* fill in the variables if they are present */
465 if (pfile_id)
466 *pfile_id = access_rec->file_id;
467 if (ptag)
468 *ptag = data_tag;
469 if (pref)
470 *pref = data_ref;
471 if (plength)
472 *plength = info->length; /* pass along our value, which might be different from that on disk */
473 if (poffset)
474 *poffset = data_off;
475 if (pposn)
476 *pposn = access_rec->posn;
477 if (paccess)
478 *paccess = (int16)access_rec->access;
479 if (pspecial)
480 *pspecial = (int16)access_rec->special;
481
482 done:
483 if(ret_value == FAIL)
484 { /* Error condition cleanup */
485
486 } /* end if */
487
488 /* Normal function cleanup */
489
490 return ret_value;
491 } /* HBPinquire */
492
493 /* ----------------------------- HBPendaccess ----------------------------- */
494 /*
495 NAME
496 HBPendacess -- flush buffer, free AID
497 USAGE
498 intn HBPendaccess(access_rec)
499 access_t * access_rec; IN: access record to close
500 RETURNS
501 SUCCEED / FAIL
502 DESCRIPTION
503 Flush the buffer (if modified) and free the AID
504
505 ---------------------------------------------------------------------------*/
506 intn
HBPendaccess(accrec_t * access_rec)507 HBPendaccess(accrec_t * access_rec)
508 {
509 CONSTR(FUNC, "HBPendaccess"); /* for HERROR */
510 intn ret_value = SUCCEED;
511
512 /* validate argument */
513 if (access_rec == NULL)
514 HGOTO_ERROR(DFE_ARGS, FAIL);
515
516 /* shut down the memory buffer and dependant access record */
517 if (HBPcloseAID(access_rec) == FAIL)
518 HGOTO_ERROR(DFE_CANTCLOSE, FAIL);
519
520 /* free the access record */
521 HIrelease_accrec_node(access_rec);
522
523 done:
524 if(ret_value == FAIL)
525 { /* Error condition cleanup */
526 if(access_rec!=NULL)
527 HIrelease_accrec_node(access_rec);
528 } /* end if */
529
530 /* Normal function cleanup */
531
532 return ret_value;
533 } /* HBPendaccess */
534
535 /* ----------------------------- HBPcloseAID ------------------------------ */
536 /*
537 NAME
538 HBPcloseAID -- flush buffer and free memory but keep AID active
539 USAGE
540 int32 HXPcloseAID(access_rec)
541 access_t * access_rec; IN: access record of file to close
542 RETURNS
543 SUCCEED / FAIL
544 DESCRIPTION
545 Flush the buffered data (if modified) and free special element information,
546 but do *NOT* free the AID.
547
548 This is called by Hnextread() which reuses an AID to point to
549 the 'next' object as requested. If the current object was an
550 buffered object, the buffer needs to be flushed and freed before all
551 reference to it is lost.
552
553 ---------------------------------------------------------------------------*/
554 int32
HBPcloseAID(accrec_t * access_rec)555 HBPcloseAID(accrec_t * access_rec)
556 {
557 CONSTR(FUNC, "HBPcloseAID"); /* for HERROR */
558 bufinfo_t *info = /* special information record */
559 (bufinfo_t *) access_rec->special_info;
560 int32 ret_value = SUCCEED;
561
562 /* detach the special information record.
563 If no more references to that, free the record */
564
565 if (--(info->attached) == 0)
566 {
567 /* Flush the data if it's been modified */
568 if(info->modified) {
569 if (Hwrite(info->buf_aid, info->length, info->buf) == FAIL)
570 HGOTO_ERROR(DFE_WRITEERROR, FAIL);
571 } /* end if */
572
573 /* Free the memory buffer */
574 HDfree(info->buf);
575
576 /* Close the dependent access record */
577 Hendaccess(info->buf_aid);
578
579 HDfree(info);
580 access_rec->special_info = NULL;
581 }
582
583 done:
584 if(ret_value == FAIL)
585 { /* Error condition cleanup */
586
587 } /* end if */
588
589 /* Normal function cleanup */
590
591 return(ret_value);
592 } /* HBPcloseAID */
593
594 /* ------------------------------- HBPinfo -------------------------------- */
595 /*
596 NAME
597 HBPinfo -- return info about an external element
598 USAGE
599 int32 HBPinfo(access_rec, info_block)
600 accrec_t * access_rec; IN: access record of element
601 sp_info_block_t * info_block; OUT: information about the special element
602 RETURNS
603 SUCCEED / FAIL
604 DESCRIPTION
605 Return information about the given external element. Info_block is
606 assumed to be non-NULL.
607
608 --------------------------------------------------------------------------- */
609 int32
HBPinfo(accrec_t * access_rec,sp_info_block_t * info_block)610 HBPinfo(accrec_t * access_rec, sp_info_block_t * info_block)
611 {
612 CONSTR(FUNC, "HBPinfo"); /* for HERROR */
613 bufinfo_t *info = /* special information record */
614 (bufinfo_t *) access_rec->special_info;
615 int32 ret_value = SUCCEED;
616
617 /* validate access record */
618 if (access_rec->special != SPECIAL_BUFFERED)
619 HGOTO_ERROR(DFE_INTERNAL, FAIL);
620
621 /* fill in the info_block */
622 info_block->key = SPECIAL_BUFFERED;
623
624 info_block->buf_aid = info->buf_aid;
625
626 done:
627 if(ret_value == FAIL)
628 { /* Error condition cleanup */
629
630 } /* end if */
631
632 /* Normal function cleanup */
633
634 return(ret_value);
635 } /* HBPinfo */
636
637