/* 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 #include #include #include #include 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 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 */ #define ENDIANESS_SUPPORT true #define UNALIGNED_ACCESS true #define ADAPTIVE_ACCESS true #define USE_INLINE_MEMCPY true /** * This class implements {@code Bytebuffer} that is like the {@code java.nio.ByteBuffer}. *

A {@code 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(uintptr_t size, int byteOrder = EPICS_BYTE_ORDER) : _buffer(0), _size(size), _reverseEndianess(byteOrder != EPICS_BYTE_ORDER), _reverseFloatEndianess(byteOrder != EPICS_FLOAT_WORD_ORDER) { _buffer = (char*)malloc(size); clear(); } /** * Destructor */ ~ByteBuffer() { if (_buffer) 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() { 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 for a new sequence of channel-write or relative get operations: * It sets the limit to the current position and then sets the position to zero. */ 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. */ inline void rewind() { _position = _buffer; } /** * Returns the current position. * @return The current position in the raw data. */ inline uintptr_t getPosition() { return (((uintptr_t)(const void *)_position) - ((uintptr_t)(const void *)_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(uintptr_t pos) { _position = _buffer + pos; } /** * Returns this buffer's limit. * * @return The offset into the raw buffer. */ inline uintptr_t getLimit() { return (((uintptr_t)(const void *)_limit) - ((uintptr_t)(const void *)_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(uintptr_t limit) { _limit = _buffer + limit; } /** * Returns the number of elements between the current position and the limit. * * @return The number of elements remaining in this buffer. */ inline uintptr_t getRemaining() { return (((uintptr_t)(const void *)_limit) - ((uintptr_t)(const void *)_position)); } /** * Returns The size, i.e. capacity of the raw data buffer in bytes. * * @return The size of the raw data buffer. */ inline uintptr_t getSize() { 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 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 && reverse()) { value = swap(value); } if (UNALIGNED_ACCESS) { // NOTE: some CPU handle unaligned access pretty 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); } } } } /** * 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 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 && reverse()) { value = swap(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]; } } } } /** * Get the new object from the byte buffer. The item MUST have type {@code T}. * The position is adjusted based on the type. * * @return The object. */ template 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 && reverse()) { value = swap(value); } return value; } /** * Get the new object from the byte buffer at the specified index. * The item MUST have type {@code T}. * The position is adjusted based on the type. * * @param index The location in the byte buffer. * @return The object. */ template 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 && reverse()) { value = swap(value); } return value; } /** * Put a sub-array of bytes into the byte buffer. * The position is increased by the count. * * @param src The source array. * @param offset The starting position within src. * @param count The number of bytes to put into the byte buffer, */ 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; } /** * Get a sub-array of bytes from the byte buffer. * The position is increased by the count. * * @param dest The destination array. * @param offset The starting position within src. * @param count The number of bytes to put into the byte buffer, */ 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; } /** * Put an array of type {@code T} into the byte buffer. * The position is adjusted. * * @param values The input array. * @param count The number of elements. */ template 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 && reverse()) { for (uintptr_t i = 0; i < count; i++) { *start = swap(*start); start++; } } } /** * Get an array of type {@code T} from the byte buffer. * The position is adjusted. * * @param values The destination array. * @param count The number of elements. */ template 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 && reverse()) { for (uintptr_t i = 0; i < count; i++) { *start = swap(*start); start++; } } } /** * Is the byte order the EPICS_BYTE_ORDER * @return (false,true) if (is, is not) the EPICS_BYTE_ORDER */ template inline bool reverse() { return _reverseEndianess; } /** * Adjust position so that it is aligned to the specified size. * Size MUST be a power of 2. * @param size The alignment requirement. */ inline void align(int size) { const uintptr_t k = size - 1; _position = (char*)((((uintptr_t)(const void *)_position) + k) & ~(k)); } /** * Put a boolean value into the byte buffer. * * @param value The value. */ inline void putBoolean( bool value) { put< int8>(value ? 1 : 0); } /** * Put a byte value into the byte buffer. * * @param value The value. */ inline void putByte ( int8 value) { put< int8>(value); } /** * Put a short value into the byte buffer. * * @param value The value. */ inline void putShort ( int16 value) { put< int16>(value); } /** * Put an int value into the byte buffer. * * @param value The value. */ inline void putInt ( int32 value) { put< int32>(value); } /** * Put a long value into the byte buffer. * * @param value The value. */ inline void putLong ( int64 value) { put< int64>(value); } /** * Put a float value into the byte buffer. * * @param value The value. */ inline void putFloat ( float value) { put< float>(value); } /** * Put a double value into the byte buffer. * * @param value The value. */ inline void putDouble (double value) { put(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. */ inline void putBoolean(uintptr_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. */ inline void putByte (uintptr_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. */ inline void putShort (uintptr_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. */ inline void putInt (uintptr_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. */ inline void putLong (uintptr_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. */ inline void putFloat (uintptr_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. */ inline void putDouble (uintptr_t index,double value) { put(index, value); } /** * Get a boolean value from the byte buffer. * * @return The value. */ inline bool getBoolean() { return get< int8>() != 0; } /** * Get a byte value from the byte buffer. * * @return The value. */ inline int8 getByte () { return get< int8>(); } /** * Get a short value from the byte buffer. * * @return The value. */ inline int16 getShort () { return get< int16>(); } /** * Get a int value from the byte buffer. * * @return The value. */ inline int32 getInt () { return get< int32>(); } /** * Get a long value from the byte buffer. * * @return The value. */ inline int64 getLong () { return get< int64>(); } /** * Get a float value from the byte buffer. * * @return The value. */ inline float getFloat () { return get< float>(); } /** * Get a double value from the byte buffer. * * @return The value. */ inline double getDouble () { return get(); } /** * Get a boolean value from the byte buffer at the specified index. * * @param index The offset in the byte buffer. * @return The value. */ inline bool getBoolean(uintptr_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. */ inline int8 getByte (uintptr_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. */ inline int16 getShort (uintptr_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. */ inline int32 getInt (uintptr_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. */ inline int64 getLong (uintptr_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. */ inline float getFloat (uintptr_t index) { return get< float>(index); } /** * Get a boolean value from the byte buffer at the specified index. * * @param double The offset in the byte buffer. * @return The value. */ inline double getDouble (uintptr_t index) { return get(index); } // TODO remove inline const char* getArray() { return _buffer; } private: char* _buffer; char* _position; char* _limit; uintptr_t _size; bool _reverseEndianess; bool _reverseFloatEndianess; }; template<> inline bool ByteBuffer::reverse() { return _reverseFloatEndianess; } template<> inline bool ByteBuffer::reverse() { return _reverseFloatEndianess; } } } #endif /* BYTEBUFFER_H */