2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 * binio.cpp - Binary stream I/O classes
17 * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
32 // If 'math.h' doesn't define HUGE_VAL, we try to use HUGE instead.
34 # define HUGE_VAL HUGE
41 #if BINIO_ENABLE_STRING
42 // String buffer size for std::string readString() method
43 #define STRINGBUFSIZE 256
48 const binio::Flags binio::system_flags = binio::detect_system_flags();
50 const binio::Flags binio::detect_system_flags()
61 if(endian_test.byte != 1) f |= BigEndian;
63 // IEEE-754 floating-point test
65 Byte *dat = (Byte *)&fl;
67 if(sizeof(float) == 4 && sizeof(double) == 8)
69 if(dat[0] == 0x40 && dat[1] == 0xD0 && !dat[2] && !dat[3])
72 if(dat[3] == 0x40 && dat[2] == 0xD0 && !dat[1] && !dat[0])
79 : my_flags(system_flags), err(NoError)
87 void binio::setFlag(Flag f, bool set)
95 bool binio::getFlag(Flag f)
97 return (my_flags & f ? true : false);
100 binio::Error binio::error()
110 return (err & Eof ? true : false);
113 /***** binistream *****/
115 binistream::binistream()
119 binistream::~binistream()
123 binistream::Int binistream::readInt(unsigned int size)
128 // Check if 'size' doesn't exceed our system's biggest type.
129 if(size > sizeof(Int)) {
134 for(i = 0; i < size; i++) {
136 if(getFlag(BigEndian))
146 binistream::Float binistream::readFloat(FType ft)
148 if(getFlag(FloatIEEE)) { // Read IEEE-754 floating-point value
149 unsigned int i, size;
153 // Determine appropriate size for given type.
155 case Single: size = 4; break; // 32 bits
156 case Double: size = 8; break; // 64 bits
159 // Determine byte ordering, depending on what we do next
160 if(system_flags & FloatIEEE)
161 swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
163 swap = !getFlag(BigEndian);
165 // Read the float byte by byte, converting endianess
166 for(i = 0; i < size; i++)
168 in[size - i - 1] = getByte();
172 if(system_flags & FloatIEEE) {
173 // Compatible system, let the hardware do the conversion
175 case Single: return *(float *)in;
176 case Double: return *(double *)in;
178 } else { // Incompatible system, convert manually
180 case Single: return ieee_single2float(in);
181 case Double: return ieee_double2float(in);
186 // User tried to read a (yet) unsupported floating-point type. Bail out.
187 err |= Unsupported; return 0.0;
190 binistream::Float binistream::ieee_single2float(Byte *data)
192 signed int sign = data[0] >> 7 ? -1 : 1;
193 unsigned int exp = ((data[0] << 1) & 0xff) | ((data[1] >> 7) & 1),
194 fracthi7 = data[1] & 0x7f;
195 Float fract = fracthi7 * 65536.0 + data[2] * 256.0 + data[3];
197 // Signed and unsigned zero
198 if(!exp && !fracthi7 && !data[2] && !data[3]) return sign * 0.0;
200 // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
202 if(!fracthi7 && !data[2] && !data[3]) {
204 if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
207 if(sign == -1) return -1.0; else return 1.0;
209 } else { // Not a number (maybe unsupported on non-IEEE systems)
213 err |= Unsupported; return 0.0;
217 if(!exp) // Unnormalized float values
218 return sign * pow(2, -126) * fract * pow(2, -23);
219 else // Normalized float values
220 return sign * pow(2, exp - 127) * (fract * pow(2, -23) + 1);
222 err |= Fatal; return 0.0;
225 binistream::Float binistream::ieee_double2float(Byte *data)
227 signed int sign = data[0] >> 7 ? -1 : 1;
228 unsigned int exp = ((unsigned int)(data[0] & 0x7f) << 4) | (data[1] >> 4),
229 fracthi4 = data[1] & 0xf;
230 Float fract = fracthi4 * pow(2, 48) + data[2] * pow(2, 40) + data[3] *
231 pow(2, 32) + data[4] * pow(2, 24) + data[5] * pow(2, 16) + data[6] *
234 // Signed and unsigned zero
235 if(!exp && !fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] &&
236 !data[6] && !data[7]) return sign * 0.0;
238 // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
240 if(!fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] && !data[6] &&
243 if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
246 if(sign == -1) return -1.0; else return 1.0;
248 } else { // Not a number (maybe unsupported on non-IEEE systems)
252 err |= Unsupported; return 0.0;
256 if(!exp) // Unnormalized float values
257 return sign * pow(2, -1022) * fract * pow(2, -52);
258 else // Normalized float values
259 return sign * pow(2, exp - 1023) * (fract * pow(2, -52) + 1);
261 err |= Fatal; return 0.0;
265 binio::Float binio::pow(Float base, signed int exp)
266 /* Our own, stripped-down version of pow() for not having to depend on 'math.h'.
267 * This one handles float values for the base and an integer exponent, both
268 * positive and negative.
276 for(i = 1; i < (exp < 0 ? -exp : exp); i++)
279 if(exp < 0) val = 1.0 / val;
285 unsigned long binistream::readString(char *str, unsigned long maxlen)
289 for(i = 0; i < maxlen; i++) {
290 str[i] = (char)getByte();
291 if(err) { str[i] = '\0'; return i; }
297 unsigned long binistream::readString(char *str, unsigned long maxlen,
302 for(i = 0; i < maxlen; i++) {
303 str[i] = (char)getByte();
304 if(str[i] == delim || err) { str[i] = '\0'; return i; }
311 #if BINIO_ENABLE_STRING
312 std::string binistream::readString(const char delim)
314 char buf[STRINGBUFSIZE + 1];
319 read = readString(buf, STRINGBUFSIZE, delim);
320 tempstr.append(buf, read);
321 } while(read == STRINGBUFSIZE);
327 binistream::Int binistream::peekInt(unsigned int size)
329 Int val = readInt(size);
330 if(!err) seek(-(long)size, Add);
334 binistream::Float binistream::peekFloat(FType ft)
336 Float val = readFloat(ft);
340 case Single: seek(-4, Add); break;
341 case Double: seek(-8, Add); break;
347 bool binistream::ateof()
349 Error olderr = err; // Save current error state
353 eof_then = eof(); // Get error state of next byte
354 err = olderr; // Restore original error state
358 void binistream::ignore(unsigned long amount)
362 for(i = 0; i < amount; i++)
366 /***** binostream *****/
368 binostream::binostream()
372 binostream::~binostream()
376 void binostream::writeInt(Int val, unsigned int size)
380 // Check if 'size' doesn't exceed our system's biggest type.
381 if(size > sizeof(Int)) { err |= Unsupported; return; }
383 for(i = 0; i < size; i++) {
384 if(getFlag(BigEndian))
385 putByte((val >> (size - i - 1) * 8) & 0xff);
393 void binostream::writeFloat(Float f, FType ft)
395 if(getFlag(FloatIEEE)) { // Write IEEE-754 floating-point value
396 unsigned int i, size;
400 if(system_flags & FloatIEEE) {
401 // compatible system, let the hardware do the conversion
405 // Hardware could be big or little endian, convert appropriately
406 swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
408 // Determine appropriate size for given type and convert by hardware
410 case Single: size = 4; out = (Byte *)&outf; break; // 32 bits
411 case Double: size = 8; out = (Byte *)&outd; break; // 64 bits
415 // incompatible system, do the conversion manually
418 // Our own value is always big endian, just check whether we have to
419 // convert for a different stream format.
420 swap = !getFlag(BigEndian);
422 // Convert system's float to requested IEEE-754 float
424 case Single: size = 4; float2ieee_single(f, buf); break;
425 case Double: size = 8; float2ieee_double(f, buf); break;
428 out = buf; // Make the value ready for writing
430 // No necessary support routines to do the conversion, bail out!
431 err |= Unsupported; return;
435 // Write the float byte by byte, converting endianess
436 if(swap) out += size - 1;
437 for(i = 0; i < size; i++) {
439 if(swap) out--; else out++;
442 return; // We're done.
445 // User tried to write an unsupported floating-point type. Bail out.
449 #ifdef BINIO_WITH_MATH
452 * Single and double floating-point to IEEE-754 equivalent conversion functions
453 * courtesy of Ken Turkowski.
455 * Copyright (C) 1989-1991 Ken Turkowski. <turk@computer.org>
457 * All rights reserved.
459 * Warranty Information
460 * Even though I have reviewed this software, I make no warranty
461 * or representation, either express or implied, with respect to this
462 * software, its quality, accuracy, merchantability, or fitness for a
463 * particular purpose. As a result, this software is provided "as is,"
464 * and you, its user, are assuming the entire risk as to its quality
467 * This code may be used and freely distributed as long as it includes
468 * this copyright notice and the above warranty information.
471 /****************************************************************
472 * The following two routines make up for deficiencies in many
473 * compilers to convert properly between unsigned integers and
474 * floating-point. Some compilers which have this bug are the
475 * THINK_C compiler for the Macintosh and the C compiler for the
476 * Silicon Graphics MIPS-based Iris.
477 ****************************************************************/
479 #ifdef applec /* The Apple C compiler works */
480 # define FloatToUnsigned(f) ((unsigned long)(f))
482 # define FloatToUnsigned(f) ((unsigned long)(((long)((f) - 2147483648.0)) + 2147483647L + 1))
486 #define SEXP_OFFSET 127
488 #define SEXP_POSITION (32-SEXP_SIZE-1)
490 void binostream::float2ieee_single(Float num, Byte *bytes)
495 if (num < 0) { /* Can't distinguish a negative zero */
508 fMant = frexp(num, &expon);
510 if ((expon > (SEXP_MAX-SEXP_OFFSET+1)) || !(fMant < 1)) {
511 /* NaN's and infinities fail second test */
512 bits = sign | 0x7F800000; /* +/- infinity */
518 if (expon < -(SEXP_OFFSET-2)) { /* Smaller than normalized */
519 int shift = (SEXP_POSITION+1) + (SEXP_OFFSET-2) + expon;
520 if (shift < 0) { /* Way too small: flush to zero */
523 else { /* Nonzero denormalized number */
524 mantissa = (long)(fMant * (1L << shift));
525 bits = sign | mantissa;
529 else { /* Normalized number */
530 mantissa = (long)floor(fMant * (1L << (SEXP_POSITION+1)));
531 mantissa -= (1L << SEXP_POSITION); /* Hide MSB */
532 bits = sign | ((long)((expon + SEXP_OFFSET - 1)) << SEXP_POSITION) | mantissa;
537 bytes[0] = bits >> 24; /* Copy to byte string */
538 bytes[1] = bits >> 16;
539 bytes[2] = bits >> 8;
543 #define DEXP_MAX 2047
544 #define DEXP_OFFSET 1023
546 #define DEXP_POSITION (32-DEXP_SIZE-1)
548 void binostream::float2ieee_double(Float num, Byte *bytes)
553 if (num < 0) { /* Can't distinguish a negative zero */
567 fMant = frexp(num, &expon);
569 if ((expon > (DEXP_MAX-DEXP_OFFSET+1)) || !(fMant < 1)) {
570 /* NaN's and infinities fail second test */
571 first = sign | 0x7FF00000; /* +/- infinity */
578 if (expon < -(DEXP_OFFSET-2)) { /* Smaller than normalized */
579 int shift = (DEXP_POSITION+1) + (DEXP_OFFSET-2) + expon;
580 if (shift < 0) { /* Too small for something in the MS word */
583 if (shift < 0) { /* Way too small: flush to zero */
586 else { /* Pretty small demorn */
587 second = FloatToUnsigned(floor(ldexp(fMant, shift)));
590 else { /* Nonzero denormalized number */
591 fsMant = ldexp(fMant, shift);
592 mantissa = (long)floor(fsMant);
593 first = sign | mantissa;
594 second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
598 else { /* Normalized number */
599 fsMant = ldexp(fMant, DEXP_POSITION+1);
600 mantissa = (long)floor(fsMant);
601 mantissa -= (1L << DEXP_POSITION); /* Hide MSB */
602 fsMant -= (1L << DEXP_POSITION);
603 first = sign | ((long)((expon + DEXP_OFFSET - 1)) << DEXP_POSITION) | mantissa;
604 second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
609 bytes[0] = first >> 24;
610 bytes[1] = first >> 16;
611 bytes[2] = first >> 8;
613 bytes[4] = second >> 24;
614 bytes[5] = second >> 16;
615 bytes[6] = second >> 8;
619 #endif // BINIO_WITH_MATH
621 unsigned long binostream::writeString(const char *str, unsigned long amount)
625 if(!amount) amount = strlen(str);
627 for(i = 0; i < amount; i++) {
635 #if BINIO_ENABLE_STRING
636 unsigned long binostream::writeString(const std::string &str)
638 return writeString(str.c_str());