mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-25 01:31:08 +00:00
Don't autorelease the malloc'ed pointer here; thus the behavior is analogous to decoding an object: it gives you a reference, it is the callers responsibility to free it. ([BinaryCStream -decodeValueOfCType:at:withName:]): [_C_DBL]: Fix typo. Use mantissa1, not mantissa2 twice. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1289 72102866-910b-0410-8b05-ffd578937521
477 lines
12 KiB
Objective-C
477 lines
12 KiB
Objective-C
/* Implementation of GNU Objective-C binary stream object for use serializing
|
||
Copyright (C) 1996 Free Software Foundation, Inc.
|
||
|
||
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
|
||
Written: Jan 1996
|
||
|
||
This file is part of the GNU Objective C Class Library.
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Library General Public
|
||
License as published by the Free Software Foundation; either
|
||
version 2 of the License, or (at your option) any later version.
|
||
|
||
This library is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
Library General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Library General Public
|
||
License along with this library; if not, write to the Free
|
||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
*/
|
||
|
||
#include <objects/stdobjects.h>
|
||
#include <objects/BinaryCStream.h>
|
||
#include <objects/NSString.h>
|
||
#include <objects/StdioStream.h>
|
||
#include <objects/TextCStream.h>
|
||
#include <objects/MallocAddress.h>
|
||
#include <Foundation/NSException.h>
|
||
#include <math.h>
|
||
|
||
#define DEFAULT_FORMAT_VERSION 0
|
||
|
||
#define ROUND(V, A) \
|
||
({ typeof(V) __v=(V); typeof(A) __a=(A); \
|
||
__a*((__v+__a-1)/__a); })
|
||
|
||
/* The number of bytes used to encode the length of a _C_CHARPTR
|
||
string that is encoded. */
|
||
#define NUM_BYTES_STRING_LENGTH 4
|
||
|
||
/* The value by which we multiply a float or double in order to bring
|
||
mantissa digits to the left-hand-side of the decimal point, so that
|
||
we can extra them by assigning the float or double to an int. */
|
||
#define FLOAT_FACTOR ((double)(1 << ((sizeof(int)*BITSPERBYTE)-2)))
|
||
|
||
@implementation BinaryCStream
|
||
|
||
+ (void) initialize
|
||
{
|
||
if (self == [BinaryCStream class])
|
||
/* Make sure that we don't overrun memory when reading _C_CHARPTR. */
|
||
assert (sizeof(unsigned) >= NUM_BYTES_STRING_LENGTH);
|
||
}
|
||
|
||
|
||
/* For debugging */
|
||
|
||
static BOOL debug_binary_coder;
|
||
|
||
+ setDebugging: (BOOL)f
|
||
{
|
||
debug_binary_coder = f;
|
||
return self;
|
||
}
|
||
|
||
+ debugStderrCoder
|
||
{
|
||
static id c = nil;
|
||
|
||
if (!c)
|
||
c = [[TextCStream alloc]
|
||
initForWritingToStream: [StdioStream standardError]];
|
||
return c;
|
||
}
|
||
|
||
|
||
/* Encoding/decoding C values */
|
||
|
||
- (void) encodeValueOfCType: (const char*)type
|
||
at: (const void*)d
|
||
withName: (id <String>) name
|
||
{
|
||
/* Make sure we're not being asked to encode an "ObjC" type. */
|
||
assert(type);
|
||
assert(*type != '@');
|
||
assert(*type != '^');
|
||
assert(*type != ':');
|
||
|
||
if (debug_binary_coder)
|
||
{
|
||
[[[self class] debugStderrCoder]
|
||
encodeValueOfCType: type
|
||
at: d
|
||
withName: name];
|
||
}
|
||
|
||
[stream writeByte: *type];
|
||
|
||
#define WRITE_SIGNED_TYPE(_PTR, _TYPE, _CONV_FUNC) \
|
||
{ \
|
||
char buffer[1+sizeof(_TYPE)]; \
|
||
buffer[0] = sizeof (_TYPE); \
|
||
if (*(_TYPE*)_PTR < 0) \
|
||
{ \
|
||
buffer[0] |= 0x80; \
|
||
*(_TYPE*)(buffer+1) = _CONV_FUNC (- *(_TYPE*)_PTR); \
|
||
} \
|
||
else \
|
||
{ \
|
||
*(_TYPE*)(buffer+1) = _CONV_FUNC (*(_TYPE*)_PTR); \
|
||
} \
|
||
[stream writeBytes: buffer length: 1+sizeof(_TYPE)]; \
|
||
}
|
||
|
||
#define READ_SIGNED_TYPE(_PTR, _TYPE, _CONV_FUNC) \
|
||
{ \
|
||
char sign, size; \
|
||
[stream readByte: &size]; \
|
||
sign = size & 0x80; \
|
||
size &= ~0x80; \
|
||
{ \
|
||
char buffer[size]; \
|
||
int read_size; \
|
||
read_size = [stream readBytes: buffer length: size]; \
|
||
assert (read_size == size); \
|
||
assert (size == sizeof(_TYPE)); \
|
||
*(unsigned _TYPE*)_PTR = \
|
||
_CONV_FUNC (*(unsigned _TYPE*)buffer); \
|
||
if (sign) \
|
||
*(_TYPE*)_PTR = - *(_TYPE*)_PTR; \
|
||
} \
|
||
}
|
||
|
||
/* Reading and writing unsigned scalar types. */
|
||
|
||
#define WRITE_UNSIGNED_TYPE(_PTR, _TYPE, _CONV_FUNC) \
|
||
{ \
|
||
char buffer[1+sizeof(_TYPE)]; \
|
||
buffer[0] = sizeof (_TYPE); \
|
||
*(_TYPE*)(buffer+1) = _CONV_FUNC (*(_TYPE*)_PTR); \
|
||
[stream writeBytes: buffer length: (1+sizeof(_TYPE))]; \
|
||
}
|
||
|
||
#define READ_UNSIGNED_TYPE(_PTR, _TYPE, _CONV_FUNC) \
|
||
{ \
|
||
char size; \
|
||
[stream readByte: &size]; \
|
||
{ \
|
||
char buffer[size]; \
|
||
int read_size; \
|
||
read_size = [stream readBytes: buffer length: size]; \
|
||
assert (read_size == size); \
|
||
assert (size == sizeof(_TYPE)); \
|
||
*(_TYPE*)_PTR = \
|
||
_CONV_FUNC (*(_TYPE*)buffer); \
|
||
} \
|
||
}
|
||
|
||
switch (*type)
|
||
{
|
||
case _C_CHARPTR:
|
||
{
|
||
unsigned length = strlen (*(char**)d);
|
||
unsigned nlength;
|
||
nlength = htonl (length);
|
||
[stream writeBytes: &nlength
|
||
length: NUM_BYTES_STRING_LENGTH];
|
||
[stream writeBytes: *(char**)d
|
||
length: length];
|
||
break;
|
||
}
|
||
|
||
case _C_CHR:
|
||
case _C_UCHR:
|
||
[stream writeByte: *(unsigned char*)d];
|
||
break;
|
||
|
||
/* Reading and writing signed scalar types. */
|
||
|
||
case _C_SHT:
|
||
WRITE_SIGNED_TYPE (d, short, htons);
|
||
break;
|
||
case _C_USHT:
|
||
WRITE_UNSIGNED_TYPE (d, unsigned short, htons);
|
||
break;
|
||
|
||
case _C_INT:
|
||
WRITE_SIGNED_TYPE (d, int, htonl);
|
||
break;
|
||
case _C_UINT:
|
||
WRITE_UNSIGNED_TYPE (d, unsigned int, htonl);
|
||
break;
|
||
|
||
case _C_LNG:
|
||
WRITE_SIGNED_TYPE (d, long, htonl);
|
||
break;
|
||
case _C_ULNG:
|
||
WRITE_UNSIGNED_TYPE (d, unsigned long, htonl);
|
||
break;
|
||
|
||
/* xxx The handling of floats and doubles could be improved.
|
||
e.g. I should account for varying sizeof(int) vs sizeof(double). */
|
||
|
||
case _C_FLT:
|
||
{
|
||
volatile double value;
|
||
int exp, mantissa;
|
||
value = *(float*)d;
|
||
/* Get the exponent */
|
||
value = frexp (value, &exp);
|
||
/* Get the mantissa. */
|
||
value *= FLOAT_FACTOR;
|
||
mantissa = value;
|
||
assert (value - mantissa == 0);
|
||
/* Encode the value as its two integer components. */
|
||
WRITE_SIGNED_TYPE (&exp, int, htonl);
|
||
WRITE_SIGNED_TYPE (&mantissa, int, htonl);
|
||
break;
|
||
}
|
||
|
||
case _C_DBL:
|
||
{
|
||
volatile double value;
|
||
int exp, mantissa1, mantissa2;
|
||
value = *(double*)d;
|
||
/* Get the exponent */
|
||
value = frexp (value, &exp);
|
||
/* Get the first part of the mantissa. */
|
||
value *= FLOAT_FACTOR;
|
||
mantissa1 = value;
|
||
value -= mantissa1;
|
||
value *= FLOAT_FACTOR;
|
||
mantissa2 = value;
|
||
assert (value - mantissa2 == 0);
|
||
/* Encode the value as its three integer components. */
|
||
WRITE_SIGNED_TYPE (&exp, int, htonl);
|
||
WRITE_SIGNED_TYPE (&mantissa1, int, htonl);
|
||
WRITE_SIGNED_TYPE (&mantissa2, int, htonl);
|
||
break;
|
||
}
|
||
|
||
case _C_ARY_B:
|
||
{
|
||
int len = atoi (type+1); /* xxx why +1 ? */
|
||
int offset;
|
||
|
||
while (isdigit(*++type));
|
||
offset = objc_sizeof_type(type);
|
||
[self encodeName:name];
|
||
[self encodeIndent];
|
||
while (len-- > 0)
|
||
{
|
||
/* Change this so we don't re-write type info every time. */
|
||
/* xxx We should be able to encode arrays "ObjC" types also! */
|
||
[self encodeValueOfCType:type
|
||
at:d
|
||
withName:@"array component"];
|
||
((char*)d) += offset;
|
||
}
|
||
[self encodeUnindent];
|
||
break;
|
||
}
|
||
case _C_STRUCT_B:
|
||
{
|
||
int acc_size = 0;
|
||
int align;
|
||
|
||
while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */
|
||
[self encodeName:name];
|
||
[self encodeIndent];
|
||
while (*type != _C_STRUCT_E)
|
||
{
|
||
align = objc_alignof_type (type); /* pad to alignment */
|
||
acc_size = ROUND (acc_size, align);
|
||
/* xxx We should be able to encode structs "ObjC" types also! */
|
||
[self encodeValueOfCType:type
|
||
at:((char*)d)+acc_size
|
||
withName:@"structure component"];
|
||
acc_size += objc_sizeof_type (type); /* add component size */
|
||
type = objc_skip_typespec (type); /* skip component */
|
||
}
|
||
[self encodeUnindent];
|
||
break;
|
||
}
|
||
default:
|
||
[NSException raise: NSGenericException
|
||
format: @"Unrecognized type %s", type];
|
||
}
|
||
}
|
||
|
||
- (void) decodeValueOfCType: (const char*)type
|
||
at: (void*)d
|
||
withName: (id <String> *)namePtr
|
||
{
|
||
char encoded_type;
|
||
|
||
assert(type);
|
||
assert(*type != '@');
|
||
assert(*type != '^');
|
||
assert(*type != ':');
|
||
|
||
[stream readByte: &encoded_type];
|
||
if (encoded_type != *type
|
||
&& !((encoded_type=='c' || encoded_type=='C')
|
||
&& (*type=='c' || *type=='C')))
|
||
[NSException raise: NSGenericException
|
||
format: @"Expected type \"%c\", got type \"%c\"",
|
||
*type, encoded_type];
|
||
|
||
switch (encoded_type)
|
||
{
|
||
case _C_CHARPTR:
|
||
{
|
||
unsigned length;
|
||
unsigned read_count;
|
||
read_count = [stream readBytes: &length
|
||
length: NUM_BYTES_STRING_LENGTH];
|
||
assert (read_count == NUM_BYTES_STRING_LENGTH);
|
||
length = ntohl (length);
|
||
OBJC_MALLOC (*(char**)d, char, length+1);
|
||
read_count = [stream readBytes: *(char**)d
|
||
length: length];
|
||
assert (read_count == length);
|
||
(*(char**)d)[length] = '\0';
|
||
/* Autorelease the newly malloc'ed pointer? Grep for (*objc_free)
|
||
to see the places the may have to be changed
|
||
[MallocAddress autoreleaseMallocAddress: *(char**)d]; */
|
||
break;
|
||
}
|
||
|
||
case _C_CHR:
|
||
case _C_UCHR:
|
||
[stream readByte: (unsigned char*)d];
|
||
break;
|
||
|
||
case _C_SHT:
|
||
READ_SIGNED_TYPE (d, short, ntohs);
|
||
break;
|
||
case _C_USHT:
|
||
READ_UNSIGNED_TYPE (d, unsigned short, ntohs);
|
||
break;
|
||
|
||
case _C_INT:
|
||
READ_SIGNED_TYPE (d, int, ntohl);
|
||
break;
|
||
case _C_UINT:
|
||
READ_UNSIGNED_TYPE (d, unsigned int, ntohl);
|
||
break;
|
||
|
||
case _C_LNG:
|
||
READ_SIGNED_TYPE (d, long, ntohl);
|
||
break;
|
||
case _C_ULNG:
|
||
READ_UNSIGNED_TYPE (d, unsigned long, ntohl);
|
||
break;
|
||
|
||
case _C_FLT:
|
||
{
|
||
int exp, mantissa;
|
||
double value;
|
||
/* Decode the exponent and mantissa. */
|
||
READ_SIGNED_TYPE (&exp, int, ntohl);
|
||
READ_SIGNED_TYPE (&mantissa, int, ntohl);
|
||
/* Assemble them into a double */
|
||
value = mantissa / FLOAT_FACTOR;
|
||
value = ldexp (value, exp);
|
||
/* Put the double into the requested memory location as a float */
|
||
*(float*)d = value;
|
||
break;
|
||
}
|
||
|
||
case _C_DBL:
|
||
{
|
||
int exp, mantissa1, mantissa2;
|
||
volatile double value;
|
||
/* Decode the exponent and the two pieces of the mantissa. */
|
||
READ_SIGNED_TYPE (&exp, int, ntohl);
|
||
READ_SIGNED_TYPE (&mantissa1, int, ntohl);
|
||
READ_SIGNED_TYPE (&mantissa2, int, ntohl);
|
||
/* Assemble them into a double */
|
||
value = ((mantissa2 / FLOAT_FACTOR) + mantissa1) / FLOAT_FACTOR;
|
||
value = ldexp (value, exp);
|
||
/* Put the double into the requested memory location. */
|
||
*(double*)d = value;
|
||
break;
|
||
}
|
||
|
||
case _C_ARY_B:
|
||
{
|
||
/* xxx Do we need to allocate space, just like _C_CHARPTR ? */
|
||
int len = atoi(type+1);
|
||
int offset;
|
||
[self decodeName:namePtr];
|
||
[self decodeIndent];
|
||
while (isdigit(*++type));
|
||
offset = objc_sizeof_type(type);
|
||
while (len-- > 0)
|
||
{
|
||
[self decodeValueOfCType:type
|
||
at:d
|
||
withName:namePtr];
|
||
((char*)d) += offset;
|
||
}
|
||
[self decodeUnindent];
|
||
break;
|
||
}
|
||
case _C_STRUCT_B:
|
||
{
|
||
/* xxx Do we need to allocate space just like char* ? No. */
|
||
int acc_size = 0;
|
||
int align;
|
||
const char *save_type = type;
|
||
|
||
while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */
|
||
[self decodeName:namePtr];
|
||
[self decodeIndent]; /* xxx insert [self decodeName:] */
|
||
while (*type != _C_STRUCT_E)
|
||
{
|
||
align = objc_alignof_type (type); /* pad to alignment */
|
||
acc_size = ROUND (acc_size, align);
|
||
[self decodeValueOfCType:type
|
||
at:((char*)d)+acc_size
|
||
withName:namePtr];
|
||
acc_size += objc_sizeof_type (type); /* add component size */
|
||
type = objc_skip_typespec (type); /* skip component */
|
||
}
|
||
type = save_type;
|
||
[self decodeUnindent];
|
||
break;
|
||
}
|
||
default:
|
||
[NSException raise: NSGenericException
|
||
format: @"Unrecognized Type %s", type];
|
||
}
|
||
|
||
if (debug_binary_coder)
|
||
{
|
||
[[[self class] debugStderrCoder]
|
||
encodeValueOfCType:type
|
||
at:d
|
||
withName:@"decoding unnamed"];
|
||
}
|
||
}
|
||
|
||
|
||
/* Returning default format version. */
|
||
|
||
+ (int) defaultFormatVersion
|
||
{
|
||
return DEFAULT_FORMAT_VERSION;
|
||
}
|
||
|
||
|
||
/* Encoding and decoding names. */
|
||
|
||
- (void) encodeName: (id <String>) name
|
||
{
|
||
if (debug_binary_coder)
|
||
[[[self class] debugStderrCoder]
|
||
encodeName:name];
|
||
}
|
||
|
||
- (void) decodeName: (id <String> *)n
|
||
{
|
||
#if 1
|
||
if (n)
|
||
*n = nil;
|
||
#else
|
||
if (n)
|
||
*n = [[[NSString alloc] init] autorelease];
|
||
#endif
|
||
}
|
||
|
||
@end
|
||
|