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