diff --git a/QtC-pvData.files b/QtC-pvData.files index 537a550..c767546 100644 --- a/QtC-pvData.files +++ b/QtC-pvData.files @@ -73,4 +73,9 @@ pvDataApp/test/testIntrospect.cpp pvDataApp/test/testPVAuxInfo.cpp pvDataApp/test/testPVScalar.cpp pvDataApp/test/testPVScalarArray.cpp -pvDataApp/test/testSimpleStructure.cpp \ No newline at end of file +pvDataApp/test/testSimpleStructure.cpp +pvDataApp/misc/epicsException.h +pvDataApp/misc/byteBuffer.cpp +pvDataApp/misc/bitSet.cpp +pvDataApp/test/testByteBuffer.cpp +pvDataApp/test/testBitSet.cpp diff --git a/pvDataApp/misc/Makefile b/pvDataApp/misc/Makefile index e56dd89..d54669d 100644 --- a/pvDataApp/misc/Makefile +++ b/pvDataApp/misc/Makefile @@ -8,7 +8,9 @@ INC += requester.h INC += serialize.h INC += bitSet.h INC += byteBuffer.h +INC += epicsException.h +LIBSRCS += byteBuffer.cpp LIBSRCS += bitSet.cpp LIBRARY=pvMisc diff --git a/pvDataApp/misc/bitSet.cpp b/pvDataApp/misc/bitSet.cpp index 30d05de..2dd29bb 100644 --- a/pvDataApp/misc/bitSet.cpp +++ b/pvDataApp/misc/bitSet.cpp @@ -1,3 +1,5 @@ +#include "string.h" +#include "stdio.h" #include "bitSet.h" namespace epics { namespace pvData { @@ -302,6 +304,11 @@ namespace epics { namespace pvData { return true; } + bool BitSet::operator!=(const BitSet &set) const + { + return !(*this == set); + } + void BitSet::toString(StringBuilder buffer) { toString(buffer, 0); } void BitSet::toString(StringBuilder buffer, int indentLevel) const diff --git a/pvDataApp/misc/bitSet.h b/pvDataApp/misc/bitSet.h index a2eed4b..d20d00c 100644 --- a/pvDataApp/misc/bitSet.h +++ b/pvDataApp/misc/bitSet.h @@ -210,6 +210,8 @@ namespace epics { namespace pvData { */ bool operator==(const BitSet &set) const; + bool operator!=(const BitSet &set) const; + void toString(StringBuilder buffer); void toString(StringBuilder buffer, int indentLevel) const; diff --git a/pvDataApp/misc/byteBuffer.cpp b/pvDataApp/misc/byteBuffer.cpp new file mode 100644 index 0000000..2a200f8 --- /dev/null +++ b/pvDataApp/misc/byteBuffer.cpp @@ -0,0 +1,180 @@ +/* + * byteBuffer.cpp + * + * Created on: Oct 18, 2010 + * Author: Miha Vitorovic + */ + +#include "byteBuffer.h" +#include "epicsException.h" + +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 EpicsException("negative size"); + + if (byteOrder!=EPICS_ENDIAN_BIG && byteOrder!=EPICS_ENDIAN_LITTLE) + throw EpicsException("invalid endian"); + + _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() { + if(_position<_limit) + return _buffer[_position++]==0 ? false : true; + else + throw EpicsException("buffer underflow"); + } + + epicsInt8 ByteBuffer::getByte() { + if(_position<_limit) + return (epicsInt8)_buffer[_position++]; + else + throw EpicsException("buffer underflow"); + } + + epicsInt16 ByteBuffer::getShort() { + if(_limit-_position<(int)sizeof(epicsInt16)) + throw EpicsException("buffer underflow"); + epicsInt16 val; + getWithEndianness((char*)&val, sizeof(epicsInt16)); // store short into val + return val; + } + + epicsInt32 ByteBuffer::getInt() { + if(_limit-_position<(int)sizeof(epicsInt32)) + throw EpicsException("buffer underflow"); + epicsInt32 val; + getWithEndianness((char*)&val, sizeof(epicsInt32)); // store int into val + return val; + } + + epicsInt64 ByteBuffer::getLong() { + if(_limit-_position<(int)sizeof(epicsInt64)) + throw EpicsException("buffer underflow"); + epicsInt64 val; + getWithEndianness((char*)&val, sizeof(epicsInt64)); // store long into val + return val; + } + + float ByteBuffer::getFloat() { + if(_limit-_position<(int)sizeof(float)) + throw EpicsException("buffer underflow"); + float val; + getWithEndianness((char*)&val, sizeof(float)); // store float into val + return val; + } + + double ByteBuffer::getDouble() { + if(_limit-_position<(int)sizeof(double)) + throw EpicsException("buffer underflow"); + double val; + getWithEndianness((char*)&val, sizeof(double)); // store double into val + return val; + } + + ByteBuffer* ByteBuffer::putBoolean(bool value) { + if(_position<_limit) + _buffer[_position++] = value ? 1 : 0; + else + throw EpicsException("buffer overflow"); + return this; + } + + ByteBuffer* ByteBuffer::putByte(epicsInt8 value) { + if(_position<_limit) + _buffer[_position++] = (char)value; + else + throw EpicsException("buffer overflow"); + return this; + } + + ByteBuffer* ByteBuffer::putShort(epicsInt16 value) { + if(_limit-_position<(int)sizeof(epicsInt16)) + throw EpicsException("buffer overflow"); + putWithEndianness((char*)&value, sizeof(epicsInt16)); // store short into buffer + return this; + } + + ByteBuffer* ByteBuffer::putInt(epicsInt32 value) { + if(_limit-_position<(int)sizeof(epicsInt32)) + throw EpicsException("buffer overflow"); + putWithEndianness((char*)&value, sizeof(epicsInt32)); // store int into buffer + return this; + } + + ByteBuffer* ByteBuffer::putLong(epicsInt64 value) { + if(_limit-_position<(int)sizeof(epicsInt64)) + throw EpicsException("buffer overflow"); + putWithEndianness((char*)&value, sizeof(epicsInt64)); // store long into buffer + return this; + } + + ByteBuffer* ByteBuffer::putFloat(float value) { + if(_limit-_position<(int)sizeof(float)) + throw EpicsException("buffer overflow"); + putWithEndianness((char*)&value, sizeof(float)); // store float into buffer + return this; + } + + ByteBuffer* ByteBuffer::putDouble(double value) { + if(_limit-_position<(int)sizeof(double)) + throw EpicsException("buffer overflow"); + putWithEndianness((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]; + } + + } +} diff --git a/pvDataApp/misc/byteBuffer.h b/pvDataApp/misc/byteBuffer.h index b42d273..504dd2f 100644 --- a/pvDataApp/misc/byteBuffer.h +++ b/pvDataApp/misc/byteBuffer.h @@ -1,38 +1,302 @@ /* byteBuffer.h */ -#include #ifndef BYTEBUFFER_H #define BYTEBUFFER_H -#include "epicsTypes.h" -namespace epics { namespace pvData { -// not sure why I have to define epicsInt64 -typedef long long epicsInt64; +#include +#include +#include -class ByteBuffer; +namespace epics { + namespace pvData { -class ByteBuffer { - public: - virtual ~ByteBuffer(); - virtual int getSize() = 0; - virtual int getArrayOffset() = 0; - virtual bool getBoolean() = 0; - virtual epicsInt8 getByte() = 0; - virtual epicsInt16 geShort() = 0; - virtual epicsInt32 getInt() = 0; - virtual epicsInt64 getLong() = 0; - virtual float getFloat() = 0; - virtual double getDouble() = 0; - virtual String getString() = 0; - virtual ByteBuffer *putBoolean(bool value) = 0; - virtual ByteBuffer *putByte(epicsInt8 value) = 0; - virtual ByteBuffer *geShort(epicsInt16 value) = 0; - virtual ByteBuffer *putInt(epicsInt32 value) = 0; - virtual ByteBuffer *putLong(epicsInt64 value) = 0; - virtual ByteBuffer *putFloat(float value) = 0; - virtual ByteBuffer *putDouble(double value) = 0; - virtual ByteBuffer *putString(String value) = 0; - // Must define arrays -}; + // not sure why I have to define epicsInt64 + typedef long long epicsInt64; -}} + /** @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); + + /** + * @brief ByteBuffer destructor. + */ + ~ByteBuffer(); + + /** + * Clears the buffer. Sets the {@code position} to 0 and {@code limit} + * to {@code capacity}. + */ + ByteBuffer* clear(); + + /** + * 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(); + + /** + * Rewinds the buffer. This sets {@code position} to {@code 0} + * without affecting the {@code limit}. + */ + ByteBuffer* rewind(); + + /** + * 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(); + + /** + * 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. + */ + epicsInt8 getByte(); + + /** + * 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. + */ + epicsInt16 getShort(); + + /** + * 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. + */ + epicsInt32 getInt(); + + /** + * 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. + */ + epicsInt64 getLong(); + + /** + * 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(); + + /** + * 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(); + + //virtual String getString() = 0; // TODO + + + /** + * 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); + + + /** + * 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(epicsInt8 value); + + /** + * 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(epicsInt16 value); + + /** + * 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(epicsInt32 value); + + /** + * 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(epicsInt64 value); + + /** + * 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); + + /** + * 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); + + //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; + } + + /** + * 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; + } + + /** + * 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; + } + + /** + * 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; + } + + /** + * 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; + } + + /** + * The byte order of the buffer. + * + * @returns Either {@code EPICS_ENDIAN_LITTLE} or + * {@code EPICS_ENDIAN_BIG}. + */ + inline int getByteOrder() const { + return _bufferByteOrder; + } + + /** + * 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; + } + + // 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); + /** + * 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); + }; + + } +} #endif /* BYTEBUFFER_H */ diff --git a/pvDataApp/misc/epicsException.h b/pvDataApp/misc/epicsException.h new file mode 100644 index 0000000..f1b5e59 --- /dev/null +++ b/pvDataApp/misc/epicsException.h @@ -0,0 +1,29 @@ +/* + * epicsException.hpp + * + * Created on: Oct 18, 2010 + * Author: miha_vitorovic + */ + +#ifndef EPICSEXCEPTION_H_ +#define EPICSEXCEPTION_H_ + +// TODO to be redefined!!!!!! + +#include +#include + +namespace epics { namespace pvData { + + /** Base Epics Exception */ + class EpicsException : public std::logic_error { + public: + explicit EpicsException(const std::string& arg) : + std::logic_error(arg) { + } + }; + } +} + + +#endif /* EPICSEXCEPTION_H_ */ diff --git a/pvDataApp/test/Makefile b/pvDataApp/test/Makefile index eb4060b..cb8c0f7 100644 --- a/pvDataApp/test/Makefile +++ b/pvDataApp/test/Makefile @@ -26,6 +26,10 @@ PROD_HOST += testBitSet testBitSet_SRCS += testBitSet.cpp testBitSet_LIBS += pvMisc Com +PROD_HOST += testByteBuffer +testByteBuffer_SRCS += testByteBuffer.cpp +testByteBuffer_LIBS += pvMisc Com + include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE diff --git a/pvDataApp/test/testByteBuffer.cpp b/pvDataApp/test/testByteBuffer.cpp new file mode 100644 index 0000000..3824e83 --- /dev/null +++ b/pvDataApp/test/testByteBuffer.cpp @@ -0,0 +1,126 @@ +/* + * testByteBuffer.cpp + * + * Created on: Oct 20, 2010 + * Author: Miha Vitorovic + */ + +#include +#include + +#include +#include + +#include "byteBuffer.h" + +using namespace epics::pvData; +using std::cout; + +void testBasicOperations() { + cout<<"Basic operation tests...\n"; + + ByteBuffer* buff = new ByteBuffer(); + assert(buff->getSize()==32); + assert(buff->getByteOrder()==EPICS_BYTE_ORDER); + assert(buff->getPosition()==0); + assert(buff->getLimit()==32); + assert(buff->getRemaining()==32); + + buff->putBoolean(true); + assert(buff->getPosition()==1); + assert(buff->getRemaining()==31); + + buff->putByte(-12); + assert(buff->getPosition()==2); + assert(buff->getRemaining()==30); + + buff->putShort(10516); + assert(buff->getPosition()==4); + assert(buff->getRemaining()==28); + + buff->putInt(0x1937628B); + assert(buff->getPosition()==8); + assert(buff->getRemaining()==24); + + buff->putLong(2345678123LL); + assert(buff->getPosition()==16); + assert(buff->getRemaining()==16); + + float testFloat = 34.67; + buff->putFloat(testFloat); + assert(buff->getPosition()==20); + assert(buff->getRemaining()==12); + + double testDouble = -512.23974; + buff->putDouble(testDouble); + assert(buff->getPosition()==28); + assert(buff->getRemaining()==4); + + buff->flip(); + assert(buff->getLimit()==28); + assert(buff->getPosition()==0); + assert(buff->getRemaining()==28); + + assert(buff->getBoolean()==true); + assert(buff->getPosition()==1); + + assert(buff->getByte()==-12); + assert(buff->getPosition()==2); + + assert(buff->getShort()==10516); + assert(buff->getPosition()==4); + + assert(buff->getInt()==0x1937628B); + assert(buff->getPosition()==8); + + assert(buff->getLong()==2345678123LL); + assert(buff->getPosition()==16); + + assert(buff->getFloat()==testFloat); + assert(buff->getPosition()==20); + + assert(buff->getDouble()==testDouble); + assert(buff->getPosition()==28); + + buff->clear(); + assert(buff->getPosition()==0); + assert(buff->getLimit()==32); + assert(buff->getRemaining()==32); + + delete buff; + + cout<<" PASSED\n"; +} + +void testInverseEndianness() { + cout<<"Testing inverse endianness...\n"; + +#if EPICS_BYTE_ORDER==EPICS_ENDIAN_BIG + ByteBuffer* buff = new ByteBuffer(32,EPICS_ENDIAN_LITTLE); + char refBuffer[] = + { (char)0x02, (char)0x01, (char)0x0D, (char)0x0C, (char)0x0B, (char)0x0A}; +#else + ByteBuffer* buff = new ByteBuffer(32, EPICS_ENDIAN_BIG); + char refBuffer[] = { (char)0x01, (char)0x02, (char)0x0A, (char)0x0B, + (char)0x0C, (char)0x0D }; +#endif + + buff->putShort(0x0102); + buff->putInt(0x0A0B0C0D); + + assert(strncmp(buff->getArray(),refBuffer,6)==0); + + buff->flip(); + + assert(buff->getShort()==0x0102); + assert(buff->getInt()==0x0A0B0C0D); + + delete buff; + cout<<" PASSED\n"; +} + +int main(int argc, char *argv[]) { + testBasicOperations(); + testInverseEndianness(); + return (0); +}