From c78ec7718b17262ec4319a6aa9d256857d4d19e3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 27 Jan 2020 17:52:56 -0800 Subject: [PATCH] Value iteration --- src/data.cpp | 47 ++++++++++++++++++++++++++ src/pvxs/data.h | 85 +++++++++++++++++++++++++++++++++++++++++++++-- test/testdata.cpp | 52 ++++++++++++++++++++++++++++- 3 files changed, 180 insertions(+), 4 deletions(-) diff --git a/src/data.cpp b/src/data.cpp index 1b3d20b..3d31be1 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -643,6 +643,53 @@ const Value Value::operator[](const char *name) const return ret; } +void Value::_iter_fl(Value::IterInfo &info, bool first) const +{ + if(!store) + throw NoField(); + + if(info.depth) { + info.pos = info.nextcheck = store->index() + (first ? 1u : desc->size()); + + if(info.marked) + _iter_advance(info); + + } else { + info.pos = info.nextcheck = first ? 0u : desc->miter.size(); + } +} + +void Value::_iter_advance(IterInfo& info) const +{ + assert(info.depth); + + // scan forward to find next non-marked + for(auto idx : range(info.pos, desc->size())) { + auto S = store.get() + idx; + if(S->valid) { + auto D = desc + idx; + info.pos = idx; + info.nextcheck = idx + D->size(); + return; + } + } + + info.pos = info.nextcheck = desc->size(); +} + +Value Value::_iter_deref(const IterInfo& info) const +{ + auto idx = info.pos; + if(!info.depth) + idx = desc->miter[idx].second; + + decltype (store) store2(store, store.get()+idx); + Value ret; + ret.store = std::move(store2); + ret.desc = desc + idx; + return ret; +} + static void show_Value(std::ostream& strm, const std::string& member, diff --git a/src/pvxs/data.h b/src/pvxs/data.h index 9763901..5a036cb 100644 --- a/src/pvxs/data.h +++ b/src/pvxs/data.h @@ -381,9 +381,9 @@ public: bool idStartsWith(const std::string& prefix) const; //! test for instance equality. - inline bool compareInst(const Value& o) { return store==o.store; } -// int compareValue(const Value&); - inline int compareType(const Value& o) { return desc==o.desc; } + inline bool compareInst(const Value& o) const { return store==o.store; } +// int compareValue(const Value&) const; + inline int compareType(const Value& o) const { return desc==o.desc; } /** Return our name for a decendent field. * @code @@ -493,6 +493,85 @@ public: inline Value operator[](const std::string& name) { return (*this)[name.c_str()]; } const Value operator[](const char *name) const; inline const Value operator[](const std::string& name) const { return (*this)[name.c_str()]; } + + 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; + bool marked; + 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) + {} + }; + template + class Iter : private IterInfo { + V *ref; + constexpr Iter(V* ref, size_t pos, bool marked, bool depth) + :IterInfo(pos, marked, depth), ref(ref) + {} + friend class Value; + friend class Iterable; + public: + Iter() {} + + V operator*() const { return ref->_iter_deref(*this); } + Iter& operator++() { + pos++; + if(marked && pos >= nextcheck) + ref->_iter_advance(*this); + return *this; + } + Iter operator++(int) { + Iter ret(*this); + pos++; + if(marked && pos >= nextcheck) + ref->_iter_advance(*this); + return ret; + } + bool operator==(const Iter& o) const { return pos == o.pos; } + bool operator!=(const Iter& o) const { return !(o==*this); } + }; + template + friend class Iter; + + void _iter_fl(IterInfo& info, bool first) const; + void _iter_advance(IterInfo& info) const; // cheats, actually mutates (but doesn't change container values) + Value _iter_deref(const IterInfo& info) const; +public: + + template + class Iterable { + typedef Iter iterator; + V* owner; + bool marked; + bool depth; + public: + constexpr Iterable(V* 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 iall() { return Iterable{this, false, true}; } + Iterable ichildren() { return Iterable{this, false, false}; } + Iterable imarked() { return Iterable{this, true , true}; } + + Iterable iall() const { return Iterable{this, false, true}; } + Iterable ichildren() const { return Iterable{this, false, false}; } + Iterable imarked() const { return Iterable{this, true , true}; } }; PVXS_API diff --git a/test/testdata.cpp b/test/testdata.cpp index 20e2c22..9cf743d 100644 --- a/test/testdata.cpp +++ b/test/testdata.cpp @@ -460,11 +460,60 @@ void testName() }); } +void testIter() +{ + testDiag("%s", __func__); + + auto def = nt::NTScalar{TypeCode::String}.build(); + auto val = def.create(); + + unsigned i=0; + for(auto fld : val.iall()) { + testDiag("field %s", val.nameOf(fld).c_str()); + i++; + } + testEq(i, 9u)<<"# of decendent fields"; + + i=0; + for(auto fld : val.ichildren()) { + testDiag("field %s", val.nameOf(fld).c_str()); + i++; + } + testEq(i, 3u)<<"# of child fields"; + + auto testMarked = [&val](unsigned expect) -> testCase { + unsigned i=0; + for(auto fld : val.imarked()) { + testDiag("field %s", val.nameOf(fld).c_str()); + i++; + } + return testEq(i, expect); + }; + + testMarked(0u)<<"no decendent fields"; + + val["alarm.status"].mark(); + + testMarked(1u)<<"mark one field"; + + val.unmark(); + val["alarm"].mark(); + + testMarked(4u)<<"mark sub-struct"; + + val.unmark(); + val["value"].mark(); + val["alarm.status"].mark(); + val["timeStamp"].mark(); + + testMarked(6u)<<"mark sub-struct"; +} + } // namespace MAIN(testdata) { - testPlan(70); + testPlan(76); testSerialize1(); testDeserialize1(); testSimpleDef(); @@ -473,6 +522,7 @@ MAIN(testdata) testTraverse(); testAssign(); testName(); + testIter(); cleanup_for_valgrind(); return testDone(); }