Lumiera  0.pre.03
»edit your freedom«
extent-family.hpp
Go to the documentation of this file.
1 /*
2  EXTENT-FAMILY.hpp - maintain a sequence of memory extents used cyclically
3 
4  Copyright (C) Lumiera.org
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
40 #ifndef SRC_VAULT_MEM_EXTENT_FAMILY_H_
41 #define SRC_VAULT_MEM_EXTENT_FAMILY_H_
42 
43 
44 #include "vault/common.hpp"
46 #include "lib/iter-adapter.hpp"
47 #include "lib/nocopy.hpp"
48 #include "lib/util.hpp"
49 
50 #include <algorithm>
51 #include <memory>
52 #include <vector>
53 #include <array>
54 
55 
56 namespace vault{
57 namespace mem {
58  namespace err = lumiera::error;
59 
60  namespace {
61  const size_t ALLOC_SAFETY_LIMIT = 8_GiB;
62  }
63 
64  using util::unConst;
65 
66  template<typename T, size_t siz>
68 
69 
70 
79  template<typename T, size_t siz>
82  {
85  static const size_t EXCESS_ALLOC{5};
86 
87 
88  public:
90  struct Extent
91  : std::array<T,siz>
92  {
93  using Payload = T;
94  using SIZ = std::integral_constant<size_t,siz>;
95  };
96 
97 
98  private:
99  using _UniqueStoragePtr = std::unique_ptr<lib::UninitialisedStorage<T,siz>>;
100 
102  struct Storage
103  : _UniqueStoragePtr
104  {
110  : _UniqueStoragePtr{new lib::UninitialisedStorage<T,siz>}
111  { }
112 
116  Extent&
118  {
119  auto* rawStorage = this->get();
120  ENSURE (rawStorage != nullptr);
121  return static_cast<Extent&> (rawStorage->array());
122  }
123  };
124  using Extents = std::vector<Storage>;
125 
126 
132  struct IdxLink
133  {
134  ExtentFamily* exFam{nullptr};
135  size_t index{0};
136 
137  /* === state protocol API for IterStateWrapper === */
138  bool
139  checkPoint() const
140  {
141  return exFam and index != exFam->after_;
142  }
143 
144  Extent&
145  yield() const
146  {
147  return exFam->access (index);
148  }
149 
150  void
151  iterNext()
152  {
153  index = exFam->incWrap (index);
154  }
155 
156  bool
157  operator== (IdxLink const& oi) const
158  {
159  return exFam == oi.exFam
160  and index == oi.index;
161  }
162 
163 
164  /* === pass-through extended functionality === */
165 
166  size_t getIndex() { return index; }
167 
168  void
169  expandAlloc (size_t cnt =1)
170  {
171  size_t prevStart = exFam->start_;
172  exFam->openNew(cnt);
173  if (index >= prevStart)
174  index += (exFam->start_-prevStart);
175  // was in a segment that might be moved up
176  ENSURE (exFam->isValidPos (index));
177  }
178 
185  void
186  validatePos (Extent* knownTarget)
187  {
188  if (exFam->matchPos (index,knownTarget))
189  return;
190  size_t prevIdx = index;
191  do{
192  iterNext();
193  if (exFam->matchPos (index,knownTarget))
194  return;
195  }
196  while (index != prevIdx);
197  // went full circle without hitting the expected target Extent....
198  throw err::Logic {"Unable to fix-up an iterator after Extent allocation. "
199  "Reference position obsolete or unknown to the memory manager."};
200  }
201  };
202 
203 
204 
205  /* ==== Management Data ==== */
206 
207  Extents extents_;
208  size_t start_,after_;
209 
210  public:
211  explicit
212  ExtentFamily(size_t initialCnt =1)
213  : extents_{initialCnt}
214  , start_{0} // Extents allocated yet marked unused
215  , after_{0}
216  { }
217 
218  void
219  reserve (size_t expectedMaxExtents)
220  { // pertaining management data only
221  extents_.reserve (expectedMaxExtents);
222  }
223 
231  void
232  openNew (size_t cnt =1)
233  {
234  if (not canAccomodate (cnt))
235  {//insufficient reserve => allocate
236  size_t oldSiz = slotCnt();
237  size_t addSiz = cnt - freeSlotCnt()
238  + EXCESS_ALLOC;
239  // add a strike of new extents at the end
240  ___sanityCheckAllocSize (addSiz);
241  extents_.resize (oldSiz + addSiz);
242  if (isWrapped())
243  {// need the new elements in the middle, before the existing start_
244  auto p = extents_.begin();
245  auto first = p + start_;
246  auto mid = p + oldSiz;
247  auto last = p + oldSiz + addSiz;
248  // relocate [fist..mid) after [mid..last)
249  std::rotate (first, mid, last);
250  start_ += addSiz;
251  }
252  }
253  // now sufficient reserve extents are available
254  ENSURE (canAccomodate (cnt));
255  after_ = incWrap (after_, cnt);
256  }
257 
259  void
260  dropOld (size_t cnt)
261  {
262  REQUIRE (cnt <= activeSlotCnt());
263  start_ = incWrap (start_, cnt);
264  }
265 
266 
270 
272  iterator begin() { return iterator{IdxLink{this, start_}}; }
273  iterator end() { return iterator{IdxLink{this, after_}}; }
274 
275  friend iterator begin (ExtentFamily& exFam) { return exFam.begin(); }
276  friend iterator end (ExtentFamily& exFam) { return exFam.end(); }
277 
278 
279  bool empty() const { return start_ == after_; }
280 
284  iterator
286  {
287  REQUIRE (not empty()); // trick to safely decrement by one
288  size_t penultimate = incWrap (after_, slotCnt()-1);
289  return iterator{IdxLink{this, penultimate}};
290  }
291 
292 
293  private: /* ====== storage management implementation ====== */
294  bool
295  isWrapped() const
296  {
297  return after_ < start_;
298  } // note: both are equal only when empty
299 
300  size_t
301  slotCnt() const
302  {
303  return extents_.size();
304  }
305 
307  size_t
309  {
310  REQUIRE (start_ < slotCnt());
311  REQUIRE (after_ <= slotCnt());
312 
313  return not isWrapped()? after_ - start_
314  : (after_ - 0)
315  +(slotCnt() - start_);
316  }
317 
318  size_t
319  freeSlotCnt() const
320  { // always keep one in reserve...
321  REQUIRE (activeSlotCnt() < slotCnt());
322 
323  return slotCnt() - activeSlotCnt();
324  }
325 
326  bool
327  canAccomodate (size_t addCnt) const
328  {
329  return addCnt < freeSlotCnt();
330  } // keep one in reserve to allow for
331  // unambiguous iteration end in wrapped state
332 
336  size_t
337  incWrap (size_t idx, size_t inc =1)
338  {
339  return (idx+inc) % slotCnt();
340  }
341 
342  bool
343  isValidPos (size_t idx) const
344  {
345  REQUIRE (idx < slotCnt());
346  REQUIRE (activeSlotCnt() > 0);
347 
348  return isWrapped()? (start_ <= idx and idx < slotCnt())
349  or idx < after_
350  : (start_ <= idx and idx < after_);
351  }
352 
353  bool
354  matchPos (size_t idx, void* address)
355  {
356  REQUIRE (idx < slotCnt());
357  return address == extents_[idx].get();
358  }
359 
360  Extent&
361  access (size_t idx) const
362  {
363  REQUIRE (isValidPos (idx));
364  return unConst(this)->extents_[idx].access();
365  } // deliberately const-ness does not cover payload
366 
367  void
368  ___sanityCheckAllocSize (size_t addCnt)
369  {
370  size_t resultSiz = slotCnt()+addCnt;
371  size_t requiredSpace = resultSiz * sizeof(Extent);
372  if (requiredSpace > ALLOC_SAFETY_LIMIT)
373  throw err::Fatal{"Raw allocation exceeds safety limit: "
374  +util::showSize(requiredSpace) +" > "
375  +util::showSize(ALLOC_SAFETY_LIMIT)
376  ,err::LUMIERA_ERROR_CAPACITY};
377  }
378 
379 
381  friend class ExtentDiagnostic<T,siz>;
382  };
383 
384 
385 
386 
387 
388 
389 
390  /* ===== Test / Diagnostic ===== */
391 
392  template<typename T, size_t siz>
393  struct ExtentDiagnostic
394  {
395  using ExFam = ExtentFamily<T,siz>;
396 
397  ExFam& exFam_;
398 
399  size_t first() { return exFam_.start_; }
400  size_t last() { return exFam_.after_; }
401  size_t size() { return exFam_.slotCnt(); }
402  size_t active() { return exFam_.activeSlotCnt(); }
403  };
404 
405  template<typename T, size_t siz>
407  watch (ExtentFamily<T,siz>& extentFamily)
408  {
409  return ExtentDiagnostic<T,siz>{extentFamily};
410  }
411 
412 }} // namespace vault::mem
413 #endif /*SRC_VAULT_MEM_EXTENT_FAMILY_H_*/
logical structure of a memory Extent
iterator begin()
iterate over all the currently active Extents
void openNew(size_t cnt=1)
claim next cnt extents, possibly allocate.
Helper template(s) for creating Lumiera Forward Iterators.
Any copy and copy construction prohibited.
Definition: nocopy.hpp:46
iterator last()
positioned to the last / latest storage extent opened
size_t activeSlotCnt() const
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Mix-Ins to allow or prohibit various degrees of copying and cloning.
Entry in the Extents management datastructure.
Extent & access()
access projected Extent storage type
void dropOld(size_t cnt)
discard oldest cnt extents
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Block of raw uninitialised storage with array like access.
size_t incWrap(size_t idx, size_t inc=1)
increment index, but wrap at array end.
A raw memory block with proper alignment and array access.
Memory manager to provide a sequence of Extents for cyclic usage.
Basic set of definitions and includes commonly used together (Vault).
Decorator-Adapter to make a »state core« iterable as Lumiera Forward Iterator.
Vault-Layer implementation namespace root.