blob: 3eb1f2e55b9bcb9450f2cf785177108b342fd27c [file] [log] [blame]
#ifndef __GEMFIRE_DATAOUTPUT_H__
#define __GEMFIRE_DATAOUTPUT_H__
/*=========================================================================
* Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
#include "gfcpp_globals.hpp"
#include "ExceptionTypes.hpp"
#include "Log.hpp"
#include "Serializable.hpp"
#include "CacheableString.hpp"
extern "C" {
#include <string.h>
#include <stdlib.h>
}
/**
* @file
*/
namespace gemfire {
/**
* C style memory allocation that throws OutOfMemoryException
* if it fails
*/
#define GF_ALLOC(v,t,s) \
{ \
v = (t*)malloc((s) * sizeof(t)); \
if ((v) == NULL) { \
throw OutOfMemoryException( \
"Out of Memory while allocating buffer for "#t" of size "#s); \
} \
}
/**
* C style memory re-allocation that throws OutOfMemoryException
* if it fails
*/
#define GF_RESIZE(v,t,s) \
{ \
v = (t*)realloc(v, (s) * sizeof(t)); \
if ((v) == NULL) { \
throw OutOfMemoryException( \
"Out of Memory while resizing buffer for "#t); \
} \
}
#define GF_FREE(v) free(v)
/**
* Provide operations for writing primitive data values, byte arrays,
* strings, <code>Serializable</code> objects to a byte stream.
* This class is intentionally not thread safe.
*/
class CPPCACHE_EXPORT DataOutput
{
public:
/**
* Construct a new DataOutput with an optional parameter to indicate whether
* the buffer created should be released in DataOutput destructor.
*/
DataOutput();
/**
* Write an unsigned byte to the <code>DataOutput</code>.
*
* @param value the unsigned byte to be written
*/
inline void write(uint8_t value)
{
ensureCapacity(1);
writeNoCheck(value);
}
/**
* Write a signed byte to the <code>DataOutput</code>.
*
* @param value the signed byte to be written
*/
inline void write(int8_t value)
{
write((uint8_t)value);
}
/**
* Write a boolean value to the <code>DataOutput</code>.
*
* @param value the boolean value to be written
*/
inline void writeBoolean( bool value )
{
write( (uint8_t)value );
}
/**
* Write an array of unsigned bytes to the <code>DataOutput</code>.
*
* @param value the array of unsigned bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytes( const uint8_t* bytes, int32_t len )
{
if (len >= 0) {
ensureCapacity( len + 5 );
writeArrayLen( bytes==NULL ? 0 : len ); // length of bytes...
if ( len > 0 && bytes != NULL) {
memcpy( m_buf, bytes, len );
m_buf += len;
}
} else {
write((int8_t) -1 );
}
}
/**
* Write an array of signed bytes to the <code>DataOutput</code>.
*
* @param value the array of signed bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytes( const int8_t* bytes, int32_t len )
{
writeBytes( (const uint8_t*)bytes, len );
}
/**
* Write an array of unsigned bytes without its length to the
* <code>DataOutput</code>.
* @remarks The difference between this and <code>writeBytes</code> is that
* this does write the length of bytes so the corresponding
* <code>DataInput::readBytesOnly</code> (unlike
* <code>DataInput::readBytes</code>) needs the length argument explicitly.
*
* @param value the array of unsigned bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytesOnly( const uint8_t* bytes, uint32_t len )
{
ensureCapacity( len );
memcpy( m_buf, bytes, len );
m_buf += len;
}
/**
* Write an array of signed bytes without its length to the
* <code>DataOutput</code>.
* @remarks The difference between this and <code>writeBytes</code> is that
* this does write the length of bytes so the corresponding
* <code>DataInput::readBytesOnly</code> (unlike
* <code>DataInput::readBytes</code>) needs the length argument explicitly.
*
* @param value the array of signed bytes to be written
* @param len the number of bytes from the start of array to be written
*/
inline void writeBytesOnly( const int8_t* bytes, uint32_t len )
{
writeBytesOnly( (const uint8_t*)bytes, len );
}
/**
* Write a 16-bit unsigned integer value to the <code>DataOutput</code>.
*
* @param value the 16-bit unsigned integer value to be written
*/
inline void writeInt( uint16_t value )
{
ensureCapacity( 2 );
*(m_buf++) = (uint8_t)(value >> 8);
*(m_buf++) = (uint8_t)value;
}
/**
* Write a 16-bit Char (wchar_t) value to the <code>DataOutput</code>.
*
* @param value the 16-bit wchar_t value to be written
*/
inline void writeChar( uint16_t value )
{
ensureCapacity( 2 );
*(m_buf++) = (uint8_t)(value >> 8);
*(m_buf++) = (uint8_t)value;
}
/**
* Write a 32-bit unsigned integer value to the <code>DataOutput</code>.
*
* @param value the 32-bit unsigned integer value to be written
*/
inline void writeInt( uint32_t value )
{
ensureCapacity( 4 );
*(m_buf++) = (uint8_t)(value >> 24);
*(m_buf++) = (uint8_t)(value >> 16);
*(m_buf++) = (uint8_t)(value >> 8);
*(m_buf++) = (uint8_t)value;
}
/**
* Write a 64-bit unsigned integer value to the <code>DataOutput</code>.
*
* @param value the 64-bit unsigned integer value to be written
*/
inline void writeInt( uint64_t value )
{
ensureCapacity( 8 );
// the defines are not reliable and can be changed by compiler options.
// Hence using sizeof() test instead.
//#if defined(_LP64) || ( defined(__WORDSIZE) && __WORDSIZE == 64 ) ||
//( defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64 )
if ( sizeof( long ) == 8 ) {
*(m_buf++) = (uint8_t)(value >> 56);
*(m_buf++) = (uint8_t)(value >> 48);
*(m_buf++) = (uint8_t)(value >> 40);
*(m_buf++) = (uint8_t)(value >> 32);
*(m_buf++) = (uint8_t)(value >> 24);
*(m_buf++) = (uint8_t)(value >> 16);
*(m_buf++) = (uint8_t)(value >> 8);
*(m_buf++) = (uint8_t)value;
} else {
uint32_t hword = (uint32_t)(value >> 32);
*(m_buf++) = (uint8_t)(hword >> 24);
*(m_buf++) = (uint8_t)(hword >> 16);
*(m_buf++) = (uint8_t)(hword >> 8);
*(m_buf++) = (uint8_t)hword;
hword = (uint32_t)value;
*(m_buf++) = (uint8_t)(hword >> 24);
*(m_buf++) = (uint8_t)(hword >> 16);
*(m_buf++) = (uint8_t)(hword >> 8);
*(m_buf++) = (uint8_t)hword;
}
}
/**
* Write a 16-bit signed integer value to the <code>DataOutput</code>.
*
* @param value the 16-bit signed integer value to be written
*/
inline void writeInt( int16_t value )
{
writeInt( (uint16_t)value );
}
/**
* Write a 32-bit signed integer value to the <code>DataOutput</code>.
*
* @param value the 32-bit signed integer value to be written
*/
inline void writeInt( int32_t value )
{
writeInt( (uint32_t)value );
}
/**
* Write a 64-bit signed integer value to the <code>DataOutput</code>.
*
* @param value the 64-bit signed integer value to be written
*/
inline void writeInt( int64_t value )
{
writeInt( (uint64_t)value );
}
/**
* Write a 32-bit signed integer array length value to the
* <code>DataOutput</code> in a manner compatible with java server's
* <code>DataSerializer.writeArrayLength</code>.
*
* @param value the 32-bit signed integer array length to be written
*/
inline void writeArrayLen( int32_t len )
{
if (len == -1) {
write((int8_t) -1);
} else if (len <= 252) { // 252 is java's ((byte)-4 && 0xFF)
write((uint8_t)len);
} else if (len <= 0xFFFF) {
write((int8_t) -2);
writeInt((uint16_t)len);
} else {
write((int8_t) -3);
writeInt(len);
}
}
/**
* Write a float value to the <code>DataOutput</code>.
*
* @param value the float value to be written
*/
inline void writeFloat( float value )
{
union float_uint32_t
{
float f;
uint32_t u;
}v;
v.f = value;
writeInt( v.u );
}
/**
* Write a double precision real number to the <code>DataOutput</code>.
*
* @param value the double precision real number to be written
*/
inline void writeDouble( double value )
{
union double_uint64_t
{
double d;
uint64_t ll;
}v;
v.d = value;
writeInt( v.ll );
}
/**
* Writes the given ASCII string supporting maximum length of 64K
* (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeBytes</code> or
* <code>writeASCIIHuge</code> to write ASCII strings of length larger
* than this.
*
* @param value the C string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeASCII(const char* value, uint32_t length = 0)
{
if ( value != NULL ) {
if ( length == 0 ) {
length = (uint32_t)strlen( value );
}
uint16_t len = (uint16_t)( length > 0xFFFF ? 0xFFFF : length );
writeInt( len );
writeBytesOnly( (int8_t*)value, len ); // K64
} else {
writeInt( (uint16_t)0 );
}
}
inline void writeNativeString(const char* value)
{
//create cacheable string
//write typeid id.
//call todata
CacheableStringPtr csPtr = CacheableString::create(value);
write(csPtr->typeId());
csPtr->toData(*this);
}
/**
* Writes the given ASCII string supporting upto maximum 32-bit
* integer value.
* @remarks Use this to write large ASCII strings. The other
* <code>writeASCII</code> method will truncate strings greater than
* 64K in size.
*
* @param value the wide-character string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeASCIIHuge(const char* value, uint32_t length = 0)
{
if ( value != NULL ) {
if ( length == 0 ) {
length = (uint32_t)strlen( value );
}
writeInt( (uint32_t) length );
writeBytesOnly( (int8_t*)value, (uint32_t) length );
} else {
writeInt( (uint32_t)0 );
}
}
/**
* Writes the given given string using java modified UTF-8 encoding
* supporting maximum encoded length of 64K (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeUTFHuge</code> to write
* strings of length larger than this.
*
* @param value the C string to be written
*
*/
inline void writeFullUTF(const char* value, uint32_t length = 0)
{
if ( value != NULL ) {
int32_t len = getEncodedLength( value, length );
uint16_t encodedLen = (uint16_t)( len > 0xFFFF ? 0xFFFF : len );
writeInt( (int32_t)encodedLen );
ensureCapacity( encodedLen );
write((int8_t)0); // isObject = 0 BYTE_CODE
uint8_t* end = m_buf + encodedLen;
while( m_buf < end ) {
encodeChar( *value++ );
}
if ( m_buf > end ) m_buf = end;
} else {
writeInt( (uint16_t)0 );
}
}
/**
* Writes the given given string using java modified UTF-8 encoding
* supporting maximum encoded length of 64K (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeUTFHuge</code> to write
* strings of length larger than this.
*
* @param value the C string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeUTF(const char* value, uint32_t length = 0)
{
if ( value != NULL ) {
int32_t len = getEncodedLength( value, length );
uint16_t encodedLen = (uint16_t)( len > 0xFFFF ? 0xFFFF : len );
writeInt( encodedLen );
ensureCapacity( encodedLen );
uint8_t* end = m_buf + encodedLen;
while( m_buf < end ) {
encodeChar( *value++ );
}
if ( m_buf > end ) m_buf = end;
} else {
writeInt( (uint16_t)0 );
}
}
/**
* Writes the given string using java modified UTF-8 encoding.
* @remarks Use this to write large strings. The other
* <code>writeUTF</code> method will truncate strings greater than
* 64K in size.
*
* @param value the C string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
* assuming a null terminated string; do not use this unless sure
* that the UTF string does not contain any null characters
*/
inline void writeUTFHuge(const char* value, uint32_t length = 0)
{
if (value != NULL) {
if (length == 0) {
length = (uint32_t)strlen(value);
}
writeInt(length);
ensureCapacity(length * 2);
for (uint32_t pos = 0; pos < length; pos++) {
writeNoCheck((int8_t)0);
writeNoCheck((int8_t)value[pos]);
}
}
else {
writeInt((uint32_t)0);
}
}
/**
* Writes the given given string using java modified UTF-8 encoding
* supporting maximum encoded length of 64K (i.e. unsigned 16-bit integer).
* @remarks The string will be truncated if greater than the maximum
* permissible length of 64K. Use <code>writeUTFHuge</code> to write
* strings of length larger than this.
*
* @param value the wide-character string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeUTF(const wchar_t* value, uint32_t length = 0)
{
if ( value != NULL ) {
int32_t len = getEncodedLength( value, length );
uint16_t encodedLen = (uint16_t)( len > 0xFFFF ? 0xFFFF : len );
writeInt( encodedLen );
ensureCapacity( encodedLen );
uint8_t* end = m_buf + encodedLen;
while( m_buf < end ) {
encodeChar( *value++ );
}
if ( m_buf > end ) m_buf = end;
} else {
writeInt( (uint16_t)0 );
}
}
/**
* Writes the given string using java modified UTF-8 encoding.
* @remarks Use this to write large strings. The other
* <code>writeUTF</code> method will truncate strings greater than
* 64K in size.
*
* @param value the wide-character string to be written
* @param length the number of characters from start of string to be
* written; the default value of 0 implies the complete string
*/
inline void writeUTFHuge(const wchar_t* value, uint32_t length = 0)
{
if (value != NULL) {
if (length == 0) {
length = (uint32_t)wcslen(value);
}
writeInt(length);
ensureCapacity(length * 2);
for (uint32_t pos = 0; pos < length; pos++) {
uint16_t item = (uint16_t)value[pos];
writeNoCheck((uint8_t)((item & 0xFF00) >> 8));
writeNoCheck((uint8_t)(item & 0xFF));
}
}
else {
writeInt((uint32_t)0);
}
}
/**
* Get the length required to represent a given wide-character string in
* java modified UTF-8 format.
*
* @param value The C string.
* @param length The length of the string; or zero to use the full string.
* @return The length required for representation in java modified
* UTF-8 format.
* @see DataInput::getDecodedLength
*/
inline static int32_t getEncodedLength( const char* value,
int32_t length = 0, uint32_t* valLength = NULL )
{
if ( value == NULL ) return 0;
char currentChar;
int32_t encodedLen = 0;
const char* start = value;
if ( length == 0 ) {
while ( (currentChar = *value) != '\0' ) {
getEncodedLength( currentChar, encodedLen );
value++;
}
} else {
const char* end = value + length;
while ( value < end ) {
currentChar = *value;
getEncodedLength( currentChar, encodedLen );
value++;
}
}
if ( valLength != NULL ) {
*valLength = static_cast<uint32_t>(value - start);
}
return encodedLen;
}
/**
* Get the length required to represent a given wide-character string in
* java modified UTF-8 format.
*
* @param value The wide-character string.
* @param length The length of the string.
* @return The length required for representation in java modified
* UTF-8 format.
* @see DataInput::getDecodedLength
*/
inline static int32_t getEncodedLength( const wchar_t* value,
int32_t length = 0, uint32_t* valLength = NULL )
{
if ( value == NULL ) return 0;
wchar_t currentChar;
int32_t encodedLen = 0;
const wchar_t* start = value;
if ( length == 0 ) {
while ( (currentChar = *value) != 0 ) {
getEncodedLength( currentChar, encodedLen );
value++;
}
} else {
const wchar_t* end = value + length;
while ( value < end ) {
currentChar = *value;
getEncodedLength( currentChar, encodedLen );
value++;
}
}
if ( valLength != NULL ) {
*valLength = static_cast<uint32_t>(value - start);
}
return encodedLen;
}
/**
* Write a <code>Serializable</code> object to the <code>DataOutput</code>.
*
* @param objptr smart pointer to the <code>Serializable</code> object
* to be written
*/
template< class PTR >
void writeObject( const SharedPtr<PTR>& objptr, bool isDelta = false )
{
writeObjectInternal( objptr.ptr( ), isDelta );
}
/**
* Write a <code>Serializable</code> object to the <code>DataOutput</code>.
*
* @param objptr pointer to the <code>Serializable</code> object
* to be written
*/
void writeObject( const Serializable* objptr )
{
writeObjectInternal( objptr );
}
/**
* Get an internal pointer to the current location in the
* <code>DataOutput</code> byte array.
*/
const uint8_t* getCursor()
{
return m_buf;
}
/**
* Advance the buffer cursor by the given offset.
*
* @param offset the offset by which to advance the cursor
*/
void advanceCursor(uint32_t offset)
{
ensureCapacity(offset);
m_buf += offset;
}
/**
* Rewind the buffer cursor by the given offset.
*
* @param offset the offset by which to rewind the cursor
*/
void rewindCursor(uint32_t offset)
{
m_buf -= offset;
}
void updateValueAtPos(uint32_t offset, uint8_t value)
{
m_bytes[offset] = value;
}
uint8_t getValueAtPos(uint32_t offset)
{
return m_bytes[offset];
}
/** destructor */
~DataOutput( )
{
reset();
DataOutput::checkinBuffer(m_bytes, m_size);
}
/**
* Get a pointer to the internal buffer of <code>DataOutput</code>.
*/
inline const uint8_t* getBuffer( ) const
{
//GF_R_ASSERT(!((uint32_t)(m_bytes) % 4));
return m_bytes;
}
/**
* Get a pointer to the internal buffer of <code>DataOutput</code>.
*/
inline uint32_t getRemainingBufferLength( ) const
{
//GF_R_ASSERT(!((uint32_t)(m_bytes) % 4));
return m_size - getBufferLength();
}
/**
* Get a pointer to the internal buffer of <code>DataOutput</code>.
*
* @param rsize the size of buffer is filled in this output parameter;
* should not be NULL
*/
inline const uint8_t* getBuffer( uint32_t* rsize ) const
{
*rsize = (uint32_t)( m_buf - m_bytes );
//GF_R_ASSERT(!((uint32_t)(m_bytes) % 4));
return m_bytes;
}
inline uint8_t* getBufferCopy( )
{
uint32_t size = (uint32_t)( m_buf - m_bytes );
uint8_t * result;
GF_ALLOC(result, uint8_t, size);
memcpy( result, m_bytes, size );
return result;
}
/**
* Get the length of current data in the internal buffer of
* <code>DataOutput</code>.
*/
inline uint32_t getBufferLength() const {
return (uint32_t)( m_buf - m_bytes );
}
/**
* Reset the internal cursor to the start of the buffer.
*/
inline void reset()
{
if (m_haveBigBuffer) {
// free existing buffer
GF_FREE(m_bytes);
// create smaller buffer
GF_ALLOC(m_bytes, uint8_t, m_lowWaterMark);
m_size = m_lowWaterMark;
// reset the flag
m_haveBigBuffer = false;
// release the lock
releaseLock();
}
m_buf = m_bytes;
}
// make sure there is room left for the requested size item.
inline void ensureCapacity( uint32_t size )
{
uint32_t offset = (uint32_t)( m_buf - m_bytes );
if ( (m_size - offset) < size ) {
uint32_t newSize = m_size * 2 + (8192 * (size / 8192));
if (newSize >= m_highWaterMark && !m_haveBigBuffer) {
// acquire the lock
acquireLock();
// set flag
m_haveBigBuffer = true;
}
m_size = newSize;
GF_RESIZE( m_bytes, uint8_t, m_size );
m_buf = m_bytes + offset;
}
}
/*
* This is for internal use
*/
const char* getPoolName()
{
return m_poolName;
}
/*
* This is for internal use
*/
void setPoolName(const char* poolName)
{
m_poolName = poolName;
}
uint8_t * getBufferCopyFrom(const uint8_t *from, uint32_t length)
{
uint8_t * result;
GF_NEW( result, uint8_t[ length ] );
memcpy( result, from, length);
return result;
}
static void safeDelete(uint8_t* src)
{
GF_SAFE_DELETE(src);
}
static DataOutput* getDataOutput()
{
return new DataOutput();
}
static void releaseDataOutput(DataOutput* dataOutput)
{
GF_SAFE_DELETE(dataOutput);
}
private:
void writeObjectInternal( const Serializable* ptr, bool isDelta = false );
static void acquireLock();
static void releaseLock();
const char* m_poolName;
// memory m_buffer to encode to.
uint8_t* m_bytes;
// cursor.
uint8_t* m_buf;
// size of m_bytes.
uint32_t m_size;
// high and low water marks for buffer size
static uint32_t m_lowWaterMark;
static uint32_t m_highWaterMark;
// flag to indicate we have a big buffer
volatile bool m_haveBigBuffer;
inline static void getEncodedLength( const char val, int32_t& encodedLen )
{
if ( (val == 0) || (val & 0x80) ) {
// two byte.
encodedLen += 2;
} else {
// one byte.
encodedLen++;
}
}
inline static void getEncodedLength( const wchar_t val, int32_t& encodedLen )
{
if( val == 0)
{
encodedLen += 2;
}
else if ( val < 0x80 )//ASCII character
{
encodedLen++;
}
else if ( val < 0x800 )
{
encodedLen += 2;
}
else
{
encodedLen += 3;
}
}
inline void encodeChar( const char value )
{
uint8_t tmp = (uint8_t)value;
if ( (tmp == 0) || (tmp & 0x80) ) {
// two byte.
*(m_buf++) = (uint8_t)(0xc0 | ((tmp & 0xc0 ) >> 6));
*(m_buf++) = (uint8_t)(0x80 | (tmp & 0x3f ));
} else {
// one byte.
*(m_buf++) = tmp;
}
}
// this will lose the character set encoding.
inline void encodeChar( const wchar_t value )
{
uint16_t c = (uint16_t)value;
if ( c == 0 ) {
*(m_buf++) = 0xc0;
*(m_buf++) = 0x80;
}
else if ( c < 0x80 ) {//ASCII character
*(m_buf++) = (uint8_t)c;
}
else if ( c < 0x800 ) {
*(m_buf++) = (uint8_t)( 0xC0 | c >> 6 );
*(m_buf++) = (uint8_t)( 0x80 | (c & 0x3F) );
}
else {
*(m_buf++) = (uint8_t)( 0xE0 | c >> 12 );
*(m_buf++) = (uint8_t)( 0x80 | ((c >> 6) & 0x3F) );
*(m_buf++) = (uint8_t)( 0x80 | (c & 0x3F) );
}
}
inline void writeNoCheck(uint8_t value)
{
*(m_buf++) = value;
}
inline void writeNoCheck(int8_t value)
{
writeNoCheck((uint8_t)value);
}
static uint8_t* checkoutBuffer( uint32_t* size );
static void checkinBuffer( uint8_t* buffer, uint32_t size );
// disable copy constructor and assignment
DataOutput(const DataOutput&);
DataOutput& operator =(const DataOutput&);
};
}
#endif // __GEMFIRE_DATAOUTPUT_H__