Files
pvData/pvDataApp/misc/byteBuffer.h
2011-09-15 20:44:08 +02:00

582 lines
15 KiB
C++

/* byteBuffer.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#ifndef BYTEBUFFER_H
#define BYTEBUFFER_H
#include <string>
#include <pv/pvType.h>
#include <epicsEndian.h>
#include <string.h>
#include <pv/epicsException.h>
namespace epics {
namespace pvData {
/*
TODO can be used:
MS Visual C++:
You include intrin.h and call the following functions:
For 16 bit numbers:
unsigned short _byteswap_ushort(unsigned short value);
For 32 bit numbers:
unsigned long _byteswap_ulong(unsigned long value);
For 64 bit numbers:
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
*/
/*
For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order.
You can get little-endian floats on big-endian machines and vice versa.
*/
#define GCC_VERSION_SINCE(major, minor, patchlevel) \
(defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
((__GNUC__ > (major)) || \
(__GNUC__ == (major) && __GNUC_MINOR__ > (minor)) || \
(__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel))))
#if GCC_VERSION_SINCE(4,3,0)
#define swap32(x) __builtin_bswap32(x)
#define swap64(x) __builtin_bswap64(x)
#define __byte_swap16(x) \
(((x) >> 8) | \
((x) << 8))
static inline uint16_t
swap16(uint16_t _x)
{
return (__byte_swap16(_x));
}
#else
#define __byte_swap16(x) \
(((x) >> 8) | \
((x) << 8))
#define __byte_swap32(x) \
((((x) & 0xff000000) >> 24) | \
(((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | \
(((x) & 0x000000ff) << 24))
#define __byte_swap64(x) \
(((x) >> 56) | \
(((x) >> 40) & 0xff00) | \
(((x) >> 24) & 0xff0000) | \
(((x) >> 8) & 0xff000000) | \
(((x) << 8) & ((uint64_t)0xff << 32)) | \
(((x) << 24) & ((uint64_t)0xff << 40)) | \
(((x) << 40) & ((uint64_t)0xff << 48)) | \
(((x) << 56)))
static inline uint16_t
swap16(uint16_t _x)
{
return (__byte_swap16(_x));
}
static inline uint32_t
swap32(uint32_t _x)
{
return (__byte_swap32(_x));
}
static inline uint64_t
swap64(uint64_t _x)
{
return (__byte_swap64(_x));
}
#endif
template<typename T>
inline T swap(T val) { return val; } // not valid
template<>
inline int16 swap(int16 val)
{
return swap16(val);
}
template<>
inline int32 swap(int32 val)
{
return swap32(val);
}
template<>
inline int64 swap(int64 val)
{
return swap64(val);
}
template<>
inline float swap(float val)
{
union {
int32 i;
float f;
} conv;
conv.f = val;
conv.i = swap32(conv.i);
return conv.f;
}
template<>
inline double swap(double val)
{
union {
int64 i;
double d;
} conv;
conv.d = val;
conv.i = swap64(conv.i);
return conv.d;
}
#define is_aligned(POINTER, BYTE_COUNT) \
(((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)
/*template <bool ENDIANESS_SUPPORT = false,
bool UNALIGNED_ACCESS = false,
bool ADAPTIVE_ACCESS = true,
bool USE_INLINE_MEMCPY = true>*/
#define ENDIANESS_SUPPORT true
#define UNALIGNED_ACCESS true
#define ADAPTIVE_ACCESS true
#define USE_INLINE_MEMCPY true
class ByteBuffer
{
public:
ByteBuffer(uintptr_t size, int byteOrder = EPICS_BYTE_ORDER) :
_buffer(0), _size(size), _reverseEndianess(byteOrder != EPICS_BYTE_ORDER)
{
_buffer = (char*)malloc(size);
clear();
}
~ByteBuffer()
{
if (_buffer) free(_buffer);
}
inline void setEndianess(int byteOrder)
{
_reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
}
inline const char* getBuffer()
{
return _buffer;
}
inline void clear()
{
_position = _buffer;
_limit = _buffer + _size;
}
inline void flip() {
_limit = _position;
_position = _buffer;
}
inline void rewind() {
_position = _buffer;
}
inline uintptr_t getPosition()
{
return (((uintptr_t)(const void *)_position) - ((uintptr_t)(const void *)_buffer));
}
inline void setPosition(uintptr_t pos)
{
_position = _buffer + pos;
}
inline uintptr_t getLimit()
{
return (((uintptr_t)(const void *)_limit) - ((uintptr_t)(const void *)_buffer));
}
inline void setLimit(uintptr_t limit)
{
_limit = _buffer + limit;
}
inline uintptr_t getRemaining()
{
return (((uintptr_t)(const void *)_limit) - ((uintptr_t)(const void *)_position));
}
inline uintptr_t getSize()
{
return _size;
}
template<typename T>
inline void put(T value)
{
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
*(_position++) = (int8)value;
return;
}
if (ENDIANESS_SUPPORT && _reverseEndianess)
{
value = swap<T>(value);
}
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
*((T*)_position) = value;
_position += sizeof(T);
}
else
{
// NOTE: this check and branching does not always payoff
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
*((T*)_position) = value;
_position += sizeof(T);
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(_position, &value, sizeof(T));
_position += sizeof(T);
}
else
{
// NOTE: compiler should optimize this and unroll the loop
for (size_t i = 0; i < sizeof(T); i++)
_position[i] = ((char*)&value)[i];
_position += sizeof(T);
}
}
}
}
template<typename T>
inline void put(uintptr_t index, T value)
{
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
*(_buffer + index) = (int8)value;
return;
}
if (ENDIANESS_SUPPORT && _reverseEndianess)
{
value = swap<T>(value);
}
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
*((T*)(_buffer + index)) = value;
}
else
{
// NOTE: this check and branching does not always payoff
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
*((T*)(_buffer + index)) = value;
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(_buffer + index, &value, sizeof(T));
}
else
{
// NOTE: compiler should optimize this and unroll the loop
char *p = _buffer + index;
for (size_t i = 0; i < sizeof(T); i++)
p[i] = ((char*)&value)[i];
}
}
}
}
template<typename T>
inline T get()
{
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
return (int8)(*(_position++));
}
T value;
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
value = *((T*)_position);
_position += sizeof(T);
}
else
{
// NOTE: this check and branching does not always payoff
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
value = *((T*)_position);
_position += sizeof(T);
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(&value, _position, sizeof(T));
_position += sizeof(T);
}
else
{
// NOTE: compiler should optimize this and unroll the loop
for (size_t i = 0; i < sizeof(T); i++)
((char*)&value)[i] = _position[i];
_position += sizeof(T);
}
}
}
if (ENDIANESS_SUPPORT && _reverseEndianess)
{
value = swap<T>(value);
}
return value;
}
template<typename T>
inline T get(uintptr_t index)
{
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
return (int8)(*(_buffer + index));
}
T value;
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access preety good (e.g. x86)
value = *((T*)(_buffer + index));
}
else
{
// NOTE: this check and branching does not always payoff
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
value = *((T*)(_buffer + index));
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(&value, _buffer + index, sizeof(T));
}
else
{
// NOTE: compiler should optimize this and unroll the loop
char* p = _buffer + index;
for (size_t i = 0; i < sizeof(T); i++)
((char*)&value)[i] = p[i];
}
}
}
if (ENDIANESS_SUPPORT && _reverseEndianess)
{
value = swap<T>(value);
}
return value;
}
inline void put(const char* src, uintptr_t src_offset, uintptr_t count) {
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
memcpy(_position, src + src_offset, count);
_position += count;
}
inline void get(char* dest, uintptr_t dest_offset, uintptr_t count) {
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
memcpy(dest + dest_offset, _position, count);
_position += count;
}
template<typename T>
inline void putArray(T* values, uintptr_t count)
{
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
put((const char*)values, 0, count);
return;
}
T* start = (T*)_position;
size_t n = sizeof(T)*count;
// we require aligned arrays...
memcpy(_position, values, n);
_position += n;
// ... so that we can be fast changing endianess
if (ENDIANESS_SUPPORT && _reverseEndianess)
{
for (uintptr_t i = 0; i < count; i++)
{
*start = swap<T>(*start);
start++;
}
}
}
template<typename T>
inline void getArray(T* values, uintptr_t count)
{
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
get((char*)values, 0, count);
return;
}
T* start = (T*)values;
size_t n = sizeof(T)*count;
// we require aligned arrays...
memcpy(values, _position, n);
_position += n;
// ... so that we can be fast changing endianess
if (ENDIANESS_SUPPORT && _reverseEndianess)
{
for (uintptr_t i = 0; i < count; i++)
{
*start = swap<T>(*start);
start++;
}
}
}
// NOTE: size must be power of 2
inline void align(int size)
{
_position = (char*)((((uintptr_t)(const void *)_position) + size - 1) & ~((uintptr_t)(size - 1)));
}
inline void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
inline void putByte ( int8 value) { put< int8>(value); }
inline void putShort ( int16 value) { put< int16>(value); }
inline void putInt ( int32 value) { put< int32>(value); }
inline void putLong ( int64 value) { put< int64>(value); }
inline void putFloat ( float value) { put< float>(value); }
inline void putDouble (double value) { put<double>(value); }
inline void putBoolean(uintptr_t index, bool value) { put< int8>(index, value); }
inline void putByte (uintptr_t index, int8 value) { put< int8>(index, value); }
inline void putShort (uintptr_t index, int16 value) { put< int16>(index, value); }
inline void putInt (uintptr_t index, int32 value) { put< int32>(index, value); }
inline void putLong (uintptr_t index, int64 value) { put< int64>(index, value); }
inline void putFloat (uintptr_t index, float value) { put< float>(index, value); }
inline void putDouble (uintptr_t index,double value) { put<double>(index, value); }
inline bool getBoolean() { return get< int8>() != 0; }
inline int8 getByte () { return get< int8>(); }
inline int16 getShort () { return get< int16>(); }
inline int32 getInt () { return get< int32>(); }
inline int64 getLong () { return get< int64>(); }
inline float getFloat () { return get< float>(); }
inline double getDouble () { return get<double>(); }
inline bool getBoolean(uintptr_t index) { return get< int8>(index) != 0; }
inline int8 getByte (uintptr_t index) { return get< int8>(index); }
inline int16 getShort (uintptr_t index) { return get< int16>(index); }
inline int32 getInt (uintptr_t index) { return get< int32>(index); }
inline int64 getLong (uintptr_t index) { return get< int64>(index); }
inline float getFloat (uintptr_t index) { return get< float>(index); }
inline double getDouble (uintptr_t index) { return get<double>(index); }
// TODO remove
inline const char* getArray()
{
return _buffer;
}
private:
char* _buffer;
char* _position;
char* _limit;
uintptr_t _size;
bool _reverseEndianess;
};
}
}
#endif /* BYTEBUFFER_H */