diff --git a/src/data.cpp b/src/data.cpp index 187c6fd..f21a857 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -52,6 +52,13 @@ Value::Value(const std::shared_ptr& desc) this->store = std::move(val); } +Value::Value(const std::shared_ptr& desc, Value& parent) + :Value(desc) +{ + // TODO ref. loop detection + store->top->enclosing = parent; +} + Value::~Value() {} Value Value::cloneEmpty() const @@ -83,14 +90,14 @@ Value Value::clone() const // return *this; //} -Value Value::allocMember() const +Value Value::allocMember() { // allocate member type for Struct[] or Union[] 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); - return Value(fld); + return Value::Helper::build(fld, *this); } bool Value::isMarked(bool parents, bool children) const @@ -103,6 +110,14 @@ void Value::mark(bool v) { if(desc) store->top->valid[store->index()] = v; + if(!v) + return; + + auto top = store->top; + while(top && top->enclosing) { + top->enclosing.mark(); + top = top->enclosing.store->top; + } } void Value::unmark(bool parents, bool children) @@ -296,7 +311,7 @@ void Value::copyIn(const void *ptr, StoreType type) throw NoConvert(); } - store->top->valid[store->index()] = true; + mark(); } void Value::traverse(const std::string &expr, bool modify) @@ -352,7 +367,7 @@ void Value::traverse(const std::string &expr, bool modify) if(fld.desc!=desc+it->second) { // select std::shared_ptr mtype(store->top->desc, desc+it->second); - fld = Value(mtype); + fld = Value(mtype, *this); } pos = sep; *this = fld; @@ -422,6 +437,12 @@ const Value Value::operator[](const char *name) const return ret; } +static +void show_Value(std::ostream& strm, + const std::string& member, + const Value& val, + unsigned level=0); + static void show_Value(std::ostream& strm, const std::string& member, @@ -466,13 +487,13 @@ 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 == fld._desc()) { + if(desc + pair.second == Value::Helper::desc(fld)) { strm<<"."<(); strm<<" [\n"; for(auto& val : arr) { - show_Value(strm, std::string(), val._desc(), val._store(), level+1); + show_Value(strm, std::string(), val, level+1); } indent(strm, level); strm<<"]\n"; @@ -496,9 +517,21 @@ void show_Value(std::ostream& strm, } } +static +void show_Value(std::ostream& strm, + const std::string& member, + const Value& val, + unsigned level) +{ + show_Value(strm, member, + Value::Helper::desc(val), + Value::Helper::store_ptr(val), + level); +} + std::ostream& operator<<(std::ostream& strm, const Value& val) { - show_Value(strm, std::string(), val._desc(), val._store()); + show_Value(strm, std::string(), val); return strm; } diff --git a/src/dataencode.cpp b/src/dataencode.cpp index 962be5f..3589528 100644 --- a/src/dataencode.cpp +++ b/src/dataencode.cpp @@ -232,7 +232,7 @@ void from_wire(Buffer& buf, shared_array& varr) // serialize a field and all children (if Compound) static -void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store) +void to_wire_field(Buffer& buf, const FieldDesc* desc, const std::shared_ptr& store) { switch(store->code) { case StoreType::Null: @@ -242,7 +242,7 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store // 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; + std::shared_ptr cstore(store, store.get()+off); if(cdesc->code!=TypeCode::Struct) to_wire_field(buf, cdesc, cstore); } @@ -302,14 +302,14 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store } else { size_t index = 0u; for(auto& pair : desc->miter) { - if(fld._desc()== desc+pair.second) + if(Value::Helper::desc(fld)== 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()); + to_wire_full(buf, fld); } return; @@ -318,8 +318,8 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store to_wire(buf, uint8_t(0xff)); } else { - to_wire(buf, fld._desc()); - to_wire_field(buf, fld._desc(), fld._store()); + to_wire(buf, Value::Helper::desc(fld)); + to_wire_full(buf, fld); } return; default: break; @@ -361,8 +361,8 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store 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()); + assert(Value::Helper::desc(elem)==desc+1); + to_wire_full(buf, elem); } } } @@ -376,7 +376,7 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store } else { to_wire(buf, uint8_t(1u)); - to_wire_field(buf, elem._desc(), elem._store()); + to_wire_full(buf, elem); } } } @@ -390,8 +390,8 @@ void to_wire_field(Buffer& buf, const FieldDesc* desc, const FieldStorage* store } else { to_wire(buf, uint8_t(1u)); - to_wire(buf, elem._desc()); - to_wire_field(buf, elem._desc(), elem._store()); + to_wire(buf, Value::Helper::desc(elem)); + to_wire_full(buf, elem); } } } @@ -410,14 +410,15 @@ void to_wire_full(Buffer& buf, const Value& val) { assert(!!val); - to_wire_field(buf, val._desc(), val._store()); + to_wire_field(buf, Value::Helper::desc(val), Value::Helper::store(val)); } void to_wire_valid(Buffer& buf, const Value& val) { - auto desc = val._desc(); + auto desc = Value::Helper::desc(val); + auto store = Value::Helper::store(val); assert(!!desc); - auto top = val._store()->top; + auto top = store->top; to_wire(buf, top->valid); top->valid.resize(top->members.size()); @@ -427,7 +428,8 @@ void to_wire_valid(Buffer& buf, const Value& val) bitnext_offset; bit = top->valid.findSet(bit+1)) { - to_wire_field(buf, desc + top->member_indicies[bit], val._store()+bit); + std::shared_ptr cstore(store, store.get()+bit); + to_wire_field(buf, desc + top->member_indicies[bit], cstore); } } @@ -442,7 +444,7 @@ T from_wire_as(Buffer& buf) } static -void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, FieldStorage* store) +void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, const std::shared_ptr& store) { switch(store->code) { case StoreType::Null: @@ -452,7 +454,7 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, Field // 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; + std::shared_ptr cstore(store, store.get()+off); if(cdesc->code!=TypeCode::Struct) from_wire_field(buf, ctxt, cdesc, cstore); } @@ -514,9 +516,9 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, Field } else if(select.size < desc->miter.size()) { std::shared_ptr stype(store->top->desc, desc + desc->miter[select.size].second); // alias - fld = Value(stype); + fld = Value::Helper::build(stype, store, desc); - from_wire_field(buf, ctxt, fld._desc(), fld._store()); + from_wire_full(buf, ctxt, fld); return; } } @@ -534,9 +536,9 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, Field } else { std::shared_ptr stype(descs, descs->data()); // alias - fld = Value(stype); + fld = Value::Helper::build(stype); - from_wire_field(buf, ctxt, fld._desc(), fld._store()); + from_wire_full(buf, ctxt, fld); return; } @@ -582,9 +584,9 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, Field desc + 1); // alias for(auto& elem : arr) { if(from_wire_as(buf)!=0) { // strictly 1 or 0 - elem = Value(etype); + elem = Value::Helper::build(etype, store, desc); - from_wire_field(buf, ctxt, elem._desc(), elem._store()); + from_wire_full(buf, ctxt, elem); } } @@ -608,9 +610,9 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, Field } else if(select.size < cdesc->miter.size()) { std::shared_ptr stype(store->top->desc, cdesc + cdesc->miter[select.size].second); // alias - elem = Value(stype); + elem = Value::Helper::build(stype, store, desc); - from_wire_field(buf, ctxt, elem._desc(), elem._store()); + from_wire_full(buf, ctxt, elem); return; } else { @@ -637,9 +639,9 @@ void from_wire_field(Buffer& buf, TypeStore& ctxt, const FieldDesc* desc, Field from_wire(buf, dc); if(!descs->empty()) { std::shared_ptr stype(descs, descs->data()); // alias - elem = Value(stype); + elem = Value::Helper::build(stype, store, desc); - from_wire_field(buf, ctxt, elem._desc(), elem._store()); + from_wire_full(buf, ctxt, elem); } } } @@ -662,14 +664,15 @@ void from_wire_full(Buffer& buf, TypeStore& ctxt, Value& val) { assert(!!val); - from_wire_field(buf, ctxt, val._desc(), val._store()); + from_wire_field(buf, ctxt, Value::Helper::desc(val), Value::Helper::store(val)); } void from_wire_valid(Buffer& buf, TypeStore& ctxt, Value& val) { - auto desc = val._desc(); + auto desc = Value::Helper::desc(val); + auto store = Value::Helper::store(val); assert(!!desc); - auto top = val._store()->top; + auto top = store->top; from_wire(buf, top->valid); // encoding rounds # of bits to whole bytes, so we may trim @@ -681,7 +684,8 @@ void from_wire_valid(Buffer& buf, TypeStore& ctxt, Value& val) bitnext_offset; bit = top->valid.findSet(bit+1)) { - from_wire_field(buf, ctxt, desc + top->member_indicies[bit], val._store()+bit); + std::shared_ptr cstore(store, store.get()+bit); + from_wire_field(buf, ctxt, desc + top->member_indicies[bit], cstore); } } diff --git a/src/dataimpl.h b/src/dataimpl.h index 7a0abbc..2f7a9e2 100644 --- a/src/dataimpl.h +++ b/src/dataimpl.h @@ -15,6 +15,26 @@ #include "utilpvt.h" namespace pvxs { + +struct Value::Helper { + // internal access to private operations + static inline Value build(const std::shared_ptr& desc) { + return Value(desc); + } + static inline Value build(const std::shared_ptr& desc, Value& parent) { + return Value(desc, parent); + } + static inline Value build(const std::shared_ptr& desc, + const std::shared_ptr& pstore, const impl::FieldDesc* pdesc); + + static inline std::shared_ptr& store( Value& v) { return v.store; } + static inline std::shared_ptr store(const Value& v) { return v.store; } + static inline const FieldDesc* desc(const Value& v) { return v.desc; } + + static inline impl::FieldStorage* store_ptr( Value& v) { return v.store.get(); } + static inline const impl::FieldStorage* store_ptr(const Value& v) { return v.store.get(); } +}; + namespace impl { struct Buffer; @@ -104,7 +124,7 @@ struct FieldStorage { inline const uint8_t* buffer() const { return reinterpret_cast(&store); } }; -// hidden (publically) management of an allocated Struct +// hidden (publicly) management of an allocated Struct struct StructTop { // which members have been assigned/updated (use to track "changes") BitMask valid; @@ -115,6 +135,9 @@ struct StructTop { std::vector member_indicies; // our members (inclusive). always size()>=1 std::vector members; + + // empty, or the field of a structure which encloses this. + Value enclosing; }; static_assert (std::is_standard_layout{}, "Needed for offsetof()"); @@ -140,6 +163,19 @@ void FieldDesc_calculate_offset(FieldDesc* top); PVXS_API std::ostream& operator<<(std::ostream& strm, const FieldDesc* desc); -}} // namespace pvxs::impl +} // namespace impl + + +Value Value::Helper::build(const std::shared_ptr& desc, + const std::shared_ptr& pstore, const impl::FieldDesc* pdesc) +{ + Value ret(desc); + auto& enc = ret.store->top->enclosing; + enc.store = pstore; + enc.desc = pdesc; + return ret; +} + +} // namespace pvxs #endif // DATAIMPL_H diff --git a/src/pvxs/data.h b/src/pvxs/data.h index 562b980..6939617 100644 --- a/src/pvxs/data.h +++ b/src/pvxs/data.h @@ -224,11 +224,22 @@ struct PVXS_API NoConvert : public std::runtime_error //! pointer-like reference to a single data field class PVXS_API Value { friend class TypeDef; + // (maybe) storage for this field. alias of StructTop::members[] std::shared_ptr store; - const impl::FieldDesc* desc; // owned thourgh StructTop (aliased as FieldStorage) + // (maybe) owned thourgh StructTop (aliased as FieldStorage) + const impl::FieldDesc* desc; public: + struct Helper; + friend struct Helper; + + //! default empty Value constexpr Value() :desc(nullptr) {} +private: + // Build new Value with the given type. Used by TypeDef explicit Value(const std::shared_ptr& desc); + Value(const std::shared_ptr& desc, Value& parent); +public: + // movable and copyable Value(const Value&) = default; Value(Value&&) = default; Value& operator=(const Value&) = default; @@ -242,7 +253,7 @@ public: //! copy values from other. Must have matching types. // Value& assign(const Value&); - Value allocMember() const; + Value allocMember(); inline bool valid() const { return desc; } inline explicit operator bool() const { return desc; } @@ -359,10 +370,6 @@ public: inline iterator end() { iterator ret(this); return ret;} inline const_iterator cend() const { const_iterator ret(this); return ret;} inline const_iterator end() const { return cend(); } - - // impl cheating - const impl::FieldDesc* _desc() const { return desc; } - impl::FieldStorage* _store() const { return store.get(); } }; PVXS_API diff --git a/src/serverintrospect.cpp b/src/serverintrospect.cpp index 8209099..480d567 100644 --- a/src/serverintrospect.cpp +++ b/src/serverintrospect.cpp @@ -38,7 +38,7 @@ struct ServerIntrospectControl : public server::Introspect virtual void reply(const Value& prototype) override final { - auto desc = prototype._desc(); + auto desc = Value::Helper::desc(prototype); if(!desc) throw std::logic_error("Can't reply to GET_FIELD with Null prototype"); Status sts{Status::Ok}; diff --git a/test/testdata.cpp b/test/testdata.cpp index b24864d..8879a45 100644 --- a/test/testdata.cpp +++ b/test/testdata.cpp @@ -125,7 +125,7 @@ void testSerialize2() auto val = def.create(); val["choice->b"] = "test"; - val["choice"].mark(); // TODO: auto mark back through union + testOk1(!!val["choice"].isMarked()); testToBytes(true, [&val](Buffer& buf) { to_wire_valid(buf, val); @@ -199,7 +199,7 @@ void testSerialize2() MAIN(testdata) { - testPlan(11); + testPlan(12); testSerialize1(); testSerialize2(); cleanup_for_valgrind(); diff --git a/test/testtype.cpp b/test/testtype.cpp index 0d41626..b914862 100644 --- a/test/testtype.cpp +++ b/test/testtype.cpp @@ -127,8 +127,8 @@ void testTypeDef() auto val = def.create(); testOk1(!!val.valid()); - testShow()< [0:18)\n" " achoice -> 14 [14]\n" " any -> 9 [9]\n"