Lumiera  0.pre.03
»edit your freedom«
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 
56 namespace 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 
74  class Extension
75  {
76  using PStorage = Literal*;
77 
78  PStorage storage_;
79 
80 
81  static size_t&
82  size (PStorage& p)
83  {
84  REQUIRE (p);
85  return reinterpret_cast<size_t&> (p[0]);
86  }
87 
92  PStorage
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:
103  ~Extension()
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 
122  Extension (Extension const& r)
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 
133  Extension& operator= (Extension o)
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>
225  class PathArray
226  {
227  static_assert (0 < chunk_size, "PathArray chunk_size must be nonempty");
228 
229  using CcP = const char*;
230  using LiteralArray = std::array<Literal, chunk_size>;
231 
232  LiteralArray elms_;
233  con::Extension tail_;
234 
244  template<size_t...prefix, size_t...rest, typename...ARGS>
247  ,ARGS&& ...args)
248  : elms_{pickInit<prefix,CcP> (forward<ARGS>(args)...) ...}
249  , tail_{pickArg<rest> (forward<ARGS>(args)...) ...}
250  {
251  this->normalise();
252  }
253 
260  template<typename...ARGS>
261  struct Split
262  {
263  using Prefix = typename meta::BuildIndexSeq<chunk_size>::Ascending;
264  using Rest = typename meta::BuildIdxIter<ARGS...>::template After<chunk_size>;
265  };
266 
267  public:
268  template<typename...ARGS>
269  explicit
270  PathArray (ARGS&& ...args)
271  : PathArray(typename Split<ARGS...>::Prefix()
272  ,typename Split<ARGS...>::Rest()
273  ,forward<ARGS> (args)...)
274  { }
275 
276  PathArray(PathArray&&) = default;
277  PathArray(PathArray const&) = default;
278  PathArray(PathArray& o) : PathArray((PathArray const&)o) { }
279  PathArray& operator= (PathArray const&) = default;
280  PathArray& operator= (PathArray &&) = default;
282 
283 
284  size_t
285  size() const
286  {
287  return tail_? chunk_size + tail_.size()
288  : findInlineEnd() - elms_.begin();
289  }
290 
291  size_t
292  leafLevel() const
293  {
294  return empty()? 0 : size()-1;
295  }
296 
297  bool
298  empty() const
299  {
300  return not elms_[0]; // normalise() ensures nonnull unless completely empty
301  }
302 
304  operator string() const;
305 
306 
313  Literal const&
314  operator[] (size_t idx) const
315  {
316  Literal* elm = unConst(this)->getPosition (idx);
317  if (not elm)
318  throw error::Invalid ("Accessing index "+util::toString(idx)
319  +" on PathArray of size "+ util::toString(size())
320  ,error::LUMIERA_ERROR_INDEX_BOUNDS);
321  return *elm;
322  }
323 
330  size_t
331  indexOf (Literal const& content) const
332  {
333  if (elms_.begin() <= &content and &content < elms_.end())
334  return &content - elms_.begin();
335  if (tail_.isValid (&content))
336  return chunk_size + tail_.indexOf (&content);
337 
338  throw error::Invalid ("Referred content "+util::toString(&content)
339  +" is not located within the storage of PathArray "
340  +string(*this));
341  }
342 
343 
344  protected: /* ==== Iteration control API for IterAdapter ==== */
345 
347  friend void
348  iterNext (const PathArray*, const Literal*& pos)
349  {
350  ++pos;
351  }
352 
354  friend bool
355  checkPoint (const PathArray* src, const Literal*& pos)
356  {
357  REQUIRE (src);
358  if (pos == src->elms_.end() and src->tail_)
359  pos = &src->tail_[0];
360  else
361  if (not src->isValid (pos))
362  {
363  pos = nullptr;
364  return false;
365  }
366  ENSURE ( (src->elms_.begin() <= pos and pos < src->elms_.end())
367  or src->tail_.isValid(pos));
368  return true;
369  }
370 
371  public:
373  using iterator = const_iterator;
374 
375  using value_type = Literal;
376  using reference = Literal&;
377  using const_reference = Literal const&;
378 
380  iterator begin() const { return firstNonempty(); }
381  iterator end() const { return iterator{}; }
382 
383  friend iterator begin(PathArray const& pa) { return pa.begin();}
384  friend iterator end (PathArray const& pa) { return pa.end(); }
385 
386 
387 
388  private: /* ===== implementation details ===== */
389 
390  bool
391  isValid (Literal const* pos) const
392  {
393  return pos
394  and (tail_.isValid(pos)
395  or (elms_.begin() <= pos and pos < elms_.end()
396  and *pos));
397  }
398 
399  iterator
400  firstNonempty () const
401  {
402  iterator startPos{this, elms_.begin()};
403  while (startPos && isnil (*startPos))
404  ++startPos;
405  return startPos;
406  }
407 
412  Literal const*
414  {
415  Literal const* lastPos = elms_.begin() + chunk_size-1;
416  Literal const* beforeStart = elms_.begin() - 1;
417  while (lastPos != beforeStart and not *lastPos)
418  --lastPos;
419  return ++lastPos; // at start if empty, else one behind the last
420  }
421 
422 
423  /* ===== manipulation by UICoord and Builder subclasses ===== */
424  protected:
429  Literal*
430  getPosition (size_t idx)
431  {
432  Literal const* elm =nullptr;
433  if (idx < chunk_size)
434  elm = elms_[idx]? &elms_[idx] : nullptr;
435  else
436  if (idx-chunk_size < tail_.size())
437  elm = &tail_[idx-chunk_size];
438 
439  return const_cast<Literal*> (elm);
440  }
441 
446  Literal*
447  expandPosition (size_t idx)
448  {
449  if (chunk_size <= idx and size() <= idx)
450  tail_.resizeTo(idx+1 - chunk_size);
451  if (idx < chunk_size)
452  return elms_.begin() + idx;
453 
454  ENSURE (idx-chunk_size < tail_.size());
455  return const_cast<Literal*> (&tail_[idx-chunk_size]);
456  }
457 
459  void
460  setContent (Literal* pos, const char* val)
461  {
462  REQUIRE (pos);
463  *reinterpret_cast<const char**> (pos) = val;
464  }
465 
466  void
467  truncateTo (size_t newSize)
468  {
469  if (newSize < chunk_size)
470  {
471  Literal* end = elms_.end();
472  Literal* pos = getPosition(newSize);
473  if (pos)
474  for ( ; pos!=end; ++pos)
475  setContent (pos, nullptr);
476  tail_.resizeTo (0);
477  }
478  else
479  if (newSize-chunk_size < tail_.size())
480  tail_.resizeTo (newSize - chunk_size);
481  // otherwise: keep current size, don't grow
482  }
483 
491  void
493  {
494  if (size() == 0) return;
495  const char* fill = Symbol::EMPTY;
496 
497  Literal* end = elms_.end();
498  Literal* pos = elms_.begin();
499  for ( ; pos!=end; ++pos)
500  if (isnil (*pos))
501  setContent (pos, fill);
502  else
503  if (fill==Symbol::EMPTY)
504  fill = Symbol::ANY;
505 
506  if (tail_)
507  {
508  // normalise data in extension storage
509  pos = getPosition (chunk_size);
510  end = pos + tail_.size();
511  for ( ; pos!=end; ++pos)
512  if (isnil (*pos))
513  setContent (pos, fill);
514  else
515  if (fill==Symbol::EMPTY)
516  fill = Symbol::ANY;
517  }
518 
519  size_t idx = size();
520  // trim trailing fill
521  while (idx and fill == *getPosition (--idx))
522  setContent (getPosition(idx), nullptr);
523 
524  if (idx >= chunk_size-1)
525  tail_.resizeTo (idx+1 - chunk_size);
526  else
527  tail_.resizeTo (0);
528  }
529  };
530 
531 
532 
533 
534  template<size_t chunk_size>
535  inline
537  {
538  if (this->empty()) return "";
539 
540  string buff;
541  size_t expectedLen = this->size() * 10;
542  buff.reserve (expectedLen); // heuristic
543  for (Literal elm : *this)
544  buff += elm + "/";
545 
546  // chop off last delimiter
547  size_t len = buff.length();
548  ASSERT (len >= 1);
549  buff.resize(len-1);
550  return buff;
551  }
552 
553 
554 
558  template<size_t cl, size_t cr>
559  bool
561  {
562  if (l.size() != r.size()) return false;
563 
564  typename PathArray<cl>::iterator lp = l.begin();
565  typename PathArray<cl>::iterator rp = r.begin();
566  while (lp and rp)
567  {
568  if (*lp != *rp) return false;
569  ++lp;
570  ++rp;
571  }
572  return isnil(lp) and isnil(rp);
573  }
574 
575  template<size_t cl, size_t cr>
576  bool
577  operator!= (PathArray<cl> const& l, PathArray<cr> const& r)
578  {
579  return not (l == r);
580  }
581 
582 
583 
584 }// namespace lib
585 #endif /*LIB_PATH_ARRAY_H*/
Literal * expandPosition(size_t idx)
Definition: path-array.hpp:447
Hold a sequence of index numbers as template parameters.
build a sequence of index numbers based on a type sequence
Helper template(s) for creating Lumiera Forward Iterators.
PStorage newCopy() const
allocate a copy.
Definition: path-array.hpp:93
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
bool operator==(PtrDerefIter< I1 > const &il, PtrDerefIter< I2 > const &ir)
Supporting equality comparisons...
Literal const * findInlineEnd() const
find effective end of data in the inline array, i.e.
Definition: path-array.hpp:413
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Abstraction for path-like topological coordinates.
Definition: path-array.hpp:225
Marker types to indicate a literal string and a Symbol.
void normalise()
establish the contract of PathArray
Definition: path-array.hpp:492
iterator begin() const
Definition: path-array.hpp:380
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
friend void iterNext(const PathArray *, const Literal *&pos)
Implementation of Iteration-logic: pull next element.
Definition: path-array.hpp:348
friend bool checkPoint(const PathArray *src, const Literal *&pos)
Implementation of Iteration-logic: detect iteration end.
Definition: path-array.hpp:355
PathArray(IndexSeq< prefix... >, IndexSeq< rest... >, ARGS &&...args)
Definition: path-array.hpp:245
Lumiera error handling (C++ interface).
Simple functions to represent objects, for debugging and diagnostics.
void setContent(Literal *pos, const char *val)
Definition: path-array.hpp:460
Literal * getPosition(size_t idx)
Definition: path-array.hpp:430
Heap-allocated extension storage for an immutable sequence of literal strings.
Definition: path-array.hpp:74
size_t indexOf(Literal const &content) const
reverse lookup of actual path content
Definition: path-array.hpp:331
Adapter for building an implementation of the »Lumiera Forward Iterator« concept. ...
Metaprogramming with type sequences based on variadic template parameters.