2017-09-24 22:41:45 +02:00
|
|
|
|
/*
|
|
|
|
|
|
PATH-ARRAY.hpp - sequence of path-like component-IDs in fixed storage
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2017, Hermann Vosseler <Ichthyostega@web.de>
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file path-array.hpp
|
|
|
|
|
|
** Foundation abstraction to implement path-like component sequences.
|
|
|
|
|
|
** This library class can be used to build a path abstraction for data structure access
|
|
|
|
|
|
** or some similar topological coordinate system, like e.g. [UI coordinates](\ref ui-coord.hpp)
|
2017-10-01 06:08:54 +02:00
|
|
|
|
** A PathArray is an iterable sequence of literal component IDs, implemented as tuple of lib::Literal
|
2017-09-24 22:41:45 +02:00
|
|
|
|
** held in fixed inline storage with possible heap allocated (and thus unlimited) extension storage.
|
|
|
|
|
|
** It offers range checks, standard iteration and array-like indexed component access; as a whole
|
2017-10-01 06:08:54 +02:00
|
|
|
|
** it is copyable, while actual components are immutable after construction. PathArray can be
|
|
|
|
|
|
** bulk initialised from an explicit sequence of literals; it is normalised on construction
|
|
|
|
|
|
** to trim and fill interspersed missing elements. Iteration and equality comparison are
|
|
|
|
|
|
** built on top of the normalisation; iteration starts with the first nonempty element.
|
2017-09-24 22:41:45 +02:00
|
|
|
|
**
|
2017-09-24 23:42:55 +02:00
|
|
|
|
** @remark the choice of implementation layout is arbitrary and not based on evidence.
|
2017-10-01 06:08:54 +02:00
|
|
|
|
** A delegate structure with fixed inline storage looked like an interesting programming challenge.
|
2017-09-24 23:42:55 +02:00
|
|
|
|
** Using just a heap based array storage would have been viable likewise.
|
|
|
|
|
|
** @todo when UICoord is in widespread use, collect performance statistics and revisit this design.
|
2017-09-24 22:41:45 +02:00
|
|
|
|
**
|
|
|
|
|
|
** @see PathArray_test
|
|
|
|
|
|
** @see UICoord_test
|
2018-11-15 23:59:23 +01:00
|
|
|
|
** @see stage::interact::UICoord
|
2017-09-24 22:41:45 +02:00
|
|
|
|
** @see view-spec-dsl.hpp
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_PATH_ARRAY_H
|
|
|
|
|
|
#define LIB_PATH_ARRAY_H
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
|
#include "lib/symbol.hpp"
|
|
|
|
|
|
#include "lib/iter-adapter.hpp"
|
2017-09-30 00:46:52 +02:00
|
|
|
|
#include "lib/meta/variadic-helper.hpp"
|
2017-09-30 02:34:56 +02:00
|
|
|
|
#include "lib/format-obj.hpp"
|
2017-09-29 16:06:28 +02:00
|
|
|
|
#include "lib/util.hpp"
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
2017-09-29 16:06:28 +02:00
|
|
|
|
#include <algorithm>
|
2017-09-30 00:46:52 +02:00
|
|
|
|
#include <utility>
|
2017-09-24 22:41:45 +02:00
|
|
|
|
#include <string>
|
2017-09-24 23:42:55 +02:00
|
|
|
|
#include <memory>
|
|
|
|
|
|
#include <array>
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
2017-09-30 02:34:56 +02:00
|
|
|
|
namespace error = lumiera::error;
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
using std::string;
|
2017-10-01 06:08:54 +02:00
|
|
|
|
using std::forward;
|
2017-09-24 22:41:45 +02:00
|
|
|
|
using lib::Literal;
|
2017-09-29 16:06:28 +02:00
|
|
|
|
using util::unConst;
|
2017-10-01 01:30:53 +02:00
|
|
|
|
using util::isnil;
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
2017-09-29 16:06:28 +02:00
|
|
|
|
namespace con { // Implementation helper: flexible heap based extension storage....
|
2017-09-26 19:23:03 +02:00
|
|
|
|
|
2017-09-29 15:03:14 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Heap-allocated extension storage for an immutable sequence of literal strings.
|
|
|
|
|
|
* The size of the allocation is determined and fixed once, at construction time,
|
|
|
|
|
|
* derived from the number of initialisers. The first slot within the allocation
|
|
|
|
|
|
* stores this length. Extension can be _empty_ (default constructed),
|
|
|
|
|
|
* in which case no heap allocation is performed.
|
|
|
|
|
|
*/
|
|
|
|
|
|
class Extension
|
|
|
|
|
|
{
|
2017-09-30 18:32:08 +02:00
|
|
|
|
using PStorage = Literal*;
|
2017-09-29 15:03:14 +02:00
|
|
|
|
|
|
|
|
|
|
PStorage storage_;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static size_t&
|
|
|
|
|
|
size (PStorage& p)
|
|
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (p);
|
|
|
|
|
|
return reinterpret_cast<size_t&> (p[0]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-30 19:57:13 +02:00
|
|
|
|
/** allocate a copy.
|
|
|
|
|
|
* @note caller has to manage the allocated memory
|
|
|
|
|
|
* @warning call to Literal's ctor deliberately elided
|
|
|
|
|
|
*/
|
|
|
|
|
|
PStorage
|
|
|
|
|
|
newCopy() const
|
|
|
|
|
|
{
|
2017-10-02 02:18:58 +02:00
|
|
|
|
size_t siz = 1 + size (unConst(this)->storage_);
|
2017-09-30 19:57:13 +02:00
|
|
|
|
const char** alloc = new const char*[siz];
|
|
|
|
|
|
std::copy (storage_, storage_+siz, alloc);
|
|
|
|
|
|
return reinterpret_cast<PStorage> (alloc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-30 00:46:52 +02:00
|
|
|
|
|
2017-09-29 15:03:14 +02:00
|
|
|
|
public:
|
2017-09-30 19:57:13 +02:00
|
|
|
|
~Extension()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (storage_)
|
|
|
|
|
|
delete[] storage_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-06 03:38:52 +01:00
|
|
|
|
Extension() noexcept
|
2017-09-29 15:03:14 +02:00
|
|
|
|
: storage_{nullptr}
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
template<typename...ELMS>
|
|
|
|
|
|
explicit
|
2017-09-30 18:32:08 +02:00
|
|
|
|
Extension (ELMS&& ...elms)
|
2019-10-11 20:26:02 +02:00
|
|
|
|
: storage_{new Literal[1 + sizeof...(ELMS)]} // proper alignment maintained here (TICKET #1204)
|
2017-09-29 15:03:14 +02:00
|
|
|
|
{
|
|
|
|
|
|
size(storage_) = sizeof...(ELMS);
|
2017-09-30 18:32:08 +02:00
|
|
|
|
new(storage_+1) Literal[sizeof...(ELMS)] {forward<ELMS>(elms)...};
|
2017-09-29 15:03:14 +02:00
|
|
|
|
}
|
2017-09-29 16:06:28 +02:00
|
|
|
|
|
|
|
|
|
|
Extension (Extension const& r)
|
2017-09-30 19:57:13 +02:00
|
|
|
|
: storage_{r.storage_? r.newCopy() : nullptr}
|
|
|
|
|
|
{ }
|
2017-09-29 16:06:28 +02:00
|
|
|
|
|
2018-01-06 03:38:52 +01:00
|
|
|
|
Extension (Extension&& rr) noexcept
|
2017-09-29 16:06:28 +02:00
|
|
|
|
: storage_{nullptr}
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rr.storage_)
|
|
|
|
|
|
std::swap (storage_, rr.storage_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-24 19:53:07 +01:00
|
|
|
|
Extension& operator= (Extension o)
|
2017-09-29 16:06:28 +02:00
|
|
|
|
{
|
2024-11-24 19:53:07 +01:00
|
|
|
|
std::swap (storage_, o.storage_);
|
2017-09-29 16:06:28 +02:00
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-09-30 01:55:19 +02:00
|
|
|
|
operator bool() const { return not empty(); }
|
|
|
|
|
|
bool empty() const { return not storage_;}
|
|
|
|
|
|
|
2017-09-29 16:06:28 +02:00
|
|
|
|
size_t
|
|
|
|
|
|
size() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return storage_? size(unConst(this)->storage_)
|
|
|
|
|
|
: 0;
|
|
|
|
|
|
}
|
2017-09-30 02:34:56 +02:00
|
|
|
|
|
2017-09-30 18:32:08 +02:00
|
|
|
|
Literal const&
|
2017-09-30 02:34:56 +02:00
|
|
|
|
operator[] (size_t idx) const
|
|
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (storage_ and idx < size());
|
|
|
|
|
|
return storage_[1+idx];
|
|
|
|
|
|
}
|
2017-09-30 21:12:59 +02:00
|
|
|
|
|
2017-10-01 20:12:45 +02:00
|
|
|
|
size_t
|
|
|
|
|
|
indexOf (Literal const* pos) const
|
|
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (isValid (pos));
|
|
|
|
|
|
return pos - (storage_+1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-30 21:12:59 +02:00
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid (Literal const* pos) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return storage_
|
|
|
|
|
|
and storage_ < pos
|
|
|
|
|
|
and pos < storage_ + (1 + size (unConst(this)->storage_));
|
|
|
|
|
|
}
|
2017-10-01 01:30:53 +02:00
|
|
|
|
|
|
|
|
|
|
void
|
2017-10-02 02:18:58 +02:00
|
|
|
|
resizeTo (size_t cnt)
|
2017-10-01 01:30:53 +02:00
|
|
|
|
{
|
2017-10-02 02:18:58 +02:00
|
|
|
|
if (cnt > size())
|
2017-10-02 04:04:22 +02:00
|
|
|
|
{ // need more storage
|
2017-10-02 02:18:58 +02:00
|
|
|
|
if (storage_)
|
2017-10-02 04:04:22 +02:00
|
|
|
|
{ // copy content to new expanded storage
|
|
|
|
|
|
auto target = new const char* [cnt+1];
|
|
|
|
|
|
auto pos = std::copy (storage_, storage_+1+size(), target);
|
|
|
|
|
|
for ( ; pos < target+1+cnt; ++pos)
|
|
|
|
|
|
*pos = nullptr;
|
|
|
|
|
|
delete[] storage_;
|
|
|
|
|
|
storage_ = reinterpret_cast<PStorage> (target);
|
|
|
|
|
|
}
|
|
|
|
|
|
else // allocate and init empty
|
|
|
|
|
|
storage_ = new Literal [cnt+1];
|
|
|
|
|
|
|
2017-10-02 02:18:58 +02:00
|
|
|
|
size (storage_) = cnt;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2017-10-01 03:58:42 +02:00
|
|
|
|
if (not storage_) return;
|
|
|
|
|
|
if (cnt == 0)
|
2017-10-01 01:30:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
delete[] storage_;
|
|
|
|
|
|
storage_ = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
size(storage_) = cnt;
|
|
|
|
|
|
// excess elements now unreachable
|
|
|
|
|
|
// note: delete[] knows original size
|
|
|
|
|
|
}
|
2017-09-29 15:03:14 +02:00
|
|
|
|
};
|
2017-09-26 19:23:03 +02:00
|
|
|
|
}//(End)Implementation helper
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-09-30 00:46:52 +02:00
|
|
|
|
using meta::pickArg;
|
|
|
|
|
|
using meta::pickInit;
|
|
|
|
|
|
using meta::IndexSeq;
|
|
|
|
|
|
|
2017-09-24 22:41:45 +02:00
|
|
|
|
/**
|
2017-09-24 23:42:55 +02:00
|
|
|
|
* Abstraction for path-like topological coordinates.
|
|
|
|
|
|
* A sequence of Literal strings, with array-like access and
|
|
|
|
|
|
* standard iteration. Implemented as fixed size inline tuple
|
|
|
|
|
|
* with heap allocated unlimited extension space.
|
2017-10-01 06:08:54 +02:00
|
|
|
|
* @note contents are normalised on initialisation
|
|
|
|
|
|
* - leading empty elements are filled with Symbol::EMPTY
|
|
|
|
|
|
* - empty elements in the middle are replaced by "*"
|
|
|
|
|
|
* - trailing empty elements and "*" are trimmed
|
2017-09-24 22:41:45 +02:00
|
|
|
|
*/
|
2017-09-24 23:42:55 +02:00
|
|
|
|
template<size_t chunk_size>
|
2017-09-24 22:41:45 +02:00
|
|
|
|
class PathArray
|
|
|
|
|
|
{
|
2017-09-30 01:55:19 +02:00
|
|
|
|
static_assert (0 < chunk_size, "PathArray chunk_size must be nonempty");
|
2017-10-01 06:08:54 +02:00
|
|
|
|
|
2017-09-30 18:32:08 +02:00
|
|
|
|
using CcP = const char*;
|
|
|
|
|
|
using LiteralArray = std::array<Literal, chunk_size>;
|
2017-09-29 15:03:14 +02:00
|
|
|
|
|
2017-09-29 16:06:28 +02:00
|
|
|
|
LiteralArray elms_;
|
|
|
|
|
|
con::Extension tail_;
|
2017-09-24 23:42:55 +02:00
|
|
|
|
|
2017-09-30 00:46:52 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* @internal delegate ctor to place the initialiser arguments appropriately
|
|
|
|
|
|
* @remarks the two index sequences passed by pattern match determine which
|
|
|
|
|
|
* arguments go to the inline array, and which go to heap allocated extension.
|
|
|
|
|
|
* The inline array has fixed size an is thus filled with trailing `NULL` ptrs,
|
|
|
|
|
|
* which is achieved with the help of meta::pickInit(). The con::Extension
|
|
|
|
|
|
* is an embedded smart-ptr, which, when receiving additional tail arguments,
|
|
|
|
|
|
* will place and manage them within a heap allocated array.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<size_t...prefix, size_t...rest, typename...ARGS>
|
|
|
|
|
|
PathArray (IndexSeq<prefix...>
|
|
|
|
|
|
,IndexSeq<rest...>
|
|
|
|
|
|
,ARGS&& ...args)
|
2017-09-30 18:32:08 +02:00
|
|
|
|
: elms_{pickInit<prefix,CcP> (forward<ARGS>(args)...) ...}
|
2017-09-30 00:46:52 +02:00
|
|
|
|
, tail_{pickArg<rest> (forward<ARGS>(args)...) ...}
|
2017-09-24 22:41:45 +02:00
|
|
|
|
{
|
2017-09-30 00:46:52 +02:00
|
|
|
|
this->normalise();
|
2017-09-24 22:41:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-30 00:46:52 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* @internal rebinding helper for building sequences of index numbers,
|
|
|
|
|
|
* to route the initialiser arguments into the corresponding storage
|
|
|
|
|
|
* - the leading (`chunk_size`) arguments go into the LiteralArray inline
|
|
|
|
|
|
* - all the remaining arguments go into heap allocated extension storage
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
|
struct Split
|
2017-09-24 23:42:55 +02:00
|
|
|
|
{
|
2017-09-30 00:46:52 +02:00
|
|
|
|
using Prefix = typename meta::BuildIndexSeq<chunk_size>::Ascending;
|
|
|
|
|
|
using Rest = typename meta::BuildIdxIter<ARGS...>::template After<chunk_size>;
|
|
|
|
|
|
};
|
2017-10-01 06:08:54 +02:00
|
|
|
|
|
2017-09-30 00:46:52 +02:00
|
|
|
|
public:
|
|
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
|
explicit
|
|
|
|
|
|
PathArray (ARGS&& ...args)
|
|
|
|
|
|
: PathArray(typename Split<ARGS...>::Prefix()
|
|
|
|
|
|
,typename Split<ARGS...>::Rest()
|
|
|
|
|
|
,forward<ARGS> (args)...)
|
|
|
|
|
|
{ }
|
2017-09-24 23:42:55 +02:00
|
|
|
|
|
2017-09-30 00:46:52 +02:00
|
|
|
|
PathArray(PathArray&&) = default;
|
|
|
|
|
|
PathArray(PathArray const&) = default;
|
|
|
|
|
|
PathArray(PathArray& o) : PathArray((PathArray const&)o) { }
|
|
|
|
|
|
PathArray& operator= (PathArray const&) = default;
|
|
|
|
|
|
PathArray& operator= (PathArray &&) = default;
|
|
|
|
|
|
////////////////////////TICKET #963 Forwarding shadows copy operations
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
|
size() const
|
|
|
|
|
|
{
|
2017-09-30 01:55:19 +02:00
|
|
|
|
return tail_? chunk_size + tail_.size()
|
|
|
|
|
|
: findInlineEnd() - elms_.begin();
|
2017-09-24 22:41:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-14 17:02:34 +02:00
|
|
|
|
size_t
|
|
|
|
|
|
leafLevel() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return empty()? 0 : size()-1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-24 22:41:45 +02:00
|
|
|
|
bool
|
|
|
|
|
|
empty() const
|
|
|
|
|
|
{
|
2017-09-30 18:32:08 +02:00
|
|
|
|
return not elms_[0]; // normalise() ensures nonnull unless completely empty
|
2017-09-24 22:41:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-01 06:08:54 +02:00
|
|
|
|
/** joins nonempty content, separated by slash */
|
2017-09-24 22:41:45 +02:00
|
|
|
|
operator string() const;
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-01 06:08:54 +02:00
|
|
|
|
/** Array style indexed access.
|
|
|
|
|
|
* @throws error::Invalid on bound violation
|
|
|
|
|
|
* @return reference to the normalised content
|
|
|
|
|
|
* @note returns Symbol::EMPTY for leading empty elements,
|
|
|
|
|
|
* even while iteration will skip such entries.
|
|
|
|
|
|
*/
|
2017-09-30 20:03:05 +02:00
|
|
|
|
Literal const&
|
|
|
|
|
|
operator[] (size_t idx) const
|
2017-09-24 22:41:45 +02:00
|
|
|
|
{
|
2017-10-01 01:30:53 +02:00
|
|
|
|
Literal* elm = unConst(this)->getPosition (idx);
|
2017-09-30 02:34:56 +02:00
|
|
|
|
if (not elm)
|
|
|
|
|
|
throw error::Invalid ("Accessing index "+util::toString(idx)
|
|
|
|
|
|
+" on PathArray of size "+ util::toString(size())
|
|
|
|
|
|
,error::LUMIERA_ERROR_INDEX_BOUNDS);
|
2017-09-30 20:03:05 +02:00
|
|
|
|
return *elm;
|
2017-09-24 22:41:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-01 19:44:55 +02:00
|
|
|
|
/** reverse lookup of actual path content
|
|
|
|
|
|
* @param content reference to actual content _residing within the path_
|
|
|
|
|
|
* @return index position of the content within the path
|
|
|
|
|
|
* @throws error::Invalid when the given storage location
|
|
|
|
|
|
* is outside the data content storage of this path
|
|
|
|
|
|
*/
|
|
|
|
|
|
size_t
|
|
|
|
|
|
indexOf (Literal const& content) const
|
|
|
|
|
|
{
|
2017-10-01 20:12:45 +02:00
|
|
|
|
if (elms_.begin() <= &content and &content < elms_.end())
|
|
|
|
|
|
return &content - elms_.begin();
|
|
|
|
|
|
if (tail_.isValid (&content))
|
|
|
|
|
|
return chunk_size + tail_.indexOf (&content);
|
|
|
|
|
|
|
|
|
|
|
|
throw error::Invalid ("Referred content "+util::toString(&content)
|
|
|
|
|
|
+" is not located within the storage of PathArray "
|
|
|
|
|
|
+string(*this));
|
2017-10-01 19:44:55 +02:00
|
|
|
|
}
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
2017-10-01 06:08:54 +02:00
|
|
|
|
|
2017-09-24 22:41:45 +02:00
|
|
|
|
protected: /* ==== Iteration control API for IterAdapter ==== */
|
|
|
|
|
|
|
|
|
|
|
|
/** Implementation of Iteration-logic: pull next element. */
|
|
|
|
|
|
friend void
|
2017-10-01 03:25:33 +02:00
|
|
|
|
iterNext (const PathArray*, const Literal*& pos)
|
2017-09-30 21:12:59 +02:00
|
|
|
|
{
|
|
|
|
|
|
++pos;
|
|
|
|
|
|
}
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
/** Implementation of Iteration-logic: detect iteration end. */
|
|
|
|
|
|
friend bool
|
2017-09-30 21:12:59 +02:00
|
|
|
|
checkPoint (const PathArray* src, const Literal*& pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (src);
|
2017-10-01 03:58:42 +02:00
|
|
|
|
if (pos == src->elms_.end() and src->tail_)
|
2017-09-30 21:12:59 +02:00
|
|
|
|
pos = &src->tail_[0];
|
|
|
|
|
|
else
|
|
|
|
|
|
if (not src->isValid (pos))
|
|
|
|
|
|
{
|
|
|
|
|
|
pos = nullptr;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
ENSURE ( (src->elms_.begin() <= pos and pos < src->elms_.end())
|
|
|
|
|
|
or src->tail_.isValid(pos));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
public:
|
2017-09-30 21:12:59 +02:00
|
|
|
|
using const_iterator = lib::IterAdapter<Literal const*, PathArray const*>;
|
|
|
|
|
|
using iterator = const_iterator;
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
2018-02-16 06:48:52 +01:00
|
|
|
|
using value_type = Literal;
|
|
|
|
|
|
using reference = Literal&;
|
|
|
|
|
|
using const_reference = Literal const&;
|
|
|
|
|
|
|
2017-10-01 03:58:42 +02:00
|
|
|
|
/** @remark iteration is defined to begin with real content */
|
|
|
|
|
|
iterator begin() const { return firstNonempty(); }
|
2017-09-30 21:12:59 +02:00
|
|
|
|
iterator end() const { return iterator{}; }
|
2017-09-24 22:41:45 +02:00
|
|
|
|
|
|
|
|
|
|
friend iterator begin(PathArray const& pa) { return pa.begin();}
|
|
|
|
|
|
friend iterator end (PathArray const& pa) { return pa.end(); }
|
|
|
|
|
|
|
2017-10-01 01:30:53 +02:00
|
|
|
|
|
2017-10-01 06:08:54 +02:00
|
|
|
|
|
2017-10-01 01:30:53 +02:00
|
|
|
|
private: /* ===== implementation details ===== */
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid (Literal const* pos) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return pos
|
|
|
|
|
|
and (tail_.isValid(pos)
|
2017-10-01 03:58:42 +02:00
|
|
|
|
or (elms_.begin() <= pos and pos < elms_.end()
|
|
|
|
|
|
and *pos));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
iterator
|
|
|
|
|
|
firstNonempty () const
|
|
|
|
|
|
{
|
|
|
|
|
|
iterator startPos{this, elms_.begin()};
|
|
|
|
|
|
while (startPos && isnil (*startPos))
|
|
|
|
|
|
++startPos;
|
|
|
|
|
|
return startPos;
|
2017-10-01 01:30:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-01 06:08:54 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* find _effective end_ of data in the inline array,
|
|
|
|
|
|
* i.e. the position _behind_ the last usable content
|
2017-09-30 01:55:19 +02:00
|
|
|
|
*/
|
2017-09-30 18:32:08 +02:00
|
|
|
|
Literal const*
|
2017-09-30 01:55:19 +02:00
|
|
|
|
findInlineEnd() const
|
|
|
|
|
|
{
|
2017-09-30 18:32:08 +02:00
|
|
|
|
Literal const* lastPos = elms_.begin() + chunk_size-1;
|
|
|
|
|
|
Literal const* beforeStart = elms_.begin() - 1;
|
2017-09-30 01:55:19 +02:00
|
|
|
|
while (lastPos != beforeStart and not *lastPos)
|
|
|
|
|
|
--lastPos;
|
|
|
|
|
|
return ++lastPos; // at start if empty, else one behind the last
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-02 14:03:04 +01:00
|
|
|
|
|
|
|
|
|
|
/* ===== manipulation by UICoord and Builder subclasses ===== */
|
2017-10-01 21:10:18 +02:00
|
|
|
|
protected:
|
2017-10-01 01:30:53 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* @internal access content element by index
|
|
|
|
|
|
* @return pointer to storage, `null` if out of bounds
|
|
|
|
|
|
*/
|
|
|
|
|
|
Literal*
|
|
|
|
|
|
getPosition (size_t idx)
|
2017-09-24 22:41:45 +02:00
|
|
|
|
{
|
2017-10-01 01:30:53 +02:00
|
|
|
|
Literal const* elm =nullptr;
|
|
|
|
|
|
if (idx < chunk_size)
|
2017-10-01 21:54:35 +02:00
|
|
|
|
elm = elms_[idx]? &elms_[idx] : nullptr;
|
2017-10-01 01:30:53 +02:00
|
|
|
|
else
|
|
|
|
|
|
if (idx-chunk_size < tail_.size())
|
|
|
|
|
|
elm = &tail_[idx-chunk_size];
|
|
|
|
|
|
|
|
|
|
|
|
return const_cast<Literal*> (elm);
|
2017-09-24 22:41:45 +02:00
|
|
|
|
}
|
2017-09-30 00:46:52 +02:00
|
|
|
|
|
2017-10-02 02:18:58 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* @internal ensure storage for the indicated position exists
|
|
|
|
|
|
* @return pointer to the storage, either existing or reallocated
|
|
|
|
|
|
*/
|
|
|
|
|
|
Literal*
|
2017-10-02 04:04:22 +02:00
|
|
|
|
expandPosition (size_t idx)
|
2017-10-02 02:18:58 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (chunk_size <= idx and size() <= idx)
|
|
|
|
|
|
tail_.resizeTo(idx+1 - chunk_size);
|
|
|
|
|
|
if (idx < chunk_size)
|
|
|
|
|
|
return elms_.begin() + idx;
|
|
|
|
|
|
|
|
|
|
|
|
ENSURE (idx-chunk_size < tail_.size());
|
|
|
|
|
|
return const_cast<Literal*> (&tail_[idx-chunk_size]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-01 01:30:53 +02:00
|
|
|
|
/** @internal force new content into the given entry */
|
|
|
|
|
|
void
|
|
|
|
|
|
setContent (Literal* pos, const char* val)
|
|
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (pos);
|
|
|
|
|
|
*reinterpret_cast<const char**> (pos) = val;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-30 02:59:56 +01:00
|
|
|
|
void
|
|
|
|
|
|
truncateTo (size_t newSize)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (newSize < chunk_size)
|
|
|
|
|
|
{
|
|
|
|
|
|
Literal* end = elms_.end();
|
|
|
|
|
|
Literal* pos = getPosition(newSize);
|
2017-12-27 01:57:15 +01:00
|
|
|
|
if (pos)
|
|
|
|
|
|
for ( ; pos!=end; ++pos)
|
|
|
|
|
|
setContent (pos, nullptr);
|
2017-10-30 02:59:56 +01:00
|
|
|
|
tail_.resizeTo (0);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
if (newSize-chunk_size < tail_.size())
|
|
|
|
|
|
tail_.resizeTo (newSize - chunk_size);
|
|
|
|
|
|
// otherwise: keep current size, don't grow
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-01 01:30:53 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* 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 "*"
|
|
|
|
|
|
*/
|
2017-09-30 00:46:52 +02:00
|
|
|
|
void
|
|
|
|
|
|
normalise()
|
|
|
|
|
|
{
|
2017-10-01 03:58:42 +02:00
|
|
|
|
if (size() == 0) return;
|
2017-10-01 01:30:53 +02:00
|
|
|
|
const char* fill = Symbol::EMPTY;
|
|
|
|
|
|
|
|
|
|
|
|
Literal* end = elms_.end();
|
2017-10-01 03:58:42 +02:00
|
|
|
|
Literal* pos = elms_.begin();
|
|
|
|
|
|
for ( ; pos!=end; ++pos)
|
2017-10-01 01:30:53 +02:00
|
|
|
|
if (isnil (*pos))
|
|
|
|
|
|
setContent (pos, fill);
|
|
|
|
|
|
else
|
|
|
|
|
|
if (fill==Symbol::EMPTY)
|
2017-10-02 23:06:23 +02:00
|
|
|
|
fill = Symbol::ANY;
|
2017-10-01 01:30:53 +02:00
|
|
|
|
|
2017-10-01 03:58:42 +02:00
|
|
|
|
if (tail_)
|
|
|
|
|
|
{
|
|
|
|
|
|
// normalise data in extension storage
|
|
|
|
|
|
pos = getPosition (chunk_size);
|
|
|
|
|
|
end = pos + tail_.size();
|
|
|
|
|
|
for ( ; pos!=end; ++pos)
|
|
|
|
|
|
if (isnil (*pos))
|
|
|
|
|
|
setContent (pos, fill);
|
|
|
|
|
|
else
|
|
|
|
|
|
if (fill==Symbol::EMPTY)
|
2017-10-02 23:06:23 +02:00
|
|
|
|
fill = Symbol::ANY;
|
2017-10-01 03:58:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-01 01:30:53 +02:00
|
|
|
|
size_t idx = size();
|
2017-10-01 03:58:42 +02:00
|
|
|
|
// trim trailing fill
|
|
|
|
|
|
while (idx and fill == *getPosition (--idx))
|
2017-10-01 01:30:53 +02:00
|
|
|
|
setContent (getPosition(idx), nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
if (idx >= chunk_size-1)
|
2017-10-02 02:18:58 +02:00
|
|
|
|
tail_.resizeTo (idx+1 - chunk_size);
|
2017-10-01 03:58:42 +02:00
|
|
|
|
else
|
2017-10-02 02:18:58 +02:00
|
|
|
|
tail_.resizeTo (0);
|
2017-09-30 00:46:52 +02:00
|
|
|
|
}
|
2017-09-24 22:41:45 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-01 01:30:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
2017-09-24 23:42:55 +02:00
|
|
|
|
template<size_t chunk_size>
|
2017-09-24 22:41:45 +02:00
|
|
|
|
inline
|
2017-09-24 23:42:55 +02:00
|
|
|
|
PathArray<chunk_size>::operator string() const
|
2017-09-24 22:41:45 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (this->empty()) return "";
|
|
|
|
|
|
|
|
|
|
|
|
string buff;
|
|
|
|
|
|
size_t expectedLen = this->size() * 10;
|
2017-10-01 01:30:53 +02:00
|
|
|
|
buff.reserve (expectedLen); // heuristic
|
2017-09-24 22:41:45 +02:00
|
|
|
|
for (Literal elm : *this)
|
|
|
|
|
|
buff += elm + "/";
|
|
|
|
|
|
|
|
|
|
|
|
// chop off last delimiter
|
|
|
|
|
|
size_t len = buff.length();
|
|
|
|
|
|
ASSERT (len >= 1);
|
|
|
|
|
|
buff.resize(len-1);
|
|
|
|
|
|
return buff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-10-01 06:08:54 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Equality comparison of arbitrary PathArray objects
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<size_t cl, size_t cr>
|
|
|
|
|
|
bool
|
|
|
|
|
|
operator== (PathArray<cl> const& l, PathArray<cr> const& r)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (l.size() != r.size()) return false;
|
|
|
|
|
|
|
|
|
|
|
|
typename PathArray<cl>::iterator lp = l.begin();
|
|
|
|
|
|
typename PathArray<cl>::iterator rp = r.begin();
|
|
|
|
|
|
while (lp and rp)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (*lp != *rp) return false;
|
|
|
|
|
|
++lp;
|
|
|
|
|
|
++rp;
|
|
|
|
|
|
}
|
|
|
|
|
|
return isnil(lp) and isnil(rp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<size_t cl, size_t cr>
|
|
|
|
|
|
bool
|
|
|
|
|
|
operator!= (PathArray<cl> const& l, PathArray<cr> const& r)
|
|
|
|
|
|
{
|
|
|
|
|
|
return not (l == r);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-09-24 22:41:45 +02:00
|
|
|
|
}// namespace lib
|
|
|
|
|
|
#endif /*LIB_PATH_ARRAY_H*/
|