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 */