start xcode data

This commit is contained in:
Michael Davidsaver
2019-12-15 16:43:22 -08:00
parent 21ac70db46
commit dd7a3d2dd3
5 changed files with 721 additions and 6 deletions
+487 -2
View File
@@ -7,6 +7,8 @@
#ifndef DATAENCODE_H
#define DATAENCODE_H
#include <cassert>
#include <stdexcept>
#include <functional>
#include <ostream>
@@ -62,7 +64,10 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
from_wire(buf, code.code);
const size_t index = ctxt.descs.size(); // index of first node we add to ctxt.descs[]
if(code.code==0xfd) {
if(code == TypeCode::Null) {
return;
} else if(code.code==0xfd) {
// update cache
uint16_t key=0;
from_wire(buf, key);
@@ -175,7 +180,7 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
default:
// not handling fixed/bounded
// other types have simple/single node description
switch(code.code&~0x08) {
switch(code.scalarOf().code) {
case TypeCode::Bool:
case TypeCode::Int8:
case TypeCode::Int16:
@@ -200,6 +205,486 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
}
}
namespace {
template<typename E, typename C = E>
void to_wire(Buffer& buf, const shared_array<const void>& varr)
{
auto arr = shared_array_static_cast<const E>(varr);
to_wire(buf, Size{arr.size()});
for(auto i : range(arr.size())) {
to_wire(buf, C(arr[i]));
}
}
template<typename E, typename C = E>
void from_wire(Buffer& buf, shared_array<const void>& varr)
{
Size slen;
from_wire(buf, slen);
shared_array<E> arr(slen.size);
for(auto i : range(arr.size())) {
C temp{};
from_wire(buf, temp);
arr[i] = temp;
}
}
}
// serialize a field and all children (if Compound)
static
void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store)
{
switch(store->code) {
case StoreType::Null:
switch(desc->code.code) {
case TypeCode::Struct: {
auto& top = *store->top;
// serialize entire sub-structure
for(auto off : range(desc->offset+1u, desc->next_offset)) {
auto cdesc = desc + top.member_indicies[off];
auto cstore = store + off;
if(cdesc->code!=TypeCode::Struct)
to_wire_field(buf, cdesc, cstore);
}
}
return;
default: break;
}
break;
case StoreType::Real: {
auto& fld = store->as<double>();
switch(desc->code.code) {
case TypeCode::Float32: to_wire(buf, float(fld)); return;
case TypeCode::Float64: to_wire(buf, double(fld)); return;
default: break;
}
}
break;
case StoreType::Integer: {
auto& fld = store->as<int64_t>();
switch(desc->code.code) {
case TypeCode::Int8: to_wire(buf, int8_t (fld)); return;
case TypeCode::Int16: to_wire(buf, int16_t(fld)); return;
case TypeCode::Int32: to_wire(buf, int32_t(fld)); return;
case TypeCode::Int64: to_wire(buf, int64_t(fld)); return;
default: break;
}
}
break;
case StoreType::UInteger: {
auto& fld = store->as<uint64_t>();
switch(desc->code.code) {
case TypeCode::Bool: to_wire(buf, uint8_t (fld!=0)); return;
case TypeCode::UInt8: to_wire(buf, uint8_t (fld)); return;
case TypeCode::UInt16: to_wire(buf, uint16_t(fld)); return;
case TypeCode::UInt32: to_wire(buf, uint32_t(fld)); return;
case TypeCode::UInt64: to_wire(buf, uint64_t(fld)); return;
default: break;
}
}
break;
case StoreType::String: {
auto& fld = store->as<std::string>();
switch(desc->code.code) {
case TypeCode::String: to_wire(buf, fld); return;
default: break;
}
}
break;
case StoreType::Compound: {
auto& fld = store->as<Value>();
switch (desc->code.code) {
case TypeCode::Union:
if(!fld) {
// implied NULL Union member
to_wire(buf, Size{size_t(-1)});
} else {
size_t index = 0u;
for(auto& pair : desc->miter) {
if(fld._desc()== desc+pair.second)
break;
index++;
}
if(index>=desc->miter.size())
throw std::logic_error("Union contains non-member type");
to_wire(buf, Size{index});
to_wire_field(buf, fld._desc(), fld._store());
}
return;
case TypeCode::Any:
if(!fld) {
to_wire(buf, uint8_t(0xff));
} else {
to_wire(buf, fld._desc());
to_wire_field(buf, fld._desc(), fld._store());
}
return;
default: break;
}
}
break;
case StoreType::Array: {
auto& fld = store->as<shared_array<const void>>();
switch (desc->code.code) {
case TypeCode::BoolA:
to_wire<bool, uint8_t>(buf, fld);
return;
case TypeCode::Int8:
case TypeCode::UInt8:
to_wire<uint8_t>(buf, fld);
return;
case TypeCode::Int16:
case TypeCode::UInt16:
to_wire<uint16_t>(buf, fld);
return;
case TypeCode::Int32:
case TypeCode::UInt32:
case TypeCode::Float32:
to_wire<uint32_t>(buf, fld);
return;
case TypeCode::Int64:
case TypeCode::UInt64:
case TypeCode::Float64:
to_wire<uint64_t>(buf, fld);
return;
case TypeCode::StringA:
to_wire<std::string, const std::string&>(buf, fld);
return;
case TypeCode::StructA:{
auto arr = shared_array_static_cast<const Value>(fld);
to_wire(buf, Size{arr.size()});
for(auto& elem : arr) {
if(!elem) {
to_wire(buf, uint8_t(0u));
} else {
to_wire(buf, uint8_t(1u));
assert(elem._desc()==desc+1);
to_wire_field(buf, elem._desc(), elem._store());
}
}
}
return;
case TypeCode::UnionA: {
auto arr = shared_array_static_cast<const Value>(fld);
to_wire(buf, Size{arr.size()});
for(auto& elem : arr) {
if(!elem) {
to_wire(buf, uint8_t(0u));
} else {
to_wire(buf, uint8_t(1u));
to_wire_field(buf, elem._desc(), elem._store());
}
}
}
return;
case TypeCode::AnyA:{
auto arr = shared_array_static_cast<const Value>(fld);
to_wire(buf, Size{arr.size()});
for(auto& elem : arr) {
if(!elem) {
to_wire(buf, uint8_t(0u));
} else {
to_wire(buf, uint8_t(1u));
to_wire(buf, elem._desc());
to_wire_field(buf, elem._desc(), elem._store());
}
}
}
return;
default: break;
}
break;
}
} // end case
assert(false);
buf.fault();
}
void to_wire_full(Buffer& buf, const Value& val)
{
assert(!!val);
to_wire_field(buf, val._desc(), val._store());
}
void to_wire_valid(Buffer& buf, const Value& val)
{
auto desc = val._desc();
assert(!!desc);
auto top = val._store()->top;
to_wire(buf, top->valid);
top->valid.resize(top->members.size());
// iterate marked fields
for(auto bit = top->valid.findSet(desc->offset);
bit<desc->next_offset;
bit = top->valid.findSet(bit+1))
{
to_wire_field(buf, desc + top->member_indicies[bit], val._store()+bit);
}
}
namespace {
template<typename T>
T from_wire_as(Buffer& buf)
{
T ret{};
from_wire(buf, ret);
return ret;
}
}
static
void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, FieldStorage* store)
{
switch(store->code) {
case StoreType::Null:
switch(desc->code.code) {
case TypeCode::Struct: {
auto& top = *store->top;
// serialize entire sub-structure
for(auto off : range(desc->offset+1u, desc->next_offset)) {
auto cdesc = desc + top.member_indicies[off];
auto cstore = store + off;
if(cdesc->code!=TypeCode::Struct)
from_wire_field(buf, ctxt, cdesc, cstore);
}
}
return;
default: break;
}
break;
case StoreType::Real: {
auto& fld = store->as<double>();
switch(desc->code.code) {
case TypeCode::Float32: fld = from_wire_as<float>(buf); return;
case TypeCode::Float64: fld = from_wire_as<double>(buf); return;
default: break;
}
}
break;
case StoreType::Integer: {
auto& fld = store->as<int64_t>();
switch(desc->code.code) {
case TypeCode::Int8: fld = from_wire_as<int8_t>(buf); return;
case TypeCode::Int16: fld = from_wire_as<int16_t>(buf); return;
case TypeCode::Int32: fld = from_wire_as<int32_t>(buf); return;
case TypeCode::Int64: fld = from_wire_as<int64_t>(buf); return;
default: break;
}
}
break;
case StoreType::UInteger: {
auto& fld = store->as<uint64_t>();
switch(desc->code.code) {
case TypeCode::Bool: fld = 0!=from_wire_as<uint8_t>(buf); return;
case TypeCode::UInt8: fld = from_wire_as<int8_t>(buf); return;
case TypeCode::UInt16: fld = from_wire_as<int16_t>(buf); return;
case TypeCode::UInt32: fld = from_wire_as<int32_t>(buf); return;
case TypeCode::UInt64: fld = from_wire_as<int64_t>(buf); return;
default: break;
}
}
break;
case StoreType::String: {
auto& fld = store->as<std::string>();
switch(desc->code.code) {
case TypeCode::String: from_wire(buf, fld); return;
default: break;
}
}
break;
case StoreType::Compound: {
auto& fld = store->as<Value>();
switch (desc->code.code) {
case TypeCode::Union: {
Size select{};
from_wire(buf, select);
if(select.size==size_t(-1)) {
fld = Value();
return;
} else if(select.size < desc->miter.size()) {
std::shared_ptr<const FieldDesc> stype(store->top->desc,
desc + desc->miter[select.size].second); // alias
fld = Value(stype);
from_wire_field(buf, ctxt, fld._desc(), fld._store());
return;
}
}
break;
case TypeCode::Any: {
std::shared_ptr<std::vector<FieldDesc>> descs(new std::vector<FieldDesc>);
TypeDeserContext dc{*descs, ctxt};
from_wire(buf, dc);
if(descs->empty()) {
fld = Value();
return;
} else {
std::shared_ptr<const FieldDesc> stype(descs, descs->data()); // alias
fld = Value(stype);
from_wire_field(buf, ctxt, fld._desc(), fld._store());
return;
}
}
break;
default: break;
}
}
break;
case StoreType::Array: {
auto& fld = store->as<shared_array<const void>>();
switch (desc->code.code) {
case TypeCode::BoolA:
from_wire<bool, uint8_t>(buf, fld);
return;
case TypeCode::Int8:
case TypeCode::UInt8:
from_wire<uint8_t>(buf, fld);
return;
case TypeCode::Int16:
case TypeCode::UInt16:
from_wire<uint16_t>(buf, fld);
return;
case TypeCode::Int32:
case TypeCode::UInt32:
case TypeCode::Float32:
from_wire<uint32_t>(buf, fld);
return;
case TypeCode::Int64:
case TypeCode::UInt64:
case TypeCode::Float64:
from_wire<uint64_t>(buf, fld);
return;
case TypeCode::StringA:
from_wire<std::string>(buf, fld);
return;
case TypeCode::StructA:{
Size alen{};
from_wire(buf, alen);
shared_array<Value> arr(alen.size);
std::shared_ptr<const FieldDesc> etype(store->top->desc,
desc + 1); // alias
for(auto& elem : arr) {
if(from_wire_as<uint8_t>(buf)!=0) { // strictly 1 or 0
elem = Value(etype);
from_wire_field(buf, ctxt, elem._desc(), elem._store());
}
}
fld = shared_array_static_cast<const void>(freeze(std::move(arr)));
}
return;
case TypeCode::UnionA: {
Size alen{};
from_wire(buf, alen);
shared_array<Value> arr(alen.size);
auto cdesc = desc+1;
for(auto& elem : arr) {
if(from_wire_as<uint8_t>(buf)!=0) { // strictly 1 or 0
Size select{};
from_wire(buf, select);
if(select.size==size_t(-1)) {
// null element. treated the same as 0 case (which is what actually happens)
} else if(select.size < cdesc->miter.size()) {
std::shared_ptr<const FieldDesc> stype(store->top->desc,
cdesc + cdesc->miter[select.size].second); // alias
elem = Value(stype);
from_wire_field(buf, ctxt, elem._desc(), elem._store());
return;
} else {
// invalid selector
buf.fault();
break;
}
}
}
fld = shared_array_static_cast<const void>(freeze(std::move(arr)));
}
return;
case TypeCode::AnyA:{
Size alen{};
from_wire(buf, alen);
shared_array<Value> arr(alen.size);
for(auto& elem : arr) {
if(from_wire_as<uint8_t>(buf)!=0) { // strictly 1 or 0
std::shared_ptr<std::vector<FieldDesc>> descs(new std::vector<FieldDesc>);
TypeDeserContext dc{*descs, ctxt};
from_wire(buf, dc);
if(!descs->empty()) {
std::shared_ptr<const FieldDesc> stype(descs, descs->data()); // alias
elem = Value(stype);
from_wire_field(buf, ctxt, elem._desc(), elem._store());
}
}
}
fld = shared_array_static_cast<const void>(freeze(std::move(arr)));
}
return;
default: break;
}
break;
}
} // end case
assert(false);
buf.fault();
}
void from_wire_full(Buffer& buf, TypeStore& ctxt, Value& val)
{
assert(!!val);
from_wire_field(buf, ctxt, val._desc(), val._store());
}
void from_wire_valid(Buffer& buf, TypeStore& ctxt, Value& val)
{
auto desc = val._desc();
assert(!!desc);
auto top = val._store()->top;
from_wire(buf, top->valid);
// encoding rounds # of bits to whole bytes, so we may trim
top->valid.resize(top->members.size());
if(!buf.good())
return;
for(auto bit = top->valid.findSet(desc->offset);
bit<desc->next_offset;
bit = top->valid.findSet(bit+1))
{
from_wire_field(buf, ctxt, desc + top->member_indicies[bit], val._store()+bit);
}
}
}} // namespace pvxs::impl
#endif // DATAENCODE_H
+17
View File
@@ -99,6 +99,9 @@ struct FieldStorage {
T& as() { return *reinterpret_cast<T*>(&store); }
template<typename T>
const T& as() const { return *reinterpret_cast<const T*>(&store); }
inline uint8_t* buffer() { return reinterpret_cast<uint8_t*>(&store); }
inline const uint8_t* buffer() const { return reinterpret_cast<const uint8_t*>(&store); }
};
// hidden (publically) management of an allocated Struct
@@ -117,6 +120,20 @@ static_assert (std::is_standard_layout<StructTop>{}, "Needed for offsetof()");
using Type = std::shared_ptr<const FieldDesc>;
PVXS_API
void to_wire_full(Buffer& buf, const Value& val);
PVXS_API
void to_wire_valid(Buffer& buf, const Value& val);
PVXS_API
void from_wire_full(Buffer& buf, TypeStore& ctxt, Value& val);
PVXS_API
void from_wire_valid(Buffer& buf, TypeStore& ctxt, Value& val);
PVXS_API
void FieldDesc_calculate_offset(FieldDesc* top);
+6 -4
View File
@@ -251,6 +251,10 @@ void to_wire(Buffer& buf, const Size& size)
buf.push(254);
to_wire(buf, uint32_t(size.size));
} else if(size.size==size_t(-1)) {
// special "null" used to encode empty Union
buf.push(255);
} else {
buf.fault();
}
@@ -268,10 +272,8 @@ void from_wire(Buffer& buf, Size& size)
size.size = s;
} else if(s==255) {
// "null" size. not sure it is used.
// Replicate weirdness of pvDataCPP
// FIXME this is almost certainly a bug
size.size = -1;
// special "null" used to encode empty Union
size.size = size_t(-1);
} else if(s==254) {
uint32_t ls = 0;
+4
View File
@@ -38,6 +38,10 @@ TESTPROD += testtype
testtype_SRCS += testtype.cpp
TESTS += testtype
TESTPROD += testdata
testdata_SRCS += testdata.cpp
TESTS += testdata
TESTPROD += dummyserv
dummyserv_SRCS += dummyserv.cpp
# not a unittest
+207
View File
@@ -0,0 +1,207 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvxs is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <testMain.h>
#include <epicsUnitTest.h>
#include <pvxs/unittest.h>
#include <pvxs/data.h>
#include <pvxs/nt.h>
#include "utilpvt.h"
#include "pvaproto.h"
#include "dataimpl.h"
using namespace pvxs;
namespace {
template<size_t N>
testCase
testBytes(const std::vector<uint8_t>& actual, const char(&buf)[N])
{
bool ok = actual.size()==(N-1) && std::equal(actual.begin(),
actual.end(),
(const uint8_t*)buf);
testCase ret(ok);
ret<<"Expect: \""<<escape(std::string((const char*)buf, N-1))<<"\"\n"
<<"Actual: \""<<escape(std::string((const char*)actual.data(), actual.size()))<<"\"\n";
return ret;
}
template<typename Fn, size_t N>
void testToBytes(bool be, Fn&& fn, const char(&expect)[N])
{
std::vector<uint8_t> buf;
VectorOutBuf S(be, buf);
fn(S);
buf.resize(buf.size()-S.size());
testBytes(buf, expect);
}
void testSerialize1()
{
testDiag("%s", __func__);
auto val = nt::NTScalar{TypeCode::UInt32}.build().create();
testToBytes(true, [&val](Buffer& buf) {
to_wire_full(buf, val);
}, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x00");
val["value"] = 0xdeadbeef;
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x01\x02\xde\xad\xbe\xef");
val["value"].unmark();
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x00");
val["timeStamp.nanoseconds"] = 0xab;
val["alarm.message"] = "hello world";
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x02 \x01\x0bhello world\x00\x00\x00\xab");
}
void testSerialize2()
{
testDiag("%s", __func__);
TypeDef def(TypeCode::Struct, "simple_t", {
Member(TypeCode::Float64A, "value"),
Member(TypeCode::Struct, "timeStamp", "time_t", {
Member(TypeCode::UInt64, "secondsPastEpoch"),
Member(TypeCode::UInt32, "nanoseconds"),
}),
Member(TypeCode::Struct, "arbitrary", {
Member(TypeCode::StructA, "sarr", {
Member(TypeCode::UInt32, "value"),
}),
}),
Member(TypeCode::Any, "any"),
Member(TypeCode::AnyA, "anya"),
Member(TypeCode::Union, "choice", {
Member(TypeCode::Float32, "a"),
Member(TypeCode::String, "b"),
}),
Member(TypeCode::UnionA, "achoice", {
Member(TypeCode::String, "x"),
Member(TypeCode::String, "y"),
}),
});
{
auto val = def.create();
auto fld = val["arbitrary.sarr"];
shared_array<Value> arr(3);
arr[0] = fld.allocMember();
arr[1] = fld.allocMember();
// leave [2] as null
arr[0]["value"] = 0xdeadbeef;
arr[1]["value"] = 0x1badface;
fld.from(shared_array_static_cast<const void>(freeze(std::move(arr))));
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x01@\x03\x01\xde\xad\xbe\xef\x01\x1b\xad\xfa\xce\x00");
}
{
auto val = def.create();
val["choice->b"] = "test";
val["choice"].mark(); // TODO: auto mark back through union
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x02\x00\x02\x01\x04test");
}
{
auto val = def.create();
auto fld = val["achoice"];
shared_array<Value> arr(3);
arr[0] = fld.allocMember();
arr[1] = fld.allocMember();
// leave [2] as null
arr[0]["->x"] = "theX";
arr[1]["->y"] = "theY";
fld.from(shared_array_static_cast<const void>(freeze(std::move(arr))));
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x02\x00\x04\x03\x01\x00\x04theX\x01\x01\x04theY\x00");
}
// Any
{
auto val = def.create();
auto v = TypeDef(TypeCode::UInt32).create();
v = 0x600df00d;
val["any"].from(v);
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x01\x80\x26\x60\x0d\xf0\x0d");
}
// Any
{
auto val = def.create();
val["any"].mark();
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x01\x80\xff");
}
// Any[]
{
auto val = def.create();
auto fld = val["anya"];
shared_array<Value> arr(3);
arr[0] = TypeDef(TypeCode::UInt32).create();
arr[1] = TypeDef(TypeCode::Struct, {Member(TypeCode::String, "q")}).create();
// leave [2] as null
arr[0] = 0x7b;
arr[1]["q"] = "theq";
fld.from(shared_array_static_cast<const void>(freeze(std::move(arr))));
testToBytes(true, [&val](Buffer& buf) {
to_wire_valid(buf, val);
}, "\x02\x00\x01\x03\x01\x26\x00\x00\x00\x7b\x01\x80\x00\x01\x01q\x60\x04theq\x00");
}
}
} // namespace
MAIN(testdata)
{
testPlan(11);
testSerialize1();
testSerialize2();
cleanup_for_valgrind();
return testDone();
}