820 lines
23 KiB
C++
820 lines
23 KiB
C++
/* byteBuffer.h */
|
|
/*
|
|
* Copyright information and license terms for this software can be
|
|
* found in the file LICENSE that is included with the distribution
|
|
*/
|
|
/**
|
|
* @author mse
|
|
*/
|
|
#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 (ARM and powerpc) 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.
|
|
*
|
|
* Treat x86 32/64 as an outlier, and assume all other targets
|
|
* need, or greatly benefit, from aligned access.
|
|
*/
|
|
|
|
#if !(defined(__x86_64__) || defined(_M_AMD64) || defined(__i386__) || defined(_M_IX86))
|
|
|
|
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
|
|
|
|
//! Unconditional byte order swap.
|
|
//! defined for integer and floating point types
|
|
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
|
|
|
|
/**
|
|
* @brief This class implements a Bytebuffer that is like the java.nio.ByteBuffer.
|
|
*
|
|
* <p>A @c BitSet is not safe for multithreaded use without
|
|
* external synchronization.
|
|
*
|
|
* Based on Java implementation.
|
|
*/
|
|
class ByteBuffer
|
|
{
|
|
public:
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param size The number of bytes.
|
|
* @param byteOrder The byte order.
|
|
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
|
*/
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* Constructor for wrapping an existing buffer.
|
|
* Given buffer will not be released by the ByteBuffer instance.
|
|
* @param buffer Existing buffer. May not be NULL.
|
|
* @param size The number of bytes.
|
|
* @param byteOrder The byte order.
|
|
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG.
|
|
*/
|
|
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();
|
|
}
|
|
/**
|
|
* Destructor
|
|
*/
|
|
~ByteBuffer()
|
|
{
|
|
if (_buffer && !_wrapped) std::free(_buffer);
|
|
}
|
|
/**
|
|
* Set the byte order.
|
|
*
|
|
* @param byteOrder The byte order.
|
|
* Must be one of EPICS_BYTE_ORDER,EPICS_ENDIAN_LITTLE,EPICS_ENDIAN_BIG,
|
|
*/
|
|
inline void setEndianess(int byteOrder)
|
|
{
|
|
_reverseEndianess = (byteOrder != EPICS_BYTE_ORDER);
|
|
_reverseFloatEndianess = (byteOrder != EPICS_FLOAT_WORD_ORDER);
|
|
}
|
|
/**
|
|
* Get the raw buffer data.
|
|
* @return the raw buffer data.
|
|
*/
|
|
inline const char* getBuffer() const
|
|
{
|
|
return _buffer;
|
|
}
|
|
/**
|
|
* Makes a buffer ready for a new sequence of channel-read or relative put operations:
|
|
* It sets the limit to the capacity and the position to zero.
|
|
*/
|
|
inline void clear()
|
|
{
|
|
_position = _buffer;
|
|
_limit = _buffer + _size;
|
|
}
|
|
/**
|
|
* Makes a buffer ready to read out previously written values.
|
|
*
|
|
* Typically _limit==_buffer+_size is the initial state, but this is not
|
|
* required.
|
|
*
|
|
* V _buffer V _position V _limit V _buffer+_size
|
|
* |_______written_______|____uninitialized___|____allocated___|
|
|
*
|
|
* becomes
|
|
*
|
|
* V _buffer/_position V _limit V _buffer+size
|
|
* |_______written_______|________________allocated____________|
|
|
*/
|
|
inline void flip() {
|
|
_limit = _position;
|
|
_position = _buffer;
|
|
}
|
|
/**
|
|
* Makes a buffer ready for re-reading the data that it already contains:
|
|
* It leaves the limit unchanged and sets the position to zero.
|
|
*
|
|
* Note that this may allow reading of uninitialized values. flip() should be considered
|
|
*
|
|
* V _buffer V _position V _limit V _buffer+_size
|
|
* |_______written_______|____uninitialized___|____allocated___|
|
|
*
|
|
* becomes
|
|
*
|
|
* V _buffer/_position V _limit V _buffer+size
|
|
* |_______written_______|____uninitialized___|____allocated___|
|
|
*/
|
|
inline void rewind() {
|
|
_position = _buffer;
|
|
}
|
|
/**
|
|
* Returns the current position.
|
|
* @return The current position in the raw data.
|
|
*/
|
|
inline std::size_t getPosition() const
|
|
{
|
|
return _position - _buffer;
|
|
}
|
|
/**
|
|
* Sets the buffer position.
|
|
* If the mark is defined and larger than the new position then it is discarded.
|
|
*
|
|
* @param pos The offset into the raw buffer.
|
|
* The new position value; must be no larger than the current limit
|
|
*/
|
|
inline void setPosition(std::size_t pos)
|
|
{
|
|
assert(pos<=_size);
|
|
_position = _buffer + pos;
|
|
assert(_position<=_limit);
|
|
}
|
|
/**
|
|
* Returns this buffer's limit.
|
|
*
|
|
* @return The offset into the raw buffer.
|
|
*/
|
|
inline std::size_t getLimit() const
|
|
{
|
|
return _limit - _buffer;
|
|
}
|
|
/**
|
|
* Sets this buffer's limit.
|
|
* If the position is larger than the new limit then it is set to the new limit.s
|
|
* If the mark is defined and larger than the new limit then it is discarded.
|
|
*
|
|
* @param limit The new position value;
|
|
* must be no larger than the current limit
|
|
*/
|
|
inline void setLimit(std::size_t limit)
|
|
{
|
|
assert(limit<=_size);
|
|
_limit = _buffer + limit;
|
|
assert(_position<=_limit);
|
|
}
|
|
/**
|
|
* Returns the number of elements between the current position and the limit.
|
|
*
|
|
* @return The number of elements remaining in this buffer.
|
|
*/
|
|
inline std::size_t getRemaining() const
|
|
{
|
|
return _limit - _position;
|
|
}
|
|
/**
|
|
* Returns The size, i.e. capacity of the raw data buffer in bytes.
|
|
*
|
|
* @return The size of the raw data buffer.
|
|
*/
|
|
EPICS_ALWAYS_INLINE std::size_t getSize() const
|
|
{
|
|
return _size;
|
|
}
|
|
/**
|
|
* Put the value into the raw buffer as a byte stream in the current byte order.
|
|
*
|
|
* @param value The value to be put into the byte buffer.
|
|
*/
|
|
template<typename T>
|
|
inline void put(T value);
|
|
/**
|
|
* Put the value into the raw buffer at the specified index as a byte stream in the current byte order.
|
|
*
|
|
* @param index Offset in the byte buffer.
|
|
* @param value The value to be put into the byte buffer.
|
|
*/
|
|
template<typename T>
|
|
inline void put(std::size_t index, T value) const;
|
|
/**
|
|
* Get the new object from the byte buffer. The item MUST have type @c T.
|
|
* The position is adjusted based on the type.
|
|
*
|
|
* @return The object.
|
|
*/
|
|
#if defined (__GNUC__) && (__GNUC__ < 3)
|
|
template<typename T>
|
|
inline T get(const T*);
|
|
#else
|
|
template<typename T>
|
|
inline T get();
|
|
#endif
|
|
/**
|
|
* Get the new object from the byte buffer at the specified index.
|
|
* The item MUST have type @c T.
|
|
* The position is adjusted based on the type.
|
|
*
|
|
* @param index The location in the byte buffer.
|
|
* @return The object.
|
|
*/
|
|
template<typename T>
|
|
inline T get(std::size_t index) const;
|
|
/**
|
|
* Put a sub-array of bytes into the byte buffer.
|
|
* The position is increased by the count.
|
|
*
|
|
* @param src The source array.
|
|
* @param src_offset The starting position within src.
|
|
* @param count The number of bytes to put into the byte buffer.
|
|
* Must be less than getRemaining()
|
|
*/
|
|
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;
|
|
}
|
|
/**
|
|
* Get a sub-array of bytes from the byte buffer.
|
|
* The position is increased by the count.
|
|
*
|
|
* @param dest The destination array.
|
|
* @param dest_offset The starting position within src.
|
|
* @param count The number of bytes to put into the byte buffer.
|
|
* Must be less than getRemaining()
|
|
*/
|
|
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;
|
|
}
|
|
/**
|
|
* Put an array of type @c T into the byte buffer.
|
|
* The position is adjusted.
|
|
*
|
|
* @param values The input array.
|
|
* @param count The number of elements.
|
|
*/
|
|
template<typename T>
|
|
inline void putArray(const T* values, std::size_t count);
|
|
/**
|
|
* Get an array of type @c T from the byte buffer.
|
|
* The position is adjusted.
|
|
*
|
|
* @param values The destination array.
|
|
* @param count The number of elements.
|
|
*/
|
|
template<typename T>
|
|
inline void getArray(T* values, std::size_t count);
|
|
/**
|
|
* Is the byte order the EPICS_BYTE_ORDER
|
|
* @return (false,true) if (is, is not) the EPICS_BYTE_ORDER
|
|
*/
|
|
template<typename T>
|
|
EPICS_ALWAYS_INLINE bool reverse() const
|
|
{
|
|
return sizeof(T)>1 && _reverseEndianess;
|
|
}
|
|
/**
|
|
* Put a boolean value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
|
|
/**
|
|
* Put a byte value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putByte ( int8 value) { put< int8>(value); }
|
|
/**
|
|
* Put a short value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putShort ( int16 value) { put< int16>(value); }
|
|
/**
|
|
* Put an int value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putInt ( int32 value) { put< int32>(value); }
|
|
/**
|
|
* Put a long value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putLong ( int64 value) { put< int64>(value); }
|
|
/**
|
|
* Put a float value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putFloat ( float value) { put< float>(value); }
|
|
/**
|
|
* Put a double value into the byte buffer.
|
|
*
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putDouble (double value) { put<double>(value); }
|
|
|
|
/**
|
|
* Put a boolean value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
|
|
/**
|
|
* Put a byte value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
|
|
/**
|
|
* Put a short value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
|
|
/**
|
|
* Put an int value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
|
|
/**
|
|
* Put a long value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
|
|
/**
|
|
* Put a float value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putFloat (std::size_t index, float value) { put< float>(index, value); }
|
|
/**
|
|
* Put a double value into the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer,
|
|
* @param value The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE void putDouble (std::size_t index, double value) { put<double>(index, value); }
|
|
/**
|
|
* Get a boolean value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE bool getBoolean() { return GET( int8) != 0; }
|
|
/**
|
|
* Get a byte value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int8 getByte () { return GET( int8); }
|
|
/**
|
|
* Get a short value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int16 getShort () { return GET( int16); }
|
|
/**
|
|
* Get a int value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int32 getInt () { return GET( int32); }
|
|
/**
|
|
* Get a long value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int64 getLong () { return GET( int64); }
|
|
/**
|
|
* Get a float value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE float getFloat () { return GET( float); }
|
|
/**
|
|
* Get a double value from the byte buffer.
|
|
*
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE double getDouble () { return GET(double); }
|
|
/**
|
|
* Get a boolean value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
|
|
/**
|
|
* Get a byte value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int8 getByte (std::size_t index) { return get< int8>(index); }
|
|
/**
|
|
* Get a short value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int16 getShort (std::size_t index) { return get< int16>(index); }
|
|
/**
|
|
* Get an int value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int32 getInt (std::size_t index) { return get< int32>(index); }
|
|
/**
|
|
* Get a long value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE int64 getLong (std::size_t index) { return get< int64>(index); }
|
|
/**
|
|
* Get a float value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
EPICS_ALWAYS_INLINE float getFloat (std::size_t index) { return get< float>(index); }
|
|
/**
|
|
* Get a boolean value from the byte buffer at the specified index.
|
|
*
|
|
* @param index The offset in the byte buffer.
|
|
* @return The value.
|
|
*/
|
|
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 */
|