diff --git a/src/data.cpp b/src/data.cpp index 940ffd2..0c5aaf6 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -45,22 +45,20 @@ Value::Value(const std::shared_ptr& desc) auto top = std::make_shared(); 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(); bitvalid) { + bit++; + continue; + } + + dstore->valid = true; + + switch(dstore->code) { + case StoreType::Real: + case StoreType::Integer: + case StoreType::UInteger: + dstore->as() = sstore->as(); + bit++; + break; + case StoreType::String: + dstore->as() = sstore->as(); + bit++; + break; + case StoreType::Array: + dstore->as>() = sstore->as>(); + bit++; + break; + case StoreType::Compound: + dstore->as() = sstore->as(); + bit++; + break; + case StoreType::Null: { + // copy entire sub-structure + auto sdesc = desc + bit; + + for(auto end2 = bit + sdesc->size(); bitvalid = true; + + switch(dstore->code) { + case StoreType::Real: + case StoreType::Integer: + case StoreType::UInteger: + dstore->as() = sstore->as(); + bit++; + break; + case StoreType::String: + dstore->as() = sstore->as(); + bit++; + break; + case StoreType::Array: + dstore->as>() = sstore->as>(); + bit++; + break; + case StoreType::Compound: + dstore->as() = sstore->as(); + 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(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 pstore(store, store.get() - desc->offset + pdesc->offset); + std::shared_ptr 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(); - 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 mtype(store->top->desc, desc+it->second); + std::shared_ptr 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(); 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<<"."<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& 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{}(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{}(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_ptrcode.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 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_ptrmiter) { - 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_ptrmembers[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); - bitnext_offset-top->desc->offset; - bit = top->valid.findSet(bit+1)) - { std::shared_ptr 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 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 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>()); - 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 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 arr(alen.size); std::shared_ptr etype(store->top->desc, - desc + 1); // alias + &desc->members[0]); // alias for(auto& elem : arr) { if(from_wire_as(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 arr(alen.size); - auto cdesc = desc+1; + auto cdesc = &desc->members[0]; for(auto& elem : arr) { if(from_wire_as(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 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(buf)!=0) { // strictly 1 or 0 auto descs(std::make_shared>()); - 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 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); + bitsize();) { - std::shared_ptr 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 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>()); - 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 stype(descs, descs->data()); // alias val = Value::Helper::build(stype); diff --git a/src/dataimpl.h b/src/dataimpl.h index c7b8149..df7ce37 100644 --- a/src/dataimpl.h +++ b/src/dataimpl.h @@ -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 + // "fld.sub.leaf" -> rel index + // For Struct, relative to this + // For Union, offset in members array std::map mlookup; + // child iteration. child# -> ("sub", rel index in enclosing vector) std::vector> 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 - 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 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> TypeStore; -struct TypeDeserContext { - std::vector& descs; - TypeStore& cache; -}; - PVXS_API -void from_wire(Buffer& buf, TypeDeserContext& ctxt, unsigned depth=0); +void from_wire(Buffer& buf, std::vector& 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 std::shared_ptr desc; - // map from FieldStorage offsets to FieldDesc offsets. inverse of FieldDesc::offset - std::vector member_indicies; // our members (inclusive). always size()>=1 std::vector 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); diff --git a/src/pvxs/data.h b/src/pvxs/data.h index 56b958b..7baf432 100644 --- a/src/pvxs/data.h +++ b/src/pvxs/data.h @@ -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(); diff --git a/src/type.cpp b/src/type.cpp index 60655b5..5c7415a 100644 --- a/src/type.cpp +++ b/src/type.cpp @@ -164,62 +164,6 @@ TypeDef::TypeDef(const Value& o) TypeDef::~TypeDef() {} -static -void build_tree(std::vector& 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{}(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{}(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 children) return *this; } +static +void build_tree(std::vector& 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{}(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{}(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>(); build_tree(*desc, *top); - FieldDesc_calculate_offset(desc->data()); std::shared_ptr 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<<"["<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<<" "< "< "<next_offset = offset; } std::ostream& operator<<(std::ostream& strm, const FieldDesc* desc) { - for(auto idx : range(desc->size())) { - auto& fld = desc[idx]; - strm<<"["<" - " ["< "< 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"< [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"< 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"<(), "theq"); } - testShow()< [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"< 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"< [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"< 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"< [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"< 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"< [0:1)\n")<<"\nActual descs1\n"< [0:1)\n")<<"\nActual descs2\n"<