diff --git a/QtC-pvData.files b/QtC-pvData.files index 41caef7..9feb805 100644 --- a/QtC-pvData.files +++ b/QtC-pvData.files @@ -80,3 +80,5 @@ pvDataApp/misc/bitSet.cpp pvDataApp/test/testByteBuffer.cpp pvDataApp/test/testBitSet.cpp pvDataApp/test/testBaseException.cpp +pvDataApp/misc/serializeHelper.h +pvDataApp/misc/serializeHelper.cpp diff --git a/pvDataApp/misc/Makefile b/pvDataApp/misc/Makefile index d54669d..250a5ce 100644 --- a/pvDataApp/misc/Makefile +++ b/pvDataApp/misc/Makefile @@ -9,9 +9,11 @@ INC += serialize.h INC += bitSet.h INC += byteBuffer.h INC += epicsException.h +INC += serializeHelper.h LIBSRCS += byteBuffer.cpp LIBSRCS += bitSet.cpp +LIBSRCS += serializeHelper.cpp LIBRARY=pvMisc diff --git a/pvDataApp/misc/byteBuffer.cpp b/pvDataApp/misc/byteBuffer.cpp index 2a200f8..9c3a55f 100644 --- a/pvDataApp/misc/byteBuffer.cpp +++ b/pvDataApp/misc/byteBuffer.cpp @@ -101,6 +101,19 @@ namespace epics { return val; } + ByteBuffer* ByteBuffer::put(const char* src, int offset, int count) { + if(count>getRemaining()) throw EpicsException("buffer overflow"); + strncpy(&_buffer[_position], &src[offset], count); + _position += count; + return this; + } + + void ByteBuffer::get(char*dst, int offset, int count) { + if(count>getRemaining()) throw EpicsException("buffer underflow"); + strncpy(&dst[offset], &_buffer[_position], count); + _position += count; + } + ByteBuffer* ByteBuffer::putBoolean(bool value) { if(_position<_limit) _buffer[_position++] = value ? 1 : 0; diff --git a/pvDataApp/misc/byteBuffer.h b/pvDataApp/misc/byteBuffer.h index 504dd2f..c1b7204 100644 --- a/pvDataApp/misc/byteBuffer.h +++ b/pvDataApp/misc/byteBuffer.h @@ -133,9 +133,34 @@ namespace epics { */ double getDouble(); + /** + * 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 + /** + * 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); + /** * Relative boolean write, {@code position} is incremented by * {@code 1}. diff --git a/pvDataApp/misc/serialize.h b/pvDataApp/misc/serialize.h index 5016e8b..ad4f14d 100644 --- a/pvDataApp/misc/serialize.h +++ b/pvDataApp/misc/serialize.h @@ -12,11 +12,13 @@ namespace epics { namespace pvData { class SerializableArray; class SerializableControl { + public: virtual void flushSerializeBuffer() =0; virtual void ensureBuffer(int size) =0; }; class DeserializableControl { + public: virtual void ensureData(int size) =0; }; diff --git a/pvDataApp/misc/serializeHelper.cpp b/pvDataApp/misc/serializeHelper.cpp new file mode 100644 index 0000000..d946a00 --- /dev/null +++ b/pvDataApp/misc/serializeHelper.cpp @@ -0,0 +1,120 @@ +/* + * serializeHelper.cpp + * + * Created on: Oct 22, 2010 + * Author: Miha vitorovic + */ + +#include +#include + +#include "epicsException.h" +#include "byteBuffer.h" +#include "serializeHelper.h" + + +using namespace std; + +namespace epics { + namespace pvData { + + void SerializeHelper::writeSize(int s, ByteBuffer* buffer, + SerializableControl* flusher) { + flusher->ensureBuffer(sizeof(epicsInt64)+1); + SerializeHelper::writeSize(s, buffer); + } + + void SerializeHelper::writeSize(int s, ByteBuffer* buffer) { + if(s==-1) // null + buffer->putByte(-1); + else if(s<254) + buffer->putByte(s); + else + buffer->putByte(-2)->putInt(s); // (byte)-2 + size + } + + int SerializeHelper::readSize(ByteBuffer* buffer, + DeserializableControl* control) { + control->ensureData(1); + epicsInt8 b = buffer->getByte(); + if(b==-1) + return -1; + else if(b==-2) { + control->ensureData(sizeof(epicsInt32)); + epicsInt32 s = buffer->getInt(); + if(s<0) throw EpicsException("negative array size"); + return s; + } + else + return (int)(b<0 ? b+256 : b); + } + + void SerializeHelper::serializeString(const String& value, + ByteBuffer* buffer, SerializableControl* flusher) { + int len = value.length(); + SerializeHelper::writeSize(len, buffer, flusher); + int i = 0; + while(true) { + int maxToWrite = min(len-i, buffer->getRemaining()); + buffer->put(value.data(), i, maxToWrite); // UTF-8 + i += maxToWrite; + if(iflushSerializeBuffer(); + else + break; + } + } + + void SerializeHelper::serializeSubstring(const String& value, + int offset, int count, ByteBuffer* buffer, + SerializableControl* flusher) { + if(offset<0) + offset = 0; + else if(offset>(int)value.length()) offset = value.length(); + + if(offset+count>(int)value.length()) count = value.length()-offset; + + SerializeHelper::writeSize(count, buffer, flusher); + int i = 0; + while(true) { + int maxToWrite = min(count-i, buffer->getRemaining()); + buffer->put(value.data(), offset+i, maxToWrite); // UTF-8 + i += maxToWrite; + if(iflushSerializeBuffer(); + else + break; + } + } + + String SerializeHelper::deserializeString(ByteBuffer* buffer, + DeserializableControl* control) { + + int size = SerializeHelper::readSize(buffer, control); + if(size>=0) { + char* retBuffer = new char[size]; // get the return buffer + try { + int i = 0; + while(true) { + int toRead = min(size-i, buffer->getRemaining()); + buffer->get(retBuffer, i, toRead); + i += toRead; + if(iensureData(1); + else + break; + } + String s = String(retBuffer, size); + delete retBuffer; + return s; + } catch(...) { + delete retBuffer; // remove the buffer + throw; + } + } + else + return String(""); + } + + } +} diff --git a/pvDataApp/misc/serializeHelper.h b/pvDataApp/misc/serializeHelper.h new file mode 100644 index 0000000..8b54bc2 --- /dev/null +++ b/pvDataApp/misc/serializeHelper.h @@ -0,0 +1,98 @@ +/* + * serializeHelper.h + * + * Created on: Oct 21, 2010 + * Author: Miha Vitorovic + */ + +#ifndef SERIALIZEHELPER_H_ +#define SERIALIZEHELPER_H_ + +#include "serialize.h" +#include "byteBuffer.h" +#include "noDefaultMethods.h" +#include "../pv/pvIntrospect.h" + +namespace epics { + namespace pvData { + + class SerializeHelper : public NoDefaultMethods { + public: + + /** + * Serialize array size. + * + * @param[in] s size to encode + * @param[in] buffer serialization buffer + * @param[in] flusher flusher + */ + static void writeSize(int s, ByteBuffer* buffer, + SerializableControl* flusher); + + /** + * Deserialize array size. + * + * @param[in] buffer deserialization buffer. + * @returns array size. + */ + static int readSize(ByteBuffer* buffer, + DeserializableControl* control); + + /** + * String serialization helper method. + * + * @param[in] value String to serialize + * @param[in] buffer serialization buffer + * @param[in] flusher flusher + */ + static void serializeString(const String& value, ByteBuffer* buffer, + SerializableControl* flusher); + + /** + * String serialization helper method. + * + * @param[in] value String to serialize + * @param[in] offset start of the substring in {@code value} + * @param[in] count the number of characters to write + * @param[in] buffer serialization buffer + * @param[in] flusher flusher + */ + static void serializeSubstring(const String& value, int offset, + int count, ByteBuffer* buffer, + SerializableControl* flusher); + + /** + * String deserialization helper method. + * TODO This method cannot return "null", but Java implementation + * could have serialized "null" value as well. We need to decide + * how to deserialize "null". + * + * @param[in] buffer deserialization buffer + * @param[in] control control + * @returns deserialized string + * + * @todo This method cannot return "null", but Java implementation + * could have serialized "null" value as well. We need to decide + * how to deserialize "null". + */ + static String deserializeString(ByteBuffer* buffer, + DeserializableControl* control); + + private: + SerializeHelper() {}; + ~SerializeHelper() {}; + + /** + * Serialize array size. + * + * @param[in] s size to encode + * @param[in] buffer serialization buffer + */ + static void writeSize(int s, ByteBuffer* buffer); + + }; + + } +} + +#endif /* SERIALIZEHELPER_H_ */ diff --git a/pvDataApp/test/testByteBuffer.cpp b/pvDataApp/test/testByteBuffer.cpp index 3824e83..b9f9424 100644 --- a/pvDataApp/test/testByteBuffer.cpp +++ b/pvDataApp/test/testByteBuffer.cpp @@ -12,115 +12,137 @@ #include #include "byteBuffer.h" +#include "pvIntrospect.h" using namespace epics::pvData; using std::cout; void testBasicOperations() { - cout<<"Basic operation tests...\n"; + 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); + 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->putBoolean(true); + assert(buff->getPosition()==1); + assert(buff->getRemaining()==31); - buff->putByte(-12); - assert(buff->getPosition()==2); - assert(buff->getRemaining()==30); + buff->putByte(-12); + assert(buff->getPosition()==2); + assert(buff->getRemaining()==30); - buff->putShort(10516); - assert(buff->getPosition()==4); - assert(buff->getRemaining()==28); + buff->putShort(10516); + assert(buff->getPosition()==4); + assert(buff->getRemaining()==28); - buff->putInt(0x1937628B); - assert(buff->getPosition()==8); - assert(buff->getRemaining()==24); + buff->putInt(0x1937628B); + assert(buff->getPosition()==8); + assert(buff->getRemaining()==24); - buff->putLong(2345678123LL); - assert(buff->getPosition()==16); - assert(buff->getRemaining()==16); + 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); + 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); + 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); + buff->flip(); + assert(buff->getLimit()==28); + assert(buff->getPosition()==0); + assert(buff->getRemaining()==28); - assert(buff->getBoolean()==true); - assert(buff->getPosition()==1); + assert(buff->getBoolean()==true); + assert(buff->getPosition()==1); - assert(buff->getByte()==-12); - assert(buff->getPosition()==2); + assert(buff->getByte()==-12); + assert(buff->getPosition()==2); - assert(buff->getShort()==10516); - assert(buff->getPosition()==4); + assert(buff->getShort()==10516); + assert(buff->getPosition()==4); - assert(buff->getInt()==0x1937628B); - assert(buff->getPosition()==8); + assert(buff->getInt()==0x1937628B); + assert(buff->getPosition()==8); - assert(buff->getLong()==2345678123LL); - assert(buff->getPosition()==16); + assert(buff->getLong()==2345678123LL); + assert(buff->getPosition()==16); - assert(buff->getFloat()==testFloat); - assert(buff->getPosition()==20); + assert(buff->getFloat()==testFloat); + assert(buff->getPosition()==20); - assert(buff->getDouble()==testDouble); - assert(buff->getPosition()==28); + assert(buff->getDouble()==testDouble); + assert(buff->getPosition()==28); - buff->clear(); - assert(buff->getPosition()==0); - assert(buff->getLimit()==32); - assert(buff->getRemaining()==32); + buff->clear(); + assert(buff->getPosition()==0); + assert(buff->getLimit()==32); + assert(buff->getRemaining()==32); - delete buff; + char src[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm' }; + char dst[] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ' }; - cout<<" PASSED\n"; + buff->put(src, 2, 6); + assert(buff->getPosition()==6); + assert(strncmp(buff->getArray(),&src[2],6)==0); + + buff->flip(); + assert(buff->getLimit()==6); + assert(buff->getPosition()==0); + assert(buff->getRemaining()==6); + + buff->get(dst, 2, 6); + assert(buff->getLimit()==6); + assert(buff->getPosition()==6); + assert(strncmp(&src[2],&dst[2],6)==0); + + cout<<" First 10 characters of destination: >>"<putShort(0x0102); - buff->putInt(0x0A0B0C0D); + buff->putShort(0x0102); + buff->putInt(0x0A0B0C0D); - assert(strncmp(buff->getArray(),refBuffer,6)==0); + assert(strncmp(buff->getArray(),refBuffer,6)==0); - buff->flip(); + buff->flip(); - assert(buff->getShort()==0x0102); - assert(buff->getInt()==0x0A0B0C0D); + assert(buff->getShort()==0x0102); + assert(buff->getInt()==0x0A0B0C0D); - delete buff; - cout<<" PASSED\n"; + delete buff; + cout<<"!!! PASSED\n"; } int main(int argc, char *argv[]) { - testBasicOperations(); - testInverseEndianness(); - return (0); + testBasicOperations(); + testInverseEndianness(); + return (0); }