1 // D4StreamUnMarshaller.cc
2
3 // -*- mode: c++; c-basic-offset:4 -*-
4
5 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
6 // Access Protocol.
7
8 // Copyright (c) 2012 OPeNDAP, Inc.
9 // Author: James Gallagher <jgallagher@opendap.org>
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 //
25 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
26
27 #include "config.h"
28
29 #include <byteswap.h>
30 #include <cassert>
31
32 #include <iostream>
33 #include <iomanip>
34 #include <limits>
35 #include <string>
36 #include <sstream>
37
38 //#define DODS_DEBUG2 1
39 //#define DODS_DEBUG 1
40
41 #include "util.h"
42 #include "InternalErr.h"
43 #include "D4StreamUnMarshaller.h"
44 #include "debug.h"
45 #include "DapIndent.h"
46
47 namespace libdap {
48
49 /**
50 * @brief Build a DAP4 Stream unMarshaller.
51 *
52 * Build a DAP4 Stream UnMarshaller initialed to read from am istream object.
53 * Figure out if the words read for values need to be 'twiddled' based on the
54 * byte-order of the stream an this host (see set_twiddle_bytes()).
55 *
56 * @param in Read from this input stream
57 * @param is_stream_bigendian The byte order of the data in the stream
58 */
D4StreamUnMarshaller(istream & in,bool twiddle_bytes)59 D4StreamUnMarshaller::D4StreamUnMarshaller(istream &in, bool twiddle_bytes) : d_in( in ), d_twiddle_bytes(twiddle_bytes)
60 {
61 assert(sizeof(std::streamsize) >= sizeof(int64_t));
62
63 #if USE_XDR_FOR_IEEE754_ENCODING
64 // XDR is used to handle transforming non-ieee754 reals, nothing else.
65 xdrmem_create(&d_source, d_buf, sizeof(dods_float64), XDR_DECODE);
66 #endif
67
68 // This will cause exceptions to be thrown on i/o errors. The exception
69 // will be ostream::failure
70 d_in.exceptions(istream::failbit | istream::badbit);
71
72 }
73
74 /**
75 * When using this constructor, set_twiddle_bytes() should be called
76 * before data are processed.
77 *
78 * @param in
79 */
D4StreamUnMarshaller(istream & in)80 D4StreamUnMarshaller::D4StreamUnMarshaller(istream &in) : d_in( in ), d_twiddle_bytes(false)
81 {
82 assert(sizeof(std::streamsize) >= sizeof(int64_t));
83
84 #if USE_XDR_FOR_IEEE754_ENCODING
85 // XDR is used to handle transforming non-ieee754 reals, nothing else.
86 xdrmem_create(&d_source, d_buf, sizeof(dods_float64), XDR_DECODE);
87 #endif
88
89 // This will cause exceptions to be thrown on i/o errors. The exception
90 // will be ostream::failure
91 d_in.exceptions(istream::failbit | istream::badbit);
92 }
93
~D4StreamUnMarshaller()94 D4StreamUnMarshaller::~D4StreamUnMarshaller( )
95 {
96 #if USE_XDR_FOR_IEEE754_ENCODING
97 xdr_destroy(&d_source);
98 #endif
99 }
100
get_checksum()101 Crc32::checksum D4StreamUnMarshaller::get_checksum()
102 {
103 Crc32::checksum c;
104 d_in.read(reinterpret_cast<char*>(&c), sizeof(Crc32::checksum));
105
106 return c;
107 }
108
get_checksum_str()109 string D4StreamUnMarshaller::get_checksum_str()
110 {
111 ostringstream oss;
112 oss.setf(ios::hex, ios::basefield);
113 oss << setfill('0') << setw(8) << get_checksum();
114
115 return oss.str();
116 }
117
118 void
get_byte(dods_byte & val)119 D4StreamUnMarshaller::get_byte( dods_byte &val )
120 {
121 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_byte));
122 }
123
124 void
get_int8(dods_int8 & val)125 D4StreamUnMarshaller::get_int8( dods_int8 &val )
126 {
127 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_int8));
128 }
129
130 void
get_int16(dods_int16 & val)131 D4StreamUnMarshaller::get_int16( dods_int16 &val )
132 {
133 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_int16));
134 if (d_twiddle_bytes)
135 val = bswap_16(val);
136 }
137
138 void
get_int32(dods_int32 & val)139 D4StreamUnMarshaller::get_int32( dods_int32 &val )
140 {
141 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_int32));
142 if (d_twiddle_bytes)
143 val = bswap_32(val);
144 }
145
146 void
get_int64(dods_int64 & val)147 D4StreamUnMarshaller::get_int64( dods_int64 &val )
148 {
149 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_int64));
150 if (d_twiddle_bytes)
151 val = bswap_64(val);
152 }
153
154 void
get_float32(dods_float32 & val)155 D4StreamUnMarshaller::get_float32( dods_float32 &val )
156 {
157 #if !USE_XDR_FOR_IEEE754_ENCODING
158 assert(std::numeric_limits<float>::is_iec559);
159
160 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_float32));
161 if (d_twiddle_bytes) {
162 dods_int32 *i = reinterpret_cast<dods_int32*>(&val);
163 *i = bswap_32(*i);
164 }
165
166 #else
167 if (std::numeric_limits<float>::is_iec559) {
168 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_float32));
169 if (d_twiddle_bytes) {
170 dods_int32 *i = reinterpret_cast<dods_int32*>(&val);
171 *i = bswap_32(*i);
172 }
173
174 }
175 else {
176 xdr_setpos( &d_source, 0);
177 d_in.read(d_buf, sizeof(dods_float32));
178
179 if (!xdr_float(&d_source, &val))
180 throw Error("Network I/O Error. Could not read float 64 data.");
181 }
182 #endif
183 }
184
185 void
get_float64(dods_float64 & val)186 D4StreamUnMarshaller::get_float64( dods_float64 &val )
187 {
188 #if !USE_XDR_FOR_IEEE754_ENCODING
189 assert(std::numeric_limits<double>::is_iec559);
190
191 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_float64));
192 if (d_twiddle_bytes) {
193 dods_int64 *i = reinterpret_cast<dods_int64*>(&val);
194 *i = bswap_64(*i);
195 }
196
197 #else
198 if (std::numeric_limits<float>::is_iec559) {
199 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_float64));
200 if (d_twiddle_bytes) {
201 dods_int64 *i = reinterpret_cast<dods_int64*>(&val);
202 *i = bswap_64(*i);
203 }
204 }
205 else {
206 xdr_setpos( &d_source, 0);
207 d_in.read(d_buf, sizeof(dods_float64));
208
209 if (!xdr_double(&d_source, &val))
210 throw Error("Network I/O Error. Could not read float 64 data.");
211 }
212 #endif
213 }
214
215 void
get_uint16(dods_uint16 & val)216 D4StreamUnMarshaller::get_uint16( dods_uint16 &val )
217 {
218 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_uint16));
219 if (d_twiddle_bytes)
220 val = bswap_16(val);
221 }
222
223 void
get_uint32(dods_uint32 & val)224 D4StreamUnMarshaller::get_uint32( dods_uint32 &val )
225 {
226 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_uint32));
227 if (d_twiddle_bytes)
228 val = bswap_32(val);
229 }
230
231 void
get_uint64(dods_uint64 & val)232 D4StreamUnMarshaller::get_uint64( dods_uint64 &val )
233 {
234 d_in.read(reinterpret_cast<char*>(&val), sizeof(dods_uint64));
235 if (d_twiddle_bytes)
236 val = bswap_64(val);
237 }
238
239 void
get_str(string & val)240 D4StreamUnMarshaller::get_str( string &val )
241 {
242 int64_t len;
243 d_in.read(reinterpret_cast<char*>(&len), sizeof(int64_t));
244
245 val.resize(len);
246 d_in.read(&val[0], len);
247 }
248
249 void
get_url(string & val)250 D4StreamUnMarshaller::get_url( string &val )
251 {
252 get_str( val ) ;
253 }
254
255 /**
256 * Read a count value from the stream. This is used with D4Sequence
257 * which needs to use various other 'get' methods to read its fields.
258 * Methods like get_opaque_dap4() handle reading their count values
259 * themselves.
260 *
261 * @param count The number of elements to follow.
262 */
263 int64_t
get_count()264 D4StreamUnMarshaller::get_count()
265 {
266 int64_t count;
267 d_in.read(reinterpret_cast<char*>(&count), sizeof(count));
268 return count;
269 }
270
271 /**
272 * Get opaque data when the size of the data to be read is not known in
273 * advance.
274 *
275 * @param val Value-result parameter for the data; caller must delete.
276 * @param len value-result parameter for the length of the data
277 */
278 void
get_opaque_dap4(char ** val,int64_t & len)279 D4StreamUnMarshaller::get_opaque_dap4( char **val, int64_t &len )
280 {
281 //len = get_length_prefix();
282 d_in.read(reinterpret_cast<char*>(&len), sizeof(len));
283
284 *val = new char[len];
285 d_in.read(*val, len);
286 }
287
288 void
get_opaque_dap4(vector<uint8_t> & val)289 D4StreamUnMarshaller::get_opaque_dap4( vector<uint8_t> &val )
290 {
291 //len = get_length_prefix();
292 int64_t len;
293 d_in.read(reinterpret_cast<char*>(&len), sizeof(len));
294
295 val.resize(len);
296 d_in.read(reinterpret_cast<char*>(&val[0]), len);
297 }
298
299 void
get_vector(char * val,int64_t bytes)300 D4StreamUnMarshaller::get_vector( char *val, int64_t bytes )
301 {
302 d_in.read(val, bytes);
303 }
304
305 #if USE_XDR_FOR_IEEE754_ENCODING
m_deserialize_reals(char * val,int64_t num,int width,Type type)306 void D4StreamUnMarshaller::m_deserialize_reals(char *val, int64_t num, int width, Type type)
307 {
308 int64_t size = num * width;
309 // char *buf = (char*)malloc(size); jhrg 7/23/13
310 vector<char> buf(size);
311 XDR xdr;
312 xdrmem_create(&xdr, &buf[0], size, XDR_DECODE);
313 try {
314 xdr_setpos(&d_source, 0);
315 d_in.read(&buf[0], size);
316
317 if(!xdr_array(&xdr, &val, (unsigned int *)&num, size, width, XDRUtils::xdr_coder(type)))
318 throw InternalErr(__FILE__, __LINE__, "Error deserializing a Float64 array");
319
320 if (xdr_getpos(&xdr) != size)
321 throw InternalErr(__FILE__, __LINE__, "Error deserializing a Float64 array");
322 }
323 catch (...) {
324 xdr_destroy(&xdr);
325 throw;
326 }
327 xdr_destroy(&xdr);
328 }
329 #endif
330
m_twidle_vector_elements(char * vals,int64_t num,int width)331 void D4StreamUnMarshaller::m_twidle_vector_elements(char *vals, int64_t num, int width)
332 {
333 switch (width) {
334 case 2: {
335 dods_int16 *local = reinterpret_cast<dods_int16*>(vals);
336 while (num--) {
337 *local = bswap_16(*local);
338 local++;
339 }
340 break;
341 }
342 case 4: {
343 dods_int32 *local = reinterpret_cast<dods_int32*>(vals);;
344 while (num--) {
345 *local = bswap_32(*local);
346 local++;
347 }
348 break;
349 }
350 case 8: {
351 dods_int64 *local = reinterpret_cast<dods_int64*>(vals);;
352 while (num--) {
353 *local = bswap_64(*local);
354 local++;
355 }
356 break;
357 }
358 default:
359 throw InternalErr(__FILE__, __LINE__, "Unrecognized word size.");
360 }
361 }
362
363 void
get_vector(char * val,int64_t num_elem,int elem_size)364 D4StreamUnMarshaller::get_vector(char *val, int64_t num_elem, int elem_size)
365 {
366 assert(std::numeric_limits<float>::is_iec559);
367 assert(std::numeric_limits<double>::is_iec559);
368 assert(val);
369 assert(num_elem >= 0);
370 assert(elem_size > 0);
371
372 int64_t bytes;
373
374 switch (elem_size) {
375 case 1:
376 assert(!"Don't call this method for bytes, use put_vector(val, bytes) instead");
377 bytes = num_elem;
378 break;
379 case 2:
380 // Don't bother testing the sign bit
381 assert(!(num_elem & 0x4000000000000000)); // 0x 40 00 --> 0100 0000
382 bytes = num_elem << 1;
383 break;
384 case 4:
385 assert(!(num_elem & 0x6000000000000000)); // 0x 60 00 --> 0110 0000
386 bytes = num_elem << 2;
387 break;
388 case 8:
389 assert(!(num_elem & 0x7000000000000000)); // 0111 0000
390 bytes = num_elem << 3;
391 break;
392 default:
393 bytes = num_elem * elem_size;
394 break;
395 }
396
397 d_in.read(val, bytes);
398
399 if (d_twiddle_bytes)
400 m_twidle_vector_elements(val, num_elem, elem_size);
401 }
402
403 void
get_vector_float32(char * val,int64_t num_elem)404 D4StreamUnMarshaller::get_vector_float32(char *val, int64_t num_elem)
405 {
406 #if !USE_XDR_FOR_IEEE754_ENCODING
407 assert(std::numeric_limits<float>::is_iec559);
408 assert(val);
409 assert(num_elem >= 0);
410 assert(!(num_elem & 0x6000000000000000)); // 0x 60 00 --> 0110 0000
411
412 int64_t bytes = num_elem << 2;
413
414 d_in.read(val, bytes);
415
416 if (d_twiddle_bytes)
417 m_twidle_vector_elements(val, num_elem, sizeof(dods_float32));
418
419 #else
420 if (type == dods_float32_c && !std::numeric_limits<float>::is_iec559) {
421 // If not using IEEE 754, use XDR to get it that way.
422 m_deserialize_reals(val, num, 4, type);
423 }
424 else if (type == dods_float64_c && !std::numeric_limits<double>::is_iec559) {
425 m_deserialize_reals(val, num, 8, type);
426 }
427 else {
428 d_in.read(val, num * width);
429 if (d_twiddle_bytes)
430 m_twidle_vector_elements(val, num, width);
431 }
432 #endif
433 }
434
435 void
get_vector_float64(char * val,int64_t num_elem)436 D4StreamUnMarshaller::get_vector_float64(char *val, int64_t num_elem)
437 {
438 #if !USE_XDR_FOR_IEEE754_ENCODING
439 assert(std::numeric_limits<float>::is_iec559);
440 assert(val);
441 assert(num_elem >= 0);
442 assert(!(num_elem & 0x7000000000000000)); // 0x 70 00 --> 0111 0000
443
444 int64_t bytes = num_elem << 3;
445
446 d_in.read(val, bytes);
447
448 if (d_twiddle_bytes)
449 m_twidle_vector_elements(val, num_elem, sizeof(dods_float64));
450
451 #else
452 if (type == dods_float32_c && !std::numeric_limits<float>::is_iec559) {
453 // If not using IEEE 754, use XDR to get it that way.
454 m_deserialize_reals(val, num, 4, type);
455 }
456 else if (type == dods_float64_c && !std::numeric_limits<double>::is_iec559) {
457 m_deserialize_reals(val, num, 8, type);
458 }
459 else {
460 d_in.read(val, num * width);
461 if (d_twiddle_bytes)
462 m_twidle_vector_elements(val, num, width);
463 }
464 #endif
465 }
466
467 void
dump(ostream & strm) const468 D4StreamUnMarshaller::dump(ostream &strm) const
469 {
470 strm << DapIndent::LMarg << "D4StreamUnMarshaller::dump - ("
471 << (void *)this << ")" << endl ;
472 }
473
474 } // namespace libdap
475
476