Program Listing for File byteBuffer.h¶
↰ Return to documentation for file (src/misc/pv/byteBuffer.h)
/* byteBuffer.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef BYTEBUFFER_H
#define BYTEBUFFER_H
#include <string>
#include <cstring>
#include <cstdlib>
#include <epicsEndian.h>
#include <shareLib.h>
#include <epicsAssert.h>
#include <compilerDependencies.h>
#include <pv/templateMeta.h>
#include <pv/pvType.h>
#include <pv/epicsException.h>
#ifndef EPICS_ALWAYS_INLINE
# define EPICS_ALWAYS_INLINE inline
#endif
/* various compilers provide builtins for byte order swaps.
* conditions based on boost endian library
*/
#if defined(__clang__)
#if __has_builtin(__builtin_bswap16)
#define _PVA_swap16(X) __builtin_bswap16(X)
#endif
#if __has_builtin(__builtin_bswap32)
#define _PVA_swap32(X) __builtin_bswap32(X)
#endif
#if __has_builtin(__builtin_bswap64)
#define _PVA_swap64(X) __builtin_bswap64(X)
#endif
#elif defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3))
#if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8)
#define _PVA_swap16(X) __builtin_bswap16(X)
#endif
#define _PVA_swap32(X) __builtin_bswap32(X)
#define _PVA_swap64(X) __builtin_bswap64(X)
#elif defined(_MSC_VER)
#define _PVA_swap16(X) _byteswap_ushort(X)
#define _PVA_swap32(X) _byteswap_ulong(X)
#define _PVA_swap64(X) _byteswap_uint64(X)
#endif
namespace epics {namespace pvData {
namespace detail {
template<typename T>
struct asInt {
static EPICS_ALWAYS_INLINE T from(T v) { return v; }
static EPICS_ALWAYS_INLINE T to(T v) { return v; }
};
template<>
struct asInt<float> {
union pun {float f; uint32 i;};
static EPICS_ALWAYS_INLINE float from(uint32 v) {
pun P;
P.i = v;
return P.f;
}
static EPICS_ALWAYS_INLINE uint32 to(float v) {
pun P;
P.f = v;
return P.i;
}
};
template<>
struct asInt<double> {
union pun {double f; uint64 i;};
static EPICS_ALWAYS_INLINE double from(uint64 v) {
pun P;
P.i = v;
return P.f;
}
static EPICS_ALWAYS_INLINE uint64 to(double v) {
pun P;
P.f = v;
return P.i;
}
};
template<int N>
struct swap; // no default
template<>
struct swap<1> {
static EPICS_ALWAYS_INLINE uint8 op(uint8 v) { return v; }
};
template<>
struct swap<2> {
static EPICS_ALWAYS_INLINE uint16 op(uint16 v) {
#ifdef _PVA_swap16
return _PVA_swap16(v);
#else
return (((v) >> 8) | ((v) << 8));
#endif
}
};
template<>
struct swap<4> {
static EPICS_ALWAYS_INLINE uint32 op(uint32 v) {
#ifdef _PVA_swap32
return _PVA_swap32(v);
#else
return ((((v) & 0xff000000) >> 24) |
(((v) & 0x00ff0000) >> 8) |
(((v) & 0x0000ff00) << 8) |
(((v) & 0x000000ff) << 24));
#endif
}
};
template<>
struct swap<8> {
#ifdef _PVA_swap64
static EPICS_ALWAYS_INLINE uint64 op(uint64 v) {
return _PVA_swap64(v);
}
#else
static inline uint64 op(uint64 v) {
return (((v) >> 56) | \
(((v) >> 40) & 0x0000ff00) | \
(((v) >> 24) & 0x00ff0000) | \
(((v) >> 8) & 0xff000000) | \
(((v) << 8) & ((uint64_t)0xff << 32)) | \
(((v) << 24) & ((uint64_t)0xff << 40)) | \
(((v) << 40) & ((uint64_t)0xff << 48)) | \
(((v) << 56)));
}
#endif
};
#undef _PVA_swap16
#undef _PVA_swap32
#undef _PVA_swap64
/* PVD serialization doesn't pay attention to alignement,
* which some targets really care about and treat unaligned
* access as a fault, or with a heavy penalty (~= to a syscall).
*
* For those targets,, we will have to live with the increase
* in execution time and/or object code size of byte-wise copy.
*/
#ifdef _ARCH_PPC
template<typename T>
union alignu {
T val;
char bytes[sizeof(T)];
};
template<typename T>
EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
{
alignu<T> A;
A.val = val;
for(unsigned i=0, N=sizeof(T); i<N; i++) {
buf[i] = A.bytes[i];
}
}
template<typename T>
EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
{
alignu<T> A;
for(unsigned i=0, N=sizeof(T); i<N; i++) {
A.bytes[i] = buf[i];
}
return A.val;
}
#else /* alignement */
template<typename T>
EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
{
*reinterpret_cast<T*>(buf) = val;
}
template<typename T>
EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
{
return *reinterpret_cast<const T*>(buf);
}
#endif /* alignement */
} // namespace detail
template<typename T>
EPICS_ALWAYS_INLINE T swap(T val)
{
return detail::asInt<T>::from(
detail::swap<sizeof(T)>::op(
detail::asInt<T>::to(val)));
}
#define is_aligned(POINTER, BYTE_COUNT) \
(((std::size_t)(POINTER)) % (BYTE_COUNT) == 0)
#if defined (__GNUC__) && (__GNUC__ < 3)
#define GET(T) get((T*)0)
#else
#define GET(T) get<T>()
#endif
class ByteBuffer
{
public:
ByteBuffer(std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
_buffer((char*)std::malloc(size)), _size(size),
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
_wrapped(false)
{
if(!_buffer)
throw std::bad_alloc();
clear();
}
ByteBuffer(char* buffer, std::size_t size, int byteOrder = EPICS_BYTE_ORDER) :
_buffer(buffer), _size(size),
_reverseEndianess(byteOrder != EPICS_BYTE_ORDER),
_reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER),
_wrapped(true)
{
if(!_buffer)
throw std::invalid_argument("ByteBuffer can't be constructed with NULL");
clear();
}
~ByteBuffer()
{
if (_buffer && !_wrapped) std::free(_buffer);
}
inline void setEndianess(int byteOrder)
{
_reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
_reverseFloatEndianess = (byteOrder != EPICS_FLOAT_WORD_ORDER);
}
inline const char* getBuffer() const
{
return _buffer;
}
inline void clear()
{
_position = _buffer;
_limit = _buffer + _size;
}
inline void flip() {
_limit = _position;
_position = _buffer;
}
inline void rewind() {
_position = _buffer;
}
inline std::size_t getPosition() const
{
return _position - _buffer;
}
inline void setPosition(std::size_t pos)
{
assert(pos<=_size);
_position = _buffer + pos;
assert(_position<=_limit);
}
inline std::size_t getLimit() const
{
return _limit - _buffer;
}
inline void setLimit(std::size_t limit)
{
assert(limit<=_size);
_limit = _buffer + limit;
assert(_position<=_limit);
}
inline std::size_t getRemaining() const
{
return _limit - _position;
}
EPICS_ALWAYS_INLINE std::size_t getSize() const
{
return _size;
}
template<typename T>
inline void put(T value);
template<typename T>
inline void put(std::size_t index, T value) const;
#if defined (__GNUC__) && (__GNUC__ < 3)
template<typename T>
inline T get(const T*);
#else
template<typename T>
inline T get();
#endif
template<typename T>
inline T get(std::size_t index) const;
inline void put(const char* src, std::size_t src_offset, std::size_t count) {
assert(count<=getRemaining());
memcpy(_position, src + src_offset, count);
_position += count;
}
inline void get(char* dest, std::size_t dest_offset, std::size_t count) {
assert(count<=getRemaining());
memcpy(dest + dest_offset, _position, count);
_position += count;
}
template<typename T>
inline void putArray(const T* values, std::size_t count);
template<typename T>
inline void getArray(T* values, std::size_t count);
template<typename T>
EPICS_ALWAYS_INLINE bool reverse() const
{
return sizeof(T)>1 && _reverseEndianess;
}
EPICS_ALWAYS_INLINE void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
EPICS_ALWAYS_INLINE void putByte ( int8 value) { put< int8>(value); }
EPICS_ALWAYS_INLINE void putShort ( int16 value) { put< int16>(value); }
EPICS_ALWAYS_INLINE void putInt ( int32 value) { put< int32>(value); }
EPICS_ALWAYS_INLINE void putLong ( int64 value) { put< int64>(value); }
EPICS_ALWAYS_INLINE void putFloat ( float value) { put< float>(value); }
EPICS_ALWAYS_INLINE void putDouble (double value) { put<double>(value); }
EPICS_ALWAYS_INLINE void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
EPICS_ALWAYS_INLINE void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
EPICS_ALWAYS_INLINE void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
EPICS_ALWAYS_INLINE void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
EPICS_ALWAYS_INLINE void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
EPICS_ALWAYS_INLINE void putFloat (std::size_t index, float value) { put< float>(index, value); }
EPICS_ALWAYS_INLINE void putDouble (std::size_t index, double value) { put<double>(index, value); }
EPICS_ALWAYS_INLINE bool getBoolean() { return GET( int8) != 0; }
EPICS_ALWAYS_INLINE int8 getByte () { return GET( int8); }
EPICS_ALWAYS_INLINE int16 getShort () { return GET( int16); }
EPICS_ALWAYS_INLINE int32 getInt () { return GET( int32); }
EPICS_ALWAYS_INLINE int64 getLong () { return GET( int64); }
EPICS_ALWAYS_INLINE float getFloat () { return GET( float); }
EPICS_ALWAYS_INLINE double getDouble () { return GET(double); }
EPICS_ALWAYS_INLINE bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
EPICS_ALWAYS_INLINE int8 getByte (std::size_t index) { return get< int8>(index); }
EPICS_ALWAYS_INLINE int16 getShort (std::size_t index) { return get< int16>(index); }
EPICS_ALWAYS_INLINE int32 getInt (std::size_t index) { return get< int32>(index); }
EPICS_ALWAYS_INLINE int64 getLong (std::size_t index) { return get< int64>(index); }
EPICS_ALWAYS_INLINE float getFloat (std::size_t index) { return get< float>(index); }
EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
// TODO remove
EPICS_ALWAYS_INLINE const char* getArray() const EPICS_DEPRECATED
{
return _buffer;
}
private:
char* const _buffer;
char* _position;
char* _limit;
const std::size_t _size;
bool _reverseEndianess;
bool _reverseFloatEndianess;
const bool _wrapped;
};
template<>
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<bool>() const
{
return false;
}
template<>
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<int8>() const
{
return false;
}
template<>
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<uint8>() const
{
return false;
}
template<>
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<float>() const
{
return _reverseFloatEndianess;
}
template<>
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<double>() const
{
return _reverseFloatEndianess;
}
// the following methods must come after the specialized reverse<>() methods to make pre-gcc3 happy
template<typename T>
inline void ByteBuffer::put(T value)
{
assert(sizeof(T)<=getRemaining());
if(reverse<T>())
value = swap<T>(value);
detail::store_unaligned(_position, value);
_position += sizeof(T);
}
template<typename T>
inline void ByteBuffer::put(std::size_t index, T value) const
{
assert(_buffer+index<=_limit);
if(reverse<T>())
value = swap<T>(value);
detail::store_unaligned(_buffer+index, value);
}
#if defined (__GNUC__) && (__GNUC__ < 3)
template<typename T>
inline T ByteBuffer::get(const T*)
#else
template<typename T>
inline T ByteBuffer::get()
#endif
{
assert(sizeof(T)<=getRemaining());
T value = detail::load_unaligned<T>(_position);
_position += sizeof(T);
if(reverse<T>())
value = swap<T>(value);
return value;
}
template<typename T>
inline T ByteBuffer::get(std::size_t index) const
{
assert(_buffer+index<=_limit);
T value = detail::load_unaligned<T>(_buffer + index);
if(reverse<T>())
value = swap<T>(value);
return value;
}
template<typename T>
inline void ByteBuffer::putArray(const T* values, std::size_t count)
{
size_t n = sizeof(T)*count; // bytes
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<count; i++) {
detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
}
} else {
memcpy(_position, values, n);
}
_position += n;
}
template<typename T>
inline void ByteBuffer::getArray(T* values, std::size_t count)
{
size_t n = sizeof(T)*count; // bytes
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<count; i++) {
values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
}
} else {
memcpy(values, _position, n);
}
_position += n;
}
}}
#endif /* BYTEBUFFER_H */