bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
vdata.cc
1// This file is part of the hdf4 data handler for the OPeNDAP data server.
2
3// Copyright (c) 2005 OPeNDAP, Inc.
4// Author: James Gallagher <jgallagher@opendap.org>
5//
6// This is free software; you can redistribute it and/or modify it under the
7// terms of the GNU Lesser General Public License as published by the Free
8// Software Foundation; either version 2.1 of the License, or (at your
9// option) any later version.
10//
11// This software is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14// License for more details.
15//
16// You should have received a copy of the GNU Lesser General Public License
17// along with this software; if not, write to the Free Software Foundation,
18// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19//
20// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
21
23// Copyright 1996, by the California Institute of Technology.
24// ALL RIGHTS RESERVED. United States Government Sponsorship
25// acknowledged. Any commercial use must be negotiated with the
26// Office of Technology Transfer at the California Institute of
27// Technology. This software may be subject to U.S. export control
28// laws and regulations. By accepting this software, the user
29// agrees to comply with all applicable U.S. export laws and
30// regulations. User has the responsibility to obtain export
31// licenses, or other export authority as may be required before
32// exporting such information to foreign countries or providing
33// access to foreign persons.
34
35// U.S. Government Sponsorship under NASA Contract
36// NAS7-1260 is acknowledged.
37//
38// Author: Todd.K.Karakashian@jpl.nasa.gov
39//
40// $RCSfile: vdata.cc,v $ - classes for HDF VDATA
41//
43
44#include "config_hdf.h"
45
46#include <mfhdf.h>
47
48#ifdef __POWERPC__
49#undef isascii
50#endif
51
52#include <string>
53#include <vector>
54#include <set>
55#include <algorithm>
56
57// libdap
58#include <libdap/InternalErr.h>
59#include <libdap/util.h>
60#include <libdap/debug.h>
61
62// bes
63#include <BESDebug.h>
64#include <hcstream.h>
65#include <hdfclass.h>
66
67using std::set;
68using std::less;
69using namespace libdap;
70
71
72static void LoadField(int32 vid, int index, int32 begin, int32 end,
73 hdf_field & f);
74static bool IsInternalVdata(int32 fid, int32 ref);
75
76//
77// hdfistream_vdata -- protected member functions
78//
79
80// initialize hdfistream_vdata
81void hdfistream_vdata::_init(void) {
82 _vdata_id = _index = _attr_index = _nattrs = 0;
83 _meta = false;
84 _vdata_refs.clear();
85 _recs.set = false;
86 return;
87}
88
89void hdfistream_vdata::_get_fileinfo(void) {
90 // build list ref numbers of all Vdata's in the file
91 int32 ref = -1;
92 while ((ref = VSgetid(_file_id, ref)) != -1) {
93 if (!IsInternalVdata(_file_id, ref))
94 _vdata_refs.push_back(ref);
95 }
96 return;
97}
98
99void hdfistream_vdata::_seek_next(void) {
100 _index++;
101 if (!eos())
102 _seek(_vdata_refs[_index]);
103 return;
104}
105
106void hdfistream_vdata::_seek(const char *name) {
107 int32 ref = VSfind(_file_id, name);
108 if (ref < 0)
109 THROW(hcerr_vdatafind);
110 else
111 _seek(ref);
112
113 return;
114}
115
116void hdfistream_vdata::_seek(int32 ref) {
117 if (_vdata_id != 0)
118 VSdetach(_vdata_id);
119 auto r = find(_vdata_refs.begin(), _vdata_refs.end(),ref);
120 if (r == _vdata_refs.end())
121 THROW(hcerr_vdatafind);
122 _index = r - _vdata_refs.begin();
123 if ((_vdata_id = VSattach(_file_id, ref, "r")) < 0) {
124 _vdata_id = 0;
125 THROW(hcerr_vdataopen);
126 }
127 _attr_index = 0;
128 _nattrs = VSfnattrs(_vdata_id, _HDF_VDATA);
129 return;
130}
131
132//
133// hdfistream_vdata -- public member functions
134//
135
136hdfistream_vdata::hdfistream_vdata(const string& filename) :
137 hdfistream_obj(filename) {
138 _init();
139 if (_filename.size() != 0) // if ctor specified a null filename
140 open(_filename.c_str());
141 return;
142}
143
144void hdfistream_vdata::open(const string & filename) {
145 open(filename.c_str());
146 return;
147}
148
149void hdfistream_vdata::open(const char *filename) {
150 if (_file_id != 0)
151 close();
152 if ((_file_id = Hopen(filename, DFACC_RDONLY, 0)) < 0)
153 THROW(hcerr_openfile);
154 if (Vstart(_file_id) < 0) // Vstart is a macro for Vinitialize
155 THROW(hcerr_openfile);
156
157 BESDEBUG("h4", "vdata file opened: id=" << _file_id << endl);
158
159 _filename = filename;
160 _get_fileinfo();
161 rewind();
162 return;
163}
164
165void hdfistream_vdata::close(void) {
166 BESDEBUG("h4",
167 "vdata file closed: id=" << _file_id << ", this: " << this << endl);
168
169 if (_vdata_id != 0)
170 VSdetach(_vdata_id);
171 if (_file_id != 0) {
172 int status = Vend(_file_id); // Vend is a macro for Vfinish
173 BESDEBUG("h4",
174 "vdata Vend status: " << status << ", this: " << this << endl);
175
176 status = Hclose(_file_id);
177 BESDEBUG("h4",
178 "vdata HClose status: " << status << ", this: " << this << endl);
179 }
180 _vdata_id = _file_id = _index = _attr_index = _nattrs = 0;
181 _vdata_refs.clear(); // clear refs
182 _recs.set = false;
183 return;
184}
185
186void hdfistream_vdata::seek(int index) {
187 if (index < 0 || index >= (int) _vdata_refs.size())
188 THROW(hcerr_range);
189 _seek(_vdata_refs[index]);
190 _index = index;
191 return;
192}
193
194void hdfistream_vdata::seek_ref(int ref) {
195 _seek(ref); // _seek() sets _index
196 return;
197}
198
199void hdfistream_vdata::seek(const string & name) {
200 seek(name.c_str());
201}
202
203void hdfistream_vdata::seek(const char *name) {
204 _seek(name);
205 return;
206}
207
208bool hdfistream_vdata::setrecs(int32 begin, int32 end) {
209 if (_vdata_id != 0) {
210 int32 il;
211 VSQueryinterlace(_vdata_id, &il);
212 if (il != FULL_INTERLACE)
213 return false;
214 else {
215 int32 cnt;
216 VSQuerycount(_vdata_id, &cnt);
217 if (begin < 0 || end >= cnt)
218 return false;
219 else {
220 _recs.begin = begin;
221 _recs.end = end;
222 _recs.set = true;
223 }
224 }
225 }
226 return true;
227}
228
229// check to see if stream is positioned past the last attribute in the
230// currently open Vdata
231bool hdfistream_vdata::eo_attr(void) const {
232 if (_filename.size() == 0) // no file open
233 THROW(hcerr_invstream);
234 if (eos() && !bos()) // if eos(), then always eo_attr()
235 return true;
236 else {
237 return (_attr_index >= _nattrs); // or positioned after last Vdata attr?
238 }
239}
240
241// Read all attributes in the stream
242hdfistream_vdata & hdfistream_vdata::operator>>(vector<hdf_attr> &hav) {
243 // hav = vector<hdf_attr>0; // reset vector
244 for (hdf_attr att; !eo_attr();) {
245 *this >> att;
246 hav.push_back(att);
247 }
248 return *this;
249}
250
251// read all Vdata's in the stream
252hdfistream_vdata & hdfistream_vdata::operator>>(vector<hdf_vdata> &hvv) {
253 for (hdf_vdata hv; !eos();) {
254 *this >> hv;
255 hvv.push_back(hv);
256 }
257 return *this;
258}
259
260// read an attribute from the stream
261hdfistream_vdata & hdfistream_vdata::operator>>(hdf_attr & ha) {
262 // delete any previous data in ha
263 ha.name = string();
264 ha.values = hdf_genvec();
265
266 if (_filename.size() == 0) // no file open
267 THROW(hcerr_invstream);
268 if (eo_attr()) // if positioned past last attr, do nothing
269 return *this;
270
271 char name[hdfclass::MAXSTR];
272 int32 number_type;
273 int32 count;
274 int32 size;
275
276 if (VSattrinfo(_vdata_id, _HDF_VDATA, _attr_index, name, &number_type,
277 &count, &size) < 0)
278 THROW(hcerr_vdatainfo);
279
280 // allocate a temporary C array to hold data from VSgetattr()
281 auto data = new char[count * DFKNTsize(number_type)];
282 if (data == nullptr)
283 THROW(hcerr_nomemory);
284
285 // read attribute values and store them in an hdf_genvec
286 if (VSgetattr(_vdata_id, _HDF_VDATA, _attr_index, data) < 0) {
287 delete[] data; // problem: clean up and throw an exception
288 THROW(hcerr_vdatainfo);
289 }
290 if (count > 0) {
291 ha.values = hdf_genvec(number_type, data, count);
292 }
293 delete[] data; // deallocate temporary C array
294
295 // increment attribute index to next attribute
296 ++_attr_index;
297 ha.name = name; // assign attribute name
298 return *this;
299}
300
301// read a Vdata from the stream
302hdfistream_vdata & hdfistream_vdata::operator>>(hdf_vdata & hv) {
303
304 // delete any previous data in hv
305 hv.fields.clear();
306 hv.vclass = hv.name = string();
307
308 if (_vdata_id == 0)
309 THROW(hcerr_invstream); // no vdata open!
310 if (eos())
311 return *this;
312
313 // assign Vdata ref
314 hv.ref = _vdata_refs[_index];
315 // retrieve Vdata attributes
316 *this >> hv.attrs;
317 // retrieve Vdata name, class, number of records
318 char name[hdfclass::MAXSTR];
319 char vclass[hdfclass::MAXSTR];
320 int32 nrecs;
321 if (VSinquire(_vdata_id, &nrecs, nullptr, nullptr, nullptr, name)
322 < 0)
323 THROW(hcerr_vdatainfo);
324 hv.name = string(name);
325 if (VSgetclass(_vdata_id, vclass) < 0)
326 THROW(hcerr_vdatainfo);
327 hv.vclass = string(vclass);
328
329 // retrieve number of fields
330 int nfields = VFnfields(_vdata_id);
331 if (nfields < 0)
332 THROW(hcerr_vdatainfo);
333
334 // retrieve field information
335 hv.fields = vector<hdf_field> ();
336 for (int i = 0; i < nfields; ++i) {
337 hv.fields.push_back(hdf_field());
338 if (_meta)
339 LoadField(_vdata_id, i, 0, 0, hv.fields[i]);
340 else if (_recs.set)
341 LoadField(_vdata_id, i, _recs.begin, _recs.end, hv.fields[i]);
342 else
343 LoadField(_vdata_id, i, 0, nrecs - 1, hv.fields[i]);
344 }
345 _seek_next();
346 return *this;
347}
348
349// The following code causes memory leaking when called in vgroup.cc.
350// Since it is only used in vgroup.cc, we move the code to vgroup.cc.
351// The memory leaking is gone.
352#if 0
353bool hdfistream_vdata::isInternalVdata(int ref) const {
354 set<string, less<string> > reserved_names;
355 reserved_names.insert("RIATTR0.0N");
356
357 set<string, less<string> > reserved_classes;
358 reserved_classes.insert("Attr0.0");
359 reserved_classes.insert("RIATTR0.0C");
360 reserved_classes.insert("DimVal0.0");
361 reserved_classes.insert("DimVal0.1");
362 reserved_classes.insert("_HDF_CHK_TBL_0");
363
364 // get name, class of vdata
365 int vid;
366 if ((vid = VSattach(_file_id, ref, "r")) < 0) {
367 THROW(hcerr_vdataopen);
368 }
369 char name[hdfclass::MAXSTR];
370 char vclass[hdfclass::MAXSTR];
371 if (VSgetname(vid, name) < 0) {
372 VSdetach(vid);
373 THROW(hcerr_vdatainfo);
374 }
375 if (reserved_names.find(string(name)) != reserved_names.end()) {
376 VSdetach(vid);
377 return true;
378 }
379
380 if (VSgetclass(vid, vclass) < 0) {
381 VSdetach(vid);
382 THROW(hcerr_vdatainfo);
383 }
384
385 VSdetach(vid);
386
387 if (reserved_classes.find(string(vclass)) != reserved_classes.end())
388 return true;
389
390 return false;
391}
392#endif
393
394static void LoadField(int32 vid, int index, int32 begin, int32 end,
395 hdf_field & f) {
396 DBG(cerr << "LoadField - vid: " << vid << endl);
397
398 // position to first record too read
399 if (VSseek(vid, begin) < 0)
400 THROW(hcerr_vdataseek);
401 int32 nrecs = end - begin + 1;
402
403 // retrieve name of field
404 DBG(cerr << "vid: " << vid << ", index: " << index << endl);
405 const char *fieldname = VFfieldname(vid, index);
406 if (fieldname == nullptr)
407 THROW(hcerr_vdatainfo);
408 f.name = string(fieldname);
409
410 // retrieve order of field
411 int32 fieldorder = VFfieldorder(vid, index);
412 if (fieldorder < 0)
413 THROW(hcerr_vdatainfo);
414
415 // retrieve size of the field in memory
416 int32 fieldsize = VFfieldisize(vid, index);
417 if (fieldsize < 0)
418 THROW(hcerr_vdatainfo);
419
420 // retrieve HDF data type of field
421 int32 fieldtype = VFfieldtype(vid, index);
422 if (fieldtype < 0)
423 THROW(hcerr_vdatainfo);
424
425 // for each component, set type and optionally load data
426 hdf_genvec gv;
427 vector<char> data;
428 if (nrecs > 0) { // if nrecs > 0 then load data for field
429 data.resize(fieldsize * nrecs);
430 DBG(cerr << "LoadField: vid=" << vid << ", fieldname=" << fieldname << endl);
431 // TODO: Is this correct?
432 // This code originally treated a negative return as a failure and
433 // threw an exception. I'm still waiting on a verdict, but it seems
434 // that the negative return may have been indicating an empty vdata
435 // (or SDS dataset?) instead. For now, simply calling return here
436 // addresses a problem where some NASA data files cannot otherwise be
437 // read. See Trac ticket http://scm.opendap.org/trac/ticket/1793.
438 // jhrg 8/17/11
439 if (VSsetfields(vid, fieldname) < 0) {
440 return;
441 }
442
443 if (VSread(vid, (uchar8 *)data.data(), nrecs, FULL_INTERLACE) < 0) {
444 throw InternalErr(__FILE__, __LINE__, "VSread error with the field: " + f.name + " (" + long_to_string(vid) + ").");
445 }
446#if 0
447 if ((VSsetfields(vid, fieldname) < 0) || (VSread(vid, (uchar8 *) data,
448 nrecs, FULL_INTERLACE) < 0)) {
449 delete[] data;
450 throw hcerr_vdataread(__FILE__, __LINE__);
451 }
452#endif
453 }
454 int stride = fieldorder;
455 for (int i = 0; i < fieldorder; ++i) {
456 if (nrecs == 0)
457 gv = hdf_genvec(fieldtype, nullptr, 0, 0, 0);
458 else
459 gv = hdf_genvec(fieldtype, data.data(), i, (nrecs * fieldorder) - 1, stride);
460 f.vals.push_back(gv);
461 }
462}
463
464//
465// hdf_vdata related member functions
466//
467
468// verify that the hdf_field class is in an OK state.
469bool hdf_field::_ok(void) const {
470
471 // make sure that there is some data stored in the hdf_field
472 if (vals.empty() == true)
473 return false;
474
475 // if the field has order > 1, check to make sure that the number types of all
476 // the columns in the field are the same and are not 0
477 if (vals.size() > 1) {
478 int32 nt = vals[0].number_type();
479 if (nt == 0)
480 return false;
481 for (int i = 1; i < (int) vals.size(); ++i)
482 if (vals[i].number_type() != nt || vals[i].number_type() == 0)
483 return false;
484 }
485
486 return true; // passed all the tests
487}
488
489bool hdf_vdata::_ok(void) const {
490
491 // make sure there are fields stored in this vdata
492 if (fields.empty() == true)
493 return false;
494
495 // make sure the fields are themselves OK
496 for (const auto & field:fields)
497 if (!field)
498 return false;
499
500 return true; // passed all the tests
501}
502
503static bool IsInternalVdata(int32 fid, int32 ref) {
504 set<string, less<string> > reserved_names;
505 reserved_names.insert("RIATTR0.0N");
506
507 set<string, less<string> > reserved_classes;
508 reserved_classes.insert("Attr0.0");
509 reserved_classes.insert("RIATTR0.0C");
510 reserved_classes.insert("DimVal0.0");
511 reserved_classes.insert("DimVal0.1");
512 reserved_classes.insert("_HDF_CHK_TBL_0");
513
514 // get name, class of vdata
515 int vid;
516 if ((vid = VSattach(fid, ref, "r")) < 0) {
517 THROW(hcerr_vdataopen);
518 }
519 char name[hdfclass::MAXSTR];
520 char vclass[hdfclass::MAXSTR];
521 if (VSgetname(vid, name) < 0) {
522 VSdetach(vid);
523 THROW(hcerr_vdatainfo);
524 }
525 if (reserved_names.find(string(name)) != reserved_names.end()) {
526 VSdetach(vid);
527 return true;
528 }
529
530 if (VSgetclass(vid, vclass) < 0) {
531 VSdetach(vid);
532 THROW(hcerr_vdatainfo);
533 }
534
535 VSdetach(vid);
536
537 if (reserved_classes.find(string(vclass)) != reserved_classes.end())
538 return true;
539
540 return false;
541}