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