Merge pull request #17 from mdavidsaver/sertovector
add serializeToVector()
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
#ifndef SERIALIZE_H
|
||||
#define SERIALIZE_H
|
||||
|
||||
#include <epicsTypes.h>
|
||||
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
@@ -150,6 +152,41 @@ namespace epics { namespace pvData {
|
||||
DeserializableControl *flusher) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Push serialize and append to the provided byte vector.
|
||||
* No caching is done. Only complete serialization.
|
||||
*
|
||||
* @param S A Serializable object
|
||||
* @param byteOrder Byte order to write (EPICS_ENDIAN_LITTLE or EPICS_ENDIAN_BIG)
|
||||
* @param out The output vector. Results are appended
|
||||
*/
|
||||
void serializeToVector(const Serializable *S,
|
||||
int byteOrder,
|
||||
std::vector<epicsUInt8>& out);
|
||||
|
||||
/**
|
||||
* @brief deserializeFromBuffer Deserialize into S from provided vector
|
||||
* @param S A Serializeable object. The current contents will be replaced
|
||||
* @param in The input buffer (byte order of this buffer is used)
|
||||
* @throws std::logic_error if input buffer is too small. State of S is then undefined.
|
||||
*/
|
||||
void deserializeFromBuffer(Serializable *S,
|
||||
ByteBuffer& in);
|
||||
|
||||
/**
|
||||
* @brief deserializeFromBuffer Deserialize into S from provided vector
|
||||
* @param S A Serializeable object. The current contents will be replaced
|
||||
* @param byteOrder Byte order to write (EPICS_ENDIAN_LITTLE or EPICS_ENDIAN_BIG)
|
||||
* @param in The input vector
|
||||
* @throws std::logic_error if input buffer is too small. State of S is then undefined.
|
||||
*/
|
||||
inline void deserializeFromVector(Serializable *S,
|
||||
int byteOrder,
|
||||
const std::vector<epicsUInt8>& in)
|
||||
{
|
||||
ByteBuffer B((char*)&in[0], in.size(), byteOrder); // we promise not the modify 'in'
|
||||
deserializeFromBuffer(S, B);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Class for serializing bitSets.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
@@ -139,6 +140,132 @@ namespace epics {
|
||||
else
|
||||
return emptyStringtring;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
struct ToString : public epics::pvData::SerializableControl
|
||||
{
|
||||
typedef std::vector<epicsUInt8> buf_type;
|
||||
buf_type buf;
|
||||
buf_type& out;
|
||||
ByteBuffer bufwrap;
|
||||
|
||||
ToString(buf_type& out, int byteOrder = EPICS_BYTE_ORDER)
|
||||
:buf(16*1024)
|
||||
,out(out)
|
||||
,bufwrap((char*)&buf[0], buf.size(), byteOrder)
|
||||
{}
|
||||
|
||||
virtual void flushSerializeBuffer()
|
||||
{
|
||||
size_t N = out.size();
|
||||
out.resize(out.size()+bufwrap.getPosition());
|
||||
std::copy(buf.begin(),
|
||||
buf.begin()+bufwrap.getPosition(),
|
||||
out.begin()+N);
|
||||
bufwrap.clear();
|
||||
}
|
||||
|
||||
virtual void ensureBuffer(std::size_t size)
|
||||
{
|
||||
flushSerializeBuffer();
|
||||
assert(bufwrap.getRemaining()>0);
|
||||
}
|
||||
|
||||
virtual void alignBuffer(std::size_t alignment)
|
||||
{
|
||||
if(bufwrap.getRemaining()<alignment)
|
||||
flushSerializeBuffer();
|
||||
assert(bufwrap.getRemaining()>=alignment);
|
||||
bufwrap.align(alignment);
|
||||
}
|
||||
|
||||
virtual bool directSerialize(
|
||||
ByteBuffer *existingBuffer,
|
||||
const char* toSerialize,
|
||||
std::size_t elementCount,
|
||||
std::size_t elementSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void cachedSerialize(
|
||||
std::tr1::shared_ptr<const Field> const & field,
|
||||
ByteBuffer* buffer)
|
||||
{
|
||||
field->serialize(buffer, this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
void serializeToVector(const Serializable *S,
|
||||
int byteOrder,
|
||||
std::vector<epicsUInt8>& out)
|
||||
{
|
||||
ToString TS(out, byteOrder);
|
||||
S->serialize(&TS.bufwrap, &TS);
|
||||
TS.flushSerializeBuffer();
|
||||
assert(TS.bufwrap.getPosition()==0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct FromString : public epics::pvData::DeserializableControl
|
||||
{
|
||||
ByteBuffer &buf;
|
||||
epics::pvData::FieldCreatePtr create;
|
||||
|
||||
FromString(ByteBuffer& b)
|
||||
:buf(b)
|
||||
,create(epics::pvData::getFieldCreate())
|
||||
{}
|
||||
|
||||
virtual void ensureData(std::size_t size)
|
||||
{
|
||||
if(size>buf.getRemaining())
|
||||
throw std::logic_error("Incomplete buffer");
|
||||
}
|
||||
|
||||
virtual void alignData(std::size_t alignment)
|
||||
{
|
||||
size_t pos = buf.getPosition(), k = alignment-1;
|
||||
if(pos&k) {
|
||||
std::size_t npad = alignment-(pos&k);
|
||||
ensureData(npad);
|
||||
buf.align(alignment);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool directDeserialize(
|
||||
ByteBuffer *existingBuffer,
|
||||
char* deserializeTo,
|
||||
std::size_t elementCount,
|
||||
std::size_t elementSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual std::tr1::shared_ptr<const Field> cachedDeserialize(
|
||||
ByteBuffer* buffer)
|
||||
{
|
||||
return create->deserialize(buffer, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
void deserializeFromBuffer(Serializable *S,
|
||||
ByteBuffer& buf)
|
||||
{
|
||||
FromString F(buf);
|
||||
S->deserialize(&buf, &F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <fstream>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <testMain.h>
|
||||
#include <dbDefs.h> // for NELEMENTS
|
||||
|
||||
@@ -761,11 +762,107 @@ void testStringCopy() {
|
||||
testDiag("implementation of string assignment operator does not share content");
|
||||
}
|
||||
|
||||
static
|
||||
void printbytes(size_t N, const epicsUInt8 *buf)
|
||||
{
|
||||
bool needeol = false;
|
||||
for(size_t i=0; i<N; i++)
|
||||
{
|
||||
if(i%16==0) {
|
||||
printf("# %04x ", (unsigned)i);
|
||||
needeol = true;
|
||||
}
|
||||
printf("%02x", buf[i]);
|
||||
if(i%16==15) {
|
||||
printf("\n");
|
||||
needeol = false;
|
||||
} else if(i%4==3) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
if(needeol)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static
|
||||
const char expected_le[] = "\x2a\000\000\000\x07testing";
|
||||
|
||||
static
|
||||
const char expected_be[] = "\000\000\000\x2a\x07testing";
|
||||
|
||||
static
|
||||
void testToString(int byteOrder)
|
||||
{
|
||||
testDiag("testToString(%d)", byteOrder);
|
||||
|
||||
StructureConstPtr _type = getFieldCreate()->createFieldBuilder()
|
||||
->add("X", pvInt)->add("Y", pvString)
|
||||
->createStructure();
|
||||
|
||||
PVStructurePtr _data = getPVDataCreate()->createPVStructure(_type);
|
||||
|
||||
_data->getSubFieldT<PVInt>("X")->put(42);
|
||||
_data->getSubFieldT<PVString>("Y")->put("testing");
|
||||
|
||||
std::vector<epicsUInt8> bytes;
|
||||
serializeToVector(_data.get(), byteOrder, bytes);
|
||||
|
||||
const char *expected;
|
||||
size_t count;
|
||||
if(byteOrder==EPICS_ENDIAN_LITTLE) {
|
||||
expected = expected_le;
|
||||
count = NELEMENTS(expected_le)-1;
|
||||
} else if(byteOrder==EPICS_ENDIAN_BIG) {
|
||||
expected = expected_be;
|
||||
count = NELEMENTS(expected_be)-1;
|
||||
} else {
|
||||
throw std::logic_error("Unsupported mixed endian");
|
||||
}
|
||||
|
||||
testOk(bytes.size()==count,
|
||||
"%u == %u", (unsigned)bytes.size(),
|
||||
(unsigned)count);
|
||||
|
||||
size_t N = std::min(bytes.size(), count);
|
||||
|
||||
testOk(memcmp(&bytes[0], expected, N)==0, "Match [0,%u)", (unsigned)N);
|
||||
|
||||
testDiag("Expected (%u)", (unsigned)bytes.size());
|
||||
printbytes(count, (epicsUInt8*)expected);
|
||||
|
||||
testDiag("Actual (%u)", (unsigned)count);
|
||||
printbytes(bytes.size(), &bytes[0]);
|
||||
}
|
||||
|
||||
void testFromString(int byteOrder)
|
||||
{
|
||||
testDiag("testFromString(%d)", byteOrder);
|
||||
|
||||
StructureConstPtr _type = getFieldCreate()->createFieldBuilder()
|
||||
->add("X", pvInt)->add("Y", pvString)
|
||||
->createStructure();
|
||||
|
||||
PVStructurePtr _data = getPVDataCreate()->createPVStructure(_type);
|
||||
|
||||
if(byteOrder==EPICS_ENDIAN_LITTLE) {
|
||||
ByteBuffer buf((char*)expected_le, NELEMENTS(expected_le)-1, byteOrder);
|
||||
deserializeFromBuffer(_data.get(), buf);
|
||||
} else if(byteOrder==EPICS_ENDIAN_BIG) {
|
||||
ByteBuffer buf((char*)expected_be, NELEMENTS(expected_be)-1, byteOrder);
|
||||
deserializeFromBuffer(_data.get(), buf);
|
||||
} else {
|
||||
throw std::logic_error("Unsupported mixed endian");
|
||||
}
|
||||
|
||||
testOk1(_data->getSubFieldT<PVInt>("X")->get()==42);
|
||||
testOk1(_data->getSubFieldT<PVString>("Y")->get()=="testing");
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
MAIN(testSerialization) {
|
||||
|
||||
testPlan(226);
|
||||
testPlan(234);
|
||||
|
||||
flusher = new SerializableControlImpl();
|
||||
control = new DeserializableControlImpl();
|
||||
@@ -787,6 +884,10 @@ MAIN(testSerialization) {
|
||||
testArraySizeType();
|
||||
testBoundedString();
|
||||
|
||||
testToString(EPICS_ENDIAN_BIG);
|
||||
testToString(EPICS_ENDIAN_LITTLE);
|
||||
testFromString(EPICS_ENDIAN_BIG);
|
||||
testFromString(EPICS_ENDIAN_LITTLE);
|
||||
|
||||
delete buffer;
|
||||
delete control;
|
||||
|
||||
Reference in New Issue
Block a user