diff --git a/src/lib/path-array.hpp b/src/lib/path-array.hpp index 5bbfbd265..6b51cf484 100644 --- a/src/lib/path-array.hpp +++ b/src/lib/path-array.hpp @@ -69,6 +69,7 @@ namespace lib { using std::string; using lib::Literal; using util::unConst; + using util::isnil; namespace con { // Implementation helper: flexible heap based extension storage.... @@ -188,6 +189,21 @@ namespace lib { and storage_ < pos and pos < storage_ + (1 + size (unConst(this)->storage_)); } + + void + trimTo (size_t cnt) + { + REQUIRE (cnt <= size()); + if (cnt == 0 and storage_) + { + delete[] storage_; + storage_ = nullptr; + } + else + size(storage_) = cnt; + // excess elements now unreachable + // note: delete[] knows original size + } }; }//(End)Implementation helper @@ -282,13 +298,7 @@ namespace lib { Literal const& operator[] (size_t idx) const { - Literal const* elm =nullptr; - if (idx < chunk_size) - elm = &elms_[idx]; - else - if (idx-chunk_size < tail_.size()) - elm = &tail_[idx-chunk_size]; - + Literal* elm = unConst(this)->getPosition (idx); if (not elm) throw error::Invalid ("Accessing index "+util::toString(idx) +" on PathArray of size "+ util::toString(size()) @@ -335,7 +345,17 @@ namespace lib { friend iterator begin(PathArray const& pa) { return pa.begin();} friend iterator end (PathArray const& pa) { return pa.end(); } - private: + + private: /* ===== implementation details ===== */ + + bool + isValid (Literal const* pos) const + { + return pos + and (tail_.isValid(pos) + or (pos < elms_.end() and *pos)); + } + /** find _effective end_ of data in the inline array, * i.e. the position _behind_ the last usable content */ @@ -349,22 +369,74 @@ namespace lib { return ++lastPos; // at start if empty, else one behind the last } - bool - isValid (Literal const* pos) const + /** + * @internal access content element by index + * @return pointer to storage, `null` if out of bounds + */ + Literal* + getPosition (size_t idx) { - return pos - and (tail_.isValid(pos) - or (pos < elms_.end() and *pos)); + Literal const* elm =nullptr; + if (idx < chunk_size) + elm = &elms_[idx]; + else + if (idx-chunk_size < tail_.size()) + elm = &tail_[idx-chunk_size]; + + return const_cast (elm); } + /** @internal force new content into the given entry */ + void + setContent (Literal* pos, const char* val) + { + REQUIRE (pos); + *reinterpret_cast (pos) = val; + } + + /** + * establish the _contract_ of PathArray + * - no null pointer within the valid storage range + * - storage is precisely trimmed at the end + * - missing leading elements are filled with Symbol::EMPTY + * - missing inner elements are interpolated as "*" + */ void normalise() { - UNIMPLEMENTED ("establish invariant"); + static Symbol ANY("*"); + const char* fill = Symbol::EMPTY; + + Literal* end = elms_.end(); + Literal* seg = elms_.begin(); + for (Literal* pos = elms_.begin(); pos!=end; ++pos) + if (isnil (*pos)) + setContent (pos, fill); + else + if (fill==Symbol::EMPTY) + fill = ANY; + if (not tail_) return; + seg = const_cast (&tail_[0]); + end = seg + tail_.size(); + for (Literal* pos = seg; pos!=end; ++pos) + if (isnil (*pos)) + setContent (pos, fill); + else + if (fill==Symbol::EMPTY) + fill = ANY; + + size_t idx = size(); + while (idx and ANY == (*this)[--idx]) + setContent (getPosition(idx), nullptr); + + if (idx >= chunk_size-1) + tail_.trimTo (idx - chunk_size); } }; + + template inline PathArray::operator string() const @@ -373,7 +445,7 @@ namespace lib { string buff; size_t expectedLen = this->size() * 10; - buff.reserve (expectedLen); + buff.reserve (expectedLen); // heuristic for (Literal elm : *this) buff += elm + "/"; diff --git a/tests/gui/interact/ui-coord-test.cpp b/tests/gui/interact/ui-coord-test.cpp index 9d0786c3a..9a3e75382 100644 --- a/tests/gui/interact/ui-coord-test.cpp +++ b/tests/gui/interact/ui-coord-test.cpp @@ -122,7 +122,7 @@ namespace test { CHECK ("Ψ/Φ/Ω" == uic.getPath()); // new value can be assigned, but not altered in place - uic = UICoord{nullptr,nullptr,"Θ",nullptr,nullptr,nullptr,"Φ"}; + uic = UICoord{nullptr,nullptr,"Θ",nullptr,"Σ",nullptr,"Φ"}; CHECK (7 == uic.size()); // representation is trimmed and filled @@ -131,7 +131,7 @@ namespace test { CHECK (Symbol::EMPTY == uic[UIC_PERSP]); CHECK ("Θ" == uic[UIC_PANEL]); CHECK ("*" == uic[UIC_VIEW]); - CHECK ("*" == uic[UIC_TAB]); + CHECK ("Σ" == uic[UIC_TAB]); CHECK ("*" == uic[UIC_PART]); CHECK ("Φ" == uic[UIC_PART+1]); VERIFY_ERROR (INDEX_BOUNDS, uic[UIC_PART+2]);