Redo FieldDesc

Follow shape of FieldStorage exactly.
Avoids need for offset calculation.
This commit is contained in:
Michael Davidsaver
2019-12-20 19:38:35 -08:00
parent 9cc742d7f9
commit bbe5fa26a2
8 changed files with 685 additions and 559 deletions
+115 -52
View File
@@ -45,22 +45,20 @@ Value::Value(const std::shared_ptr<const impl::FieldDesc>& desc)
auto top = std::make_shared<StructTop>();
top->desc = desc;
top->valid.resize(desc->next_offset-desc->offset);
top->member_indicies.resize(top->valid.size());
top->members.resize(top->valid.size());
top->members.resize(desc->size());
{
auto& root = top->members[0];
root.init(desc.get());
root.top = top.get();
top->member_indicies[0u] = 0u;
}
for(auto& pair : desc->mlookup) {
auto cfld = desc.get() + pair.second;
auto& mem = top->members.at(cfld->offset-desc->offset);
mem.top = top.get();
mem.init(cfld);
top->member_indicies[cfld->offset-desc->offset] = pair.second;
if(desc->code==TypeCode::Struct) {
for(auto& pair : desc->mlookup) {
auto cfld = desc.get() + pair.second;
auto& mem = top->members.at(pair.second);
mem.top = top.get();
mem.init(cfld);
}
}
this->desc = desc.get();
@@ -93,18 +91,89 @@ Value Value::clone() const
if(desc) {
decltype (store->top->desc) fld(store->top->desc, desc);
ret = Value(fld);
//ret.assign(*this);
ret.assign(*this);
}
return ret;
}
//Value& Value::assign(const Value& o)
//{
// if(desc!=o.desc)
// throw std::runtime_error("Can only assign same TypeDef"); // TODO relax
Value& Value::assign(const Value& o)
{
if(desc!=o.desc)
throw std::runtime_error("Can only assign same TypeDef"); // TODO relax
// return *this;
//}
if(desc) {
for(size_t bit=0, end=desc->size(); bit<end;) {
auto sstore = o.store.get() + bit;
auto dstore = store.get() + bit;
if(!sstore->valid) {
bit++;
continue;
}
dstore->valid = true;
switch(dstore->code) {
case StoreType::Real:
case StoreType::Integer:
case StoreType::UInteger:
dstore->as<uint64_t>() = sstore->as<uint64_t>();
bit++;
break;
case StoreType::String:
dstore->as<std::string>() = sstore->as<std::string>();
bit++;
break;
case StoreType::Array:
dstore->as<shared_array<const void>>() = sstore->as<shared_array<const void>>();
bit++;
break;
case StoreType::Compound:
dstore->as<Value>() = sstore->as<Value>();
bit++;
break;
case StoreType::Null: {
// copy entire sub-structure
auto sdesc = desc + bit;
for(auto end2 = bit + sdesc->size(); bit<end2; bit++)
{
auto sstore = o.store.get() + bit;
auto dstore = store.get() + bit;
dstore->valid = true;
switch(dstore->code) {
case StoreType::Real:
case StoreType::Integer:
case StoreType::UInteger:
dstore->as<uint64_t>() = sstore->as<uint64_t>();
bit++;
break;
case StoreType::String:
dstore->as<std::string>() = sstore->as<std::string>();
bit++;
break;
case StoreType::Array:
dstore->as<shared_array<const void>>() = sstore->as<shared_array<const void>>();
bit++;
break;
case StoreType::Compound:
dstore->as<Value>() = sstore->as<Value>();
bit++;
break;
case StoreType::Null: // skip sub-struct nodes, we will copy all leaf nodes
break;
}
}
}
break;
}
}
}
return *this;
}
Value Value::allocMember()
{
@@ -112,7 +181,7 @@ Value Value::allocMember()
if(!desc || (desc->code!=TypeCode::UnionA && desc->code!=TypeCode::StructA))
throw std::runtime_error("allocMember() only meaningful for Struct[] or Union[]");
decltype (store->top->desc) fld(store->top->desc, desc+1);
decltype (store->top->desc) fld(store->top->desc, desc->members.data());
return Value::Helper::build(fld, *this);
}
@@ -121,28 +190,29 @@ bool Value::isMarked(bool parents, bool children) const
if(!desc)
return false;
if(store->top->valid[store->index()])
if(store->valid)
return true;
auto top = store->top;
if(children && desc->size()>1u) {
// TODO more efficient
for(auto bit : range(desc->offset-top->desc->offset,
desc->next_offset-top->desc->offset))
for(auto bit : range(desc->size()))
{
if(top->valid[bit])
auto cstore = store.get() + bit;
if(cstore->valid)
return true;
}
}
if(parents) {
auto P = desc;
while(P!=top->desc.get()) {
P -= P->parent_index;
auto pdesc = desc;
auto pstore = store.get();
while(pdesc!=top->desc.get()) {
pdesc -= pdesc->parent_index;
pstore -= pdesc->parent_index;
auto bit = P->offset - top->desc->offset;
if(top->valid[bit])
if(pstore->valid)
return true;
}
}
@@ -155,7 +225,7 @@ void Value::mark(bool v)
if(!desc)
return;
store->top->valid[store->index()] = v;
store->valid = v;
if(!v)
return;
@@ -171,26 +241,26 @@ void Value::unmark(bool parents, bool children)
if(!desc)
return;
store->top->valid[store->index()] = false;
store->valid = false;
auto top = store->top;
if(children && desc->size()>1u) {
// TODO more efficient
for(auto bit : range(desc->offset-top->desc->offset,
desc->next_offset-top->desc->offset))
for(auto bit : range(desc->size()))
{
top->valid[bit] = false;
(store.get() + bit)->valid = false;
}
}
if(parents) {
auto P = desc;
while(P!=top->desc.get()) {
P -= P->parent_index;
auto pdesc = desc;
auto pstore = store.get();
while(pdesc!=top->desc.get()) {
pdesc -= pdesc->parent_index;
pstore -= pdesc->parent_index;
auto bit = P->offset - top->desc->offset;
top->valid[bit] = false;
pstore->valid = false;
}
}
}
@@ -365,7 +435,7 @@ void Value::copyIn(const void *ptr, StoreType type)
if(desc->code!=TypeCode::AnyA) {
// enforce member type for Struct[] and Union[]
for(auto& val : tsrc) {
if(val.desc && val.desc!=desc+1) {
if(val.desc && val.desc!=desc->members.data()) {
throw NoConvert();
}
}
@@ -397,12 +467,6 @@ void Value::copyIn(const void *ptr, StoreType type)
}
throw NoConvert();
case StoreType::Null:
if(desc->code==TypeCode::Struct && type==StoreType::Compound) {
auto& val = *reinterpret_cast<const Value*>(ptr);
if(val.desc && val.desc->code==TypeCode::Struct) {
// Struct to Struct assignment.
}
}
throw NoConvert();
}
@@ -430,7 +494,7 @@ void Value::traverse(const std::string &expr, bool modify)
if(desc!=store->top->desc.get())
{
auto pdesc = desc - desc->parent_index;
std::shared_ptr<FieldStorage> pstore(store, store.get() - desc->offset + pdesc->offset);
std::shared_ptr<FieldStorage> pstore(store, store.get() - desc->parent_index);
store = std::move(pstore);
desc = pdesc;
pos++;
@@ -454,8 +518,7 @@ void Value::traverse(const std::string &expr, bool modify)
if(sep>0 && (it=desc->mlookup.find(expr.substr(pos, sep-pos)))!=desc->mlookup.end()) {
// found it
auto next = desc+it->second;
auto offset = next->offset - desc->offset;
decltype(store) value(store, store.get()+offset);
decltype(store) value(store, store.get()+it->second);
store = std::move(value);
desc = next;
pos = sep;
@@ -487,11 +550,11 @@ void Value::traverse(const std::string &expr, bool modify)
// found it.
auto& fld = store->as<Value>();
if(modify || fld.desc==desc+it->second) {
if(modify || fld.desc==&desc->members[it->second]) {
// will select, or already selected
if(fld.desc!=desc+it->second) {
if(fld.desc!=&desc->members[it->second]) {
// select
std::shared_ptr<const FieldDesc> mtype(store->top->desc, desc+it->second);
std::shared_ptr<const FieldDesc> mtype(store->top->desc, &desc->members[it->second]);
fld = Value(mtype, *this);
}
pos = sep;
@@ -593,7 +656,7 @@ void show_Value(std::ostream& strm,
strm<<" {\n";
for(auto& pair : desc->miter) {
auto cdesc = desc + pair.second;
show_Value(strm, pair.first, cdesc, store - desc->offset + cdesc->offset, level+1);
show_Value(strm, pair.first, cdesc, store + pair.second, level+1);
}
indent(strm, level);
strm<<"}";
@@ -612,7 +675,7 @@ void show_Value(std::ostream& strm,
auto& fld = store->as<Value>();
if(fld.valid() && desc->code==TypeCode::Union) {
for(auto& pair : desc->miter) {
if(desc + pair.second == Value::Helper::desc(fld)) {
if(&desc->members[pair.second] == Value::Helper::desc(fld)) {
strm<<"."<<pair.first;
break;
}
+81 -71
View File
@@ -41,11 +41,10 @@ void to_wire(Buffer& buf, const FieldDesc* cur)
switch(cur->code.code) {
case TypeCode::StructA:
case TypeCode::UnionA:
to_wire(buf, cur+1);
to_wire(buf, &cur->members[0]);
break;
case TypeCode::Struct:
case TypeCode::Union:
to_wire(buf, cur->id);
to_wire(buf, Size{cur->miter.size()});
for(auto& pair : cur->miter) {
@@ -53,12 +52,21 @@ void to_wire(Buffer& buf, const FieldDesc* cur)
to_wire(buf, cur+pair.second); // jump forward in FieldDesc array and recurse!
}
break;
case TypeCode::Union:
to_wire(buf, cur->id);
to_wire(buf, Size{cur->miter.size()});
for(auto& pair : cur->miter) {
to_wire(buf, pair.first);
to_wire(buf, &cur->members[pair.second]); // jump forward in FieldDesc array and recurse!
}
break;
default:
break;
}
}
void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
void from_wire(Buffer& buf, std::vector<FieldDesc>& descs, TypeStore& cache, unsigned depth)
{
if(!buf.good() || depth>20) {
buf.fault();
@@ -67,7 +75,7 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
TypeCode code;
from_wire(buf, code.code);
const size_t index = ctxt.descs.size(); // index of first node we add to ctxt.descs[]
const size_t index = descs.size(); // index of first node we add to descs[]
if(code == TypeCode::Null) {
return;
@@ -76,26 +84,28 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
// update cache
uint16_t key=0;
from_wire(buf, key);
from_wire(buf, ctxt, depth+1u);
if(!buf.good() || index==ctxt.descs.size()) {
from_wire(buf, descs, cache, depth+1u);
if(!buf.good() || index==descs.size()) {
buf.fault();
return;
} else {
auto& entry = ctxt.cache[key];
auto& entry = cache[key];
// copy new node, and any decendents into cache
entry.resize(ctxt.descs.size()-index);
std::copy(ctxt.descs.begin()+index,
ctxt.descs.end(),
entry.resize(descs.size()-index);
std::copy(descs.begin()+index,
descs.end(),
entry.begin());
descs[index].parent_index = 0u; // our caller will set if actually is a parent.
}
} else if(code.code==0xfe) {
// fetch cache
uint16_t key=0;
from_wire(buf, key);
auto it = ctxt.cache.find(key);
if(it==ctxt.cache.end()) {
auto it = cache.find(key);
if(it==cache.end()) {
buf.fault();
}
@@ -105,10 +115,10 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
} else {
// copy from cache
ctxt.descs.resize(index+it->second.size());
descs.resize(index+it->second.size());
std::copy(it->second.begin(),
it->second.end(),
ctxt.descs.begin()+index);
descs.begin()+index);
}
} else if(code.code!=0xff && code.code&0x10) {
@@ -118,9 +128,9 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
} else {
// actual field
ctxt.descs.emplace_back();
descs.emplace_back();
{
auto& fld = ctxt.descs.back();
auto& fld = descs.back();
fld.code = code;
fld.hash = code.code;
@@ -129,8 +139,8 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
switch(code.code) {
case TypeCode::StructA:
case TypeCode::UnionA:
from_wire(buf, ctxt, depth+1);
if(!buf.good() || ctxt.descs.size()==index || ctxt.descs[index+1].code!=code.scalarOf()) {
from_wire(buf, descs.back().members, cache, depth+1);
if(!buf.good() || descs.back().members.empty() || descs.back().members[0].code!=code.scalarOf()) {
buf.fault();
return;
}
@@ -138,40 +148,45 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
case TypeCode::Struct:
case TypeCode::Union: {
from_wire(buf, ctxt.descs.back().id);
from_wire(buf, descs.back().id);
Size nfld{0};
std::string name;
from_wire(buf, nfld); // number of children
{
auto& fld = ctxt.descs.back();
auto& fld = descs.back();
fld.miter.reserve(nfld.size);
fld.hash ^= std::hash<std::string>{}(fld.id);
}
auto& cdescs = code.code==TypeCode::Struct ? descs : descs.back().members;
auto cref = code.code==TypeCode::Struct ? index : 0u;
for(auto i: range(nfld.size)) {
(void)i;
const size_t cindex = ctxt.descs.size(); // index of this child
const size_t cindex = cdescs.size(); // index of this child
from_wire(buf, name);
from_wire(buf, ctxt, depth+1);
if(!buf.good() || cindex>=ctxt.descs.size()) {
from_wire(buf, cdescs, cache, depth+1);
if(!buf.good() || cindex>=cdescs.size()) {
buf.fault();
return;
}
// descs may be re-allocated (invalidating previous refs.)
auto& fld = ctxt.descs[index];
auto& cfld = ctxt.descs[cindex];
cfld.parent_index = cindex-index;
auto& fld = descs[index];
auto& cfld = cdescs[cindex];
if(code.code==TypeCode::Struct)
cfld.parent_index = cindex-cref;
// update hash
// TODO investigate better ways to combine hashes
fld.hash ^= std::hash<std::string>{}(name) ^ cfld.hash;
// update field refs.
fld.miter.emplace_back(name, cindex-index);
fld.mlookup[name] = cindex-index;
fld.miter.emplace_back(name, cindex-cref);
fld.mlookup[name] = cindex-cref;
name+='.';
if(code.code==TypeCode::Struct && code==cfld.code) {
@@ -206,8 +221,6 @@ void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth)
break;
}
}
ctxt.descs[index].num_index = ctxt.descs.size()-index;
}
}
@@ -245,10 +258,9 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const std::shared_ptr<con
case StoreType::Null:
switch(desc->code.code) {
case TypeCode::Struct: {
auto& top = *store->top;
// serialize entire sub-structure
for(auto off : range(desc->offset-top.desc->offset+1u, desc->next_offset-top.desc->offset)) {
auto cdesc = desc + top.member_indicies[off];
for(auto off : range(desc->size())) {
auto cdesc = desc + off;
std::shared_ptr<const FieldStorage> cstore(store, store.get()+off); // TODO avoid shared_ptr/aliasing here
if(cdesc->code!=TypeCode::Struct)
to_wire_field(buf, cdesc, cstore);
@@ -309,7 +321,7 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const std::shared_ptr<con
} else {
size_t index = 0u;
for(auto& pair : desc->miter) {
if(Value::Helper::desc(fld)== desc+pair.second)
if(Value::Helper::desc(fld)== &desc->members[pair.second])
break;
index++;
}
@@ -368,7 +380,7 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const std::shared_ptr<con
to_wire(buf, uint8_t(0u));
} else {
to_wire(buf, uint8_t(1u));
assert(Value::Helper::desc(elem)==desc+1);
assert(Value::Helper::desc(elem)==&desc->members[0]);
to_wire_full(buf, elem);
}
}
@@ -424,19 +436,23 @@ void to_wire_valid(Buffer& buf, const Value& val)
{
auto desc = Value::Helper::desc(val);
auto store = Value::Helper::store(val);
assert(!!desc);
auto top = store->top;
assert(desc && desc->code==TypeCode::Struct);
to_wire(buf, top->valid);
top->valid.resize(top->members.size());
BitMask valid(desc->size());
for(auto bit : range(desc->size())) {
if((store.get()+bit)->valid)
valid[bit] = true;
}
to_wire(buf, valid);
for(auto bit : range(desc->size())) {
if(!(store.get()+bit)->valid)
continue;
// iterate marked fields
for(auto bit = top->valid.findSet(desc->offset-top->desc->offset);
bit<desc->next_offset-top->desc->offset;
bit = top->valid.findSet(bit+1))
{
std::shared_ptr<const FieldStorage> cstore(store, store.get()+bit);
to_wire_field(buf, desc + top->member_indicies[bit], cstore);
to_wire_field(buf, desc+bit, cstore);
}
}
@@ -457,14 +473,13 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
case StoreType::Null:
switch(desc->code.code) {
case TypeCode::Struct: {
auto& top = *store->top;
// serialize entire sub-structure
for(auto off : range(desc->offset-top.desc->offset+1u, desc->next_offset-top.desc->offset)) {
auto cdesc = desc + top.member_indicies[off];
for(auto off : range(desc->size())) {
auto cdesc = desc + off;
std::shared_ptr<FieldStorage> cstore(store, store.get()+off); // TODO avoid shared_ptr/aliasing here
if(cdesc->code!=TypeCode::Struct) {
from_wire_field(buf, ctxt, cdesc, cstore);
top.valid[cstore->index()] = true;
cstore->valid = true;
}
}
}
@@ -524,7 +539,7 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
} else if(select.size < desc->miter.size()) {
std::shared_ptr<const FieldDesc> stype(store->top->desc,
desc + desc->miter[select.size].second); // alias
&desc->members[desc->miter[select.size].second]); // alias
fld = Value::Helper::build(stype, store, desc);
from_wire_full(buf, ctxt, fld);
@@ -535,9 +550,8 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
case TypeCode::Any: {
auto descs(std::make_shared<std::vector<FieldDesc>>());
TypeDeserContext dc{*descs, ctxt};
from_wire(buf, dc);
from_wire(buf, *descs, ctxt);
if(!buf.good())
return;
@@ -546,8 +560,6 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
return;
} else {
FieldDesc_calculate_offset(descs->data());
std::shared_ptr<const FieldDesc> stype(descs, descs->data()); // alias
fld = Value::Helper::build(stype);
@@ -594,7 +606,7 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
from_wire(buf, alen);
shared_array<Value> arr(alen.size);
std::shared_ptr<const FieldDesc> etype(store->top->desc,
desc + 1); // alias
&desc->members[0]); // alias
for(auto& elem : arr) {
if(from_wire_as<uint8_t>(buf)!=0) { // strictly 1 or 0
elem = Value::Helper::build(etype, store, desc);
@@ -610,7 +622,7 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
Size alen{};
from_wire(buf, alen);
shared_array<Value> arr(alen.size);
auto cdesc = desc+1;
auto cdesc = &desc->members[0];
for(auto& elem : arr) {
if(from_wire_as<uint8_t>(buf)!=0) { // strictly 1 or 0
@@ -622,7 +634,7 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
} else if(select.size < cdesc->miter.size()) {
std::shared_ptr<const FieldDesc> stype(store->top->desc,
cdesc + cdesc->miter[select.size].second); // alias
&cdesc->members[cdesc->miter[select.size].second]); // alias
elem = Value::Helper::build(stype, store, desc);
from_wire_full(buf, ctxt, elem);
@@ -646,14 +658,12 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const
for(auto& elem : arr) {
if(from_wire_as<uint8_t>(buf)!=0) { // strictly 1 or 0
auto descs(std::make_shared<std::vector<FieldDesc>>());
TypeDeserContext dc{*descs, ctxt};
from_wire(buf, dc);
from_wire(buf, *descs, ctxt);
if(!buf.good())
return;
if(!descs->empty()) {
FieldDesc_calculate_offset(descs->data());
std::shared_ptr<const FieldDesc> stype(descs, descs->data()); // alias
elem = Value::Helper::build(stype, store, desc);
@@ -690,33 +700,33 @@ void from_wire_valid(Buffer& buf, TypeStore& ctxt, Value& val)
assert(!!desc);
auto top = store->top;
from_wire(buf, top->valid);
BitMask valid;
from_wire(buf, valid);
// encoding rounds # of bits to whole bytes, so we may trim
top->valid.resize(top->members.size());
valid.resize(top->members.size());
if(!buf.good())
return;
for(auto bit = top->valid.findSet(desc->offset-top->desc->offset);
bit<(desc->next_offset-top->desc->offset);
bit = top->valid.findSet(bit+1))
for(auto bit = valid.findSet(0u);
bit<desc->size();)
{
std::shared_ptr<FieldStorage> cstore(store, store.get()+bit-desc->offset);
from_wire_field(buf, ctxt, desc + top->member_indicies[bit], cstore);
top->valid[bit] = true;
std::shared_ptr<FieldStorage> cstore(store, store.get()+bit);
auto cdesc = desc + bit;
from_wire_field(buf, ctxt, cdesc, cstore);
cstore->valid = true;
bit = valid.findSet(bit + cdesc->size());
}
}
void from_wire_type_value(Buffer& buf, TypeStore& ctxt, Value& val)
{
auto descs(std::make_shared<std::vector<FieldDesc>>());
TypeDeserContext dc{*descs, ctxt};
from_wire(buf, dc);
from_wire(buf, *descs, ctxt);
if(!buf.good())
return;
if(!descs->empty()) {
FieldDesc_calculate_offset(descs->data());
std::shared_ptr<const FieldDesc> stype(descs, descs->data()); // alias
val = Value::Helper::build(stype);
+18 -23
View File
@@ -53,30 +53,36 @@ struct Buffer;
* Relative to current FieldDesc*.
*/
struct FieldDesc {
// type ID string (struct/union)
// type ID string (Struct/Union)
std::string id;
// Lookup of all decendent fields of this Structure or Union.
// "fld.sub.leaf" -> rel index in enclosing vector<FieldDesc>
// "fld.sub.leaf" -> rel index
// For Struct, relative to this
// For Union, offset in members array
std::map<std::string, size_t> mlookup;
// child iteration. child# -> ("sub", rel index in enclosing vector<FieldDesc>)
std::vector<std::pair<std::string, size_t>> miter;
// hash of this type (aggragating from children)
// created using the code ^ id ^ (child_name ^ child_hash)*N
size_t hash;
// abs. offset in enclosing StructTop::members. (not abs. offset of FieldDesc array)
// used to navigate vector<FieldStorage>
size_t offset=0, next_offset=0;
// number of FieldDesc nodes which describe this node and decendents. Inclusive. always >=1
// eg. num_index+(FieldDesc*)this jumps to next sibling
size_t num_index=0;
// number of FieldDesc nodes between this node and it's a parent node (if any).
// number of FieldDesc nodes between this node and it's a parent Struct (or 0 if no parent).
// This value also appears in the parent's miter and mlookup mappings.
// Only usable when a StructTop is accessible and this!=StructTop::desc
size_t parent_index=0;
// For Union, UnionA, StructA
// For Union, the choices
// For UnionA/StructA, size()==1 containing a Union/Struct
std::vector<FieldDesc> members;
TypeCode code{TypeCode::Null};
// number of FieldDesc nodes which describe this node. Inclusive. always size()>=1
inline size_t size() const { return num_index; }
inline size_t size() const { return 1u + (members.empty() ? mlookup.size() : 0u); }
};
PVXS_API
@@ -84,13 +90,8 @@ void to_wire(Buffer& buf, const FieldDesc* cur);
typedef std::map<uint16_t, std::vector<FieldDesc>> TypeStore;
struct TypeDeserContext {
std::vector<FieldDesc>& descs;
TypeStore& cache;
};
PVXS_API
void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth=0);
void from_wire(Buffer& buf, std::vector<FieldDesc>& descs, TypeStore& cache, unsigned depth=0);
struct StructTop;
@@ -113,6 +114,7 @@ struct FieldStorage {
>::type store;
// index of this field in StructTop::members
StructTop *top;
bool valid=false;
StoreType code=StoreType::Null; // duplicates associated FieldDesc::code
void init(const FieldDesc* desc);
@@ -132,13 +134,9 @@ struct FieldStorage {
// hidden (publicly) management of an allocated Struct
struct StructTop {
// which members have been assigned/updated (use to track "changes")
BitMask valid;
// type of first top level struct. always !NULL.
// Actually the first element of a vector<const FieldDesc>
std::shared_ptr<const FieldDesc> desc;
// map from FieldStorage offsets to FieldDesc offsets. inverse of FieldDesc::offset
std::vector<size_t> member_indicies;
// our members (inclusive). always size()>=1
std::vector<FieldStorage> members;
@@ -164,9 +162,6 @@ void from_wire_valid(Buffer& buf, TypeStore& ctxt, Value& val);
PVXS_API
void from_wire_type_value(Buffer& buf, TypeStore& ctxt, Value& val);
PVXS_API
void FieldDesc_calculate_offset(FieldDesc* top);
PVXS_API
std::ostream& operator<<(std::ostream& strm, const FieldDesc* desc);
+1 -1
View File
@@ -354,7 +354,7 @@ public:
//! allocate new storage and copy in our values
Value clone() const;
//! copy values from other. Must have matching types.
// Value& assign(const Value&);
Value& assign(const Value&);
//! Use to allocate members for an array of Struct and array of Union
Value allocMember();
+89 -112
View File
@@ -164,62 +164,6 @@ TypeDef::TypeDef(const Value& o)
TypeDef::~TypeDef() {}
static
void build_tree(std::vector<FieldDesc>& desc, const Member& node)
{
auto code = node.code;
if(node.code==TypeCode::StructA || node.code==TypeCode::UnionA) {
desc.emplace_back();
auto& fld = desc.back();
fld.code = node.code;
// struct/union array have no ID
fld.hash = node.code.code;
code = code.scalarOf();
}
const auto index = desc.size();
desc.emplace_back();
{
auto& fld = desc.back();
fld.code = code;
fld.id = node.id;
fld.hash = code.code ^ std::hash<std::string>{}(fld.id);
}
for(auto& cnode : node.children) {
const auto cindex = desc.size();
build_tree(desc, cnode); // recurse. may realloc desc
auto& fld = desc[index];
auto& child = desc[cindex];
child.parent_index = cindex-index;
fld.hash ^= std::hash<std::string>{}(cnode.name) ^ child.hash;
fld.mlookup[cnode.name] = cindex-index;
fld.miter.emplace_back(cnode.name, cindex-index);
std::string cname = cnode.name+".";
if(fld.code.code==TypeCode::Struct && fld.code==child.code) {
// propagate names from sub-struct
for(auto& cpair : child.mlookup) {
fld.mlookup[cname+cpair.first] = cindex-index+cpair.second;
}
}
}
desc[index].num_index = desc.size()-index;
if(node.code==TypeCode::StructA || node.code==TypeCode::UnionA)
{
desc[index-1].num_index = desc.size()-index+1;
}
}
static
void append_tree(Member& node, const Member& adopt)
{
@@ -266,6 +210,66 @@ TypeDef& TypeDef::operator+=(std::initializer_list<Member> children)
return *this;
}
static
void build_tree(std::vector<FieldDesc>& desc, const Member& node)
{
auto code = node.code;
if(node.code==TypeCode::StructA || node.code==TypeCode::UnionA) {
desc.emplace_back();
auto& fld = desc.back();
fld.code = node.code;
// struct/union array have no ID
fld.hash = node.code.code;
Member next{code.scalarOf(), node.name};
next.id = node.id;
next.children = node.children; // TODO ick copy
build_tree(desc.back().members, next);
return;
}
const auto index = desc.size();
desc.emplace_back();
{
auto& fld = desc.back();
fld.code = code;
fld.id = node.id;
fld.hash = code.code ^ std::hash<std::string>{}(fld.id);
}
auto& cdescs = code.code==TypeCode::Struct ? desc : desc.back().members;
auto cref = code.code==TypeCode::Struct ? index : 0u;
for(auto& cnode : node.children) {
const auto cindex = cdescs.size();
build_tree(cdescs, cnode); // recurse. may realloc desc
auto& fld = desc[index];
auto& child = cdescs[cindex];
if(code.code==TypeCode::Struct)
child.parent_index = cindex-cref;
fld.hash ^= std::hash<std::string>{}(cnode.name) ^ child.hash;
fld.mlookup[cnode.name] = cindex-cref;
fld.miter.emplace_back(cnode.name, cindex-cref);
std::string cname = cnode.name+".";
if(fld.code.code==TypeCode::Struct && fld.code==child.code) {
// propagate names from sub-struct
for(auto& cpair : child.mlookup) {
fld.mlookup[cname+cpair.first] = cindex-cref+cpair.second;
}
}
}
assert(desc.size()==index+desc[index].size());
}
Value TypeDef::create() const
{
if(!top)
@@ -273,7 +277,6 @@ Value TypeDef::create() const
auto desc = std::make_shared<std::vector<FieldDesc>>();
build_tree(*desc, *top);
FieldDesc_calculate_offset(desc->data());
std::shared_ptr<const FieldDesc> type(desc, desc->data()); // alias
return Value(type);
@@ -315,79 +318,53 @@ std::ostream& operator<<(std::ostream& strm, const TypeDef& def)
namespace impl {
void FieldDesc_calculate_offset(FieldDesc* const top)
void show_FieldDesc(std::ostream& strm, const FieldDesc* desc, unsigned level)
{
top->offset = 0;
uint16_t offset = 1;
for(size_t index = 1; index < top->size(); ) {
auto& fld = top[index];
for(auto idx : range(desc->size())) {
auto& fld = desc[idx];
indent(strm, level);
strm<<"["<<idx<<"] "<<fld.code<<' '<<fld.id
<<" parent=["<<(idx-fld.parent_index) <<"]"
" ["<<idx<<":"<<idx+fld.size()<<")\n";
switch (fld.code.code) {
switch(fld.code.code) {
case TypeCode::Struct:
if(top->code==fld.code) {
// sub-structure
fld.offset = offset++;
index++;
} else {
// structure inside union or array of struct
// new offset zero
FieldDesc_calculate_offset(top);
index+=top->size();
// note: need to ensure stable lexical iteration order if fld.mlookup ever becomes unordered_map
for(auto& pair : fld.mlookup) {
indent(strm, level);
strm<<" "<<pair.first<<" -> "<<pair.second<<" ["<<(idx+pair.second)<<"]\n";
}
break;
case TypeCode::Union:
// number in parent structure/union
fld.offset = offset++;
// new offset zero for each child
for(auto& pair : fld.miter) {
FieldDesc_calculate_offset(top+index+pair.second);
indent(strm, level);
strm<<" "<<pair.first<<" : "<<pair.second<<" ["<<(idx+pair.second)<<"]\n";
}
index += fld.size();
break;
case TypeCode::Union:
for(auto& pair : fld.mlookup) {
indent(strm, level);
strm<<" "<<pair.first<<" -> "<<pair.second<<" ["<<(pair.second)<<"]\n";
}
for(auto& pair : fld.miter) {
indent(strm, level);
strm<<" "<<pair.first<<" : "<<pair.second<<" ["<<(pair.second)<<"]\n";
show_FieldDesc(strm, fld.members.data()+pair.second, level+1u);
}
break;
case TypeCode::StructA:
case TypeCode::UnionA:
// number in parent structure/union
fld.offset = offset++;
index++;
// new offset zero for child
FieldDesc_calculate_offset(top+index);
index += top[index].size();
show_FieldDesc(strm, fld.members.data(), level+1u);
break;
default:
fld.offset = offset++;
index++;
break;
}
fld.next_offset = offset;
}
top->next_offset = offset;
}
std::ostream& operator<<(std::ostream& strm, const FieldDesc* desc)
{
for(auto idx : range(desc->size())) {
auto& fld = desc[idx];
strm<<"["<<idx<<"] "<<fld.code<<' '<<fld.id
<<" <"<<fld.offset<<":"<<fld.next_offset<<">"
" ["<<idx<<":"<<idx+fld.num_index<<")\n";
switch(fld.code.code) {
case TypeCode::Struct:
case TypeCode::Union: {
// note: need to ensure stable lexical iteration order if fld.mlookup ever becomes unordered_map
for(auto& pair : fld.mlookup) {
strm<<" "<<pair.first<<" -> "<<pair.second<<" ["<<(idx+pair.second)<<"]\n";
}
for(auto& pair : fld.miter) {
strm<<" "<<pair.first<<" : "<<pair.second<<" ["<<(idx+pair.second)<<"]\n";
}
break;
}
default:
break;
}
}
show_FieldDesc(strm, desc, 0u);
return strm;
}
+87 -1
View File
@@ -160,6 +160,68 @@ TypeDef simpledef(TypeCode::Struct, "simple_t", {
}),
});
void testSimpleDef()
{
testDiag("%s", __func__);
auto val = simpledef.create();
testEq(std::string(SB()<<"\n"<<Value::Helper::desc(val)),
R"out(
[0] struct simple_t parent=[0] [0:11)
achoice -> 10 [10]
any -> 7 [7]
anya -> 8 [8]
arbitrary -> 5 [5]
arbitrary.sarr -> 6 [6]
choice -> 9 [9]
timeStamp -> 2 [2]
timeStamp.nanoseconds -> 4 [4]
timeStamp.secondsPastEpoch -> 3 [3]
value -> 1 [1]
value : 1 [1]
timeStamp : 2 [2]
arbitrary : 5 [5]
any : 7 [7]
anya : 8 [8]
choice : 9 [9]
achoice : 10 [10]
[1] double[] parent=[0] [1:2)
[2] struct time_t parent=[0] [2:5)
nanoseconds -> 2 [4]
secondsPastEpoch -> 1 [3]
secondsPastEpoch : 1 [3]
nanoseconds : 2 [4]
[3] uint64_t parent=[2] [3:4)
[4] uint32_t parent=[2] [4:5)
[5] struct parent=[0] [5:7)
sarr -> 1 [6]
sarr : 1 [6]
[6] struct[] parent=[5] [6:7)
[0] struct parent=[0] [0:2)
value -> 1 [1]
value : 1 [1]
[1] uint32_t parent=[0] [1:2)
[7] any parent=[0] [7:8)
[8] any[] parent=[0] [8:9)
[9] union parent=[0] [9:10)
a -> 0 [0]
b -> 1 [1]
a : 0 [0]
[0] float parent=[0] [0:1)
b : 1 [1]
[0] string parent=[0] [0:1)
[10] union[] parent=[0] [10:11)
[0] union parent=[0] [0:1)
x -> 0 [0]
y -> 1 [1]
x : 0 [0]
[0] string parent=[0] [0:1)
y : 1 [1]
[0] string parent=[0] [0:1)
)out")<<"Actual:\n"<<Value::Helper::desc(val);
}
void testSerialize2()
{
testDiag("%s", __func__);
@@ -344,6 +406,8 @@ void testDeserialize2()
void testTraverse()
{
testDiag("%s", __func__);
auto top = nt::NTScalar{TypeCode::Int32, true}.create();
testOk1(!top["<"].valid());
@@ -361,16 +425,38 @@ void testTraverse()
}
}
void testAssign()
{
testDiag("%s", __func__);
auto def = nt::NTScalar{TypeCode::String}.build();
auto val = def.create();
val["value"] = "Testing";
val["timeStamp"].mark();
val["alarm.severity"] = 3u;
auto val2 = val.cloneEmpty();
val2.assign(val);
testOk1(!val["alarm.status"].isMarked(true, true));
testOk1(!!val["alarm"].isMarked(true, true));
testOk1(!val["alarm"].isMarked(true, false));
}
} // namespace
MAIN(testdata)
{
testPlan(63);
testPlan(67);
testSerialize1();
testDeserialize1();
testSimpleDef();
testSerialize2();
testDeserialize2();
testTraverse();
testAssign();
cleanup_for_valgrind();
return testDone();
}
+55 -56
View File
@@ -128,60 +128,60 @@ void testTypeDef()
auto val = def.create();
testOk1(!!val.valid());
testShow()<<Value::Helper::desc(val);
testEq(std::string(SB()<<Value::Helper::desc(val)),
"[0] struct simple_t <0:11> [0:18)\n"
" achoice -> 14 [14]\n"
" any -> 9 [9]\n"
" anya -> 10 [10]\n"
" arbitrary -> 5 [5]\n"
" arbitrary.sarr -> 6 [6]\n"
" choice -> 11 [11]\n"
" timeStamp -> 2 [2]\n"
" timeStamp.nanoseconds -> 4 [4]\n"
" timeStamp.secondsPastEpoch -> 3 [3]\n"
" value -> 1 [1]\n"
" value : 1 [1]\n"
" timeStamp : 2 [2]\n"
" arbitrary : 5 [5]\n"
" any : 9 [9]\n"
" anya : 10 [10]\n"
" choice : 11 [11]\n"
" achoice : 14 [14]\n"
"[1] double[] <1:2> [1:2)\n"
"[2] struct time_t <2:3> [2:5)\n"
" nanoseconds -> 2 [4]\n"
" secondsPastEpoch -> 1 [3]\n"
" secondsPastEpoch : 1 [3]\n"
" nanoseconds : 2 [4]\n"
"[3] uint64_t <3:4> [3:4)\n"
"[4] uint32_t <4:5> [4:5)\n"
"[5] struct <5:6> [5:9)\n"
" sarr -> 1 [6]\n"
" sarr : 1 [6]\n"
"[6] struct[] <6:7> [6:9)\n"
"[7] struct <0:2> [7:9)\n"
" value -> 1 [8]\n"
" value : 1 [8]\n"
"[8] double <1:2> [8:9)\n"
"[9] any <7:8> [9:10)\n"
"[10] any[] <8:9> [10:11)\n"
"[11] union <9:10> [11:14)\n"
" a -> 1 [12]\n"
" b -> 2 [13]\n"
" a : 1 [12]\n"
" b : 2 [13]\n"
"[12] float <0:1> [12:13)\n"
"[13] string <0:1> [13:14)\n"
"[14] union[] <10:11> [14:18)\n"
"[15] union <0:3> [15:18)\n"
" x -> 1 [16]\n"
" y -> 2 [17]\n"
" x : 1 [16]\n"
" y : 2 [17]\n"
"[16] float <1:2> [16:17)\n"
"[17] float <2:3> [17:18)\n"
"");
testEq(std::string(SB()<<"\n"<<Value::Helper::desc(val)),
R"out(
[0] struct simple_t parent=[0] [0:11)
achoice -> 10 [10]
any -> 7 [7]
anya -> 8 [8]
arbitrary -> 5 [5]
arbitrary.sarr -> 6 [6]
choice -> 9 [9]
timeStamp -> 2 [2]
timeStamp.nanoseconds -> 4 [4]
timeStamp.secondsPastEpoch -> 3 [3]
value -> 1 [1]
value : 1 [1]
timeStamp : 2 [2]
arbitrary : 5 [5]
any : 7 [7]
anya : 8 [8]
choice : 9 [9]
achoice : 10 [10]
[1] double[] parent=[0] [1:2)
[2] struct time_t parent=[0] [2:5)
nanoseconds -> 2 [4]
secondsPastEpoch -> 1 [3]
secondsPastEpoch : 1 [3]
nanoseconds : 2 [4]
[3] uint64_t parent=[2] [3:4)
[4] uint32_t parent=[2] [4:5)
[5] struct parent=[0] [5:7)
sarr -> 1 [6]
sarr : 1 [6]
[6] struct[] parent=[5] [6:7)
[0] struct parent=[0] [0:2)
value -> 1 [1]
value : 1 [1]
[1] double parent=[0] [1:2)
[7] any parent=[0] [7:8)
[8] any[] parent=[0] [8:9)
[9] union parent=[0] [9:10)
a -> 0 [0]
b -> 1 [1]
a : 0 [0]
[0] float parent=[0] [0:1)
b : 1 [1]
[0] string parent=[0] [0:1)
[10] union[] parent=[0] [10:11)
[0] union parent=[0] [0:1)
x -> 0 [0]
y -> 1 [1]
x : 0 [0]
[0] float parent=[0] [0:1)
y : 1 [1]
[0] float parent=[0] [0:1)
)out")<<"Actual:\n"<<Value::Helper::desc(val);
// try to access all field Kinds
@@ -254,7 +254,6 @@ void testTypeDef()
testEq(fld["[1]q"].as<std::string>(), "theq");
}
testShow()<<val;
testEq(std::string(SB()<<val),
"struct \"simple_t\" {\n"
" double[] value = {2}[1, 2]\n"
@@ -287,7 +286,7 @@ void testTypeDef()
" union.y float = 5\n"
" null\n"
" ]\n"
"}\n");
"}\n")<<"Actual:\n"<<val;
}
} // namespace
+239 -243
View File
@@ -1,4 +1,4 @@
/**
/**
* 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.
@@ -54,10 +54,8 @@ void testDecode1()
{
FixedBuf buf(true, msg);
TypeDeserContext ctxt{descs, cache};
from_wire(buf, ctxt);
if(testOk1(buf.good()))
FieldDesc_calculate_offset(descs.data());
from_wire(buf, descs, cache);
testOk1(buf.good());
testEq(buf.size(), 0u)<<"Of "<<msg.size();
}
@@ -75,17 +73,19 @@ void testDecode1()
// cat <<EOF | sed -e 's|"|\\"|g' -e 's|^# | "|' -e 's|$|\\n"|g'
// paste in Actual
testEq(std::string(SB()<<descs.data()),
"[0] struct timeStamp_t <0:4> [0:4)\n"
" nanoSeconds -> 2 [2]\n"
" secondsPastEpoch -> 1 [1]\n"
" userTag -> 3 [3]\n"
" secondsPastEpoch : 1 [1]\n"
" nanoSeconds : 2 [2]\n"
" userTag : 3 [3]\n"
"[1] int64_t <1:2> [1:2)\n"
"[2] int32_t <2:3> [2:3)\n"
"[3] int32_t <3:4> [3:4)\n"
testEq(std::string(SB()<<"\n"<<descs.data()),
R"out(
[0] struct timeStamp_t parent=[0] [0:4)
nanoSeconds -> 2 [2]
secondsPastEpoch -> 1 [1]
userTag -> 3 [3]
secondsPastEpoch : 1 [1]
nanoSeconds : 2 [2]
userTag : 3 [3]
[1] int64_t parent=[0] [1:2)
[2] int32_t parent=[0] [2:3)
[3] int32_t parent=[0] [3:4)
)out"
)<<"Actual:\n"<<descs.data();
}
@@ -120,10 +120,8 @@ void testXCodeNTScalar()
TypeStore cache;
{
FixedBuf buf(true, msg);
TypeDeserContext ctxt{descs, cache};
from_wire(buf, ctxt);
if(testOk1(buf.good()))
FieldDesc_calculate_offset(descs.data());
from_wire(buf, descs, cache);
testOk1(buf.good());
testEq(buf.size(), 0u)<<"remaining of "<<msg.size();
}
@@ -131,41 +129,43 @@ void testXCodeNTScalar()
testEq(descs.size(), descs.front().size());
}
testEq(std::string(SB()<<descs.data()),
"[0] struct epics:nt/NTScalarArray:1.0 <0:10> [0:10)\n"
" alarm -> 2 [2]\n"
" alarm.message -> 5 [5]\n"
" alarm.severity -> 3 [3]\n"
" alarm.status -> 4 [4]\n"
" timeStamp -> 6 [6]\n"
" timeStamp.nanoseconds -> 8 [8]\n"
" timeStamp.secondsPastEpoch -> 7 [7]\n"
" timeStamp.userTag -> 9 [9]\n"
" value -> 1 [1]\n"
" value : 1 [1]\n"
" alarm : 2 [2]\n"
" timeStamp : 6 [6]\n"
"[1] double[] <1:2> [1:2)\n"
"[2] struct alarm_t <2:3> [2:6)\n"
" message -> 3 [5]\n"
" severity -> 1 [3]\n"
" status -> 2 [4]\n"
" severity : 1 [3]\n"
" status : 2 [4]\n"
" message : 3 [5]\n"
"[3] int32_t <3:4> [3:4)\n"
"[4] int32_t <4:5> [4:5)\n"
"[5] string <5:6> [5:6)\n"
"[6] struct time_t <6:7> [6:10)\n"
" nanoseconds -> 2 [8]\n"
" secondsPastEpoch -> 1 [7]\n"
" userTag -> 3 [9]\n"
" secondsPastEpoch : 1 [7]\n"
" nanoseconds : 2 [8]\n"
" userTag : 3 [9]\n"
"[7] int64_t <7:8> [7:8)\n"
"[8] int32_t <8:9> [8:9)\n"
"[9] int32_t <9:10> [9:10)\n"
testEq(std::string(SB()<<"\n"<<descs.data()),
R"out(
[0] struct epics:nt/NTScalarArray:1.0 parent=[0] [0:10)
alarm -> 2 [2]
alarm.message -> 5 [5]
alarm.severity -> 3 [3]
alarm.status -> 4 [4]
timeStamp -> 6 [6]
timeStamp.nanoseconds -> 8 [8]
timeStamp.secondsPastEpoch -> 7 [7]
timeStamp.userTag -> 9 [9]
value -> 1 [1]
value : 1 [1]
alarm : 2 [2]
timeStamp : 6 [6]
[1] double[] parent=[0] [1:2)
[2] struct alarm_t parent=[0] [2:6)
message -> 3 [5]
severity -> 1 [3]
status -> 2 [4]
severity : 1 [3]
status : 2 [4]
message : 3 [5]
[3] int32_t parent=[2] [3:4)
[4] int32_t parent=[2] [4:5)
[5] string parent=[2] [5:6)
[6] struct time_t parent=[0] [6:10)
nanoseconds -> 2 [8]
secondsPastEpoch -> 1 [7]
userTag -> 3 [9]
secondsPastEpoch : 1 [7]
nanoseconds : 2 [8]
userTag : 3 [9]
[7] int64_t parent=[6] [7:8)
[8] int32_t parent=[6] [8:9)
[9] int32_t parent=[6] [9:10)
)out"
)<<"Actual:\n"<<descs.data();
testDiag("Round trip back to bytes");
@@ -246,10 +246,8 @@ void testXCodeNTNDArray()
TypeStore cache;
{
FixedBuf buf(true, msg);
TypeDeserContext ctxt{descs, cache};
from_wire(buf, ctxt);
if(testOk1(buf.good()))
FieldDesc_calculate_offset(descs.data());
from_wire(buf, descs, cache);
testOk1(buf.good());
testEq(buf.size(), 0u)<<"remaining of "<<msg.size();
}
@@ -257,180 +255,182 @@ void testXCodeNTNDArray()
testEq(descs.size(), descs.front().size());
}
testEq(std::string(SB()<<descs.data()),
"[0] struct epics:nt/NTNDArray:1.0 <0:22> [0:54)\n"
" alarm -> 23 [23]\n"
" alarm.message -> 26 [26]\n"
" alarm.severity -> 24 [24]\n"
" alarm.status -> 25 [25]\n"
" attribute -> 38 [38]\n"
" codec -> 13 [13]\n"
" codec.name -> 14 [14]\n"
" codec.parameters -> 15 [15]\n"
" compressedSize -> 16 [16]\n"
" dataTimeStamp -> 19 [19]\n"
" dataTimeStamp.nanoseconds -> 21 [21]\n"
" dataTimeStamp.secondsPastEpoch -> 20 [20]\n"
" dataTimeStamp.userTag -> 22 [22]\n"
" dimension -> 31 [31]\n"
" timeStamp -> 27 [27]\n"
" timeStamp.nanoseconds -> 29 [29]\n"
" timeStamp.secondsPastEpoch -> 28 [28]\n"
" timeStamp.userTag -> 30 [30]\n"
" uncompressedSize -> 17 [17]\n"
" uniqueId -> 18 [18]\n"
" value -> 1 [1]\n"
" value : 1 [1]\n"
" codec : 13 [13]\n"
" compressedSize : 16 [16]\n"
" uncompressedSize : 17 [17]\n"
" uniqueId : 18 [18]\n"
" dataTimeStamp : 19 [19]\n"
" alarm : 23 [23]\n"
" timeStamp : 27 [27]\n"
" dimension : 31 [31]\n"
" attribute : 38 [38]\n"
"[1] union <1:2> [1:13)\n"
" booleanValue -> 1 [2]\n"
" byteValue -> 2 [3]\n"
" doubleValue -> 11 [12]\n"
" floatValue -> 10 [11]\n"
" intValue -> 4 [5]\n"
" longValue -> 5 [6]\n"
" shortValue -> 3 [4]\n"
" ubyteValue -> 6 [7]\n"
" uintValue -> 8 [9]\n"
" ulongValue -> 9 [10]\n"
" ushortValue -> 7 [8]\n"
" booleanValue : 1 [2]\n"
" byteValue : 2 [3]\n"
" shortValue : 3 [4]\n"
" intValue : 4 [5]\n"
" longValue : 5 [6]\n"
" ubyteValue : 6 [7]\n"
" ushortValue : 7 [8]\n"
" uintValue : 8 [9]\n"
" ulongValue : 9 [10]\n"
" floatValue : 10 [11]\n"
" doubleValue : 11 [12]\n"
"[2] bool[] <0:1> [2:3)\n"
"[3] int8_t[] <0:1> [3:4)\n"
"[4] int16_t[] <0:1> [4:5)\n"
"[5] int32_t[] <0:1> [5:6)\n"
"[6] int64_t[] <0:1> [6:7)\n"
"[7] uint8_t[] <0:1> [7:8)\n"
"[8] uint16_t[] <0:1> [8:9)\n"
"[9] uint32_t[] <0:1> [9:10)\n"
"[10] uint64_t[] <0:1> [10:11)\n"
"[11] float[] <0:1> [11:12)\n"
"[12] double[] <0:1> [12:13)\n"
"[13] struct codec_t <2:3> [13:16)\n"
" name -> 1 [14]\n"
" parameters -> 2 [15]\n"
" name : 1 [14]\n"
" parameters : 2 [15]\n"
"[14] string <3:4> [14:15)\n"
"[15] any <4:5> [15:16)\n"
"[16] int64_t <5:6> [16:17)\n"
"[17] int64_t <6:7> [17:18)\n"
"[18] int32_t <7:8> [18:19)\n"
"[19] struct time_t <8:9> [19:23)\n"
" nanoseconds -> 2 [21]\n"
" secondsPastEpoch -> 1 [20]\n"
" userTag -> 3 [22]\n"
" secondsPastEpoch : 1 [20]\n"
" nanoseconds : 2 [21]\n"
" userTag : 3 [22]\n"
"[20] int64_t <9:10> [20:21)\n"
"[21] int32_t <10:11> [21:22)\n"
"[22] int32_t <11:12> [22:23)\n"
"[23] struct alarm_t <12:13> [23:27)\n"
" message -> 3 [26]\n"
" severity -> 1 [24]\n"
" status -> 2 [25]\n"
" severity : 1 [24]\n"
" status : 2 [25]\n"
" message : 3 [26]\n"
"[24] int32_t <13:14> [24:25)\n"
"[25] int32_t <14:15> [25:26)\n"
"[26] string <15:16> [26:27)\n"
"[27] struct time_t <16:17> [27:31)\n"
" nanoseconds -> 2 [29]\n"
" secondsPastEpoch -> 1 [28]\n"
" userTag -> 3 [30]\n"
" secondsPastEpoch : 1 [28]\n"
" nanoseconds : 2 [29]\n"
" userTag : 3 [30]\n"
"[28] int64_t <17:18> [28:29)\n"
"[29] int32_t <18:19> [29:30)\n"
"[30] int32_t <19:20> [30:31)\n"
"[31] struct[] <20:21> [31:38)\n"
"[32] struct dimension_t <0:6> [32:38)\n"
" binning -> 4 [36]\n"
" fullSize -> 3 [35]\n"
" offset -> 2 [34]\n"
" reverse -> 5 [37]\n"
" size -> 1 [33]\n"
" size : 1 [33]\n"
" offset : 2 [34]\n"
" fullSize : 3 [35]\n"
" binning : 4 [36]\n"
" reverse : 5 [37]\n"
"[33] int32_t <1:2> [33:34)\n"
"[34] int32_t <2:3> [34:35)\n"
"[35] int32_t <3:4> [35:36)\n"
"[36] int32_t <4:5> [36:37)\n"
"[37] bool <5:6> [37:38)\n"
"[38] struct[] <21:22> [38:54)\n"
"[39] struct epics:nt/NTAttribute:1.0 <0:15> [39:54)\n"
" alarm -> 5 [44]\n"
" alarm.message -> 47 [86]\n"
" alarm.severity -> 45 [84]\n"
" alarm.status -> 46 [85]\n"
" descriptor -> 4 [43]\n"
" name -> 1 [40]\n"
" source -> 14 [53]\n"
" sourceType -> 13 [52]\n"
" tags -> 3 [42]\n"
" timestamp -> 9 [48]\n"
" timestamp.nanoseconds -> 50 [89]\n"
" timestamp.secondsPastEpoch -> 49 [88]\n"
" timestamp.userTag -> 51 [90]\n"
" value -> 2 [41]\n"
" name : 1 [40]\n"
" value : 2 [41]\n"
" tags : 3 [42]\n"
" descriptor : 4 [43]\n"
" alarm : 5 [44]\n"
" timestamp : 9 [48]\n"
" sourceType : 13 [52]\n"
" source : 14 [53]\n"
"[40] string <1:2> [40:41)\n"
"[41] any <2:3> [41:42)\n"
"[42] string[] <3:4> [42:43)\n"
"[43] string <4:5> [43:44)\n"
"[44] struct alarm_t <5:6> [44:48)\n"
" message -> 3 [47]\n"
" severity -> 1 [45]\n"
" status -> 2 [46]\n"
" severity : 1 [45]\n"
" status : 2 [46]\n"
" message : 3 [47]\n"
"[45] int32_t <6:7> [45:46)\n"
"[46] int32_t <7:8> [46:47)\n"
"[47] string <8:9> [47:48)\n"
"[48] struct time_t <9:10> [48:52)\n"
" nanoseconds -> 2 [50]\n"
" secondsPastEpoch -> 1 [49]\n"
" userTag -> 3 [51]\n"
" secondsPastEpoch : 1 [49]\n"
" nanoseconds : 2 [50]\n"
" userTag : 3 [51]\n"
"[49] int64_t <10:11> [49:50)\n"
"[50] int32_t <11:12> [50:51)\n"
"[51] int32_t <12:13> [51:52)\n"
"[52] int32_t <13:14> [52:53)\n"
"[53] string <14:15> [53:54)\n"
testEq(std::string(SB()<<"\n"<<descs.data()),
R"out(
[0] struct epics:nt/NTNDArray:1.0 parent=[0] [0:22)
alarm -> 12 [12]
alarm.message -> 15 [15]
alarm.severity -> 13 [13]
alarm.status -> 14 [14]
attribute -> 21 [21]
codec -> 2 [2]
codec.name -> 3 [3]
codec.parameters -> 4 [4]
compressedSize -> 5 [5]
dataTimeStamp -> 8 [8]
dataTimeStamp.nanoseconds -> 10 [10]
dataTimeStamp.secondsPastEpoch -> 9 [9]
dataTimeStamp.userTag -> 11 [11]
dimension -> 20 [20]
timeStamp -> 16 [16]
timeStamp.nanoseconds -> 18 [18]
timeStamp.secondsPastEpoch -> 17 [17]
timeStamp.userTag -> 19 [19]
uncompressedSize -> 6 [6]
uniqueId -> 7 [7]
value -> 1 [1]
value : 1 [1]
codec : 2 [2]
compressedSize : 5 [5]
uncompressedSize : 6 [6]
uniqueId : 7 [7]
dataTimeStamp : 8 [8]
alarm : 12 [12]
timeStamp : 16 [16]
dimension : 20 [20]
attribute : 21 [21]
[1] union parent=[0] [1:2)
booleanValue -> 0 [0]
byteValue -> 1 [1]
doubleValue -> 10 [10]
floatValue -> 9 [9]
intValue -> 3 [3]
longValue -> 4 [4]
shortValue -> 2 [2]
ubyteValue -> 5 [5]
uintValue -> 7 [7]
ulongValue -> 8 [8]
ushortValue -> 6 [6]
booleanValue : 0 [0]
[0] bool[] parent=[0] [0:1)
byteValue : 1 [1]
[0] int8_t[] parent=[0] [0:1)
shortValue : 2 [2]
[0] int16_t[] parent=[0] [0:1)
intValue : 3 [3]
[0] int32_t[] parent=[0] [0:1)
longValue : 4 [4]
[0] int64_t[] parent=[0] [0:1)
ubyteValue : 5 [5]
[0] uint8_t[] parent=[0] [0:1)
ushortValue : 6 [6]
[0] uint16_t[] parent=[0] [0:1)
uintValue : 7 [7]
[0] uint32_t[] parent=[0] [0:1)
ulongValue : 8 [8]
[0] uint64_t[] parent=[0] [0:1)
floatValue : 9 [9]
[0] float[] parent=[0] [0:1)
doubleValue : 10 [10]
[0] double[] parent=[0] [0:1)
[2] struct codec_t parent=[0] [2:5)
name -> 1 [3]
parameters -> 2 [4]
name : 1 [3]
parameters : 2 [4]
[3] string parent=[2] [3:4)
[4] any parent=[2] [4:5)
[5] int64_t parent=[0] [5:6)
[6] int64_t parent=[0] [6:7)
[7] int32_t parent=[0] [7:8)
[8] struct time_t parent=[0] [8:12)
nanoseconds -> 2 [10]
secondsPastEpoch -> 1 [9]
userTag -> 3 [11]
secondsPastEpoch : 1 [9]
nanoseconds : 2 [10]
userTag : 3 [11]
[9] int64_t parent=[8] [9:10)
[10] int32_t parent=[8] [10:11)
[11] int32_t parent=[8] [11:12)
[12] struct alarm_t parent=[0] [12:16)
message -> 3 [15]
severity -> 1 [13]
status -> 2 [14]
severity : 1 [13]
status : 2 [14]
message : 3 [15]
[13] int32_t parent=[12] [13:14)
[14] int32_t parent=[12] [14:15)
[15] string parent=[12] [15:16)
[16] struct time_t parent=[0] [16:20)
nanoseconds -> 2 [18]
secondsPastEpoch -> 1 [17]
userTag -> 3 [19]
secondsPastEpoch : 1 [17]
nanoseconds : 2 [18]
userTag : 3 [19]
[17] int64_t parent=[16] [17:18)
[18] int32_t parent=[16] [18:19)
[19] int32_t parent=[16] [19:20)
[20] struct[] parent=[0] [20:21)
[0] struct dimension_t parent=[0] [0:6)
binning -> 4 [4]
fullSize -> 3 [3]
offset -> 2 [2]
reverse -> 5 [5]
size -> 1 [1]
size : 1 [1]
offset : 2 [2]
fullSize : 3 [3]
binning : 4 [4]
reverse : 5 [5]
[1] int32_t parent=[0] [1:2)
[2] int32_t parent=[0] [2:3)
[3] int32_t parent=[0] [3:4)
[4] int32_t parent=[0] [4:5)
[5] bool parent=[0] [5:6)
[21] struct[] parent=[0] [21:22)
[0] struct epics:nt/NTAttribute:1.0 parent=[0] [0:15)
alarm -> 5 [5]
alarm.message -> 8 [8]
alarm.severity -> 6 [6]
alarm.status -> 7 [7]
descriptor -> 4 [4]
name -> 1 [1]
source -> 14 [14]
sourceType -> 13 [13]
tags -> 3 [3]
timestamp -> 9 [9]
timestamp.nanoseconds -> 11 [11]
timestamp.secondsPastEpoch -> 10 [10]
timestamp.userTag -> 12 [12]
value -> 2 [2]
name : 1 [1]
value : 2 [2]
tags : 3 [3]
descriptor : 4 [4]
alarm : 5 [5]
timestamp : 9 [9]
sourceType : 13 [13]
source : 14 [14]
[1] string parent=[0] [1:2)
[2] any parent=[0] [2:3)
[3] string[] parent=[0] [3:4)
[4] string parent=[0] [4:5)
[5] struct alarm_t parent=[0] [5:9)
message -> 3 [8]
severity -> 1 [6]
status -> 2 [7]
severity : 1 [6]
status : 2 [7]
message : 3 [8]
[6] int32_t parent=[5] [6:7)
[7] int32_t parent=[5] [7:8)
[8] string parent=[5] [8:9)
[9] struct time_t parent=[0] [9:13)
nanoseconds -> 2 [11]
secondsPastEpoch -> 1 [10]
userTag -> 3 [12]
secondsPastEpoch : 1 [10]
nanoseconds : 2 [11]
userTag : 3 [12]
[10] int64_t parent=[9] [10:11)
[11] int32_t parent=[9] [11:12)
[12] int32_t parent=[9] [12:13)
[13] int32_t parent=[0] [13:14)
[14] string parent=[0] [14:15)
)out"
)<<"Actual:\n"<<descs.data();
testDiag("Round trip back to bytes");
@@ -459,10 +459,8 @@ void testEmptyRequest()
{
uint8_t msg[] = "\xfd\x02\x00\x80\x00\x00";
FixedBuf buf(false, msg);
TypeDeserContext ctxt{descs1, registry};
from_wire(buf, ctxt);
if(testOk1(buf.good()))
FieldDesc_calculate_offset(descs1.data());
from_wire(buf, descs1, registry);
testOk1(buf.good());
testEq(buf.size(), 0u)<<"remaining of "<<sizeof(msg-1);
}
@@ -474,10 +472,8 @@ void testEmptyRequest()
{
uint8_t msg[] = "\xfe\x02\x00";
FixedBuf buf(false, msg);
TypeDeserContext ctxt{descs2, registry};
from_wire(buf, ctxt);
if(testOk1(buf.good()))
FieldDesc_calculate_offset(descs2.data());
from_wire(buf, descs2, registry);
testOk1(buf.good());
testEq(buf.size(), 0u)<<"remaining of "<<sizeof(msg-1);
}
@@ -485,10 +481,10 @@ void testEmptyRequest()
testEq(descs2.size(), 1u);
testEq(std::string(SB()<<descs1.data()),
"[0] struct <0:1> [0:1)\n")<<"\nActual descs1\n"<<descs1.data();
"[0] struct parent=[0] [0:1)\n")<<"\nActual descs1\n"<<descs1.data();
testEq(std::string(SB()<<descs2.data()),
"[0] struct <0:1> [0:1)\n")<<"\nActual descs2\n"<<descs2.data();
"[0] struct parent=[0] [0:1)\n")<<"\nActual descs2\n"<<descs2.data();
}
} // namespace