Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
path-array.hpp
Go to the documentation of this file.
1/*
2 PATH-ARRAY.hpp - sequence of path-like component-IDs in fixed storage
3
4 Copyright (C)
5 2017, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12*/
13
14
39#ifndef LIB_PATH_ARRAY_H
40#define LIB_PATH_ARRAY_H
41
42#include "lib/error.hpp"
43#include "lib/symbol.hpp"
44#include "lib/iter-adapter.hpp"
46#include "lib/format-obj.hpp"
47#include "lib/util.hpp"
48
49#include <algorithm>
50#include <utility>
51#include <string>
52#include <memory>
53#include <array>
54
55
56namespace lib {
57 namespace error = lumiera::error;
58
59 using std::string;
60 using std::forward;
61 using lib::Literal;
62 using util::unConst;
63 using util::isnil;
64
65 namespace con { // Implementation helper: flexible heap based extension storage....
66
75 {
76 using PStorage = Literal*;
77
79
80
81 static size_t&
83 {
84 REQUIRE (p);
85 return reinterpret_cast<size_t&> (p[0]);
86 }
87
93 newCopy() const
94 {
95 size_t siz = 1 + size (unConst(this)->storage_);
96 const char** alloc = new const char*[siz];
97 std::copy (storage_, storage_+siz, alloc);
98 return reinterpret_cast<PStorage> (alloc);
99 }
100
101
102 public:
104 {
105 if (storage_)
106 delete[] storage_;
107 }
108
109 Extension() noexcept
110 : storage_{nullptr}
111 { }
112
113 template<typename...ELMS>
114 explicit
115 Extension (ELMS&& ...elms)
116 : storage_{new Literal[1 + sizeof...(ELMS)]} // proper alignment maintained here (TICKET #1204)
117 {
118 size(storage_) = sizeof...(ELMS);
119 new(storage_+1) Literal[sizeof...(ELMS)] {forward<ELMS>(elms)...};
120 }
121
123 : storage_{r.storage_? r.newCopy() : nullptr}
124 { }
125
126 Extension (Extension&& rr) noexcept
127 : storage_{nullptr}
128 {
129 if (rr.storage_)
130 std::swap (storage_, rr.storage_);
131 }
132
134 {
135 std::swap (storage_, o.storage_);
136 return *this;
137 }
138
139
140
141 operator bool() const { return not empty(); }
142 bool empty() const { return not storage_;}
143
144 size_t
145 size() const
146 {
147 return storage_? size(unConst(this)->storage_)
148 : 0;
149 }
150
151 Literal const&
152 operator[] (size_t idx) const
153 {
154 REQUIRE (storage_ and idx < size());
155 return storage_[1+idx];
156 }
157
158 size_t
159 indexOf (Literal const* pos) const
160 {
161 REQUIRE (isValid (pos));
162 return pos - (storage_+1);
163 }
164
165
166 bool
167 isValid (Literal const* pos) const
168 {
169 return storage_
170 and storage_ < pos
171 and pos < storage_ + (1 + size (unConst(this)->storage_));
172 }
173
174 void
175 resizeTo (size_t cnt)
176 {
177 if (cnt > size())
178 { // need more storage
179 if (storage_)
180 { // copy content to new expanded storage
181 auto target = new const char* [cnt+1];
182 auto pos = std::copy (storage_, storage_+1+size(), target);
183 for ( ; pos < target+1+cnt; ++pos)
184 *pos = nullptr;
185 delete[] storage_;
186 storage_ = reinterpret_cast<PStorage> (target);
187 }
188 else // allocate and init empty
189 storage_ = new Literal [cnt+1];
190
191 size (storage_) = cnt;
192 return;
193 }
194 if (not storage_) return;
195 if (cnt == 0)
196 {
197 delete[] storage_;
198 storage_ = nullptr;
199 }
200 else
201 size(storage_) = cnt;
202 // excess elements now unreachable
203 // note: delete[] knows original size
204 }
205 };
206 }//(End)Implementation helper
207
208
209
210 using meta::pickArg;
211 using meta::pickInit;
212 using meta::IndexSeq;
213
224 template<size_t chunk_size>
226 {
227 static_assert (0 < chunk_size, "PathArray chunk_size must be nonempty");
228
229 using LiteralArray = std::array<Literal, chunk_size>;
230
233
243 template<size_t...prefix, size_t...rest, typename...ARGS>
246 ,ARGS&& ...args)
247 : elms_{pickInit<prefix, CStr> (forward<ARGS>(args)...) ...}
248 , tail_{pickArg<rest> (forward<ARGS>(args)...) ...}
249 {
250 this->normalise();
251 }
252
259 template<typename...ARGS>
260 struct Split
261 {
263 using Rest = meta::BuildIdxIter<ARGS...>::template After<chunk_size>;
264 };
265
266 public:
267 template<typename...ARGS>
268 explicit
269 PathArray (ARGS&& ...args)
270 : PathArray(typename Split<ARGS...>::Prefix()
271 ,typename Split<ARGS...>::Rest()
272 ,forward<ARGS> (args)...)
273 { }
274
275 PathArray(PathArray&&) = default;
276 PathArray(PathArray const&) = default;
278 PathArray& operator= (PathArray const&) = default;
281
282
283 size_t
284 size() const
285 {
286 return tail_? chunk_size + tail_.size()
287 : findInlineEnd() - elms_.begin();
288 }
289
290 size_t
291 leafLevel() const
292 {
293 return empty()? 0 : size()-1;
294 }
295
296 bool
297 empty() const
298 {
299 return not elms_[0]; // normalise() ensures nonnull unless completely empty
300 }
301
303 operator string() const;
304
305
312 Literal const&
313 operator[] (size_t idx) const
314 {
315 Literal* elm = unConst(this)->getPosition (idx);
316 if (not elm)
317 throw error::Invalid ("Accessing index "+util::toString(idx)
318 +" on PathArray of size "+ util::toString(size())
319 ,error::LUMIERA_ERROR_INDEX_BOUNDS);
320 return *elm;
321 }
322
329 size_t
330 indexOf (Literal const& content) const
331 {
332 if (elms_.begin() <= &content and &content < elms_.end())
333 return &content - elms_.begin();
334 if (tail_.isValid (&content))
335 return chunk_size + tail_.indexOf (&content);
336
337 throw error::Invalid ("Referred content "+util::toString(&content)
338 +" is not located within the storage of PathArray "
339 +string(*this));
340 }
341
342
343 protected: /* ==== Iteration control API for IterAdapter ==== */
344
346 friend void
347 iterNext (const PathArray*, const Literal*& pos)
348 {
349 ++pos;
350 }
351
353 friend bool
354 checkPoint (const PathArray* src, const Literal*& pos)
355 {
356 REQUIRE (src);
357 if (pos == src->elms_.end() and src->tail_)
358 pos = &src->tail_[0];
359 else
360 if (not src->isValid (pos))
361 {
362 pos = nullptr;
363 return false;
364 }
365 ENSURE ( (src->elms_.begin() <= pos and pos < src->elms_.end())
366 or src->tail_.isValid(pos));
367 return true;
368 }
369
370 public:
373
376 using const_reference = Literal const&;
377
379 iterator begin() const { return firstNonempty(); }
380 iterator end() const { return iterator{}; }
381
382 friend iterator begin(PathArray const& pa) { return pa.begin();}
383 friend iterator end (PathArray const& pa) { return pa.end(); }
384
385
386
387 private: /* ===== implementation details ===== */
388
389 bool
390 isValid (Literal const* pos) const
391 {
392 return pos
393 and (tail_.isValid(pos)
394 or (elms_.begin() <= pos and pos < elms_.end()
395 and *pos));
396 }
397
400 {
401 iterator startPos{this, elms_.begin()};
402 while (startPos and isnil (*startPos))
403 ++startPos;
404 return startPos;
405 }
406
411 Literal const*
413 {
414 Literal const* lastPos = elms_.begin() + chunk_size-1;
415 Literal const* beforeStart = elms_.begin() - 1;
416 while (lastPos != beforeStart and not *lastPos)
417 --lastPos;
418 return ++lastPos; // at start if empty, else one behind the last
419 }
420
421
422 /* ===== manipulation by UICoord and Builder subclasses ===== */
423 protected:
428 Literal*
429 getPosition (size_t idx)
430 {
431 Literal const* elm =nullptr;
432 if (idx < chunk_size)
433 elm = elms_[idx]? &elms_[idx] : nullptr;
434 else
435 if (idx-chunk_size < tail_.size())
436 elm = &tail_[idx-chunk_size];
437
438 return const_cast<Literal*> (elm);
439 }
440
445 Literal*
446 expandPosition (size_t idx)
447 {
448 if (chunk_size <= idx and size() <= idx)
449 tail_.resizeTo(idx+1 - chunk_size);
450 if (idx < chunk_size)
451 return elms_.begin() + idx;
452
453 ENSURE (idx-chunk_size < tail_.size());
454 return const_cast<Literal*> (&tail_[idx-chunk_size]);
455 }
456
458 void
459 setContent (Literal* pos, const char* val)
460 {
461 REQUIRE (pos);
462 *reinterpret_cast<const char**> (pos) = val;
463 }
464
465 void
466 truncateTo (size_t newSize)
467 {
468 if (newSize < chunk_size)
469 {
470 Literal* end = elms_.end();
471 Literal* pos = getPosition(newSize);
472 if (pos)
473 for ( ; pos!=end; ++pos)
474 setContent (pos, nullptr);
475 tail_.resizeTo (0);
476 }
477 else
478 if (newSize-chunk_size < tail_.size())
479 tail_.resizeTo (newSize - chunk_size);
480 // otherwise: keep current size, don't grow
481 }
482
490 void
492 {
493 if (size() == 0) return;
494 CStr fill = Symbol::EMPTY;
495
496 Literal* end = elms_.end();
497 Literal* pos = elms_.begin();
498 for ( ; pos!=end; ++pos)
499 if (isnil (*pos))
500 setContent (pos, fill);
501 else
502 if (fill==Symbol::EMPTY)
503 fill = Symbol::ANY;
504
505 if (tail_)
506 {
507 // normalise data in extension storage
508 pos = getPosition (chunk_size);
509 end = pos + tail_.size();
510 for ( ; pos!=end; ++pos)
511 if (isnil (*pos))
512 setContent (pos, fill);
513 else
514 if (fill==Symbol::EMPTY)
515 fill = Symbol::ANY;
516 }
517
518 size_t idx = size();
519 // trim trailing fill
520 while (idx and fill == *getPosition (--idx))
521 setContent (getPosition(idx), nullptr);
522
523 if (idx >= chunk_size-1)
524 tail_.resizeTo (idx+1 - chunk_size);
525 else
526 tail_.resizeTo (0);
527 }
528 };
529
530
531
532
533 template<size_t chunk_size>
534 inline
536 {
537 if (this->empty()) return "";
538
539 string buff;
540 size_t expectedLen = this->size() * 10;
541 buff.reserve (expectedLen); // heuristic
542 for (Literal elm : *this)
543 buff += elm + "/";
544
545 // chop off last delimiter
546 size_t len = buff.length();
547 ASSERT (len >= 1);
548 buff.resize(len-1);
549 return buff;
550 }
551
552
553
557 template<size_t cl, size_t cr>
558 bool
560 {
561 if (l.size() != r.size()) return false;
562
563 auto lp = l.begin();
564 auto rp = r.begin();
565 while (lp and rp)
566 {
567 if (*lp != *rp) return false;
568 ++lp;
569 ++rp;
570 }
571 return isnil(lp) and isnil(rp);
572 }
573
574 template<size_t cl, size_t cr>
575 bool
577 {
578 return not (l == r);
579 }
580
581
582
583}// namespace lib
584#endif /*LIB_PATH_ARRAY_H*/
Adapter for building an implementation of the »Lumiera Forward Iterator« concept.
Inline string literal.
Definition symbol.hpp:78
Abstraction for path-like topological coordinates.
Literal * getPosition(size_t idx)
size_t leafLevel() const
iterator begin() const
size_t indexOf(Literal const &content) const
reverse lookup of actual path content
lib::IterAdapter< Literal const *, PathArray const * > const_iterator
size_t size() const
PathArray(PathArray const &)=default
meta::BuildIndexSeq< chunk_size >::Ascending Prefix
iterator firstNonempty() const
bool empty() const
std::array< Literal, chunk_size > LiteralArray
iterator end() const
meta::BuildIdxIter< ARGS... >::template After< chunk_size > Rest
Literal const & operator[](size_t idx) const
Array style indexed access.
void truncateTo(size_t newSize)
bool isValid(Literal const *pos) const
PathArray(IndexSeq< prefix... >, IndexSeq< rest... >, ARGS &&...args)
LiteralArray elms_
friend iterator end(PathArray const &pa)
Literal const * findInlineEnd() const
find effective end of data in the inline array, i.e.
Literal const & const_reference
con::Extension tail_
Literal * expandPosition(size_t idx)
PathArray(ARGS &&...args)
PathArray(PathArray &o)
PathArray(PathArray &&)=default
const_iterator iterator
void normalise()
establish the contract of PathArray
PathArray & operator=(PathArray const &)=default
friend bool checkPoint(const PathArray *src, const Literal *&pos)
Implementation of Iteration-logic: detect iteration end.
void setContent(Literal *pos, const char *val)
friend iterator begin(PathArray const &pa)
friend void iterNext(const PathArray *, const Literal *&pos)
Implementation of Iteration-logic: pull next element.
static Symbol ANY
Definition symbol.hpp:122
static Symbol EMPTY
Definition symbol.hpp:123
Heap-allocated extension storage for an immutable sequence of literal strings.
Extension & operator=(Extension o)
size_t size() const
size_t indexOf(Literal const *pos) const
Extension(Extension &&rr) noexcept
Literal const & operator[](size_t idx) const
bool isValid(Literal const *pos) const
Extension(Extension const &r)
static size_t & size(PStorage &p)
void resizeTo(size_t cnt)
Extension(ELMS &&...elms)
PStorage newCopy() const
allocate a copy.
Lumiera error handling (C++ interface).
const char * CStr
Definition error.hpp:42
Simple functions to represent objects, for debugging and diagnostics.
Helper template(s) for creating Lumiera Forward Iterators.
constexpr auto pickArg(ARGS &&... args)
Helper to single out one argument from a variadic argument pack.
constexpr auto pickInit(ARGS &&... args)
Helper to pick one initialisation argument from a variadic argument pack, falling back to a default c...
BuildIndexSeq< n-1 >::Ascending::template AppendElm< n-1 > Ascending
Hold a sequence of index numbers as template parameters.
Implementation namespace for support and library code.
bool operator==(PtrDerefIter< I1 > const &il, PtrDerefIter< I2 > const &ir)
Supporting equality comparisons...
bool operator!=(PtrDerefIter< I1 > const &il, PtrDerefIter< I2 > const &ir)
LumieraError< LERR_(INVALID)> Invalid
Definition error.hpp:211
std::string toString(TY const &val) noexcept
get some string representation of any object, reliably.
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
bool isnil(lib::time::Duration const &dur)
build a sequence of index numbers based on a type sequence
Marker types to indicate a literal string and a Symbol.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Metaprogramming with type sequences based on variadic template parameters.