herald  2.0.0
data.h
1 // Copyright 2020-2021 Herald Project Contributors
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 #ifndef HERALD_DATA_H
6 #define HERALD_DATA_H
7 
8 #include <string>
9 #include <iostream>
10 
11 #include "memory_arena.h"
12 
13 namespace herald {
14 namespace datatype {
15 
24 #ifndef HERALD_MEMORYARENA_MAX
25 template <typename MemoryArenaT = MemoryArena<8192, 8>>
26 #else
27 #ifndef HERALD_MEMORYARENA_PAGE
28 template <typename MemoryArenaT = MemoryArena<HERALD_MEMORYARENA_MAX, 8>>
29 #else
30 template <typename MemoryArenaT = MemoryArena<HERALD_MEMORYARENA_MAX, HERALD_MEMORYARENA_PAGE>>
31 #endif
32 #endif
33 class DataRef {
34 public:
36  DataRef() : entry()
37  {
38  ;
39  }
41  DataRef(DataRef&& other)
42  : entry()
43  {
44  std::swap(entry,other.entry);
45  }
47  DataRef(const std::uint8_t* value, std::size_t length) :
48  entry(arena.allocate(length)) {
49  for (std::size_t i = 0;i < length; ++i) {
50  // data[i] = std::byte(value[i]);
51  arena.set(entry, i, (unsigned char)value[i]);
52  }
53  }
55  DataRef(const std::byte* value, std::size_t length) : entry(arena.allocate(length)) {
56  for (std::size_t i = 0;i < length; ++i) {
57  // data[i] = value[i];
58  arena.set(entry, i, (unsigned char)value[i]);
59  }
60  }
62  DataRef(const std::string& from) : entry(arena.allocate(from.size())) {
63  for (std::size_t i = 0;i < from.size(); ++i) {
64  // data[i] = value[i];
65  arena.set(entry, i, (unsigned char)from[i]);
66  }
67  }
69  DataRef(const DataRef& from) : entry(arena.allocate(from.entry.byteLength)) {
70  for (std::size_t i = 0;i < from.size(); ++i) {
71  arena.set(entry, i, from.arena.get(from.entry,i));
72  }
73  }
74 
76  DataRef(std::byte repeating, std::size_t count) : entry(arena.allocate(count)) {
77  for (std::size_t i = 0;i < count; ++i) {
78  arena.set(entry,i,(unsigned char)repeating);
79  }
80  }
82  DataRef(std::size_t reserveLength) : entry(arena.allocate(reserveLength)) {
83  ;
84  }
85 
86 
87  // Data(Base64String from); // use Base64String.from(std::string).decode() instea
89  DataRef& operator=(const DataRef& other)
90  {
91  entry = arena.allocate(other.entry.byteLength);
92  for (std::size_t i = 0;i < other.size(); ++i) {
93  arena.set(entry, i, other.arena.get(other.entry,i));
94  }
95  return *this;
96  }
97 
100  clear();
101  }
102 
103  // std::string base64EncodedString(); // use Base64String.encode(Data) instead
105  static DataRef fromHexEncodedString(const std::string& hex)
106  {
107  // parse string
108  const std::size_t length = hex.size();
109  std::string hexInput;
110  // Input size check - two characters per single byte
111  if (1 == length % 2) {
112  // invalid format - not an even number of characters
113  // Prepend input with a 0. (Note '8' and '08' in hex are the same)
114  hexInput += "0";
115  }
116  hexInput += hex;
117 
118  DataRef d(hexInput.size() / 2);
119 
120  for (std::size_t i = 0; i < hexInput.size(); i += 2) {
121  std::string byteString = hexInput.substr(i, 2);
122  std::byte byte = std::byte(strtol(byteString.c_str(), NULL, 16));
123  // d.data.push_back(byte);
124  arena.set(d.entry,i / 2, (unsigned char)byte);
125  }
126 
127  return d;
128  }
129 
131  std::string description() const
132  {
133  return hexEncodedString();
134  }
135 
137  DataRef subdata(std::size_t offset) const
138  {
139  if (offset >= entry.byteLength) {
140  return DataRef(0);
141  }
142  DataRef copy(entry.byteLength - offset);
143  for (std::size_t i = 0;i < entry.byteLength - offset;++i) {
144  copy.arena.set(copy.entry,i,arena.get(entry,i + offset));
145  }
146  // std::copy(data.begin() + offset, data.end(), std::back_inserter(copy.data));
147  return copy;
148  }
149 
151  DataRef subdata(std::size_t offset, std::size_t length) const
152  {
153  // Note: offset if passed as -1 could be MAX_LONG_LONG, so check it on its own too
154  if (offset >= entry.byteLength) {
155  return DataRef(0);
156  }
157  std::size_t correctedLength = length;
158  if (length > entry.byteLength || length + offset > entry.byteLength) {
159  correctedLength = entry.byteLength - offset;
160  }
161  DataRef copy(correctedLength);
162  // Note the below is necessary as calling with (4,-1), the second condition IS valid!
163  // if (length > entry.byteLength || offset + length > entry.byteLength) {
164  // for (std::size_t i = 0;i < entry.byteLength - offset;++i) {
165  // copy.arena.set(copy.entry,i,arena.get(entry,offset + i));
166  // }
167  // // std::copy(data.begin() + offset, data.end(), std::back_inserter(copy.data));
168  // } else {
169  for (std::size_t i = 0;i < correctedLength;++i) {
170  copy.arena.set(copy.entry,i,arena.get(entry,offset + i));
171  }
172  // std::copy(data.begin() + offset, data.begin() + offset + length, std::back_inserter(copy.data));
173  // }
174  return copy;
175  }
176 
178  std::byte at(std::size_t index) const {
179  if (index > (unsigned short)(entry.byteLength - 1)) {
180  return std::byte(0);
181  }
182  return std::byte(arena.get(entry,index));
183  }
184 
190  void assign(const DataRef& other)
191  {
192  if (other.size() > entry.byteLength) {
193  arena.reserve(entry,other.size());
194  }
195  for (std::size_t pos = 0; pos < other.size();++pos) {
196  arena.set(entry,pos,other.arena.get(other.entry,pos));
197  }
198  }
199 
201  void append(const DataRef& rawData, std::size_t offset, std::size_t length)
202  {
203  auto curSize = entry.byteLength;
204  arena.reserve(entry,curSize + length);
205  for (std::size_t pos = 0; pos < length;++pos) {
206  arena.set(entry,curSize + pos,rawData.arena.get(rawData.entry,pos + offset));
207  }
208  // std::copy(rawData.data.begin() + offset,
209  // rawData.data.begin() + offset + length,
210  // std::back_inserter(data)
211  // );
212  }
213 
215  void append(const std::string& rawData)
216  {
217  auto curSize = entry.byteLength;
218  arena.reserve(entry,curSize + rawData.size());
219  for (std::size_t pos = 0; pos < rawData.size();++pos) {
220  arena.set(entry,curSize + pos,rawData[pos]);
221  }
222  }
223 
225  void append(const std::uint8_t* rawData, std::size_t offset, std::size_t length)
226  {
227  auto curSize = entry.byteLength;
228  arena.reserve(entry,curSize + length);
229  for (std::size_t i = 0;i < length;++i) {
230  arena.set(entry,curSize + i,(unsigned char)(rawData[offset + i]));
231  // arena.set(entry,curSize,std::byte(rawData[offset + i]));
232  }
233  }
234 
236  void appendReversed(const DataRef& rawData, std::size_t offset, std::size_t length)
237  {
238  if (offset > rawData.size()) {
239  return; // append nothing - out of range
240  }
241  std::size_t checkedLength = length;
242  if (length > (rawData.size() - offset)) {
243  checkedLength = rawData.size() - offset;
244  }
245  auto curSize = entry.byteLength;
246  arena.reserve(entry,curSize + checkedLength);
247  for (std::size_t i = 0;i < checkedLength;++i) {
248  arena.set(entry,curSize + i,
249  rawData.arena.get(rawData.entry,offset + (checkedLength - i - 1)));
250  // std::reverse_copy(rawData.data.begin() + offset,
251  // rawData.data.begin() + offset + checkedLength,
252  // std::back_inserter(data)
253  // );
254  }
255  }
256 
258  void append(const DataRef& rawData)
259  {
260  auto orig = entry.byteLength;
261  arena.reserve(entry,rawData.size() + orig);
262  for (std::size_t pos = 0; pos < rawData.size();++pos) {
263  arena.set(entry,orig + pos,rawData.arena.get(rawData.entry,pos));
264  }
265  // std::copy(rawData.data.begin(), rawData.data.end(), std::back_inserter(data));
266  }
267 
269  void append(std::byte rawData)
270  {
271  std::size_t curSize = entry.byteLength;
272  arena.reserve(entry,curSize + 1);
273  // data.push_back(rawData);
274  arena.set(entry,curSize,(unsigned char)rawData);
275  // curSize++;
276  }
277 
279  void append(uint8_t rawData)
280  {
281  std::size_t curSize = entry.byteLength;
282  arena.reserve(entry,curSize + 1); // C++ ensures types are AT LEAST x bits
283  // arena.set(entry,curSize,std::byte(rawData));
284  arena.set(entry,curSize,(unsigned char)(rawData));
285  // curSize++;
286  }
287 
289  void append(uint16_t rawData)
290  {
291  std::size_t curSize = entry.byteLength;
292  arena.reserve(entry,curSize + 2); // C++ ensures types are AT LEAST x bits
293  arena.set(entry,curSize,(unsigned char)(rawData & 0xff));
294  arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8));
295  }
296 
298  void append(uint32_t rawData)
299  {
300  std::size_t curSize = entry.byteLength;
301  arena.reserve(entry,curSize + 4); // C++ ensures types are AT LEAST x bits
302  arena.set(entry,curSize,(unsigned char)(rawData & 0xff));
303  arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8));
304  arena.set(entry,curSize + 2,(unsigned char)(rawData >> 16));
305  arena.set(entry,curSize + 3,(unsigned char)(rawData >> 24));
306  }
307 
309  void append(uint64_t rawData)
310  {
311  std::size_t curSize = entry.byteLength;
312  arena.reserve(entry,curSize + 8); // C++ ensures types are AT LEAST x bits
313  arena.set(entry,curSize,(unsigned char)(rawData & 0xff));
314  arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8));
315  arena.set(entry,curSize + 2,(unsigned char)(rawData >> 16));
316  arena.set(entry,curSize + 3,(unsigned char)(rawData >> 24));
317  arena.set(entry,curSize + 4,(unsigned char)(rawData >> 32));
318  arena.set(entry,curSize + 5,(unsigned char)(rawData >> 40));
319  arena.set(entry,curSize + 6,(unsigned char)(rawData >> 48));
320  arena.set(entry,curSize + 7,(unsigned char)(rawData >> 56));
321  }
322 
324  bool uint8(std::size_t fromIndex, uint8_t& into) const noexcept
325  {
326  if (fromIndex > (unsigned short)(entry.byteLength - 1)) {
327  return false;
328  }
329  into = std::uint8_t(arena.get(entry,fromIndex));
330  return true;
331  }
332 
334  bool uint16(std::size_t fromIndex, uint16_t& into) const noexcept
335  {
336  if (fromIndex > (unsigned short)(entry.byteLength - 2)) {
337  return false;
338  }
339  into = (std::uint16_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) | std::uint16_t(std::uint8_t(arena.get(entry,fromIndex)));
340  return true;
341  }
342 
344  bool uint32(std::size_t fromIndex, uint32_t& into) const noexcept
345  {
346  if (fromIndex > entry.byteLength - 4) {
347  return false;
348  }
349  into = std::uint32_t(std::uint8_t(arena.get(entry,fromIndex))) | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) |
350  (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 2))) << 16) | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 3))) << 24);
351  return true;
352  }
353 
355  bool uint64(std::size_t fromIndex, uint64_t& into) const noexcept
356  {
357  if (entry.byteLength < 8 || fromIndex > entry.byteLength - 8) {
358  return false;
359  }
360  into = (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 7))) << 56) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 6))) << 48) |
361  (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 5))) << 40) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 4))) << 32) |
362  (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 3))) << 24) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 2))) << 16) |
363  (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) | std::uint64_t(std::uint8_t(arena.get(entry,fromIndex)));
364  return true;
365  }
366 
367  // TODO signed versions of the above functions too
369  bool operator==(const DataRef& other) const noexcept
370  {
371  if (size() != other.size()) {
372  return false;
373  }
374  //if (hashCode() != other.hashCode()) {
375  // return false;
376  //}
377  // else compare each value
378 
379  // alternatively, cheat...
380  return hashCode() == other.hashCode(); // Somewhat naughty
381  }
382 
384  bool operator!=(const DataRef& other) const noexcept
385  {
386  if (size() != other.size()) {
387  return true;
388  }
389  return hashCode() != other.hashCode();
390  }
391 
393  bool operator<(const DataRef& other) const noexcept
394  {
395  return hashCode() < other.hashCode();
396  }
397 
399  bool operator>(const DataRef& other) const noexcept
400  {
401  return hashCode() > other.hashCode();
402  }
403 
406  {
407  DataRef result(entry.byteLength);
408  // result.reserve(entry.byteLength);
409  for (std::size_t pos = 0;pos < entry.byteLength;++pos) {
410  result.arena.set(result.entry,pos,arena.get(entry,entry.byteLength - pos - 1));
411  }
412  // std::reverse_copy(data.begin(),data.end(),
413  // std::back_inserter(result.data)
414  // );
415  return result;
416  }
417 
420  {
421  DataRef result(entry.byteLength);
422  // result.data.reserve(entry.byteLength);
423 
424  // Keep byte order intact (caller could use reversed() to change that)
425  // but reverse the order of the individual bits by each byte
426  std::uint8_t value, original;
427  for (std::size_t i = 0;i < entry.byteLength;++i) {
428  original = std::uint8_t(arena.get(entry,i));
429  value = 0;
430  for (int b = 0;b < 8;++b) {
431  if ((original & (1 << b)) > 0) {
432  value |= 1 << (7 - b);
433  }
434  }
435  // result.data[i] = std::byte(value);
436  result.arena.set(result.entry,entry.byteLength - i - 1,value);
437  }
438 
439  return result;
440  }
441 
443  std::string hexEncodedString() const noexcept
444  {
445  if (0 == entry.byteLength) {
446  return "";
447  }
448  std::string result;
449  std::size_t size = entry.byteLength;
450  result.reserve(size * 2);
451  std::size_t v;
452  for (std::size_t i = 0; i < size; ++i) {
453  // v = std::size_t(data.at(i));
454  v = std::size_t(arena.get(entry,i));
455  result += hexChars[0x0F & (v >> 4)]; // MSB
456  result += hexChars[0x0F & v ]; // LSB
457  }
458  return result;
459  }
460 
462  std::size_t hashCode() const noexcept
463  {
464  // TODO consider a faster (E.g. SIMD) algorithm or one with less hotspots (see hashdos attacks)
465  return std::hash<DataRef<MemoryArenaT>>{}(*this);
466  }
467 
469  std::size_t size() const noexcept{
470  return entry.byteLength;
471  }
472  // TODO support other C++ STD container type functions to allow iteration over data elements (uint8)
473 
475  void clear() noexcept
476  {
477  arena.deallocate(entry);
478  }
479 
480  static MemoryArenaT& getArena() {
481  return arena;
482  }
483 
484 protected:
485  static const char hexChars[];
486  static MemoryArenaT arena;
487  MemoryArenaEntry entry;
488 };
489 
490 
491 
492 
494 template <typename MemoryArenaT>
495 MemoryArenaT DataRef<MemoryArenaT>::arena = MemoryArenaT();
496 
497 template <typename MemoryArenaT>
498 const char DataRef<MemoryArenaT>::hexChars[] = {
499  '0','1','2','3','4','5','6','7',
500  '8','9','a','b','c','d','e','f'
501 };
502 
506 using Data = DataRef<>; // uses MemoryArenaT<4096,8>
507 
508 
509 
510 
512 template <std::size_t maxSize = 8>
514 public:
515  static constexpr std::size_t MaxSize = maxSize;
516 
518  DataSections() noexcept : sections(), sz(0) {}
520  ~DataSections() noexcept = default;
521 
523  void append(const Data& toCopy) noexcept {
524  if (sz >= MaxSize) {
525  return;
526  }
527  sections[sz] = toCopy; // copy assign operator
528  ++sz;
529  }
530 
532  std::size_t size() noexcept {
533  return sz;
534  }
535 
537  const Data& get(std::size_t index) noexcept {
538  if (index >= MaxSize) {
539  return emptyRef;
540  }
541  return sections[index];
542  }
543 
545  const Data& operator[](std::size_t index) noexcept {
546  return get(index);
547  }
548 
549 private:
550  static Data emptyRef;
551  std::array<Data,MaxSize> sections;
552  std::size_t sz;
553 };
554 
556 template <std::size_t maxSize>
558 
559 } // end namespace
560 } // end namespace
561 
562 namespace std {
563  template <typename MemoryArenaT>
564  inline std::ostream& operator<<(std::ostream &os, const herald::datatype::DataRef<MemoryArenaT>& d)
565  {
566  return os << d.hexEncodedString();
567  }
568 
569  inline void hash_combine_impl(std::size_t& seed, std::size_t value)
570  {
571  seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2);
572  }
573 
574  template<typename MemoryArenaT>
575  struct hash<herald::datatype::DataRef<MemoryArenaT>>
576  {
577  size_t operator()(const herald::datatype::DataRef<MemoryArenaT>& v) const
578  {
579  std::size_t hv = 0;
580  std::uint8_t ui = 0;
581  bool ok;
582  for (std::size_t pos = 0;pos < v.size();++pos) {
583  ok = v.uint8(pos,ui);
584  hash_combine_impl(hv, std::hash<std::uint8_t>()(ui));
585  }
586  return hv;
587  }
588  };
589 } // end namespace
590 
591 #endif
The main data workhorse class of the Herald API.
Definition: data.h:33
bool operator>(const DataRef &other) const noexcept
Greater than operator for another DataRef instance (same memory arena)
Definition: data.h:399
DataRef(const DataRef &from)
Initialises a DataRef copying another data object (uses more data, to ensure only one object owns the...
Definition: data.h:69
std::byte at(std::size_t index) const
Returns the individual byte at index position, or a byte value of zero if index is out of bounds.
Definition: data.h:178
DataRef(const std::string &from)
Initialises a DataRef from a string of chars.
Definition: data.h:62
~DataRef()
Default destructor.
Definition: data.h:99
bool uint16(std::size_t fromIndex, uint16_t &into) const noexcept
Returns whether reading a single uint16_t to into at fromIndex was successful.
Definition: data.h:334
void append(uint64_t rawData)
Appends a single uint64_t.
Definition: data.h:309
DataRef subdata(std::size_t offset) const
Returns a NEWLY allocated DataRef instance returning a subset of this instance.
Definition: data.h:137
std::string hexEncodedString() const noexcept
Returns a hex encoded string of this binary data.
Definition: data.h:443
void append(std::byte rawData)
Appends a single byte.
Definition: data.h:269
std::size_t hashCode() const noexcept
Returns the hash code of this instance.
Definition: data.h:462
DataRef(std::size_t reserveLength)
Initialises a DataRef with reserveLength bytes of undefined data.
Definition: data.h:82
void append(uint32_t rawData)
Appends a single uint32_t.
Definition: data.h:298
void append(const std::string &rawData)
Appends a set of characters to the end of this DataRef.
Definition: data.h:215
DataRef(std::byte repeating, std::size_t count)
Initialises a DataRef with count number of repeating bytes.
Definition: data.h:76
bool operator<(const DataRef &other) const noexcept
Less than operator for another DataRef instance (same memory arena)
Definition: data.h:393
DataRef(const std::uint8_t *value, std::size_t length)
Initialises a DataRef from a std::uint8_t array of length length
Definition: data.h:47
void appendReversed(const DataRef &rawData, std::size_t offset, std::size_t length)
Appends the specified DataRef to this one, but in its reverse order.
Definition: data.h:236
static DataRef fromHexEncodedString(const std::string &hex)
Creates a new DataRef object from a hexadecimal encoded string.
Definition: data.h:105
DataRef(DataRef &&other)
Takes control of another DataRef's memory allocation.
Definition: data.h:41
std::size_t size() const noexcept
Returns the size in allocated bytes of this instance.
Definition: data.h:469
void append(uint8_t rawData)
appends a single uint8_t
Definition: data.h:279
bool operator!=(const DataRef &other) const noexcept
Inequality operator for another DataRef instance (same memory arena)
Definition: data.h:384
DataRef()
Creates a DataRef with no memory used.
Definition: data.h:36
DataRef & operator=(const DataRef &other)
Copy assign operator. Copies the data to be sure only one object owns the entry.
Definition: data.h:89
DataRef reversed() const
Returns a new DataRef instance with the same data as this one, but in the reverse order.
Definition: data.h:405
bool uint8(std::size_t fromIndex, uint8_t &into) const noexcept
Returns whether reading a single uint8_t to into at fromIndex was successful.
Definition: data.h:324
void clear() noexcept
Clears (deallocates) the bytes referred to by this instance.
Definition: data.h:475
bool uint32(std::size_t fromIndex, uint32_t &into) const noexcept
Returns whether reading a single uint32_t to into at fromIndex was successful.
Definition: data.h:344
void assign(const DataRef &other)
Assigns the data in-place, reserving the size required if the current size is too small.
Definition: data.h:190
bool operator==(const DataRef &other) const noexcept
Equality operator for another DataRef instance (same memory arena)
Definition: data.h:369
DataRef(const std::byte *value, std::size_t length)
Initialises a DataRef from a std::byte array of length length
Definition: data.h:55
DataRef reverseEndianness() const
Returns the same order of bytes, but with the bits in each byte reversed.
Definition: data.h:419
bool uint64(std::size_t fromIndex, uint64_t &into) const noexcept
Returns whether reading a single uint64_t to into at fromIndex was successful.
Definition: data.h:355
void append(const DataRef &rawData, std::size_t offset, std::size_t length)
Copies another DataRef into this instance, expanding if required.
Definition: data.h:201
DataRef subdata(std::size_t offset, std::size_t length) const
Returns a NEWLY allocated DataRef instance returning a subset of this instance.
Definition: data.h:151
static MemoryArenaT arena
Instantiates the MemoryArena instance used by all DataRefs that share it.
Definition: data.h:486
std::string description() const
Returns the hex encoded string represetation as the description.
Definition: data.h:131
void append(uint16_t rawData)
Appends a single uint16_t.
Definition: data.h:289
void append(const DataRef &rawData)
Appends the specified DataRef to this one.
Definition: data.h:258
void append(const std::uint8_t *rawData, std::size_t offset, std::size_t length)
Copies a uint8_t array onto the end of this instance, expanding if necessary.
Definition: data.h:225
Represents a fixed array of Data references using the default memory arena that tracks its own in-use...
Definition: data.h:513
void append(const Data &toCopy) noexcept
Adds a new section, if room exists. Otherwise quietly performs a NO OP.
Definition: data.h:523
const Data & operator[](std::size_t index) noexcept
Returns the DataRef at the relevant index, or an empty DataRef.
Definition: data.h:545
~DataSections() noexcept=default
Default destructor.
std::size_t size() noexcept
Returns the number of dataref elements in use.
Definition: data.h:532
DataSections() noexcept
Default constructors. No memory allocation other than size (8 bytes)
Definition: data.h:518
const Data & get(std::size_t index) noexcept
Returns the DataRef at the relevant index, or an empty DataRef.
Definition: data.h:537
DataRef<> Data
Defaults references to Data to equal the DataRef with the default Memory Arena dimensions,...
Definition: data.h:506
Acts as a non-global memory arena for arbitrary classes.
Definition: aggregates.h:15
Definition: data.h:562
Represents an external 'pointer' to an allocated memory area.
Definition: memory_arena.h:20