From c762d94f7af1aed0fc49ef113adb5ecadcdf8467 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 8 Sep 2011 10:59:56 +0200 Subject: [PATCH] new ByteBuffer and faster/fixed array serialization support --- pvDataApp/Makefile | 2 +- pvDataApp/factory/PVDataCreateFactory.cpp | 24 +- pvDataApp/misc/byteBuffer.cpp | 305 ------- pvDataApp/misc/byteBuffer.h | 964 +++++++++++----------- pvDataApp/misc/serializeHelper.cpp | 8 +- testApp/misc/testByteBuffer.cpp | 30 +- 6 files changed, 548 insertions(+), 785 deletions(-) diff --git a/pvDataApp/Makefile b/pvDataApp/Makefile index 4874448..d8c4d1e 100644 --- a/pvDataApp/Makefile +++ b/pvDataApp/Makefile @@ -29,7 +29,7 @@ INC += status.h INC += sharedPtr.h LIBSRCS += CDRMonitor.cpp -LIBSRCS += byteBuffer.cpp +#LIBSRCS += byteBuffer.cpp LIBSRCS += bitSet.cpp LIBSRCS += epicsException.cpp LIBSRCS += requester.cpp diff --git a/pvDataApp/factory/PVDataCreateFactory.cpp b/pvDataApp/factory/PVDataCreateFactory.cpp index 3d8c719..34cb79e 100644 --- a/pvDataApp/factory/PVDataCreateFactory.cpp +++ b/pvDataApp/factory/PVDataCreateFactory.cpp @@ -261,17 +261,24 @@ template void DefaultPVArray::deserialize(ByteBuffer *pbuffer, DeserializableControl *pcontrol) { int size = SerializeHelper::readSize(pbuffer, pcontrol); + // if (size>0) { pcontrol->ensureData(sizeof(T)-1); pbuffer->align(sizeof(T)); } if(size>=0) { // prepare array, if necessary if(size>this->getCapacity()) this->setCapacity(size); // retrieve value from the buffer int i = 0; while(true) { - int maxIndex = std::min(size-i, pbuffer->getRemaining())+i; + /* + int maxIndex = std::min(size-i, (int)(pbuffer->getRemaining()/sizeof(T)))+i; for(; iget(); + */ + int maxCount = std::min(size-i, (int)(pbuffer->getRemaining()/sizeof(T))); + pbuffer->getArray(&value[i], maxCount); + i += maxCount; + if(iensureData(1); // // TODO is there a better way to ensureData? + pcontrol->ensureData(sizeof(T)); // this is not OK since can exceen max local buffer (size-i)*sizeof(T)); else break; } @@ -299,12 +306,21 @@ void DefaultPVArray::serialize(ByteBuffer *pbuffer, // write SerializeHelper::writeSize(count, pbuffer, pflusher); + //if (count == 0) return; pcontrol->ensureData(sizeof(T)-1); pbuffer->align(sizeof(T)); int end = offset+count; int i = offset; while(true) { - int maxIndex = std::min(end-i, pbuffer->getRemaining())+i; + + /* + int maxIndex = std::min(end-i, (int)(pbuffer->getRemaining()/sizeof(T)))+i; for(; iput(value[i]); + pbuffer->put(value[i]); + */ + + int maxCount = std::min(end-i, (int)(pbuffer->getRemaining()/sizeof(T))); + pbuffer->putArray(&value[i], maxCount); + i += maxCount; + if(iflushSerializeBuffer(); else diff --git a/pvDataApp/misc/byteBuffer.cpp b/pvDataApp/misc/byteBuffer.cpp index 5f3866d..71a6cbd 100644 --- a/pvDataApp/misc/byteBuffer.cpp +++ b/pvDataApp/misc/byteBuffer.cpp @@ -3,308 +3,3 @@ * EPICS pvDataCPP is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ -/* - * byteBuffer.cpp - * - * Created on: Oct 18, 2010 - * Author: Miha Vitorovic - */ -#include -#include -#include - -// TODO optimize, avoid so many checks (endianness, positions), allow wrapping of external buffer, chance of endianess -namespace epics { - namespace pvData { - - using std::stringstream; - - ByteBuffer::ByteBuffer(int size, int byteOrder) : - _bufferByteOrder(byteOrder), _size(size), _position(0), - _limit(size), _buffer(0) { - - if(size<0) THROW_BASE_EXCEPTION("negative size"); - - if (byteOrder!=EPICS_ENDIAN_BIG && byteOrder!=EPICS_ENDIAN_LITTLE) - THROW_BASE_EXCEPTION("invalid endianness"); - - _buffer = new char[_size]; - } - - ByteBuffer::~ByteBuffer() { - if (_buffer) delete[] _buffer; - } - - ByteBuffer* ByteBuffer::clear() { - _position = 0; - _limit = _size; - return this; - } - - ByteBuffer* ByteBuffer::flip() { - _limit = _position; - _position = 0; - return this; - } - - ByteBuffer* ByteBuffer::rewind() { - _position = 0; - return this; - } - - bool ByteBuffer::getBoolean() { - return getBoolean(_position++); - } - - bool ByteBuffer::getBoolean(int index) { - if(index>=0&&index<_limit) - return _buffer[index]==0 ? false : true; - else - THROW_BASE_EXCEPTION("index out of bounds"); - } - - int8 ByteBuffer::getByte() { - return getByte(_position++); - } - - int8 ByteBuffer::getByte(int index) { - if(index>=0&&index<_limit) - return (int8)_buffer[index]; - else - THROW_BASE_EXCEPTION("index out of bounds"); - } - - int16 ByteBuffer::getShort() { - if(_limit-_position<(int)sizeof(int16)) - THROW_BASE_EXCEPTION("buffer underflow"); - int16 val; - getWithEndianness((char*)&val, sizeof(int16)); // store short into val - return val; - } - - int16 ByteBuffer::getShort(int index) { - if(index<0||_limit-index<(int)sizeof(int16)) - THROW_BASE_EXCEPTION("index out of bounds"); - int16 val; - getWithEndianness(index, (char*)&val, sizeof(int16)); // store short into val - return val; - - } - - int32 ByteBuffer::getInt() { - if(_limit-_position<(int)sizeof(int32)) - THROW_BASE_EXCEPTION("buffer underflow"); - int32 val; - getWithEndianness((char*)&val, sizeof(int32)); // store int into val - return val; - } - - int32 ByteBuffer::getInt(int index) { - if(index<0||_limit-index<(int)sizeof(int32)) - THROW_BASE_EXCEPTION("index out of bounds"); - int32 val; - getWithEndianness(index, (char*)&val, sizeof(int32)); // store int into val - return val; - } - - int64 ByteBuffer::getLong() { - if(_limit-_position<(int)sizeof(int64)) - THROW_BASE_EXCEPTION("buffer underflow"); - int64 val; - getWithEndianness((char*)&val, sizeof(int64)); // store long into val - return val; - } - - int64 ByteBuffer::getLong(int index) { - if(index<0||_limit-index<(int)sizeof(int64)) - THROW_BASE_EXCEPTION("index out of bounds"); - int64 val; - getWithEndianness(index, (char*)&val, sizeof(int64)); // store long into val - return val; - } - - float ByteBuffer::getFloat() { - if(_limit-_position<(int)sizeof(float)) - THROW_BASE_EXCEPTION("buffer underflow"); - float val; - getWithEndianness((char*)&val, sizeof(float)); // store float into val - return val; - } - - float ByteBuffer::getFloat(int index) { - if(index<0||_limit-index<(int)sizeof(float)) - THROW_BASE_EXCEPTION("index out of bounds"); - float val; - getWithEndianness(index, (char*)&val, sizeof(float)); // store float into val - return val; - } - - double ByteBuffer::getDouble() { - if(_limit-_position<(int)sizeof(double)) - THROW_BASE_EXCEPTION("buffer underflow"); - double val; - getWithEndianness((char*)&val, sizeof(double)); // store double into val - return val; - } - - double ByteBuffer::getDouble(int index) { - if(index>=0&&_limit-index<(int)sizeof(double)) THROW_BASE_EXCEPTION( - "index out of bounds"); - double val; - getWithEndianness(index, (char*)&val, sizeof(double)); // store double into val - return val; - } - - void ByteBuffer::get(char* dst, int offset, int count) { - if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer underflow"); - for(int i = 0; igetRemaining()) THROW_BASE_EXCEPTION("buffer overflow"); - for(int i = 0; i=0&&index<_limit) - _buffer[index] = (char)value; - else - THROW_BASE_EXCEPTION("index out of bounds"); - return this; - } - - ByteBuffer* ByteBuffer::putShort(int16 value) { - if(_limit-_position<(int)sizeof(int16)) - THROW_BASE_EXCEPTION("buffer overflow"); - putWithEndianness((char*)&value, sizeof(int16)); // store short into buffer - return this; - } - - ByteBuffer* ByteBuffer::putShort(int index, int16 value) { - if(index<0||_limit-index<(int)sizeof(int16)) - THROW_BASE_EXCEPTION("index out of bounds"); - putWithEndianness(index, (char*)&value, sizeof(int16)); // store short into buffer - return this; - } - - ByteBuffer* ByteBuffer::putInt(int32 value) { - if(_limit-_position<(int)sizeof(int32)) - THROW_BASE_EXCEPTION("buffer overflow"); - putWithEndianness((char*)&value, sizeof(int32)); // store int into buffer - return this; - } - - ByteBuffer* ByteBuffer::putInt(int index, int32 value) { - if(index<0||_limit-index<(int)sizeof(int32)) - THROW_BASE_EXCEPTION("index out of bounds"); - putWithEndianness(index, (char*)&value, sizeof(int32)); // store int into buffer - return this; - } - - ByteBuffer* ByteBuffer::putLong(int64 value) { - if(_limit-_position<(int)sizeof(int64)) - THROW_BASE_EXCEPTION("buffer overflow"); - putWithEndianness((char*)&value, sizeof(int64)); // store long into buffer - return this; - } - - ByteBuffer* ByteBuffer::putLong(int index, int64 value) { - if(index<0||_limit-index<(int)sizeof(int64)) - THROW_BASE_EXCEPTION("index out of bounds"); - putWithEndianness(index, (char*)&value, sizeof(int64)); // store long into buffer - return this; - } - - ByteBuffer* ByteBuffer::putFloat(float value) { - if(_limit-_position<(int)sizeof(float)) - THROW_BASE_EXCEPTION("buffer overflow"); - putWithEndianness((char*)&value, sizeof(float)); // store float into buffer - return this; - } - - ByteBuffer* ByteBuffer::putFloat(int index, float value) { - if(index<0||_limit-index<(int)sizeof(float)) - THROW_BASE_EXCEPTION("index out of bounds"); - putWithEndianness(index, (char*)&value, sizeof(float)); // store float into buffer - return this; - } - - ByteBuffer* ByteBuffer::putDouble(double value) { - if(_limit-_position<(int)sizeof(double)) - THROW_BASE_EXCEPTION("buffer overflow"); - putWithEndianness((char*)&value, sizeof(double)); // store double into buffer - return this; - } - - ByteBuffer* ByteBuffer::putDouble(int index, double value) { - if(index<0||_limit-index<(int)sizeof(double)) - THROW_BASE_EXCEPTION("index out of bounds"); - putWithEndianness(index, (char*)&value, sizeof(double)); // store double into buffer - return this; - } - - /** - * Buffer underflow or overflow checks are performed in the caller. - */ - void ByteBuffer::getWithEndianness(char* dest, size_t size) { - if(_bufferByteOrder==EPICS_BYTE_ORDER) - for(size_t i = 0; i=0; i--) - dest[i] = _buffer[_position++]; - } - - /** - * Buffer underflow or overflow checks are performed in the caller. - */ - void ByteBuffer::putWithEndianness(char* src, size_t size) { - if(_bufferByteOrder==EPICS_BYTE_ORDER) - for(size_t i = 0; i=0; i--) - _buffer[_position++] = src[i]; - } - - /** - * Buffer underflow or overflow checks are performed in the caller. - */ - void ByteBuffer::putWithEndianness(int index, char* src, size_t size) { - if(_bufferByteOrder==EPICS_BYTE_ORDER) - for(size_t i = 0; i=0; i--) - _buffer[index++] = src[i]; - } - - /** - * Buffer underflow or overflow checks are performed in the caller. - */ - void ByteBuffer::getWithEndianness(int index, char* dest, size_t size) { - if(_bufferByteOrder==EPICS_BYTE_ORDER) - for(size_t i = 0; i=0; i--) - dest[i] = _buffer[index++]; - } - - } -} diff --git a/pvDataApp/misc/byteBuffer.h b/pvDataApp/misc/byteBuffer.h index ee89279..7e6f7fb 100644 --- a/pvDataApp/misc/byteBuffer.h +++ b/pvDataApp/misc/byteBuffer.h @@ -13,531 +13,559 @@ #include -namespace epics { +namespace epics { namespace pvData { + +/* +TODO can be used: - /** @brief A buffer of bytes. - * - * A buffer of bytes, which has - * - capacity (size) - * - limit - * - position - * The capacity is the maximum capacity of the buffer, the buffer's - * limit is the index of the first element that should not be read or - * written, and its position is the index of the next element to be - * written. - * - * The buffer also ha a byte order specified which can be little or - * big endian. - */ - class ByteBuffer { - public: - /** @brief ByteBuffer constructor. - * - * Creates a buffer of a given size, and with a specified byteOrder. - * - * @param size Specify the capacity of the buffer. Default is 32. - * @param byteOrder the byte order of the buffer. Can be either - * EPICS_ENDIAN_LITTLE or EPICS_ENDIAN_BIG. - */ - ByteBuffer(int size = 32, int byteOrder = EPICS_BYTE_ORDER); +MS Visual C++: - /** - * @brief ByteBuffer destructor. - */ - ~ByteBuffer(); +You include intrin.h and call the following functions: - /** - * Clears the buffer. Sets the {@code position} to 0 and {@code limit} - * to {@code capacity}. - */ - ByteBuffer* clear(); +For 16 bit numbers: +unsigned short _byteswap_ushort(unsigned short value); - /** - * Flips the buffer. The {@code limit} is set to the current {@code position} - * and the {@code position} is set to {@code 0}. This prepares the - * buffer for a series of relative @em get operations after a series - * of relative @em put operations. - */ - ByteBuffer* flip(); +For 32 bit numbers: +unsigned long _byteswap_ulong(unsigned long value); - /** - * Rewinds the buffer. This sets {@code position} to {@code 0} - * without affecting the {@code limit}. - */ - ByteBuffer* rewind(); +For 64 bit numbers: +unsigned __int64 _byteswap_uint64(unsigned __int64 value); +*/ - template inline T get(); +/* +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. +*/ - /** - * Relative boolean read, {@code position} is incremented by - * {@code 1}. - * - * @returns The next byte in the buffer as a boolean. - * @throws EpicsException - Buffer underflow if there are no bytes - * remaining in the buffer. - */ - bool getBoolean(); - /** - * Absolute get method. Reads the bool at the given {@code index}. - * - * @param[in] index The index from which the bool will be read - * @returns The bool at the given index - * @throws EpicsException - If index is negative or not smaller - * than the buffer's limit - */ - bool getBoolean(int index); - /** - * Relative Int8 read, {@code position} is incremented by - * {@code 1}. - * - * @returns The next byte in the buffer as a signed Int8. - * @throws EpicsException - Buffer underflow if there are no bytes - * remaining in the buffer. - */ - int8 getByte(); +#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) - /** - * Absolute get method. Reads the byte at the given {@code index}. - * - * @param[in] index The index from which the byte will be read - * @returns The byte at the given index - * @throws EpicsException - If index is negative or not smaller - * than the buffer's limit - */ - int8 getByte(int index); +#define swap32(x) __builtin_bswap32(x) +#define swap64(x) __builtin_bswap64(x) - /** - * Relative Int16 read, {@code position} is incremented by - * {@code 2}. - * - * @returns The next Int16 value in the buffer. - * @throws EpicsException - Buffer underflow if there are less than - * 2 bytes remaining in the buffer. - */ - int16 getShort(); +#define __byte_swap16(x) \ + (((x) >> 8) | \ + ((x) << 8)) - /** - * Absolute get method. Reads the int16 at the given {@code index}. - * - * @param[in] index The index from which the int16 will be read - * @returns The int16 at the given index - * @throws EpicsException - If index is negative or not within - * the buffer's limit - */ - int16 getShort(int index); +static inline uint16_t +swap16(uint16_t _x) +{ + return (__byte_swap16(_x)); +} - /** - * Relative Int32 read, {@code position} is incremented by - * {@code 4}. - * - * @returns The next Int32 value in the buffer. - * @throws EpicsException - Buffer underflow if there are less than - * 4 bytes remaining in the buffer. - */ - int32 getInt(); +#else - /** - * Absolute get method. Reads the int32 at the given {@code index}. - * - * @param[in] index The index from which the int32 will be read - * @returns The int32 at the given index - * @throws EpicsException - If index is negative or not within - * the buffer's limit - */ - int32 getInt(int index); - /** - * Relative Int64 read, {@code position} is incremented by - * {@code 8}. - * - * @returns The next Int64 value in the buffer. - * @throws EpicsException - Buffer underflow if there are less than - * 8 bytes remaining in the buffer. - */ - int64 getLong(); +#define __byte_swap16(x) \ + (((x) >> 8) | \ + ((x) << 8)) - /** - * Absolute get method. Reads the int64 at the given {@code index}. - * - * @param[in] index The index from which the int64 will be read - * @returns The int64 at the given index - * @throws EpicsException - If index is negative or not within - * the buffer's limit - */ - int64 getLong(int index); +#define __byte_swap32(x) \ + ((((x) & 0xff000000) >> 24) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x000000ff) << 24)) - /** - * Relative float read, {@code position} is incremented by - * {@code 4}. - * - * @returns The next float value in the buffer. - * @throws EpicsException - Buffer underflow if there are less than - * 4 bytes remaining in the buffer. - */ - float getFloat(); +#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)); +} - /** - * Absolute get method. Reads the float at the given {@code index}. - * - * @param[in] index The index from which the float will be read - * @returns The float at the given index - * @throws EpicsException - If index is negative or not within - * the buffer's limit - */ - float getFloat(int index); +static inline uint32_t +swap32(uint32_t _x) +{ + return (__byte_swap32(_x)); +} - /** - * Relative double read, {@code position} is incremented by - * {@code 8}. - * - * @returns The next double value in the buffer. - * @throws EpicsException - Buffer underflow if there are less than - * 8 bytes remaining in the buffer. - */ - double getDouble(); +static inline uint64_t +swap64(uint64_t _x) +{ + return (__byte_swap64(_x)); +} - /** - * Absolute get method. Reads the double at the given {@code index}. - * - * @param[in] index The index from which the double will be read - * @returns The double at the given index - * @throws EpicsException - If index is negative or not within - * the buffer's limit - */ - double getDouble(int index); +#endif - /** - * Relative bulk @em get method. It transfers {@code count} bytes - * from the buffer into the {@code dst}. - * - * @param[out] dst Destination buffer - * @param[in] offset Offset in the destination buffer - * @param[in] count The number of bytes to copy - * @throws EpicsException - Buffer underflow if there are fewer - * than count bytes remaining in the buffer. - */ - void get(char* dst, int offset, int count); - //virtual String getString() = 0; // TODO +template +inline T swap(T val) { return val; } // not valid - template inline void put(T); +template<> +inline int16 swap(int16 val) +{ + return swap16(val); +} - /** - * Relative bulk @em put method. It transfers {@code count} bytes - * from the {@code src} into the the buffer. - * - * @param[in] src Source buffer - * @param[in] offset Offset in the source buffer - * @param[in] count The number of bytes to copy - * @returns Pointer to this ByteBuffer instance. - * @throws EpicsException - Buffer overflow if there are not - * enough bytes remaining in the buffer. - */ - ByteBuffer* put(const char* src, int offset, int count); +template<> +inline int32 swap(int32 val) +{ + return swap32(val); +} - /** - * Relative boolean write, {@code position} is incremented by - * {@code 1}. - * - * @param[in] value The boolean value to write. - * @throws EpicsException - Buffer overflow if there are no - * bytes remaining in the buffer. - */ - ByteBuffer* putBoolean(bool value); +template<> +inline int64 swap(int64 val) +{ + return swap64(val); +} - /** - * Absolute bool write. - * - * @param[in] index The index at which the bool will be written. - * @param[in] value The bool value to write. - * @throws EpicsException - Buffer overflow if there are no - * bytes remaining in the buffer. - */ - ByteBuffer* putBoolean(int index, bool value); +template<> +inline float swap(float val) +{ + int32* pval = (int32*)&val; + *pval = swap32(*pval); + return val; +} - /** - * Relative Int8 write, {@code position} is incremented by - * {@code 1}. - * - * @param[in] value The Int8 value to write. - * @throws EpicsException - Buffer overflow if there are no - * bytes remaining in the buffer. - */ - ByteBuffer* putByte(int8 value); +template<> +inline double swap(double val) +{ + int64* pval = (int64*)&val; + *pval = swap64(*pval); + return val; +} - /** - * Absolute Int8 write. - * - * @param[in] index The index at which the byte will be written. - * @param[in] value The Int8 value to write. - * @throws EpicsException - Buffer overflow if there are no - * bytes remaining in the buffer. - */ - ByteBuffer* putByte(int index, int8 value); +#define is_aligned(POINTER, BYTE_COUNT) \ + (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0) - /** - * Relative Int16 write, {@code position} is incremented by - * {@code 2}. - * - * @param[in] value The Int16 value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 2 bytes remaining in the buffer. - */ - ByteBuffer* putShort(int16 value); +/*template */ - /** - * Absolute Int16 write. - * - * @param[in] index The index at which the Int16 will be written. - * @param[in] value The Int16 value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 2 bytes remaining in the buffer. - */ - ByteBuffer* putShort(int index, int16 value); +#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; + } - /** - * Relative Int32 write, {@code position} is incremented by - * {@code 4}. - * - * @param[in] value The Int32 value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 4 bytes remaining in the buffer. - */ - ByteBuffer* putInt(int32 value); + inline void clear() + { + _position = _buffer; + _limit = _buffer + _size; + } + + inline void flip() { + _limit = _position; + _position = _buffer; + } - /** - * Absolute Int32 write. - * - * @param[in] index The index at which the Int32 will be written. - * @param[in] value The Int32 value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 4 bytes remaining in the buffer. - */ - ByteBuffer* putInt(int index, int32 value); + inline void rewind() { + _position = _buffer; + } - /** - * Relative Int64 write, {@code position} is incremented by - * {@code 8}. - * - * @param[in] value The Int64 value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 8 bytes remaining in the buffer. - */ - ByteBuffer* putLong(int64 value); + 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)); + } - /** - * Absolute Int64 write. - * - * @param[in] index The index at which the Int64 will be written. - * @param[in] value The Int64 value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 8 bytes remaining in the buffer. - */ - ByteBuffer* putLong(int index, int64 value); + inline void setLimit(uintptr_t limit) + { + _limit = _buffer + limit; + } - /** - * Relative float write, {@code position} is incremented by - * {@code 4}. - * - * @param[in] value The float value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 4 bytes remaining in the buffer. - */ - ByteBuffer* putFloat(float value); - - /** - * Absolute float write. - * - * @param[in] index The index at which the float will be written. - * @param[in] value The float value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 4 bytes remaining in the buffer. - */ - ByteBuffer* putFloat(int index, float value); - - /** - * Relative float write, {@code position} is incremented by - * {@code 8}. - * - * @param[in] value The double value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 8 bytes remaining in the buffer. - */ - ByteBuffer* putDouble(double value); - - /** - * Absolute double write. - * - * @param[in] index The index at which the double will be written. - * @param[in] value The double value to write. - * @throws EpicsException - Buffer overflow if there are less than - * 8 bytes remaining in the buffer. - */ - ByteBuffer* putDouble(int index, double value); - - //virtual ByteBuffer *putString(String value) = 0; // TODO - - /** - * The capacity (size) of the buffer in bytes. - * - * @returns The capacity of the buffer in bytes. - */ - inline int getSize() const { - return _size; + inline uintptr_t getRemaining() + { + return (((uintptr_t)(const void *)_limit) - ((uintptr_t)(const void *)_position)); + } + + inline uintptr_t getSize() + { + return _size; + } + + + + + 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 && _reverseEndianess) + { + value = swap(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); } - - /** - * The offset from the start of the buffer in bytes. Currently this - * method always returns {@code 0}. - * - * @returns The offset from the start of the buffer in bytes - */ - inline int getArrayOffset() const { - return 0; + 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); + } } + } + + } - /** - * The byte index of the next element to @em get or @em put. - * - * @returns The byte index of the next element. - */ - inline int getPosition() const { - return _position; + + + 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 && _reverseEndianess) + { + 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; } - - /** - * The byte index of the {@code limit}. {@code limit} is always less - * or equal to the buffer capacity. No @em put or @em get operation - * can be performed pass the {@code limit}. - * - * @returns The byte index of the {@code limit}. - */ - inline int getLimit() const { - return _limit; + 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]; + } } + } + + } - /** - * The number of the bytes that can be successfully read or written. - * - * @returns The number of the bytes that can be successfully read or - * written. - */ - inline int getRemaining() const { - return _limit-_position; + + + + 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); } - - /** - * The byte order of the buffer. - * - * @returns Either {@code EPICS_ENDIAN_LITTLE} or - * {@code EPICS_ENDIAN_BIG}. - */ - inline int getByteOrder() const { - return _bufferByteOrder; + 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(value); + } + + return value; + } - /** - * Gives a read-only access to the buffer. - * - * @returns A const (read-only) pointer to the actual buffer. - */ - inline const char* getArray() const { - return _buffer; + + + + 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)); } - - /** - * Sets this buffer's position. If the new position is invalid in - * regards to the invariant then it is discarded. - * - * @param[in] newPosition The new position value; must be - * non-negative and no larger than the current limit. - * @throws EpicsException - If the preconditions on - * {@code newPosition} do not hold - */ - inline void setPosition(int newPosition) { - if(newPosition<0||newPosition>_limit) THROW_BASE_EXCEPTION( - "invalid limit"); - _position = newPosition; + 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(value); + } + + return value; + } - inline void setLimit(int newLimit) { - if(newLimit<0||_position>newLimit) THROW_BASE_EXCEPTION( - "invalid limit"); - _limit = newLimit; - } - // TODO must define arrays - private: - int _bufferByteOrder, _size, _position, _limit; - char* _buffer; - /** - * Reads the next
size
bytes from the buffer and stores them - * into the destination taking into account endianness of the buffer - * and the current hardware. - */ - void getWithEndianness(char* dest, size_t size); + 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; + } - /** - * Puts the next
size
bytes into the buffer reading them - * from source taking into account endianness of the buffer - * and the current hardware. - */ - void putWithEndianness(char* src, size_t size); - /** - * Puts the
size
bytes into the buffer reading them - * from source taking into account endianness of the buffer - * and the current hardware. - */ - void putWithEndianness(int index, char* src, size_t size); + 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; + } - /** - * Reads the next
size
bytes from the buffer and stores them - * into the destination taking into account endianness of the buffer - * and the current hardware. - */ - void getWithEndianness(int index, char* dest, size_t size); + 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 && _reverseEndianess) + { + for (uintptr_t i = 0; i < count; i++) + { + *start = swap(*start); + start++; + } + } + } - }; - template - inline T ByteBuffer::get(){} // not valid - template<> - inline bool ByteBuffer::get(){return getBoolean();} - template<> - inline int8 ByteBuffer::get(){return getByte();} - template<> - inline int16 ByteBuffer::get(){return getShort();} - template<> - inline int32 ByteBuffer::get(){return getInt();} - template<> - inline int64 ByteBuffer::get(){return getLong();} - template<> - inline float ByteBuffer::get(){return getFloat();} - template<> - inline double ByteBuffer::get(){return getDouble();} - template - inline void ByteBuffer::put(T){} // not valid - template<> - inline void ByteBuffer::put(bool v){putBoolean(v);} - template<> - inline void ByteBuffer::put(int8 v){putByte(v);} - template<> - inline void ByteBuffer::put(int16 v){putShort(v);} - template<> - inline void ByteBuffer::put(int32 v){putInt(v);} - template<> - inline void ByteBuffer::put(int64 v){putLong(v);} - template<> - inline void ByteBuffer::put(float v){putFloat(v);} - template<> - inline void ByteBuffer::put(double v){putDouble(v);} + + + + 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 && _reverseEndianess) + { + for (uintptr_t i = 0; i < count; i++) + { + *start = swap(*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(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(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(); } + + 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(index); } + + // TODO remove + inline const char* getArray() + { + return _buffer; + } + + +private: + char* _buffer; + char* _position; + char* _limit; + uintptr_t _size; + bool _reverseEndianess; +}; } } diff --git a/pvDataApp/misc/serializeHelper.cpp b/pvDataApp/misc/serializeHelper.cpp index a9a4240..d29e18f 100644 --- a/pvDataApp/misc/serializeHelper.cpp +++ b/pvDataApp/misc/serializeHelper.cpp @@ -63,7 +63,7 @@ namespace epics { SerializeHelper::writeSize(len, buffer, flusher); int i = 0; while(true) { - int maxToWrite = min(len-i, buffer->getRemaining()); + int maxToWrite = min(len-i, (int)buffer->getRemaining()); buffer->put(value.data(), i, maxToWrite); // UTF-8 i += maxToWrite; if(igetRemaining()); + int maxToWrite = min(count-i, (int)buffer->getRemaining()); buffer->put(value.data(), offset+i, maxToWrite); // UTF-8 i += maxToWrite; if(i0) { - if (buffer->getRemaining()>=size) + if ((int)buffer->getRemaining()>=size) { // entire string is in buffer, simply create a string out of it (copy) int pos = buffer->getPosition(); @@ -118,7 +118,7 @@ namespace epics { try { int i = 0; while(true) { - int toRead = min(size-i, buffer->getRemaining()); + int toRead = min(size-i, (int)buffer->getRemaining()); int pos = buffer->getPosition(); str.append(buffer->getArray()+pos, toRead); buffer->setPosition(pos+toRead); diff --git a/testApp/misc/testByteBuffer.cpp b/testApp/misc/testByteBuffer.cpp index 1badc0e..7a4c39a 100644 --- a/testApp/misc/testByteBuffer.cpp +++ b/testApp/misc/testByteBuffer.cpp @@ -25,9 +25,9 @@ using namespace epics::pvData; void testBasicOperations(std::ostream& ofile) { ofile<<"Basic operation tests...\n"; - ByteBuffer* buff = new ByteBuffer(); + ByteBuffer* buff = new ByteBuffer(32); assert(buff->getSize()==32); - assert(buff->getByteOrder()==EPICS_BYTE_ORDER); +// assert(buff->getByteOrder()==EPICS_BYTE_ORDER); assert(buff->getPosition()==0); assert(buff->getLimit()==32); assert(buff->getRemaining()==32); @@ -70,6 +70,18 @@ void testBasicOperations(std::ostream& ofile) { assert(buff->getLong(8)==2345678123LL); assert(buff->getFloat(16)==testFloat); assert(buff->getDouble(20)==testDouble); +/* + uintptr_t sp = buff->getPosition(); + buff->setPosition(0); + assert(buff->getBoolean()==true); + assert(buff->getByte()==-12); + assert(buff->getShort()==10516); + assert(buff->getInt()==0x1937628B); + assert(buff->getLong()==2345678123LL); + assert(buff->getFloat()==testFloat); + assert(buff->getDouble()==testDouble); + buff->setPosition(sp); +*/ buff->flip(); assert(buff->getLimit()==28); @@ -122,7 +134,19 @@ void testBasicOperations(std::ostream& ofile) { buff->putLong(8, 2345678123LL); buff->putFloat(16, testFloat); buff->putDouble(20, testDouble); - +/* + buff->clear(); + buff->setPosition(28); + sp = buff->getPosition(); + buff->putBoolean(true); + buff->putByte(-12); + buff->putShort(10516); + buff->putInt(0x1937628B); + buff->putLong(2345678123LL); + buff->putFloat(testFloat); + buff->putDouble(testDouble); + buff->setPosition(sp); +*/ buff->flip(); assert(buff->getLimit()==28); assert(buff->getPosition()==0);