diff --git a/src/data.cpp b/src/data.cpp index 0ea3713..5c477d8 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -609,7 +609,7 @@ void Value::copyIn(const void *ptr, StoreType type) // copy struct to struct // all marked source field may be mapped to destination fields - for(auto& sfld : src.imarked()) { + for(const auto& sfld : src.imarked()) { if(sfld.type()==TypeCode::Struct) { // entire sub-struct marked. @@ -864,84 +864,131 @@ size_t Value::nmembers() const } } -void Value::_iter_fl(Value::IterInfo &info, bool first) const +template<> +Value::Iterable::iterator +Value::Iterable::end() const noexcept { - if(!desc || (desc->code!=TypeCode::Struct && desc->code!=TypeCode::Union)) { - // not iterable - info.pos = info.nextcheck = 0u; - return; - } - - // Union iteration has no depth - if(desc->code.scalarOf()!=TypeCode::Struct) - info.depth = false; - - // array instances have no marking - if(desc->code.isarray()) - info.marked = false; - - size_t cnt; - if(info.depth) - cnt = desc->mlookup.size(); - else - cnt = desc->miter.size(); - - info.pos = first ? 0 : cnt; - - if(first && info.marked) { - info.nextcheck = info.pos; - _iter_advance(info); - - } else { - info.nextcheck = cnt+1; // for !marked, never need to check + iterator ret{val, 0u}; + + if(val && val.type()==TypeCode::Struct) { + ret.pos = val.desc->mlookup.size(); + + } else if(val && val.type()==TypeCode::Union) { + ret.pos = val.desc->miter.size(); } + return ret; } -void Value::_iter_advance(IterInfo& info) const + +template<> +Value +Value::_Iterator::operator*() const noexcept { - assert(desc); - assert(info.marked); // should not be reached for simple iteration + Value ret; - // for Struct, scan to next marked field - if(desc->code==TypeCode::Struct) { - assert(info.depth); // the following assume + if(val.type()==TypeCode::Struct) { + decltype (ret.store) store(val.store, val.store.get() + 1u + pos); + ret.store = std::move(store); + ret.desc = val.desc + 1u + pos; - // scan forward to find next non-marked - for(auto idx : range(info.pos, desc->mlookup.size())) { - auto S = store.get() + idx + 1u; + } else if(val && val.type()==TypeCode::Union) { + auto pos_desc = &val.desc->members[val.desc->miter[pos].second]; + + if(val.store->as().desc==pos_desc) { + // pointing to selected Union field + ret = val.store->as(); + + } else { + std::shared_ptr base(val.store, pos_desc); + ret = Value(base); + } + } + return ret; +} + +template<> +Value::Iterable::iterator +Value::Iterable::end() const noexcept +{ + iterator ret{val, 0u}; + + if(val && (val.type()==TypeCode::Struct || val.type()==TypeCode::Union)) { + ret.pos = val.desc->miter.size(); + } + return ret; +} + +template<> +Value +Value::_Iterator::operator*() const noexcept +{ + auto offset = val.desc->miter[pos].second; + Value ret; + + if(val.type()==TypeCode::Struct) { + decltype (ret.store) store(val.store, val.store.get() + offset); + ret.store = std::move(store); + ret.desc = val.desc + offset; + + } else if(val && val.type()==TypeCode::Union) { + auto pos_desc = &val.desc->members[val.desc->miter[pos].second]; + + if(val.store->as().desc==pos_desc) { + // pointing to selected Union field + ret = val.store->as(); + + } else { + std::shared_ptr base(val.store, pos_desc); + ret = Value(base); + } + } + return ret; +} + +static +void _next_marked(const Value& ref, size_t& pos, size_t& nextcheck) +{ + if(pos < nextcheck) + return; + + if(ref.type()==TypeCode::Struct) { + auto base_desc = Value::Helper::desc(ref); + + while(pos < base_desc->mlookup.size()) { + auto desc = base_desc + 1u + pos; + auto S = Value::Helper::store_ptr(ref) + 1u + pos; if(S->valid) { - auto D = desc + idx + 1u; - info.pos = idx; - info.nextcheck = idx + D->size(); + nextcheck = pos + desc->size(); return; } + + ++pos; } + nextcheck = pos; - // end of iteration - info.pos = desc->mlookup.size(); - info.nextcheck = info.pos+1; + } else if(ref.type()==TypeCode::Union) { + auto desc = Value::Helper::desc(ref); - } else if(desc->code==TypeCode::Union) { - assert(!info.depth); // the following assume + if(pos >= desc->miter.size()) + return; // end of iteration - if(info.pos >= desc->miter.size()) - return; // at end of iteration + const auto& val = Value::Helper::store_ptr(ref)->as(); + size_t sel_idx = Value::Helper::desc(val) - desc->members.data(); + size_t pos_idx = desc->miter[pos].second; - auto& val = store->as(); - auto pos_desc = &desc->members[desc->miter[info.pos].second]; - - if(!val || pos_desc > val.desc) { + if(!val || pos_idx > sel_idx) { // no field selected, or pos is after selection // end of iteration - info.pos = desc->miter.size(); - info.nextcheck = info.pos+1; - } else if(pos_desc < val.desc) { + pos = desc->miter.size(); + + } else if(pos_idx < sel_idx) { + // before selected // jump forward to selection - for(auto i : range(info.pos, desc->miter.size())) { - if(val.desc==&desc->members[desc->miter[i].second]) { - info.pos = i; - info.nextcheck = i+1; + + for(auto i : range(pos, desc->miter.size())) { + if(desc->miter[i].second == sel_idx) { + pos = i; return; } } @@ -950,42 +997,66 @@ void Value::_iter_advance(IterInfo& info) const } } -Value Value::_iter_deref(const IterInfo& info) const +template<> +Value::Iterable::iterator +Value::Iterable::begin() const noexcept +{ + iterator ret{val, 0u}; + _next_marked(ret.val, ret.pos, ret.nextcheck); + return ret; +} + +template<> +Value::Iterable::iterator +Value::Iterable::end() const noexcept +{ + iterator ret{val, 0u}; + + if(val && val.type()==TypeCode::Struct) { + ret.pos = val.desc->mlookup.size(); + + } else if(val && val.type()==TypeCode::Union) { + ret.pos = val.desc->miter.size(); + } + ret.nextcheck = ret.pos; + return ret; +} + +template<> +Value +Value::_Iterator::operator*() const noexcept { Value ret; - if(desc->code==TypeCode::Struct) { - auto idx = info.pos; - if(info.depth) - idx++; // indexing starts with FieldDesc after top - else - idx = desc->miter[idx].second; + if(val.type()==TypeCode::Struct) { + decltype (ret.store) store(val.store, val.store.get() + 1u + pos); + ret.store = std::move(store); + ret.desc = val.desc + 1u + pos; - assert(idx>0u); - assert(idxsize()); - decltype (store) store2(store, store.get()+idx); - ret.store = std::move(store2); - ret.desc = desc + idx; + } else if(val && val.type()==TypeCode::Union) { + auto pos_desc = &val.desc->members[val.desc->miter[pos].second]; - } else if(desc->code==TypeCode::Union) { - auto pos_desc = &desc->members[desc->miter[info.pos].second]; - - if(desc->code==TypeCode::Union && store->as().desc==pos_desc) { + if(val.store->as().desc==pos_desc) { // pointing to selected Union field - ret = store->as(); + ret = val.store->as(); } else { - // array, or not selected union field. - // allocate temporary - - std::shared_ptr base(store, pos_desc); + std::shared_ptr base(val.store, pos_desc); ret = Value(base); } } - return ret; } +template<> +Value::_Iterator& +Value::_Iterator::operator++() noexcept +{ + pos++; + _next_marked(val, pos, nextcheck); + return *this; +} + namespace impl { void FieldStorage::init(StoreType code) diff --git a/src/pvxs/data.h b/src/pvxs/data.h index 41fe11e..647f74e 100644 --- a/src/pvxs/data.h +++ b/src/pvxs/data.h @@ -694,36 +694,25 @@ public: //! only Struct, StructA, Union, UnionA return non-zero size_t nmembers() const; - template - class Iterable; -private: - struct IterInfo { - // when Marked==true, index of next potentially unmarked field. - // all [pos, nextcheck) are marked - size_t pos; - size_t nextcheck; - // true only iterates marked fields (Struct), - // or only the selected field (Union) - bool marked; - // false only iterates children (Struct only) - bool depth; - constexpr IterInfo() :pos(0u), nextcheck(0u), marked(false), depth(false) {} - constexpr IterInfo(size_t pos, bool marked, bool depth) - :pos(pos), nextcheck(pos), marked(marked), depth(depth) - {} + struct _IAll {}; + struct _IChildren {}; + struct _IMarked { + size_t nextcheck=0u; }; - template - class Iter; - template - friend class Iter; - - void _iter_fl(IterInfo& info, bool first) const; - void _iter_advance(IterInfo& info) const; - Value _iter_deref(const IterInfo& info) const; +private: + template + struct _Iterator; + template + friend struct _Iterator; public: + template + struct Iterable; + template + friend struct Iterable; - template - class Iterable; + typedef Iterable<_IAll> IAll; + typedef Iterable<_IChildren> IChildren; + typedef Iterable<_IMarked> IMarked; /** Depth-first iteration of all descendant fields * @@ -735,20 +724,13 @@ public: * @endcode */ inline - Iterable iall(); + IAll iall() const noexcept; //! iteration of all child fields inline - Iterable ichildren(); + IChildren ichildren() const noexcept; //! Depth-first iteration of all marked descendant fields inline - Iterable imarked(); - - inline - Iterable iall() const; - inline - Iterable ichildren() const; - inline - Iterable imarked() const; + IMarked imarked() const noexcept; struct Fmt { const Value* top = nullptr; @@ -769,61 +751,126 @@ public: inline Fmt format() const { return Fmt(this); } }; -template -class Value::Iter : private Value::IterInfo { - Value ref; - constexpr Iter(const Value& ref, size_t pos, bool marked, bool depth) - :IterInfo(pos, marked, depth), ref(ref) - {} +template +struct Value::_Iterator : private T +{ +private: + Value val; + size_t pos = 0u; friend class Value; - friend class Iterable; + friend struct Iterable; + constexpr _Iterator(const Value& val, size_t pos) : val(val), pos(pos) {} public: - Iter() :ref(nullptr) {} - - V operator*() const { return ref._iter_deref(*this); } - Iter& operator++() { - pos++; - if(pos >= nextcheck) - ref._iter_advance(*this); - return *this; - } - Iter operator++(int) { - Iter ret(*this); + _Iterator() = default; + Value operator*() const noexcept; // specialized per- _IterKind + _Iterator& operator++() noexcept; // specialized per- _IterKind + _Iterator operator++(int) noexcept { + _Iterator ret(*this); ++(*this); return ret; } - bool operator==(const Iter& o) const { return pos == o.pos; } - bool operator!=(const Iter& o) const { return !(o==*this); } + inline bool operator==(const _Iterator& o) const noexcept { return pos==o.pos; } + inline bool operator!=(const _Iterator& o) const noexcept { return pos!=o.pos; } }; -template -class Value::Iterable { - Value owner; - bool marked = false; - bool depth = false; +template +struct Value::Iterable +{ +private: + Value val; + friend class Value; public: - typedef Iter iterator; - constexpr Iterable() = default; - constexpr Iterable(const Value& owner, bool marked, bool depth) :owner(owner), marked(marked), depth(depth) {} - iterator begin() const { - iterator ret{owner, 0u, marked, depth}; - owner._iter_fl(ret, true); - return ret; - } - iterator end() const { - iterator ret{owner, 0u, marked, depth}; - owner._iter_fl(ret, false); - return ret; - } + Iterable() = default; + explicit Iterable(const Value* val) :val(*val) {} + typedef _Iterator iterator; + iterator begin() const noexcept; // specialized per- _IterKind + iterator end() const noexcept; // specialized per- _IterKind }; -Value::Iterable Value::iall() { return Iterable{*this, false, true}; } -Value::Iterable Value::ichildren() { return Iterable{*this, false, false}; } -Value::Iterable Value::imarked() { return Iterable{*this, true , true}; } +template<> +inline +Value::Iterable::iterator +Value::Iterable::begin() const noexcept { + return iterator(val, 0u); // always start pos==0 +} -Value::Iterable Value::iall() const { return Iterable{*this, false, true}; } -Value::Iterable Value::ichildren() const { return Iterable{*this, false, false}; } -Value::Iterable Value::imarked() const { return Iterable{*this, true , true}; } +template<> +PVXS_API +Value::Iterable::iterator +Value::Iterable::end() const noexcept; + +template<> +PVXS_API +Value +Value::_Iterator::operator*() const noexcept; + +template<> +inline +Value::_Iterator& +Value::_Iterator::operator++() noexcept { + pos++; + return *this; +} + +template<> +inline +Value::Iterable::iterator +Value::Iterable::begin() const noexcept { + return iterator(val, 0u); // always start pos==0 +} + +template<> +PVXS_API +Value::Iterable::iterator +Value::Iterable::end() const noexcept; + +template<> +PVXS_API +Value +Value::_Iterator::operator*() const noexcept; + +template<> +inline +Value::_Iterator& +Value::_Iterator::operator++() noexcept { + pos++; + return *this; +} + +template<> +PVXS_API +Value::Iterable::iterator +Value::Iterable::begin() const noexcept; + +template<> +PVXS_API +Value::Iterable::iterator +Value::Iterable::end() const noexcept; + +template<> +PVXS_API +Value +Value::_Iterator::operator*() const noexcept; + +template<> +PVXS_API +Value::_Iterator& +Value::_Iterator::operator++() noexcept; + +Value::Iterable +Value::iall() const noexcept { + return Iterable{this}; +} + +Value::Iterable +Value::ichildren() const noexcept { + return Iterable{this}; +} + +Value::Iterable +Value::imarked() const noexcept { + return Iterable{this}; +} PVXS_API std::ostream& operator<<(std::ostream& strm, const Value::Fmt& fmt); diff --git a/test/testdata.cpp b/test/testdata.cpp index eb3cf96..a6ba262 100644 --- a/test/testdata.cpp +++ b/test/testdata.cpp @@ -149,14 +149,14 @@ void testIterStruct() val.unmark(); val["alarm"].mark(); - testMarked(4u)<<"mark sub-struct"; + testMarked(4u)<<"mark alarm sub-struct"; val.unmark(); val["value"].mark(); // 1 field val["alarm.status"].mark(); // 1 field val["timeStamp"].mark(); // 4 fields (struct node and 3x leaves) - testMarked(6u)<<"mark sub-struct"; + testMarked(6u)<<"mark multiple sub-struct"; } void testIterUnion() @@ -342,7 +342,7 @@ void testAssignSimilar() MAIN(testdata) { - testPlan(97); + testPlan(99); testSetup(); testTraverse(); testAssign();