HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_PageArray.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_PageArray.h (UT Library, C++)
7  *
8  * COMMENTS: An array class with special handling of constant pages and
9  * shared page data.
10  */
11 
12 #pragma once
13 
14 #ifndef __UT_PageArray__
15 #define __UT_PageArray__
16 
17 #include "UT_API.h"
18 #include "UT_Assert.h"
19 #include "UT_FixedVector.h"
20 #include "UT_SmallArray.h"
21 #include "UT_Storage.h"
22 #include <SYS/SYS_AtomicInt.h>
23 #include <SYS/SYS_BitUtil.h>
24 #include <SYS/SYS_Compiler.h>
25 #include <SYS/SYS_Inline.h>
26 #include <SYS/SYS_Math.h>
27 #include <SYS/SYS_Types.h>
28 #include <SYS/SYS_TypeTraits.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 
34 typedef exint UT_PageNum;
35 typedef exint UT_PageOff;
36 
37 
38 class UT_Defaults;
39 class UT_MemoryCounter;
40 
41 
42 /// @internal
43 /// W E L C O M E
44 /// to scenic
45 /// Template Hell
46 ///
47 /// Population: You
48 ///
49 /// If you're just using this class, hopefully you'll have a short and
50 /// not-so-painful visit!
51 ///
52 /// If, however, you're attempting to modify this class, BEWARE!
53 /// Any missteps can and likely will result in application of the
54 /// Doughnut Rules, so be sure to review them before visiting.
55 /// It may take several days to escape, so it may be best to just
56 /// revert and make absolutely sure that it works on all platforms
57 /// before committing again.
58 ///
59 /// This class is the result of a wide variety of needs for a wide variety
60 /// of use cases, most of which need both good performance and flexibility.
61 /// You may want to skip reading this gigantic comment, but whether it's
62 /// immediately or in a few months, you'll realize that skipping reading
63 /// this was a huge mistake.
64 ///
65 /// This array consists of a pointer to a page table, optionally a
66 /// tuple size (if TSIZE==-1), and optionally a UT_Storage indicating
67 /// the storage type (if DATA_T is void). The page table, if not nullptr,
68 /// can be shared, and has the format: @code
69 /// +-------------------+
70 /// | ref count | (1 if unshared; >1 if shared)
71 /// +-------------------+
72 /// | array size | (number of real elements/tuples in the array)
73 /// +-------------------+
74 /// | array capacity | (number of allocated elements/tuples if all pages of the table were hardened)
75 /// +-------------------+
76 /// | page 0 pointer | (pointer to page if bit 0 is 0, else constant page representation)
77 /// +-------------------+
78 /// | page 1 pointer |
79 /// +-------------------+
80 /// | page 2 pointer |
81 /// +-------------------+
82 /// | ... |
83 /// +-------------------+
84 /// @endcode
85 ///
86 /// If a page pointer has bit 0 clear, (i.e. an address aligned to a 2-byte boundary),
87 /// it is a non-constant page, it can be shared, and has the format: @code
88 /// +-------------------+
89 /// | ref count | (1 if unshared; >1 if shared)
90 /// +-------------------+
91 /// | (unused) | (padding so that data starts on a 16-byte boundary)
92 /// +-------------------+
93 /// | tuple 0 |
94 /// +-------------------+
95 /// | tuple 1 |
96 /// +-------------------+
97 /// | tuple 2 |
98 /// +-------------------+
99 /// | ... |
100 /// +-------------------+
101 /// @endcode
102 ///
103 /// A normal capacity page contains 2^THEPAGEBITS tuples, e.g. the default
104 /// THEPAGEBITS==10 gives a page size of 1024 tuples. If there is exactly
105 /// one page, it may have a capacity that is any power of two up to
106 /// THEPAGEBITS, which can be checked by checking if the overall array has
107 /// capacity less than 2^THEPAGEBITS.
108 ///
109 /// If, instead, the page pointer has bit 0 set to 1, which would be an odd
110 /// address, all tuples in the page are the same. There are 3 ways this may
111 /// be represented:
112 ///
113 /// 1) If the size of a full tuple is strictly smaller than the size of a
114 /// pointer, (e.g. 7 bytes or fewer on a 64-bit platform), the constant value
115 /// will be stored inside the space that would have been a pointer, aligned
116 /// to the end of the pointer. That ensures that 4-byte tuples will be
117 /// aligned to 4 bytes, and 2- or 6-byte tuples will be aligned to 2 bytes.
118 /// This is most useful for fpreal32[1] and int32[1] (i.e. 4-byte scalars).
119 ///
120 /// 2) If the page pointer is equal to 1 (i.e. nullptr apart from bit 0 being 1),
121 /// the value is all zero. This covers the extremely
122 /// common case of a zero default value, especially for fpreal32[3] and
123 /// fpreal64[3].
124 ///
125 /// 3) Otherwise, the page pointer, ignoring bit zero, points to a page
126 /// that contains just a single tuple.
127 ///
128 /// That covers the basic structure. It sounds so nice and simple...
129 /// but then there are the templates.
130 ///
131 /// DATA_T:
132 ///
133 /// If DATA_T is something other than void, that type is the type of each
134 /// component of each tuple. This is the only way that non-numeric types
135 /// are possible; DATA_T can also be a numeric type. If DATA_T is void,
136 /// myImpl contains a UT_Storage variable indicating what the data type is;
137 /// it must be a numeric type, i.e. it cannot be UT_Storage::INVALID.
138 /// The ...UnknownType functions switch on the UT_Storage to determine the
139 /// data type and call the corresponding function. If DATA_T is non-void,
140 /// it saves a lot of extra work at runtime. Because the codepaths that
141 /// will never be called due to DATA_T checks still need to compile with
142 /// DATA_T being void, NotVoidType ensures that there is some arbitrary type
143 /// in place so that the code will compile, even though it will never be
144 /// called. The same issue prevents using UT_ASSERT_COMPILETIME to verify
145 /// DATA_T, so UT_ASSERT_P is used instead.
146 /// myImpl is always structured so that if the type becomes known, by checking
147 /// getStorage(), this can be cast to have the now-known DATA_T set.
148 /// For example: @code
149 /// if (array.getStorage() == UT_Storage::REAL32)
150 /// array.castType<fpreal32>().someFunctionCall();
151 /// @endcode
152 ///
153 /// TSIZE:
154 ///
155 /// If TSIZE >= 1, it is the tuple size (the number of components in each
156 /// tuple), and is known at compile time, avoiding extra work at compile-time
157 /// that depends on the tuple size, especially if TSIZE is 1. Odds are that
158 /// for non-numeric types, TSIZE will almost always be 1, since it rarely
159 /// makes sense to be something else. If TSIZE is 0, something is almost
160 /// certainly wrong. If the tuple size is not known at compile-time, TSIZE
161 /// must be -1. In that case, myImpl contains an integer indicating the tuple
162 /// size. Like with DATA_T, because the codepaths that will never be called
163 /// due to TSIZE checks still need to compile with other TSIZE values, so
164 /// UT_ASSERT_COMPILETIME can't be used to verify TSIZE; UT_ASSERT_P is used
165 /// instead. There are a number of functions that require TSIZE to be the
166 /// tuple size, in order to avoid having to send an extra parameter and
167 /// incurring the slowdowns of their counterparts that assume that the tuple
168 /// size is what's passed in. If you modify one, REMEMBER TO MODIFY THE OTHER!
169 /// myImpl is always structured so that if the tuple size becomes
170 /// known, by checking the getTupleSize(), this can be cast to have the
171 /// now-known TSIZE set.
172 /// For example: @code
173 /// if (array.getTupleSize() == 3)
174 /// array.castTupleSize<3>().someFunctionCall();
175 /// @endcode
176 ///
177 /// TABLEHARDENED:
178 ///
179 /// Since page tables can be shared if all of the data are identical, modifying
180 /// the array in any way requires copying (hardening) the page table and may
181 /// require copying one or more shared pages or hardening constant pages.
182 /// Since every modification requires hardening the page table, it's useful
183 /// to harden the page table once and be able to skip checking whether it needs
184 /// hardening for all modifications after that. Calling hardenTable()
185 /// will ensure that the table is hardened, and return this with TABLEHARDENED
186 /// set to true, so that further calls on it to modify the array won't need to
187 /// check whether the table needs hardening. The page table should be
188 /// quite light compared to the data, so it's not the end of the world if
189 /// the table is hardened pre-emptively and there end up being no actual
190 /// modifications.
191 ///
192 /// PAGESHARDENED:
193 ///
194 /// When writing data in parallel to random locations, all pages that could
195 /// be written-to must be hardened in advance, to avoid a race condition on
196 /// the hardening of any page. Also, if it's known that most pages will
197 /// likely be hardened anyway when writing data, it'd be advantageous to
198 /// be able to harden all pages in advance and skip checking when making
199 /// modifications. The code for hardening pages just-in-time can
200 /// dramatically increase code size and time overhead. To avoid this,
201 /// hardenAllPages() will ensure that the table and all pages are hardened,
202 /// and will return this with TABLEHARDENED and PAGESHARDENED both set to true.
203 /// Any calls to modify page data made on that returned pointer won't check
204 /// whether any pages need hardening, because it will know that all pages are
205 /// already hardened. If you use this, be sure not to use the pointer
206 /// with PAGESHARDENED set to true after any call to tryCompressAllPages()
207 /// on the original array pointer.
208 ///
209 /// THEPAGEBITS:
210 ///
211 /// Full pages will contain 2^THEPAGEBITS tuples, e.g. THEPAGEBITS==10
212 /// means there will be 1024 tuples per full page.
213 ///
214 /// IDX_T:
215 ///
216 /// This allows templating on the array index type so that strict types
217 /// can be used, e.g. GA_Offset.
218 ///
219 /// FIXME BEFORE USING:
220 /// - Should make generic UT_R[OW]Handle.+ classes for easy accessing
221 /// of runtime-determined data type and tuple size data as tuples
222 /// with type and tuple size determined at compile-time.
223 /// - Make UT_PageNum and UT_PageOff strict types when strict types are
224 /// enabled.
225 /// - Add more functions for page-level access and block (bulk sequential)
226 /// access, for easy reading/writing to/from flat arrays.
227 /// @endinternal
228 template<typename DATA_T,exint TSIZE=1,bool TABLEHARDENED=false,bool PAGESHARDENED=false,exint THEPAGEBITS=10,typename IDX_T=exint>
230 {
231 protected:
232  /// This class is just to get code to compile when DATA_T is void,
233  /// by picking any type other than void if it is void.
234  template<typename T>
235  struct NotVoid
237 
239 
240  // These friend declarations are needed for calling protected functions on hardened/cast types.
241  template<typename DATA_T2,exint TSIZE2,bool TABLEHARDENED2,bool PAGESHARDENED2,exint THEPAGEBITS2,typename IDX_T2>
242  friend class UT_PageArray;
243 public:
245  typedef DATA_T DataType;
247  static const exint theTupleSize = TSIZE;
248  static const exint theSafeTupleSize = (TSIZE==-1) ? 3 : TSIZE;
249  static const exint thePageBits = THEPAGEBITS;
250  static const exint thePageSize = (exint(1)<<thePageBits);
251  static const exint thePageMask = thePageSize-1;
252  typedef IDX_T IndexType;
253 
254  /// The default constructor can only be used if the tuple size
255  /// and storage type are known at compile time.
257  {
258  UT_ASSERT_P(TSIZE >= 1);
260  myImpl.getPages() = nullptr;
261  }
262 
263  /// This constructor can only be used if the storage type is
264  /// known at compile time, but the tuple size is not.
265  UT_PageArray(exint tuplesize)
266  {
267  UT_ASSERT_P(TSIZE == -1 || TSIZE == tuplesize);
269  // NOTE: This is one case where a tuple size of zero is okay.
270  UT_ASSERT_P(tuplesize >= 0);
271  myImpl.getPages() = nullptr;
272  myImpl.setTupleSize(tuplesize);
273  }
274 
275  /// This constructor can only be used if the storage type is
276  /// known at compile time, but the tuple size is not.
278  {
279  UT_ASSERT_P(TSIZE >= 1);
281  UT_ASSERT_P(storage != UT_Storage::INVALID);
282  myImpl.getPages() = nullptr;
283  myImpl.setStorage(storage);
284  }
285 
286  /// This constructor can only be used if the tuple size and
287  /// storage type are unknown at compile time.
289  {
290  UT_ASSERT_P(TSIZE == -1 || TSIZE == tuplesize);
292  // NOTE: This is one case where a tuple size of zero is okay.
293  UT_ASSERT_P(tuplesize >= 0);
294  UT_ASSERT_P(storage != UT_Storage::INVALID);
295  myImpl.getPages() = nullptr;
296  myImpl.setStorage(storage);
297  myImpl.setTupleSize(tuplesize);
298  }
299 
300  /// This just references that's page table, unless TABLEHARDENED
301  /// or PAGESHARDENED. If TABLEHARDENED and !PAGESHARDENED,
302  /// the pages are referenced, and if PAGESHARDENED, the pages
303  /// are copied.
304  UT_PageArray(const ThisType &that)
305  {
306  myImpl.getPages() = nullptr;
307  *this = that;
308  }
309 
310  template<typename SRC_DATA_T,exint SRC_TSIZE,bool SRC_TABLEHARDENED,bool SRC_PAGESHARDENED>
312  {
313  myImpl.getPages() = nullptr;
314  *this = that;
315  }
316 
319  {
320  clearAndDestroy();
321  }
322 
324  void clear()
325  {
326  if (myImpl.getPages())
327  myImpl.getPages()->setSize(IDX_T(0));
328  }
329 
331  {
332  if (!myImpl.getPages())
333  return;
334 
335  const exint tuplesize = myImpl.getTupleSize();
336 
337  // Just decrement the reference count to free the table safely
338  if constexpr (!SYSisSame<DATA_T,void>())
339  myImpl.getPages()->decRef(tuplesize);
340  else
341  {
342  const UT_Storage storage = myImpl.getStorage();
343  switch (storage)
344  {
345  case UT_Storage::INVALID:
346  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
347  return;
348  case UT_Storage::INT8:
349  castType<int8>().myImpl.getPages()->decRef(tuplesize); return;
350  case UT_Storage::UINT8:
351  castType<uint8>().myImpl.getPages()->decRef(tuplesize); return;
352  case UT_Storage::INT16:
353  castType<int16>().myImpl.getPages()->decRef(tuplesize); return;
354  case UT_Storage::INT32:
355  castType<int32>().myImpl.getPages()->decRef(tuplesize); return;
356  case UT_Storage::INT64:
357  castType<int64>().myImpl.getPages()->decRef(tuplesize); return;
358  case UT_Storage::REAL16:
359  castType<fpreal16>().myImpl.getPages()->decRef(tuplesize); return;
360  case UT_Storage::REAL32:
361  castType<fpreal32>().myImpl.getPages()->decRef(tuplesize); return;
362  case UT_Storage::REAL64:
363  castType<fpreal64>().myImpl.getPages()->decRef(tuplesize); return;
364  }
365  }
366  myImpl.getPages() = nullptr;
367  }
368 
369  /// NOTE: Compilers seem not to use the operator= below when the template
370  /// arguments match, so this operator= is necessary in order to
371  /// avoid the compiler silently using its implicitly-defined
372  /// assignment operator.
375  {
376  replace(that);
377  return *this;
378  }
379 
380  template<typename SRC_DATA_T,exint SRC_TSIZE,bool SRC_TABLEHARDENED,bool SRC_PAGESHARDENED>
383  {
384  replace(that);
385  return *this;
386  }
387 
388  /// Completely replace this UT_PageArray with that, referencing
389  /// the pages or table, if allowed by the template arguments.
390  template<typename SRC_DATA_T,exint SRC_TSIZE,bool SRC_TABLEHARDENED,bool SRC_PAGESHARDENED>
392  {
393  // NOTE: Careful if you add template parameter checks on this,
394  // because, e.g., a UT_PageArray<void,-1> could refer to the
395  // same exact array as a UT_PageArray<float 3>.
396  if ((void *)this == (const void *)&that)
397  return;
398  // NOTE: The storage and tuple size checks are so that if both
399  // tables are nullptr, it still copies the storage and tuple
400  // size below.
401  if (!TABLEHARDENED &&
402  myImpl.getStorage() == that.myImpl.getStorage() &&
403  myImpl.getTupleSize() == that.myImpl.getTupleSize() &&
404  (void *)myImpl.getPages() == (const void *)that.myImpl.getPages())
405  return;
406 
407  clearAndDestroy();
408 
409  if constexpr (SYSisSame<DATA_T,void>())
410  myImpl.setStorage(that.myImpl.getStorage());
411  if constexpr (TSIZE == -1)
412  myImpl.setTupleSize(that.myImpl.getTupleSize());
413 
414  // May be able to share pages or table if matching tuple size and storage type
415  if constexpr ((SYSisSame<DATA_T,void>() || SYSisSame<DATA_T,SRC_DATA_T>()) && (TSIZE == -1 || TSIZE == SRC_TSIZE))
416  {
417  PageTable *pages = reinterpret_cast<PageTable *>(SYSconst_cast(that.myImpl.getPages()));
418  myImpl.getPages() = pages;
419  if (!pages)
420  return;
421  pages->incRef();
422  if constexpr (TABLEHARDENED || PAGESHARDENED || SRC_TABLEHARDENED || SRC_PAGESHARDENED)
423  {
424  forceHardenTable(pages->capacity());
425  if (PAGESHARDENED || SRC_PAGESHARDENED)
426  {
427  // If we don't cast to PAGESHARDENED==false, it won't do anything.
429  ->template hardenAllPages<false>();
430  }
431  }
432  }
433  else
434  {
435  // Can't share table or pages, so just use moveRange
436  IDX_T capacity = that.capacity();
437  IDX_T size = that.size();
438  forceHardenTable(capacity);
439  moveRange(that, IDX_T(0), IDX_T(0), size);
440  }
441  }
442 
443  /// Read element i's specified component (default component 0).
444  SYS_FORCE_INLINE NotVoidType operator()(IDX_T i,exint component=0) const
445  {
446  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
447  UT_ASSERT_P(TSIZE != 0);
448  UT_ASSERT_P(myImpl.getTupleSize() >= 1);
449  UT_ASSERT_P(i >= IDX_T(0) && i < size());
450  UT_ASSERT_P(component >= 0 && component < myImpl.getTupleSize());
451 
452  UT_PageNum pagenum = pageNum(i);
453  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
454 
455  // If we know that the pages are hardened, we can omit
456  // the constant page case at compile time.
457  bool isconst = !PAGESHARDENED && page->isConstant();
458 
459  if (TSIZE >= 1)
460  {
461  if (!isconst)
462  {
463  UT_PageOff pageoff = pageOff(i);
464  return page->getFirstPtr()[TSIZE*pageoff + component];
465  }
466  return getConstant(page, component);
467  }
468  else
469  {
470  if (!isconst)
471  {
472  UT_PageOff pageoff = pageOff(i);
473  return page->getFirstPtr()[myImpl.getTupleSize()*pageoff + component];
474  }
475  return getConstant(page, component, myImpl.getTupleSize());
476  }
477  }
478 
479  /// This get function may seem redundant, given operator(), but it gives a
480  /// simple way to read from a non-const UT_PageArray without calling the
481  /// non-const operator() and potentially hardening pages. It just
482  /// explicitly adds const. It also allows data type conversion and
483  /// reading when the type is unknown at compile time.
484  /// It should hopefully be inlined in the no-conversion case;
485  /// it's less critical in the conversion case.
486  template<typename DEST_DATA_T=DATA_T>
487  SYS_FORCE_INLINE DEST_DATA_T get(IDX_T i,exint component=0) const
488  {
489  UT_ASSERT_P(i >= IDX_T(0) && i < size());
490  if constexpr (!(SYSisSame<DATA_T,void>()) && (SYSisSame<DEST_DATA_T,DATA_T>() || theStorage != UT_Storage::INVALID))
491  return UTconvertStorage<DEST_DATA_T>((*this)(i,component));
492  else
493  return getUnknownType<DEST_DATA_T>(i,component);
494  }
495 
496  template<typename DEST_DATA_T=NotVoidType,exint DEST_TSIZE=theSafeTupleSize>
498  {
499  if constexpr (SYSisSame<DATA_T,void>())
500  return getVectorUnknownType<DEST_DATA_T,DEST_TSIZE>(i);
501  else if constexpr (!SYSisSame<DEST_DATA_T,DATA_T>())
502  return convertVectorStorage<DEST_DATA_T>(getVector<NotVoidType,DEST_TSIZE>(i));
503 
504  if constexpr (TSIZE==-1)
505  return getVectorUnknownSize<DEST_DATA_T,DEST_TSIZE>(i);
506  else if constexpr (TSIZE!=DEST_TSIZE)
507  return getVectorMismatchedSize<DEST_DATA_T,DEST_TSIZE>(i);
508 
509  UT_ASSERT_P(i >= IDX_T(0) && i < size());
510  UT_ASSERT_P(TSIZE==DEST_TSIZE);
511  UT_ASSERT_P((SYSisSame<DEST_DATA_T,DATA_T>()));
512 
513  // Types and tuple sizes are known and match
514  UT_PageNum pagenum = pageNum(i);
515  // NOTE: We can dereference here because we're returning by value.
516  // getConstantPtr may return a pointer into this stack variable
517  // if the page is constant and inline.
518  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
519 
520  // If we know that the pages are hardened, we can omit
521  // the constant page case at compile time.
522  bool isconst = !PAGESHARDENED && page->isConstant();
523 
524  if (!isconst)
525  {
526  UT_PageOff pageoff = pageOff(i);
527  return ((const UT_FixedVector<DEST_DATA_T,DEST_TSIZE>*)page->getFirstPtr())[pageoff];
528  }
529  const NotVoidType *constdata = getConstantPtr(page);
530  if (!constdata)
531  {
533  }
534 
535  return *(const UT_FixedVector<DEST_DATA_T,DEST_TSIZE>*)constdata;
536  }
537 
538  /// Get a non-const reference to element i's specified component
539  /// (default component 0).
540  /// WARNING: DO NOT call this if DATA_T is void!
541  /// NOTE: Unlike set(), this must always harden, even if you're just
542  /// writing a value that is already there.
544  {
545  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
546  UT_ASSERT_P(TSIZE != 0);
547  UT_ASSERT_P(myImpl.getTupleSize() >= 1);
548  UT_ASSERT_P(i >= IDX_T(0) && i < size());
549  UT_ASSERT_P(component >= 0 && component < myImpl.getTupleSize());
550 
551  // NOTE: This is a no-op if TABLEHARDENED is already true.
552  hardenTable();
553 
554  UT_PageNum pagenum = pageNum(i);
555  UT_PageOff pageoff = pageOff(i);
556  PageTable *pages = myImpl.getPages();
557  PageTableEntry *page = pages->getPPage(pagenum);
558 
559  if (!PAGESHARDENED)
560  {
561  if (page->isConstant())
562  {
563  if (TSIZE >= 1)
564  hardenConstantPage(pages, page);
565  else
566  hardenConstantPage(pages, page, myImpl.getTupleSize());
567  }
568  else if (page->isShared())
569  {
570  if (TSIZE >= 1)
571  hardenSharedPage(pages, page);
572  else
573  hardenSharedPage(pages, page, myImpl.getTupleSize());
574  }
575  }
576 
577  NotVoidType *data = page->getFirstPtr()
578  + pageoff*((TSIZE >= 1) ? TSIZE : myImpl.getTupleSize())
579  + component;
580 
581  return *data;
582  }
583 
584  /// component == 0 in this version
585  template<typename SRC_DATA_T>
586  SYS_FORCE_INLINE void set(IDX_T i,SRC_DATA_T v)
587  {
588  set(i, 0, v);
589  }
590  /// component == 0 in this version
591  template<typename SRC_DATA_T>
592  SYS_FORCE_INLINE void add(IDX_T i,SRC_DATA_T v)
593  {
594  add(i, 0, v);
595  }
596 
597  /// This looks gigantic, but it's mostly because of the different
598  /// behaviour for different template values. If
599  /// PAGESHARDENED is true, it'll skip checking for constant
600  /// or shared pages altogether.
601  template<typename SRC_DATA_T>
602  SYS_FORCE_INLINE void set(IDX_T i,exint component,SRC_DATA_T v)
603  {
604  op<0>(i, component, v);
605  }
606  template<typename SRC_DATA_T>
607  SYS_FORCE_INLINE void add(IDX_T i,exint component,SRC_DATA_T v)
608  {
609  op<1>(i, component, v);
610  }
611  template<int OP,typename SRC_DATA_T>
612  SYS_FORCE_INLINE void op(IDX_T i,exint component,SRC_DATA_T v)
613  {
614  UT_ASSERT_P(i >= IDX_T(0) && i < size());
615  UT_ASSERT_P(component >= 0 && component < getTupleSize());
616  UT_ASSERT_MSG_P(OP == 0 || OP == 1, "Only OP 0 (set) and 1 (add) are defined!");
617 
618  if constexpr (SYSisSame<DATA_T,void>())
619  {
620  opUnknownType<OP>(i, component, v);
621  return;
622  }
623 
624  // Make sure that v is the correct type before comparing.
625  NotVoidType vt(UTconvertStorage<NotVoidType>(v));
626 
627  UT_ASSERT_P(TSIZE != 0);
628  UT_ASSERT_P(myImpl.getTupleSize() >= 1);
629 
630  hardenTable();
631 
632  UT_PageNum pagenum = pageNum(i);
633  UT_PageOff pageoff = pageOff(i);
634  PageTable *pages = myImpl.getPages();
635  PageTableEntry *page = pages->getPPage(pagenum);
636 
637  if constexpr (!PAGESHARDENED)
638  {
639  if (page->isConstant())
640  {
641  // If we're setting, check to see if it's a new value
642  if (OP == 0)
643  {
644  if (TSIZE >= 1)
645  {
646  if (getConstant(page, component) == vt)
647  return;
648  }
649  else
650  {
651  if (getConstant(page, component, myImpl.getTupleSize()) == vt)
652  return;
653  }
654  }
655  if (TSIZE >= 1)
656  hardenConstantPage(pages, page);
657  else
658  hardenConstantPage(pages, page, myImpl.getTupleSize());
659  }
660  else if (page->isShared())
661  {
662  // If we're setting, check to see if it's a new value
663  if (OP == 0)
664  {
665  NotVoidType *data = page->getFirstPtr()
666  + pageoff*((TSIZE >= 1) ? TSIZE : myImpl.getTupleSize())
667  + component;
668  if (*data == vt)
669  return;
670  }
671  if (TSIZE >= 1)
672  hardenSharedPage(pages, page);
673  else
674  hardenSharedPage(pages, page, myImpl.getTupleSize());
675  }
676  }
677 
678  NotVoidType *data = page->getFirstPtr()
679  + pageoff*((TSIZE >= 1) ? TSIZE : myImpl.getTupleSize())
680  + component;
681 
682  if (OP == 0)
683  *data = vt;
684  else if (OP == 1)
685  *data += vt;
686  else
687  {
688  UT_ASSERT_MSG_P(0,"Only OP 0 (set) and 1 (add) are defined!");
689  }
690  }
691 
692  template<typename TS>
693  SYS_FORCE_INLINE void setVector(IDX_T i,const TS& as)
694  {
695  opVector<0>(i, UTmakeFixedVector(as));
696  }
697 
698  template<typename TS>
699  SYS_FORCE_INLINE void addVector(IDX_T i,const TS& as)
700  {
701  opVector<1>(i, UTmakeFixedVector(as));
702  }
703 
704  /// This looks gigantic, but it's mostly because of the different
705  /// behaviour for different template values. If
706  /// PAGESHARDENED is true, it'll skip checking for constant
707  /// or shared pages altogether.
708  template<int OP,typename SRC_DATA_T,exint SRC_TSIZE>
710  {
711  UT_ASSERT_P(i >= IDX_T(0) && i < size());
712  UT_ASSERT_MSG_P(OP == 0 || OP == 1, "Only OP 0 (set) and 1 (add) are defined!");
713 
714  if constexpr (SYSisSame<DATA_T,void>())
715  {
716  opVectorUnknownType<OP>(i, v);
717  return;
718  }
719 
720  const exint tuplesize = myImpl.getTupleSize();
721  if constexpr (TSIZE == -1)
722  {
723  if (tuplesize == SRC_TSIZE)
724  {
725  castTupleSize<SRC_TSIZE>().template opVector<OP>(i, v);
726  return;
727  }
728  }
729 
730  const exint mintuplesize = SYSmin(tuplesize, SRC_TSIZE);
731 
732  // Make sure that v is the correct type before comparing.
733  // Hopefully this will avoid copying if SRC_DATA_T is NotVoidType.
734  constexpr bool TYPE_MATCH = SYSisSame<SRC_DATA_T,NotVoidType>();
736  if (!TYPE_MATCH)
737  converted_v = convertVectorStorage<NotVoidType>(v);
738 
739  UT_ASSERT_P(TSIZE != 0);
740  UT_ASSERT_P(myImpl.getTupleSize() >= 1);
741 
742  hardenTable();
743 
744  UT_PageNum pagenum = pageNum(i);
745  UT_PageOff pageoff = pageOff(i);
746  PageTable *pages = myImpl.getPages();
747  PageTableEntry *page = pages->getPPage(pagenum);
748 
749  if constexpr (!PAGESHARDENED)
750  {
751  if (page->isConstant())
752  {
753  // If we're setting, check to see if it's a new value
754  if constexpr (OP == 0)
755  {
756  const NotVoidType *data;
757  if (PageTableEntry::typeFitsInline(tuplesize))
758  data = page->getInlinePtr(tuplesize);
759  else
760  data = page->getMaskedPtr();
761  if (data)
762  {
763  if constexpr (TYPE_MATCH)
764  {
765  if (isEqual(data, v.data(), mintuplesize))
766  return;
767  }
768  else
769  {
770  if (isEqual(data, converted_v.data(), mintuplesize))
771  return;
772  }
773  }
774  else
775  {
776  if constexpr (TYPE_MATCH)
777  {
778  if (isZero(v.data(), mintuplesize))
779  return;
780  }
781  else
782  {
783  if (isZero(converted_v.data(), mintuplesize))
784  return;
785  }
786  }
787  }
788  if (TSIZE >= 1)
789  hardenConstantPage(pages, page);
790  else
791  hardenConstantPage(pages, page, tuplesize);
792  }
793  else if (page->isShared())
794  {
795  // If we're setting, check to see if it's a new value
796  if constexpr (OP == 0)
797  {
798  const NotVoidType *data = page->getFirstPtr()
799  + pageoff*((TSIZE >= 0) ? TSIZE : tuplesize);
800  if constexpr (TYPE_MATCH)
801  {
802  if (isEqual(data, v.data(), mintuplesize))
803  return;
804  }
805  else
806  {
807  if (isEqual(data, converted_v.data(), mintuplesize))
808  return;
809  }
810  }
811  if constexpr (TSIZE >= 1)
812  hardenSharedPage(pages, page);
813  else
814  hardenSharedPage(pages, page, tuplesize);
815  }
816  }
817 
818  NotVoidType *data = page->getFirstPtr() + pageoff*((TSIZE >= 0) ? TSIZE : tuplesize);
819  for (exint component = 0; component < mintuplesize; ++component)
820  {
821  if constexpr (OP == 0)
822  {
823  if constexpr (TYPE_MATCH)
824  data[component] = v[component];
825  else
826  data[component] = converted_v[component];
827  }
828  else if constexpr (OP == 1)
829  {
830  if constexpr (TYPE_MATCH)
831  data[component] += v[component];
832  else
833  data[component] += converted_v[component];
834  }
835  else
836  {
837  UT_ASSERT_MSG_P(0,"Only OP 0 (set) and 1 (add) are defined!");
838  }
839  }
840  }
841 
842  SYS_FORCE_INLINE IDX_T size() const
843  {
844  const PageTable *pages = myImpl.getPages();
845  if (!pages)
846  return IDX_T(0);
847  return pages->size();
848  }
849 
851  {
852  const PageTable *pages = myImpl.getPages();
853  if (!pages)
854  return IDX_T(0);
855  return pages->capacity();
856  }
857 
858  /// This helper function forces a (non-negative) capacity to be an
859  /// acceptable value: either
860  /// A) a power of two less than a full page in size, or
861  /// B) a multiple of the page size.
863  static IDX_T roundUpCapacity(IDX_T capacity)
864  {
865  UT_ASSERT_P(capacity >= IDX_T(0));
866 
867  // Round up to page size if more than half of one page.
868  if (capacity > IDX_T(thePageSize>>1))
869  return IDX_T((capacity + thePageMask) & ~thePageMask);
870 
871  // Round up to power of two if half a page or less.
872  // (Zero stays zero.)
873  if (capacity <= IDX_T(2))
874  return capacity;
875  return IDX_T(SYSmakePow2(exint(capacity)));
876  }
877 
878  void setCapacity(IDX_T newcapacity)
879  {
880  UT_ASSERT_P(newcapacity >= IDX_T(0));
881  newcapacity = roundUpCapacity(newcapacity);
882  if (newcapacity == capacity())
883  return;
884  if (newcapacity < size())
885  {
886  setSize(newcapacity);
887  }
888  forceHardenTable(newcapacity);
889  }
890 
891  void setCapacityIfNeeded(IDX_T newcapacity)
892  {
893  UT_ASSERT_P(newcapacity >= IDX_T(0));
894  if (newcapacity <= capacity())
895  return;
896  newcapacity = roundUpCapacity(newcapacity);
897  forceHardenTable(newcapacity);
898  }
899 
900  /// Sets the size of the array without initializing any added
901  /// elements.
902  void setSize(IDX_T newsize)
903  {
904  UT_ASSERT_P(newsize >= IDX_T(0));
905 
906  setCapacityIfNeeded(newsize);
907  hardenTable();
908  PageTable *pages = myImpl.getPages();
909  UT_ASSERT_P(pages || newsize == IDX_T(0));
910  if (pages)
911  pages->setSize(newsize);
912  }
913 
914  /// Sets the size of the array and initializes any new elements to initval.
915  /// This version of setSize only works if DATA_T is a numeric type.
916  /// If the tuple size is > 1, it will write initval to all components.
917  /// Include UT_PageArrayImpl.h to call this.
918  void setSize(IDX_T newsize, NotVoidType initval);
919 
920  /// Sets the size of the array and initializes any new elements
921  /// to initval.
922  /// This version of setSize only works if DATA_T is a numeric type
923  /// and TSIZE >= 1.
924  /// Include UT_PageArrayImpl.h to call this.
925  void setSize(IDX_T newsize, const UT_FixedVector<NotVoidType,theSafeTupleSize> &initval);
926 
927  /// Sets the size of the array and initializes any new elements to initval.
928  /// Include UT_PageArrayImpl.h to call this.
929  void setSize(IDX_T newsize, const UT_Defaults &initval);
930 
931  /// Set all of the values in the range [start,end) to v.
932  /// This will constant-compress pages if allowed and the pages are
933  /// completely overwritten with the given value.
934  /// This version of setConstant only works if DATA_T is a numeric type.
935  /// If the tuple size is > 1, it will write v to all components.
936  void setConstant(IDX_T start, IDX_T end, NotVoidType v);
937 
938  /// Set all of the values in the range [start,end) to v.
939  /// This will constant-compress pages if allowed and the pages are
940  /// completely overwritten with the given value.
941  /// This version of setConstant only works if DATA_T is a numeric type
942  /// and TSIZE >= 1.
944 
945  /// Set all of the values in the range [start,end) to v.
946  /// This will constant-compress pages if allowed and the pages are
947  /// completely overwritten with the given value.
948  void setConstant(IDX_T start, IDX_T end, const UT_Defaults &v);
949 
950  /// Set all of the values in the range [start,end) to the tuple in values.
951  /// values must have length equal to getTupleSize().
952  /// This will constant-compress pages if allowed and the pages are
953  /// completely overwritten with the given value.
954  template<typename SRC_T>
955  void setConstant(IDX_T start, IDX_T end, const SRC_T *values);
956 
957  /// Changes the storage type and converts the data to the new type.
958  /// NOTE: This only makes sense if DATA_T is void and newstorage is
959  /// something other than INVALID.
960  /// Include UT_PageArrayImpl.h to call this.
961  void setStorage(const UT_Storage newstorage);
962 
963  /// Changes the tuple size, and if the new size is larger, pads with
964  /// values from the UT_Defaults.
965  /// NOTE: This only makes sense if TSIZE == -1.
966  /// Include UT_PageArrayImpl.h to call this.
967  void setTupleSize(exint newtuplesize, const UT_Defaults &v);
968 
969  /// Get the total of all memory referred-to by this.
970  /// NOTE: This does not handle shared memory correctly.
971  /// Include UT_PageArrayImpl.h to call this.
972  int64 getMemoryUsage(bool inclusive) const;
973 
974  /// Count memory usage using a UT_MemoryCounter in order to count
975  /// shared memory correctly.
976  /// If inclusive is true, the size of this object is counted,
977  /// else only memory owned by this object is counted.
978  /// If this is pointed to by the calling object, inclusive should be true.
979  /// If this is contained in the calling object, inclusive should be false.
980  /// (Its memory was already counted in the size of the calling object.)
981  /// Include UT_PageArrayImpl.h to call this.
982  void countMemory(UT_MemoryCounter &counter, bool inclusive) const;
983 
984  /// Copies the nelements from srcstart to deststart
985  /// The two ranges *are* allowed to overlap.
986  /// Include UT_PageArrayImpl.h to call this.
987  void moveRange(IDX_T srcstart, IDX_T deststart, IDX_T nelements);
988 
989  /// Copies the nelements from fromstart in src to tostart in this.
990  /// The two ranges *are* allowed to overlap, even if &src==this.
991  /// Include UT_PageArrayImpl.h to call this.
992  template<typename SRC_DATA_T,exint SRC_TSIZE,bool SRC_TABLEHARDENED,bool SRC_PAGESHARDENED>
993  void moveRange(const UT_PageArray<SRC_DATA_T,SRC_TSIZE,SRC_TABLEHARDENED,SRC_PAGESHARDENED,THEPAGEBITS,IDX_T> &src, IDX_T srcstart, IDX_T deststart, IDX_T nelements);
994 
995  /// Swaps the nelements at astart with the nelements at bstart.
996  /// The two ranges *must* be non-overlapping, else swapping is ill-defined.
997  /// Include UT_PageArrayImpl.h to call this.
998  void swapRange(IDX_T astart, IDX_T bstart, IDX_T nelements);
999 
1000  /// Copies a single element from src to dest within this
1001  void copy(IDX_T dest, IDX_T src)
1002  {
1003  UT_ASSERT_P(dest >= IDX_T(0) && dest < size());
1004  UT_ASSERT_P(src >= IDX_T(0) && src < size());
1005 
1006  hardenTable();
1007 
1008  // Because no conversions are necessary, only the element size
1009  // in bytes is needed.
1010  const exint bytesize = (SYSisSame<DATA_T,void>() ? UTstorageSize(getStorage()) : sizeof(NotVoidType))
1011  * getTupleSize();
1012 
1013  UT_PageNum destpagenum = pageNum(dest);
1014  UT_PageNum srcpagenum = pageNum(src);
1015 
1016  PageTable *pages = myImpl.getPages();
1017  PageTableEntry *destpage = pages->getPPage(destpagenum);
1018  const PageTableEntry *srcpage = pages->getPPage(srcpagenum);
1019 
1020  const int8 *srcdata;
1021  if (srcpage->isConstant())
1022  {
1023  if (bytesize < sizeof(PageTableEntry))
1024  {
1025  // Copying within equal constant pages does nothing
1026  if (*srcpage == *destpage)
1027  return;
1028 
1029  srcdata = ((const int8 *)(srcpage+1)) - bytesize;
1030  }
1031  else if (srcpage->getUnmaskedPtrVoid() == destpage->getUnmaskedPtrVoid())
1032  {
1033  // Copying within equal constant pages does nothing
1034  return;
1035  }
1036  else
1037  {
1038  // NOTE: srcdata may be set to nullptr here.
1039  srcdata = (const int8 *)srcpage->getMaskedPtrVoid();
1040  }
1041  }
1042  else
1043  {
1044  srcdata = ((const int8*)srcpage->getFirstPtrVoid()) + pageOff(src)*bytesize;
1045  }
1046 
1047  int8 *destdata;
1048  if (destpage->isConstant())
1049  {
1050  if (bytesize < sizeof(PageTableEntry))
1051  destdata = ((int8 *)(destpage+1)) - bytesize;
1052  else
1053  {
1054  void *destmasked = destpage->getMaskedPtrVoid();
1055  if (destmasked == nullptr)
1056  {
1057  if (isZero(srcdata, bytesize))
1058  return;
1059  destdata = nullptr;
1060  }
1061  else
1062  destdata = (int8*)destmasked;
1063  }
1064  if (destdata != nullptr && isEqual(srcdata, destdata, bytesize))
1065  return;
1066 
1067  if constexpr (!SYSisSame<DATA_T,void>())
1068  hardenConstantPage(pages, destpage, getTupleSize());
1069  else
1070  {
1071  switch (getStorage())
1072  {
1073  case UT_Storage::INT8:
1074  case UT_Storage::UINT8:
1075  {
1076  auto &a = castType<int8>();
1077  auto a_pages = a.myImpl.getPages();
1078  auto a_destpage = a_pages->getPPage(destpagenum);
1079  a.hardenConstantPage(a_pages, a_destpage, getTupleSize());
1080  break;
1081  }
1082  case UT_Storage::INT16:
1083  case UT_Storage::REAL16:
1084  {
1085  auto &a = castType<int16>();
1086  auto a_pages = a.myImpl.getPages();
1087  auto a_destpage = a_pages->getPPage(destpagenum);
1088  a.hardenConstantPage(a_pages, a_destpage, getTupleSize());
1089  break;
1090  }
1091  case UT_Storage::INT32:
1092  case UT_Storage::REAL32:
1093  {
1094  auto &a = castType<int32>();
1095  auto a_pages = a.myImpl.getPages();
1096  auto a_destpage = a_pages->getPPage(destpagenum);
1097  a.hardenConstantPage(a_pages, a_destpage, getTupleSize());
1098  break;
1099  }
1100  case UT_Storage::INT64:
1101  case UT_Storage::REAL64:
1102  {
1103  auto &a = castType<int64>();
1104  auto a_pages = a.myImpl.getPages();
1105  auto a_destpage = a_pages->getPPage(destpagenum);
1106  a.hardenConstantPage(a_pages, a_destpage, getTupleSize());
1107  break;
1108  }
1109  case UT_Storage::INVALID:
1110  UT_ASSERT_MSG(0, "Storage must be valid if DATA_T is void.");
1111  break;
1112  }
1113  }
1114  }
1115  else if (!PAGESHARDENED && destpage->isShared())
1116  {
1117  destdata = ((int8*)destpage->getFirstPtrVoid()) + pageOff(dest)*bytesize;
1118  if (srcdata)
1119  {
1120  if (isEqual(srcdata, destdata, bytesize))
1121  return;
1122  }
1123  else
1124  {
1125  if (isZero(destdata, bytesize))
1126  return;
1127  }
1128 
1129  if constexpr (!SYSisSame<DATA_T,void>())
1130  hardenSharedPage(pages, destpage, getTupleSize());
1131  else
1132  {
1133  switch (getStorage())
1134  {
1135  case UT_Storage::INT8:
1136  case UT_Storage::UINT8:
1137  {
1138  auto &a = castType<int8>();
1139  auto a_pages = a.myImpl.getPages();
1140  auto a_destpage = a_pages->getPPage(destpagenum);
1141  a.hardenSharedPage(a_pages, a_destpage, getTupleSize());
1142  break;
1143  }
1144  case UT_Storage::INT16:
1145  case UT_Storage::REAL16:
1146  {
1147  auto &a = castType<int16>();
1148  auto a_pages = a.myImpl.getPages();
1149  auto a_destpage = a_pages->getPPage(destpagenum);
1150  a.hardenSharedPage(a_pages, a_destpage, getTupleSize());
1151  break;
1152  }
1153  case UT_Storage::INT32:
1154  case UT_Storage::REAL32:
1155  {
1156  auto &a = castType<int32>();
1157  auto a_pages = a.myImpl.getPages();
1158  auto a_destpage = a_pages->getPPage(destpagenum);
1159  a.hardenSharedPage(a_pages, a_destpage, getTupleSize());
1160  break;
1161  }
1162  case UT_Storage::INT64:
1163  case UT_Storage::REAL64:
1164  {
1165  auto &a = castType<int64>();
1166  auto a_pages = a.myImpl.getPages();
1167  auto a_destpage = a_pages->getPPage(destpagenum);
1168  a.hardenSharedPage(a_pages, a_destpage, getTupleSize());
1169  break;
1170  }
1171  case UT_Storage::INVALID:
1172  UT_ASSERT_MSG(0, "Storage must be valid if DATA_T is void.");
1173  break;
1174  }
1175  }
1176  }
1177  destdata = ((int8*)destpage->getFirstPtrVoid()) + pageOff(dest)*bytesize;
1178 
1179  for (const int8 *srcdataend = srcdata+bytesize; srcdata != srcdataend; ++srcdata, ++destdata)
1180  *destdata = *srcdata;
1181  }
1182 
1183  /// Copies nelements from srcstart in this to dest array
1184  /// Include UT_PageArrayImpl.h to call this.
1185  template<typename T>
1186  SYS_FORCE_INLINE void getRange(IDX_T srcstart, IDX_T nelements, T *dest) const
1187  {
1188  // Always cast to associated UT_FixedVector type, so that
1189  // we can just delegate to getVectorRange that always uses UT_FixedVector
1190  getVectorRange(srcstart, nelements, (typename UT_FixedVectorTraits<T>::FixedVectorType*)dest);
1191  }
1192 
1193  /// Copies nelements from src array to deststart in this
1194  /// Include UT_PageArrayImpl.h to call this.
1195  template<typename T>
1196  SYS_FORCE_INLINE void setRange(IDX_T deststart, IDX_T nelements, const T *src)
1197  {
1198  // Always cast to associated UT_FixedVector type, so that
1199  // we can just delegate to getVectorRange that always uses UT_FixedVector
1200  setVectorRange(deststart, nelements, (const typename UT_FixedVectorTraits<T>::FixedVectorType*)src);
1201  }
1202 
1203  /// Copies nelements from srcstart in this to dest array
1204  /// Include UT_PageArrayImpl.h to call this.
1205  template<typename DEST_DATA_T,exint DEST_TSIZE>
1206  void getVectorRange(IDX_T srcstart, IDX_T nelements, UT_FixedVector<DEST_DATA_T,DEST_TSIZE> *dest) const;
1207 
1208  /// Copies nelements from src array to deststart in this
1209  /// Include UT_PageArrayImpl.h to call this.
1210  template<typename SRC_DATA_T,exint SRC_TSIZE>
1211  void setVectorRange(IDX_T deststart, IDX_T nelements, const UT_FixedVector<SRC_DATA_T,SRC_TSIZE> *src);
1212 
1214  {
1215  return UT_PageNum(exint(i) >> thePageBits);
1216  }
1217  SYS_FORCE_INLINE static UT_PageNum numPages(IDX_T nelements)
1218  {
1219  return UT_PageNum((exint(nelements)+thePageMask) >> thePageBits);
1220  }
1222  {
1223  return UT_PageOff(exint(i) & thePageMask);
1224  }
1226  {
1227  return IDX_T(exint(page) << thePageBits);
1228  }
1229 
1230  /// Returns true iff the table isn't shared with any other UT_PageArray's.
1232  {
1233  // If TABLEHARDENED, assert that the page table hasn't become shared
1234  // after the fact! Nobody should share the page table after templating
1235  // on having hardened!
1236  UT_ASSERT_P(!TABLEHARDENED || !myImpl.getPages() || !myImpl.getPages()->isShared());
1237 
1238  return TABLEHARDENED || !myImpl.getPages() || !myImpl.getPages()->isShared();
1239  }
1240 
1241  /// This ensures that the table itself is not shared.
1242  /// It's a good idea to do this before writing, to avoid having to check
1243  /// every time. Remember to use the returned reference, since it
1244  /// records in the template that it doesn't need to be hardened again.
1245  /// It should be a no-op if TABLEHARDENED is already true.
1247  {
1248  if constexpr (!TABLEHARDENED)
1249  {
1250  if (!isTableHardened())
1251  {
1252  // Keep same capacity
1253  forceHardenTable(myImpl.getPages()->capacity());
1254  }
1255  }
1256 
1258  }
1259 
1260  /// This forces all pages to be hardened, optionally including pages
1261  /// that are in the capacity but outside the size.
1262  template <bool including_capacity=false>
1264  {
1265  // Nothing to do if PAGESHARDENED
1266  PageTable *pages = myImpl.getPages();
1267  if (PAGESHARDENED || (pages == nullptr))
1269 
1270  auto &hard = hardenTable();
1271 
1272  if (end < IDX_T(0))
1273  end = including_capacity ? pages->capacity() : pages->size();
1274 
1275  UT_ASSERT_P(start >= IDX_T(0));
1277 
1278  if constexpr (!SYSisSame<DATA_T,void>())
1279  {
1280  if constexpr (TSIZE >= 1)
1281  pages->hardenAllPages(start, end);
1282  else
1283  pages->hardenAllPages(start, end, myImpl.getTupleSize());
1284  }
1285  else
1286  {
1287  // Don't want to have to re-check that the table is hardened,
1288  // so call hardenAllPagesUnknownType on hard.
1289  hard.hardenAllPagesUnknownType(start, end);
1290  }
1291 
1293  }
1294 
1295  template <bool including_capacity=false>
1296  SYS_FORCE_INLINE void tryCompressAllPages(IDX_T start = IDX_T(0), IDX_T end = IDX_T(-1))
1297  {
1298  // Can't have compressed pages if PAGESHARDENED
1299  PageTable *pages = myImpl.getPages();
1300  if (PAGESHARDENED || (pages == nullptr))
1301  return;
1302 
1303  auto &hard = hardenTable();
1304 
1305  if (end < IDX_T(0))
1306  end = including_capacity ? pages->capacity() : pages->size();
1307 
1308  UT_ASSERT_P(start >= IDX_T(0));
1310 
1311  if constexpr (!SYSisSame<DATA_T,void>())
1312  {
1313  if constexpr (TSIZE >= 1)
1314  pages->tryCompressAllPages(start, end);
1315  else
1316  pages->tryCompressAllPages(start, end, myImpl.getTupleSize());
1317  }
1318  else
1319  {
1320  // Don't want to have to re-check that the table is hardened,
1321  // so call hardenAllPagesUnknownType on hard.
1322  hard.tryCompressAllPagesUnknownType(start, end);
1323  }
1324  }
1325 
1326  /// This is for setting the DATA_T template after checking getStorage(),
1327  /// if DATA_T wasn't already known.
1328  /// @{
1329  template<typename DEST_DATA_T>
1331  castType() const
1332  {
1334  UT_ASSERT_P((SYSisSame<DEST_DATA_T,DATA_T>()) || (getStorage() != UT_Storage::INVALID && getStorage() == UT_StorageNum<DEST_DATA_T>::theStorage));
1336  }
1337  template<typename DEST_DATA_T>
1340  {
1342  UT_ASSERT_P((SYSisSame<DEST_DATA_T,DATA_T>()) || (getStorage() != UT_Storage::INVALID && getStorage() == UT_StorageNum<DEST_DATA_T>::theStorage));
1344  }
1345  /// @}
1346 
1347  /// This is for setting the TSIZE template after checking getTupleSize(),
1348  /// if TSIZE wasn't already known.
1349  /// @{
1350  template<exint DEST_TSIZE>
1353  {
1354  UT_ASSERT_P(TSIZE == -1 || TSIZE == DEST_TSIZE);
1355  UT_ASSERT_P(getTupleSize() == DEST_TSIZE);
1357  }
1358  template<exint DEST_TSIZE>
1361  {
1362  UT_ASSERT_P(TSIZE == -1 || TSIZE == DEST_TSIZE);
1363  UT_ASSERT_P(getTupleSize() == DEST_TSIZE);
1365  }
1366  /// @}
1367 
1369  {
1370  return myImpl.getTupleSize();
1371  }
1373  {
1374  return myImpl.getStorage();
1375  }
1376 
1377  /// Returns true iff the specified page is constant-compressed
1379  {
1380  UT_ASSERT_P(myImpl.getPages() != nullptr);
1381  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1382  return myImpl.getPages()->getPPage(pagenum)->isConstant();
1383  }
1384 
1385  /// Returns true iff the entire array is constant & zero
1387  {
1388  // Zero capacity array is zero:
1389  if (myImpl.getPages() == nullptr)
1390  return true;
1391 
1392  const PageTable *pages = myImpl.getPages();
1393 
1394  UT_PageNum npages = numPages(pages->capacity());
1395  for (UT_PageNum pagenum = 0; pagenum < npages; pagenum++)
1396  {
1397  const PageTableEntry *page = pages->getPPage(pagenum);
1398  if (!page->isConstantAndZeroSafe())
1399  return false;
1400  }
1401  return true;
1402  }
1403 
1404  /// Sets all components of all elements of the specified page to
1405  /// the given value.
1407  {
1408  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1409  UT_ASSERT_P(myImpl.getPages() != nullptr);
1410  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1411 
1412  hardenTable();
1413 
1414  PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1415  if (TSIZE >= 1)
1416  makeConstant(page, val);
1417  else
1418  makeConstant(page, val, myImpl.getTupleSize());
1419  }
1420 
1421  /// Sets all components of all elements of the specified page to
1422  /// the given value.
1423  template<typename SRC_DATA_T>
1425  {
1426  if constexpr (SYSisSame<DATA_T,void>())
1427  {
1428  setPageConstantUnknownType(pagenum, val);
1429  return;
1430  }
1431 
1432  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1433 
1434  if constexpr (!SYSisSame<DATA_T,SRC_DATA_T>())
1435  {
1437  for (exint i = 0; i < theSafeTupleSize; ++i)
1438  new_val[i] = UTconvertStorage<NotVoidType>(val[i]);
1439  setPageConstant(pagenum, new_val);
1440 
1441  return;
1442  }
1443 
1444  UT_ASSERT_P((SYSisSame<DATA_T,SRC_DATA_T>()));
1445  UT_ASSERT_P(myImpl.getPages() != nullptr);
1446  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1447  UT_ASSERT_P(TSIZE >= 1);
1448 
1449  hardenTable();
1450 
1451  PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1452  makeConstant(page, val);
1453  }
1454 
1455  /// Sets all components of all elements of the specified page to
1456  /// the given values.
1457  /// NOTE: The length of values must be equal to the tuple size.
1458  template<typename SRC_DATA_T>
1459  SYS_FORCE_INLINE void setPageConstant(UT_PageNum pagenum, const SRC_DATA_T *values)
1460  {
1461  if constexpr (SYSisSame<DATA_T,void>())
1462  {
1463  setPageConstantUnknownType(pagenum, values);
1464  return;
1465  }
1466 
1467  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1468 
1469  if constexpr (!SYSisSame<DATA_T,SRC_DATA_T>())
1470  {
1471  setPageConstantMismatchedType(pagenum, values);
1472  return;
1473  }
1474 
1475  UT_ASSERT_P((SYSisSame<DATA_T,SRC_DATA_T>()));
1476  UT_ASSERT_P(myImpl.getPages() != nullptr);
1477  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1478 
1479  hardenTable();
1480 
1481  PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1482  // NOTE: The cast will never actually be hit unless DATA_T is SRC_DATA_T,
1483  // as asserted above. It's just to get this to compile in other cases.
1484  makeConstant(page, (const NotVoidType *)values, myImpl.getTupleSize());
1485  }
1486 
1487  /// Returns true iff the specified page is "hard",
1488  /// i.e. NOT constant-compressed and NOT shared.
1489  /// NOTE: The *table* may or may not be hard.
1491  {
1492  UT_ASSERT_P(myImpl.getPages() != nullptr);
1493  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1494  if constexpr (PAGESHARDENED)
1495  return true;
1496  else
1497  {
1498  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1499  return !page->isConstant() && !page->isShared();
1500  }
1501  }
1502 
1503  /// Hardens the specified page if it is constant-compressed or shared.
1504  /// It returns a pointer to the data, but it will only be the correct
1505  /// type if DATA_T is not void.
1507  {
1508  UT_ASSERT_P(myImpl.getPages() != nullptr);
1509  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1510 
1511  hardenTable();
1512 
1513  PageTable *pages = myImpl.getPages();
1514  PageTableEntry *page = pages->getPPage(pagenum);
1515 
1516  if constexpr (PAGESHARDENED)
1517  return page->getFirstPtr();
1518 
1519  if (page->isConstant())
1520  hardenConstantPage(pages, page, myImpl.getTupleSize());
1521  else if (page->isShared())
1522  hardenSharedPage(pages, page, myImpl.getTupleSize());
1523  return page->getFirstPtr();
1524  }
1525 
1527  {
1528  UT_ASSERT_P(myImpl.getPages() != nullptr);
1529  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1530 
1531  hardenTable();
1532 
1533  PageTable *pages = myImpl.getPages();
1534  PageTableEntry *page = pages->getPPage(pagenum);
1535 
1536  if constexpr (PAGESHARDENED)
1537  return page->getFirstPtr();
1538 
1539  if (page->isConstant())
1540  hardenConstantPageNoInit(pages, page, myImpl.getTupleSize());
1541  else if (page->isShared())
1542  hardenSharedPageNoInit(pages, page, myImpl.getTupleSize());
1543  return page->getFirstPtr();
1544  }
1545 
1546  /// If the specified page is constant and shared, (only relevant for
1547  /// nonzero tuples that are at least the size of a pointer), this
1548  /// keeps the page constant, but unshares it, so that the constant value
1549  /// can be modified.
1550  /// NOTE: This should ONLY be called on constant pages! Check
1551  /// isPageConstant(pagenum).
1553  {
1554  UT_ASSERT_P(myImpl.getPages() != nullptr);
1555  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1556 
1557  hardenTable();
1558 
1559  PageTable *pages = myImpl.getPages();
1560  PageTableEntry *page = pages->getPPage(pagenum);
1561 
1562  UT_ASSERT_P(!PAGESHARDENED && page->isConstant());
1563 
1564  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1565  UT_ASSERT_P(TSIZE == -1 || TSIZE >= 0);
1566 
1567  const exint tuplesize = (TSIZE >= 0) ? TSIZE : myImpl.getTupleSize();
1568  if (PageTableEntry::typeFitsInline(tuplesize))
1569  return page->getInlinePtr(tuplesize);
1570 
1571  NotVoidType *src = page->getMaskedPtr();
1572  if (!page->isShared())
1573  return src;
1574 
1575  PageTableEntry newpage;
1576  newpage.alloc(UT_PageOff(1), tuplesize);
1577  NotVoidType *dest = newpage.getFirstPtr();
1578  memcpy(dest, src, tuplesize*sizeof(NotVoidType));
1579  newpage.setConstantBit();
1580  page->decRef();
1581  (*page) = newpage;
1582 
1583  return dest;
1584  }
1585 
1586  /// Checks if the specified page is all a single value,
1587  /// and if so, makes it a constant page.
1589  {
1590  // Can't have compressed pages if PAGESHARDENED
1591  PageTable *pages = myImpl.getPages();
1592  if (PAGESHARDENED || (pages == nullptr))
1593  return;
1594 
1595  auto &hard = hardenTable();
1596 
1597  UT_ASSERT_P(pagenum >= UT_PageNum(0));
1598  UT_ASSERT_P(pagenum < numPages(pages->capacity()));
1599 
1600  if constexpr (!SYSisSame<DATA_T,void>())
1601  {
1602  if constexpr (TSIZE >= 1)
1603  pages->tryCompressPage(pagenum);
1604  else
1605  pages->tryCompressPage(pagenum, myImpl.getTupleSize());
1606  }
1607  else
1608  {
1609  // Don't want to have to re-check that the table is hardened,
1610  // so call hardenAllPagesUnknownType on hard.
1611  hard.tryCompressPageUnknownType(pagenum);
1612  }
1613  }
1614 
1615  /// Returns a pointer to the data of a page.
1616  /// WARNING: DO NOT call this if DATA_T is void! The pointer returned
1617  /// can depend on the type.
1618  /// This will return nullptr if the page is constant compressed to zero.
1619  /// If it's non-nullptr and isPageConstant(pagenum) returns true, there
1620  /// will only be one element (tuple) at that address.
1621  const NotVoidType *getPageData(UT_PageNum pagenum) const
1622  {
1623  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1624  UT_ASSERT_P(myImpl.getPages() != nullptr);
1625  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1626  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1627  if (!page->isConstant())
1628  return page->getFirstPtr();
1629  else if constexpr (TSIZE >= 1)
1630  return getConstantPtr(page);
1631  else
1632  return getConstantPtr(page, 0, getTupleSize());
1633  }
1634 
1635  /// Returns a pointer to the data of a page.
1636  /// WARNING: DO NOT call this if DATA_T is void! The pointer returned
1637  /// can depend on the type.
1638  /// NOTE: DO NOT MODIFY THE DATA if there's any chance the page or the table
1639  /// might be shared. Check isTableHard() and isPageHard(pagenum).
1640  /// This will return nullptr if the page is constant compressed to zero.
1641  /// If it's non-nullptr and isPageConstant(pagenum) returns true, there
1642  /// will only be one element (tuple) at that address.
1644  {
1645  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1646  UT_ASSERT_P(myImpl.getPages() != nullptr);
1647  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1648  PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1649  if (!page->isConstant())
1650  return page->getFirstPtr();
1651  else if constexpr (TSIZE >= 1)
1652  return getConstantPtr(page);
1653  else
1654  return getConstantPtr(page, 0, getTupleSize());
1655  }
1656 
1657  /// NOTE: Don't use this unless you know what you're doing!
1658  /// This is for the case where a non-POD type that requires destruction
1659  /// is masquerading as a POD type in here, e.g. GA_OffsetList as
1660  /// UT_FixedVector<int64,2>. When destroying a table, this first needs
1661  /// to be called. If it returns true, the table was shared,
1662  /// so it was just decRef'd, meaning that the data don't need to be
1663  /// destructed. If it returns false, the table was not shared,
1664  /// so the data need to be destructed before deleting the table.
1666  {
1667  if (TABLEHARDENED)
1668  return false;
1669 
1670  PageTable *table = myImpl.getPages();
1671  if (!table)
1672  return false;
1673 
1674  bool decremented = table->decRefIffShared();
1675  if (decremented)
1676  myImpl.getPages() = nullptr;
1677  return decremented;
1678  }
1679 
1680  /// NOTE: Don't use this unless you know what you're doing!
1681  /// This is for the case where a non-POD type that requires destruction
1682  /// is masquerading as a POD type in here, e.g. GA_OffsetList as
1683  /// UT_FixedVector<int64,2>. When destroying a page, this first needs
1684  /// to be called. If it returns true, the page was shared,
1685  /// so it was just decRef'd, meaning that the data don't need to be
1686  /// destructed. If it returns false, the page was not shared,
1687  /// so the data need to be destructed before deleting the page.
1689  {
1690  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
1691  UT_ASSERT_P(TSIZE >= 1);
1692  UT_ASSERT_P(myImpl.getPages() != nullptr);
1693  UT_ASSERT_P((exint)pagenum >= 0 && IDX_T((exint)pagenum << thePageBits) < size());
1694 
1695  if (PAGESHARDENED)
1696  return false;
1697 
1698  hardenTable();
1699 
1700  PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1701  if (page->isConstant())
1702  {
1703  if (sizeof(NotVoidType)*TSIZE < sizeof(PageTableEntry))
1704  return false;
1705  }
1706  bool decremented = page->decRefIffShared();
1707  if (decremented)
1708  page->initZero();
1709  return decremented;
1710  }
1711 
1712  /// Only call this if DATA_T is void and you are absolutely, 100% CERTAIN
1713  /// that the storage type is int16, int32, or int64.
1714  /// NOTE: Really! Be careful!
1716  int64 getGuaranteedInt(IDX_T i) const
1717  {
1718  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
1719  UT_ASSERT_P(TSIZE == 1);
1721 
1722  UT_PageNum pagenum = pageNum(i);
1723  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1724 
1725  // If we know that the pages are hardened, we can omit
1726  // the constant page case at compile time.
1727  bool isconst = !PAGESHARDENED && page->isConstant();
1728 
1729  const UT_Storage storage = getStorage();
1730  if (!isconst)
1731  {
1732  UT_PageOff pageoff = pageOff(i);
1733  const void *first = page->getFirstPtrVoid();
1734  switch (storage)
1735  {
1736  default:
1737  UT_ASSERT_MSG_P(0, "getGuaranteedInt() only supports int16, int32, and int64.");
1739  case UT_Storage::INT16:
1740  return (int64)((int16*)first)[pageoff];
1741  case UT_Storage::INT32:
1742  return (int64)((int32*)first)[pageoff];
1743  case UT_Storage::INT64:
1744  return ((int64*)first)[pageoff];
1745  }
1746  }
1747 
1748  // Special case for zero.
1749  if (!page->isConstantZero())
1750  return int64(0);
1751 
1752  // Special case for if the constant value is too big to fit in a PageTableEntry.
1753  // At the moment, PageTableEntry is 16 bytes on 64-bit platforms,
1754  // and 8 bytes on 32-bit platforms, so this case will not occur.
1755  if ((sizeof(PageTableEntry) < 9 && storage == UT_Storage::INT64) ||
1756  (sizeof(PageTableEntry) < 5 && storage == UT_Storage::INT32))
1757  {
1758  const void *v = page->getMaskedPtrVoid();
1759  if (sizeof(PageTableEntry) < 5 && storage == UT_Storage::INT32)
1760  {
1761  // This will not be run on 64-bit platforms
1762  return (int64)*(const int32 *)v;
1763  }
1764  return *(const int64 *)v;
1765  }
1766  else
1767  {
1768  // Any types that are strictly smaller than a pointer will be stored inline.
1769  // This is checked at compile time, so there'll be no op at runtime.
1770  if (sizeof(PageTableEntry) >= 9 && storage == UT_Storage::INT64)
1771  {
1772  return *page->template castType<int64>()->getInlinePtr(1);
1773  }
1774  if (sizeof(PageTableEntry) >= 5 && storage == UT_Storage::INT32)
1775  {
1776  return (int64)*page->template castType<int32>()->getInlinePtr(1);
1777  }
1778  return (int64)*page->template castType<int16>()->getInlinePtr(1);
1779  }
1780  }
1781 
1782  /// Returns true iff there is at least one NaN value in the given range.
1783  /// Include UT_PageArrayImpl.h to call this.
1784  bool hasNanInRange(IDX_T start, IDX_T end) const;
1785 
1786 protected:
1787  template<typename DEST_DATA_T=DATA_T>
1788  DEST_DATA_T getUnknownType(IDX_T i,exint component=0) const
1789  {
1790  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
1791  UT_ASSERT_P(TSIZE != 0);
1792 
1793  // Make sure common work is extracted out where possible,
1794  // to avoid duplication.
1795 
1796  UT_PageNum pagenum = pageNum(i);
1797  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
1798 
1799  // If we know that the pages are hardened, we can omit
1800  // the constant page case at compile time.
1801  bool isconst = !PAGESHARDENED && page->isConstant();
1802 
1803  const UT_Storage storage = getStorage();
1804  if (!isconst)
1805  {
1806  UT_PageOff pageoff = pageOff(i);
1807  const void *first = page->getFirstPtrVoid();
1808  exint i;
1809  if (TSIZE == 1)
1810  i = (exint)pageoff;
1811  else if (TSIZE > 1)
1812  i = TSIZE*pageoff + component;
1813  else
1814  i = myImpl.getTupleSize()*pageoff + component;
1815  switch (storage)
1816  {
1817  case UT_Storage::INVALID:
1818  UT_ASSERT_MSG_P(0, "Can't read a non-numeric type with a cast to a different type.");
1819  return DEST_DATA_T();
1820  case UT_Storage::INT8:
1821  return (DEST_DATA_T)((int8*)first)[i];
1822  case UT_Storage::UINT8:
1823  return (DEST_DATA_T)((uint8*)first)[i];
1824  case UT_Storage::INT16:
1825  return (DEST_DATA_T)((int16*)first)[i];
1826  case UT_Storage::INT32:
1827  return (DEST_DATA_T)((int32*)first)[i];
1828  case UT_Storage::INT64:
1829  return (DEST_DATA_T)((int64*)first)[i];
1830  case UT_Storage::REAL16:
1831  return (DEST_DATA_T)((fpreal16*)first)[i];
1832  case UT_Storage::REAL32:
1833  return (DEST_DATA_T)((fpreal32*)first)[i];
1834  case UT_Storage::REAL64:
1835  return (DEST_DATA_T)((fpreal64*)first)[i];
1836  }
1837  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
1838  return DEST_DATA_T();
1839  }
1840 
1841  switch (storage)
1842  {
1843  case UT_Storage::INVALID:
1844  UT_ASSERT_MSG_P(0, "Can't read a non-numeric type with a cast to a different type.");
1845  return DEST_DATA_T();
1846  case UT_Storage::INT8:
1847  return (DEST_DATA_T)castType<int8>().getConstant(page->template castType<int8>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1848  case UT_Storage::UINT8:
1849  return (DEST_DATA_T)castType<uint8>().getConstant(page->template castType<uint8>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1850  case UT_Storage::INT16:
1851  return (DEST_DATA_T)castType<int16>().getConstant(page->template castType<int16>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1852  case UT_Storage::INT32:
1853  return (DEST_DATA_T)castType<int32>().getConstant(page->template castType<int32>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1854  case UT_Storage::INT64:
1855  return (DEST_DATA_T)castType<int64>().getConstant(page->template castType<int64>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1856  case UT_Storage::REAL16:
1857  return (DEST_DATA_T)castType<fpreal16>().getConstant(page->template castType<fpreal16>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1858  case UT_Storage::REAL32:
1859  return (DEST_DATA_T)castType<fpreal32>().getConstant(page->template castType<fpreal32>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1860  case UT_Storage::REAL64:
1861  return (DEST_DATA_T)castType<fpreal64>().getConstant(page->template castType<fpreal64>(), component, (TSIZE >= 1 ? TSIZE : myImpl.getTupleSize()));
1862  }
1863  return DEST_DATA_T();
1864  }
1865 
1866  template<int OP,typename SRC_DATA_T>
1867  void opUnknownType(IDX_T i,exint component,SRC_DATA_T v)
1868  {
1869  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
1870  const UT_Storage storage = getStorage();
1871  switch (storage)
1872  {
1873  case UT_Storage::INVALID:
1874  UT_ASSERT_MSG_P(0, "Can't write a non-numeric type with opUnknownType.");
1875  return;
1876  case UT_Storage::INT8:
1877  castType<int8 >().template op<OP>(i,component, UTconvertStorage<int8>(v)); return;
1878  case UT_Storage::UINT8:
1879  castType<uint8 >().template op<OP>(i,component, UTconvertStorage<uint8>(v)); return;
1880  case UT_Storage::INT16:
1881  castType<int16 >().template op<OP>(i,component, UTconvertStorage<int16>(v)); return;
1882  case UT_Storage::INT32:
1883  castType<int32 >().template op<OP>(i,component, UTconvertStorage<int32>(v)); return;
1884  case UT_Storage::INT64:
1885  castType<int64 >().template op<OP>(i,component, UTconvertStorage<int64>(v)); return;
1886  case UT_Storage::REAL16:
1887  castType<fpreal16>().template op<OP>(i,component, (fpreal16)v); return;
1888  case UT_Storage::REAL32:
1889  castType<fpreal32>().template op<OP>(i,component, (fpreal32)v); return;
1890  case UT_Storage::REAL64:
1891  castType<fpreal64>().template op<OP>(i,component, (fpreal64)v); return;
1892  }
1893  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
1894  }
1895  template<int OP,typename SRC_DATA_T,exint SRC_TSIZE>
1897  {
1898  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
1899  const UT_Storage storage = getStorage();
1900  switch (storage)
1901  {
1902  case UT_Storage::INVALID:
1903  UT_ASSERT_MSG_P(0, "Can't write a non-numeric type with opVectorUnknownType.");
1904  return;
1905  case UT_Storage::INT8:
1906  castType<int8 >().template opVector<OP>(i,v); return;
1907  case UT_Storage::UINT8:
1908  castType<uint8 >().template opVector<OP>(i,v); return;
1909  case UT_Storage::INT16:
1910  castType<int16 >().template opVector<OP>(i,v); return;
1911  case UT_Storage::INT32:
1912  castType<int32 >().template opVector<OP>(i,v); return;
1913  case UT_Storage::INT64:
1914  castType<int64 >().template opVector<OP>(i,v); return;
1915  case UT_Storage::REAL16:
1916  castType<fpreal16>().template opVector<OP>(i,v); return;
1917  case UT_Storage::REAL32:
1918  castType<fpreal32>().template opVector<OP>(i,v); return;
1919  case UT_Storage::REAL64:
1920  castType<fpreal64>().template opVector<OP>(i,v); return;
1921  }
1922  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
1923  }
1924  template<typename DEST_DATA_T,exint DEST_TSIZE>
1926  {
1927  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
1928 
1929  const UT_Storage storage = getStorage();
1930 
1931  // First, check for match on destination type
1933  return castType<DEST_DATA_T>().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1934 
1935  // Then, try second-guess type (e.g. reading double as float or vice versa)
1937  return castType<typename UT_StorageNum<DEST_DATA_T>::SecondGuess>().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1938 
1939  switch (storage)
1940  {
1941  case UT_Storage::INVALID:
1942  UT_ASSERT_MSG_P(0, "Can't read a non-numeric type with getVectorUnknownType.");
1943  break;
1944  case UT_Storage::INT8:
1945  return castType<int8 >().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1946  case UT_Storage::UINT8:
1947  return castType<uint8 >().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1948  case UT_Storage::INT16:
1949  return castType<int16 >().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1950  case UT_Storage::INT32:
1951  return castType<int32 >().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1952  case UT_Storage::INT64:
1953  return castType<int64 >().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1954  case UT_Storage::REAL16:
1955  return castType<fpreal16>().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1956  case UT_Storage::REAL32:
1957  return castType<fpreal32>().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1958  case UT_Storage::REAL64:
1959  return castType<fpreal64>().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1960  }
1961  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
1963  }
1964  template<typename DEST_DATA_T,exint DEST_TSIZE>
1966  {
1967  UT_ASSERT_P(TSIZE==-1);
1968  UT_ASSERT_P((SYSisSame<DEST_DATA_T,DATA_T>()));
1969 
1970  const exint tuplesize = getTupleSize();
1971 
1972  // First, check for match on destination type
1973  if (tuplesize == DEST_TSIZE)
1974  return castTupleSize<DEST_TSIZE>().template getVector<DEST_DATA_T,DEST_TSIZE>(i);
1975 
1976  // Then try small sizes
1977  if (tuplesize == 1)
1978  return castTupleSize<1>().template getVectorMismatchedSize<DEST_DATA_T,DEST_TSIZE>(i);
1979  if (tuplesize == 2)
1980  return castTupleSize<2>().template getVectorMismatchedSize<DEST_DATA_T,DEST_TSIZE>(i);
1981  if (tuplesize == 3)
1982  return castTupleSize<3>().template getVectorMismatchedSize<DEST_DATA_T,DEST_TSIZE>(i);
1983  if (tuplesize == 4)
1984  return castTupleSize<4>().template getVectorMismatchedSize<DEST_DATA_T,DEST_TSIZE>(i);
1985 
1986  // Mismatched large, unknown tuplesize (uncommon case)
1988  const exint minsize = SYSmin(tuplesize,DEST_TSIZE);
1989  for (exint component = 0; component < minsize; ++component)
1990  v[component] = get<DEST_DATA_T>(i, component);
1991 
1992  return v;
1993  }
1994  template<typename DEST_DATA_T,exint DEST_TSIZE>
1996  {
1997  UT_ASSERT_P(TSIZE!=-1);
1998  UT_ASSERT_P(TSIZE!=DEST_TSIZE);
1999  UT_ASSERT_P((SYSisSame<DEST_DATA_T,DATA_T>()));
2000 
2001  // Types and tuple sizes are known, but don't match
2002  UT_PageNum pagenum = pageNum(i);
2003  const PageTableEntry *page = myImpl.getPages()->getPPage(pagenum);
2004 
2005  // If we know that the pages are hardened, we can omit
2006  // the constant page case at compile time.
2007  bool isconst = !PAGESHARDENED && page->isConstant();
2008 
2009  const NotVoidType *srctuple;
2010  if (!isconst)
2011  {
2012  UT_PageOff pageoff = pageOff(i);
2013  srctuple = (const NotVoidType *)(((const UT_FixedVector<NotVoidType,theSafeTupleSize>*)page->getFirstPtr()) + pageoff);
2014  }
2015  else
2016  {
2017  srctuple = getConstantPtr(page);
2018  if (!srctuple)
2020  }
2021 
2022  if (DEST_TSIZE < TSIZE)
2023  return *(const UT_FixedVector<DEST_DATA_T,DEST_TSIZE>*)srctuple;
2024 
2026  for (exint component = 0; component < TSIZE; ++component)
2027  dsttuple[component] = srctuple[component];
2028  for (exint component = TSIZE; component < DEST_TSIZE; ++component)
2029  dsttuple[component] = DEST_DATA_T(0);
2030  return dsttuple;
2031  }
2033  {
2034  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
2035  const UT_Storage storage = getStorage();
2036  switch (storage)
2037  {
2038  case UT_Storage::INVALID:
2039  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
2040  return;
2041  case UT_Storage::INT8:
2042  castType<int8>().template hardenAllPages<false>(start, end); return;
2043  case UT_Storage::UINT8:
2044  castType<uint8>().template hardenAllPages<false>(start, end); return;
2045  case UT_Storage::INT16:
2046  castType<int16>().template hardenAllPages<false>(start, end); return;
2047  case UT_Storage::INT32:
2048  castType<int32>().template hardenAllPages<false>(start, end); return;
2049  case UT_Storage::INT64:
2050  castType<int64>().template hardenAllPages<false>(start, end); return;
2051  case UT_Storage::REAL16:
2052  castType<fpreal16>().template hardenAllPages<false>(start, end); return;
2053  case UT_Storage::REAL32:
2054  castType<fpreal32>().template hardenAllPages<false>(start, end); return;
2055  case UT_Storage::REAL64:
2056  castType<fpreal64>().template hardenAllPages<false>(start, end); return;
2057  }
2058  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
2059  }
2060 
2062  {
2063  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
2064  const UT_Storage storage = getStorage();
2065  switch (storage)
2066  {
2067  case UT_Storage::INVALID:
2068  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
2069  return;
2070  case UT_Storage::INT8:
2071  castType<int8>().template tryCompressAllPages<false>(start, end); return;
2072  case UT_Storage::UINT8:
2073  castType<uint8>().template tryCompressAllPages<false>(start, end); return;
2074  case UT_Storage::INT16:
2075  castType<int16>().template tryCompressAllPages<false>(start, end); return;
2076  case UT_Storage::INT32:
2077  castType<int32>().template tryCompressAllPages<false>(start, end); return;
2078  case UT_Storage::INT64:
2079  castType<int64>().template tryCompressAllPages<false>(start, end); return;
2080  case UT_Storage::REAL16:
2081  castType<fpreal16>().template tryCompressAllPages<false>(start, end); return;
2082  case UT_Storage::REAL32:
2083  castType<fpreal32>().template tryCompressAllPages<false>(start, end); return;
2084  case UT_Storage::REAL64:
2085  castType<fpreal64>().template tryCompressAllPages<false>(start, end); return;
2086  }
2087  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
2088  }
2089 
2091  {
2092  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
2093  const UT_Storage storage = getStorage();
2094  switch (storage)
2095  {
2096  case UT_Storage::INVALID:
2097  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
2098  return;
2099  case UT_Storage::INT8:
2100  castType<int8>().tryCompressPage(pagenum); return;
2101  case UT_Storage::UINT8:
2102  castType<uint8>().tryCompressPage(pagenum); return;
2103  case UT_Storage::INT16:
2104  castType<int16>().tryCompressPage(pagenum); return;
2105  case UT_Storage::INT32:
2106  castType<int32>().tryCompressPage(pagenum); return;
2107  case UT_Storage::INT64:
2108  castType<int64>().tryCompressPage(pagenum); return;
2109  case UT_Storage::REAL16:
2110  castType<fpreal16>().tryCompressPage(pagenum); return;
2111  case UT_Storage::REAL32:
2112  castType<fpreal32>().tryCompressPage(pagenum); return;
2113  case UT_Storage::REAL64:
2114  castType<fpreal64>().tryCompressPage(pagenum); return;
2115  }
2116  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
2117  }
2118 
2119  template<typename SRC_DATA_T>
2121  {
2122  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
2123  const UT_Storage storage = getStorage();
2124  switch (storage)
2125  {
2126  case UT_Storage::INVALID:
2127  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
2128  return;
2129  case UT_Storage::INT8:
2130  castType<int8>().setPageConstant(pagenum, val); return;
2131  case UT_Storage::UINT8:
2132  castType<uint8>().setPageConstant(pagenum, val); return;
2133  case UT_Storage::INT16:
2134  castType<int16>().setPageConstant(pagenum, val); return;
2135  case UT_Storage::INT32:
2136  castType<int32>().setPageConstant(pagenum, val); return;
2137  case UT_Storage::INT64:
2138  castType<int64>().setPageConstant(pagenum, val); return;
2139  case UT_Storage::REAL16:
2140  castType<fpreal16>().setPageConstant(pagenum, val); return;
2141  case UT_Storage::REAL32:
2142  castType<fpreal32>().setPageConstant(pagenum, val); return;
2143  case UT_Storage::REAL64:
2144  castType<fpreal64>().setPageConstant(pagenum, val); return;
2145  }
2146  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
2147  }
2148 
2149  template<typename SRC_DATA_T>
2150  void setPageConstantUnknownType(UT_PageNum pagenum, const SRC_DATA_T *values)
2151  {
2152  UT_ASSERT_P((SYSisSame<DATA_T,void>()));
2153  const UT_Storage storage = getStorage();
2154  switch (storage)
2155  {
2156  case UT_Storage::INVALID:
2157  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
2158  return;
2159  case UT_Storage::INT8:
2160  castType<int8>().setPageConstant(pagenum, values); return;
2161  case UT_Storage::UINT8:
2162  castType<uint8>().setPageConstant(pagenum, values); return;
2163  case UT_Storage::INT16:
2164  castType<int16>().setPageConstant(pagenum, values); return;
2165  case UT_Storage::INT32:
2166  castType<int32>().setPageConstant(pagenum, values); return;
2167  case UT_Storage::INT64:
2168  castType<int64>().setPageConstant(pagenum, values); return;
2169  case UT_Storage::REAL16:
2170  castType<fpreal16>().setPageConstant(pagenum, values); return;
2171  case UT_Storage::REAL32:
2172  castType<fpreal32>().setPageConstant(pagenum, values); return;
2173  case UT_Storage::REAL64:
2174  castType<fpreal64>().setPageConstant(pagenum, values); return;
2175  }
2176  UT_ASSERT_MSG_P(0, "Unhandled UT_Storage enum value!");
2177  }
2178 
2179  template<typename SRC_DATA_T>
2180  void setPageConstantMismatchedType(UT_PageNum pagenum, const SRC_DATA_T *values)
2181  {
2182  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2183  UT_ASSERT_P(!(SYSisSame<DATA_T,SRC_DATA_T>()));
2184 
2185  // This could be done directly, instead of converting
2186  // to a UT_SmallArray<DATA_T>, but this approach is simpler for now.
2187 
2188  UT_SmallArray<NotVoidType, ((TSIZE >= 1) ? TSIZE : 16)*sizeof(NotVoidType)> dest_values;
2189  const exint tuplesize = myImpl.getTupleSize();
2190  dest_values.setSize(tuplesize);
2191 
2192  for (exint i = 0; i < tuplesize; ++i)
2193  dest_values(i) = UTconvertStorage<NotVoidType>(values[i]);
2194 
2195  setPageConstant(pagenum, dest_values.array());
2196  }
2197 
2199  {
2200  public:
2203  static const uintptr_t theConstantPageBit = 1;
2204 
2205  /// This is always valid to call.
2207  {
2208  return (uintptr_t(myData) & theConstantPageBit) != 0;
2209  }
2210  /// This is only valid to call if the type doesn't fit inline.
2212  {
2213  return (uintptr_t(myData) == theConstantPageBit);
2214  }
2215  /// This is always valid to call, and will return true
2216  /// iff this is either inline and all zero or
2217  /// using the constant zero optimization.
2219  {
2220  return (uintptr_t(myData) == theConstantPageBit)
2221  && (myPRefCount == nullptr);
2222  }
2223  /// This is only valid to call when DATA_T is non-void.
2224  SYS_FORCE_INLINE static bool typeFitsInline(exint tuplesize = TSIZE)
2225  {
2227  if (sizeof(PageTableEntry) <= sizeof(NotVoidType))
2228  return false;
2229  return (sizeof(NotVoidType)*((TSIZE >= 0) ? TSIZE : tuplesize) < sizeof(PageTableEntry));
2230  }
2231 
2232  /// This is always valid to call, but be aware that the parameter is the
2233  /// number of *bytes* in a tuple, not the number of components in a
2234  /// tuple.
2235  SYS_FORCE_INLINE bool isRefd(exint tuplebytes) const
2236  {
2237  if (!isConstant())
2238  return true;
2239 
2240  // This will be checked at compile time, when possible.
2241  if (sizeof(PageTableEntry) > ((TSIZE >= 0 && !SYSisSame<DATA_T,void>()) ? TSIZE*sizeof(NotVoidType) : tuplebytes))
2242  return false;
2243 
2244  return !isConstantZero();
2245  }
2246 
2247  /// NOTE: This should only be called when there's an actual allocated page,
2248  /// i.e. !isConstant() || (!typeFitsInline(tuplesize) && !isConstantZero()).
2250  {
2251  UT_ASSERT_P(myPRefCount->relaxedLoad() > 0);
2252  return myPRefCount->relaxedLoad() != 1;
2253  }
2254 
2255  /// NOTE: This should only be called when there's an actual allocated page to decRef,
2256  /// i.e. !isConstant() || (!typeFitsInline(tuplesize) && !isConstantZero()).
2258  {
2259  myPRefCount->add(1);
2260  }
2261 
2262  /// NOTE: This should only be called when there's an actual allocated page to decRef,
2263  /// i.e. !isConstant() || (!typeFitsInline(tuplesize) && !isConstantZero()).
2265  {
2266  int64 new_count = myPRefCount->add(-1);
2267  UT_ASSERT_P(new_count >= 0);
2268  if (new_count == 0)
2269  {
2270  free((NotVoidType*)(uintptr_t(myData) & ~theConstantPageBit));
2271  free(myPRefCount);
2272 #if 0
2273  printf("Free page %d pages total\n", int(thePageTablePageCount.add(-1)));
2274  fflush(stdout);
2275 #endif
2276  }
2277  }
2278 
2279  /// NOTE: This should only be called when there's an actual allocated page,
2280  /// i.e. !isConstant() || (!typeFitsInline(tuplesize) && !isConstantZero()).
2282  {
2283  UT_ASSERT_P(myPRefCount->relaxedLoad() > 0);
2284  return myPRefCount->relaxedLoad();
2285  }
2286 
2287  SYS_FORCE_INLINE void alloc(UT_PageOff nelements, exint tuplesize = TSIZE)
2288  {
2290  myData = (NotVoidType *)malloc(nelements*((TSIZE >= 0) ? TSIZE : tuplesize)*sizeof(NotVoidType));
2291  myPRefCount = (SYS_AtomicCounter *)malloc(sizeof(SYS_AtomicCounter));
2292  myPRefCount->relaxedStore(1);
2293 #if 0
2294  printf("Alloc page %d pages total\n", int(thePageTablePageCount.add(1)));
2295  fflush(stdout);
2296 #endif
2297  }
2298  void realloc(UT_PageOff sizetocopy, UT_PageOff newpagecapacity, exint tuplesize = TSIZE)
2299  {
2301  UT_ASSERT_P(sizetocopy >= 0 && sizetocopy <= newpagecapacity);
2302  UT_ASSERT_P(!isConstant());
2303 
2304  UT_ASSERT_P(tuplesize >= 0);
2305  // This is just to try to force the compiler to constant-value-optimize.
2306  if (TSIZE >= 0)
2307  tuplesize = TSIZE;
2308 
2309  UT_ASSERT_P(myPRefCount->relaxedLoad() > 0);
2310  if (myPRefCount->relaxedLoad() > 1)
2311  {
2312  // If shared, need to make a new page.
2313  PageTableEntry newpage;
2314  newpage.alloc(newpagecapacity, tuplesize);
2315  NotVoidType *__restrict newdata = newpage.myData;
2316  const NotVoidType *__restrict olddata = myData;
2317  // Yes, this loop is an inline memcpy, because memcpy won't inline,
2318  // but the compiler will optimize it if it's like this.
2319  sizetocopy *= tuplesize;
2320  while (sizetocopy)
2321  {
2322  *newdata = *olddata;
2323  ++newdata;
2324  ++olddata;
2325  --sizetocopy;
2326  }
2327  decRef();
2328  myData = newpage.myData;
2329  myPRefCount = newpage.myPRefCount;
2330  }
2331  else
2332  {
2333  // If not shared, we can potentially reuse the same memory.
2334  myData = (NotVoidType*)::realloc(myData, newpagecapacity*(tuplesize*sizeof(NotVoidType)));
2335  // NOTE: Reference count doesn't need to change, since it's 1.
2336  }
2337  }
2338 
2339  /// Atomically decrement the reference count iff the page is shared.
2340  /// See the description of decRefIffSharedPage() above for
2341  /// why this function exists.
2343  bool decRefIffShared(exint tuplesize = TSIZE)
2344  {
2345  if (isConstant())
2346  {
2347  UT_ASSERT_P(tuplesize >= 0);
2348  if (typeFitsInline(tuplesize) || isConstantZero())
2349  return false;
2350  }
2351  return decCounterIffAbove1(*myPRefCount);
2352  }
2353 
2354  /// Returns the data pointer, if not a constant page
2357  {
2358  UT_ASSERT_MSG_P(!isConstant(), "getFirstPtr() is for non-constant pages");
2360 
2361  return myData;
2362  }
2363 
2364  /// Returns the data pointer, if not a constant page
2366  const NotVoidType *getFirstPtr() const
2367  {
2368  UT_ASSERT_MSG_P(!isConstant(), "getFirstPtr() is for non-constant pages");
2370 
2371  return myData;
2372  }
2373 
2374  /// Returns the data pointer, if not a constant page
2377  {
2378  UT_ASSERT_MSG_P(!isConstant(), "getFirstPtrVoid() is for non-constant pages");
2379 
2380  return myData;
2381  }
2382 
2383  /// Returns the data pointer, if not a constant page
2385  const void *getFirstPtrVoid() const
2386  {
2387  UT_ASSERT_MSG_P(!isConstant(), "getFirstPtrVoid() is for non-constant pages");
2388 
2389  return myData;
2390  }
2391 
2392  /// Returns the data pointer, or whatever's in its place
2393  /// with bit 0 set to 1 if it's something constant.
2394  /// If it's constant, it might not be a flagged pointer.
2397  {
2398  return myData;
2399  }
2400 
2401  /// Returns the data pointer, or whatever's in its place
2402  /// with bit 0 set to 1 if it's something constant.
2403  /// If it's constant, it might not be a flagged pointer.
2405  const void *getFirstPtrVoidUnsafe() const
2406  {
2407  return myData;
2408  }
2409 
2410  /// Returns the data pointer, if an inline constant page
2413  {
2414  UT_ASSERT_MSG_P(isConstant() && typeFitsInline(tuplesize), "getInlinePtr() is for inline constant pages");
2416  return (NotVoidType *)(((int8 *)(this+1)) - (sizeof(NotVoidType)*(TSIZE >= 0 ? TSIZE : tuplesize)));
2417  }
2418 
2419  /// Returns the data pointer, if an inline constant page
2421  const NotVoidType *getInlinePtr(exint tuplesize) const
2422  {
2423  UT_ASSERT_MSG_P(isConstant() && typeFitsInline(tuplesize), "getInlinePtr() is for inline constant pages");
2425  return (const NotVoidType *)(((const int8 *)(this+1)) - (sizeof(NotVoidType)*(TSIZE >= 0 ? TSIZE : tuplesize)));
2426  }
2427 
2428  /// Returns the data pointer, if a non-inline constant page.
2429  /// Returns nullptr for a zero constant page.
2432  {
2433  UT_ASSERT_MSG_P(isConstant(), "getMaskedPtr() is for constant pages");
2435 
2436  return (NotVoidType *)(uintptr_t(myData) & ~theConstantPageBit);
2437  }
2438 
2439  /// Returns the data pointer, if a non-inline constant page.
2440  /// Returns nullptr for a zero constant page.
2442  const NotVoidType *getMaskedPtr() const
2443  {
2444  UT_ASSERT_MSG_P(isConstant(), "getMaskedPtr() is for constant pages");
2446 
2447  return (const NotVoidType *)(uintptr_t(myData) & ~theConstantPageBit);
2448  }
2449 
2450  /// Returns the data pointer, if a non-inline constant page.
2451  /// Returns nullptr for a zero constant page.
2454  {
2455  UT_ASSERT_MSG_P(isConstant(), "getMaskedPtrVoid() is for constant pages");
2456 
2457  return (void *)(uintptr_t(myData) & ~theConstantPageBit);
2458  }
2459 
2460  /// Returns the data pointer, if a non-inline constant page.
2461  /// Returns nullptr for a zero constant page.
2463  const void *getMaskedPtrVoid() const
2464  {
2465  UT_ASSERT_MSG_P(isConstant(), "getMaskedPtrVoid() is for constant pages");
2466 
2467  return (const void *)(uintptr_t(myData) & ~theConstantPageBit);
2468  }
2469 
2470  /// Initializes a page to constant zero, assuming that it hasn't been
2471  /// initialized yet.
2473  void initZero()
2474  {
2475  myData = (NotVoidType*)theConstantPageBit;
2476  myPRefCount = nullptr;
2477  }
2478 
2481  {
2482  myData = (NotVoidType *)(uintptr_t(myData) | theConstantPageBit);
2483  }
2484 
2485  void tryCompressPage(UT_PageOff pagesize, exint tuplesize = TSIZE)
2486  {
2487  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2488  UT_ASSERT_P(!isConstant());
2489 
2490  tuplesize = (TSIZE >= 0 ? TSIZE : tuplesize);
2491 
2492  const NotVoidType *const first = myData;
2493  const NotVoidType *const end = first + (TSIZE >= 0 ? TSIZE : tuplesize)*exint(pagesize);
2494  for (const NotVoidType *other = first+tuplesize; other < end; other += tuplesize)
2495  {
2496  for (exint component = 0; component < (TSIZE >= 0 ? TSIZE : tuplesize); ++component)
2497  {
2498  if (other[component] != first[component])
2499  return;
2500  }
2501  }
2502  // We have a constant page! Compress it!
2503 
2504  PageTableEntry newpage;
2505  newpage.initZero();
2506  const exint bytesize = sizeof(NotVoidType)*(TSIZE >= 0 ? TSIZE : tuplesize);
2507  // Value stored inline if it's strictly smaller than a pointer
2508  // The first check can be done at compile time, so it's only logically redundant,
2509  // not functionally redundant.
2510  if (sizeof(PageTableEntry) > sizeof(NotVoidType) && sizeof(PageTableEntry) > bytesize)
2511  {
2512  memcpy(((int8*)&newpage) + (sizeof(PageTableEntry) - bytesize),
2513  first, bytesize);
2514  }
2515  else
2516  {
2517  if (!isZero(first, (TSIZE >= 0 ? TSIZE : tuplesize)))
2518  {
2519  newpage.alloc(UT_PageOff(1), tuplesize);
2520  NotVoidType *newfirst = newpage.getFirstPtr();
2521  memcpy(newfirst, first, bytesize);
2522  newpage.setConstantBit();
2523  }
2524  }
2525  decRef();
2526  *this = newpage;
2527  }
2528 
2529  /// NOTE: This always excludes sizeof(*this), and is only valid for
2530  /// non-small pages, (okay if constant).
2532  {
2533  if (!isConstant())
2534  return sizeof(SYS_AtomicCounter) + tuplebytes*thePageSize;
2535  if (tuplebytes < sizeof(PageTableEntry) || isConstantZero())
2536  return 0;
2537  return sizeof(SYS_AtomicCounter) + tuplebytes;
2538  }
2539 
2540  template<typename DEST_DATA_T>
2543  castType() const
2544  {
2547  }
2548  template<typename DEST_DATA_T>
2552  {
2555  }
2556 
2557  /// NOTE: This just does a shallow comparison.
2559  bool operator==(const ThisType &that) const
2560  {
2561  return (myData == that.myData) && (myPRefCount == that.myPRefCount);
2562  }
2563  /// NOTE: This just does a shallow comparison.
2565  bool operator!=(const ThisType &that) const
2566  {
2567  return (myData != that.myData) || (myPRefCount != that.myPRefCount);
2568  }
2569 
2570  private:
2571  NotVoidType *myData;
2572  SYS_AtomicCounter *myPRefCount;
2573  };
2574 
2577  {
2578  exint cur_count = counter.relaxedLoad();
2579 
2580  // It should be rare for this to ever loop, because looping
2581  // means that something else referring to the same page has
2582  // done an incRef, decRef, or decRefIffShared on this same page,
2583  // between the relaxedLoad and the compare_swap calls, or on
2584  // subsequent loops, between the compare_swap calls.
2585  while (true)
2586  {
2587  if (cur_count == 1)
2588  return false; // Not shared, so don't decrement
2589  exint new_count = cur_count-1;
2590  exint prev_count = counter.compare_swap(cur_count, new_count);
2591  if (prev_count == cur_count)
2592  return true; // Successfully decremented
2593  cur_count = prev_count;
2594  }
2595  // Execution never gets here, because the only escapes from
2596  // the loops return.
2597  }
2598 
2599  SYS_FORCE_INLINE static NotVoidType getConstant(const PageTableEntry *const page,exint component,exint tuplesize = TSIZE)
2600  {
2601  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2602  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
2603  UT_ASSERT_P(component >= 0 && component < tuplesize);
2604  UT_ASSERT_P(page->isConstant());
2605 
2606  // Any types whose tuples are strictly smaller than a pointer will be stored inline.
2607  // If a single one is at least as large as a pointer, it's
2608  // rejected at compile time, so there'll be no op at runtime in that case.
2609  if (PageTableEntry::typeFitsInline(tuplesize))
2610  {
2611  return page->getInlinePtr(tuplesize)[component];
2612  }
2613 
2614  const NotVoidType *masked = page->getMaskedPtr();
2615  if (!masked)
2616  {
2617  NotVoidType zero;
2618  memset(&zero, 0, sizeof(NotVoidType));
2619  return zero;
2620  }
2621 
2622  // Other types have a 1-element page.
2623  return masked[component];
2624  }
2625  /// NOTE: It may seem like overkill to take a PageTableEntry *, but the
2626  /// returned pointer may be *inside* the pointer that the caller owns,
2627  /// so we need the caller's pointer, not a copy. getConstant doesn't
2628  /// have this issue, since it can use the local pointer and read the
2629  /// value.
2630  SYS_FORCE_INLINE static const NotVoidType *getConstantPtr(const PageTableEntry *page,exint component = 0,exint tuplesize = TSIZE)
2631  {
2632  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2633  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
2634  UT_ASSERT_P(component >= 0 && component < tuplesize);
2635  UT_ASSERT_P(page->isConstant());
2636 
2637  // Any types whose tuples are strictly smaller than PageTableEntry will be stored inline.
2638  if (PageTableEntry::typeFitsInline(tuplesize))
2639  {
2640  return page->getInlinePtr(tuplesize) + component;
2641  }
2642 
2643  const NotVoidType *masked = page->getMaskedPtr();
2644  if (!masked)
2645  return nullptr;
2646 
2647  // Other types have a 1-element page.
2648  return masked + component;
2649  }
2650  /// NOTE: It may seem like overkill to take a PageTableEntry *, but the
2651  /// returned pointer may be *inside* the pointer that the caller owns,
2652  /// so we need the caller's pointer, not a copy. getConstant doesn't
2653  /// have this issue, since it can use the local pointer and read the
2654  /// value.
2655  SYS_FORCE_INLINE static NotVoidType *getConstantPtr(PageTableEntry *page,exint component = 0,exint tuplesize = TSIZE)
2656  {
2657  return SYSconst_cast(getConstantPtr((const PageTableEntry *)page, component, tuplesize));
2658  }
2660  {
2661  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2662  UT_ASSERT_P(TSIZE >= 1);
2663  UT_ASSERT_P(!PAGESHARDENED);
2664 
2665  if (PAGESHARDENED)
2666  return;
2667 
2669 
2670  // Unref existing non-constant page
2671  bool wasconst = page->isConstant();
2672  if (!wasconst)
2673  page->decRef();
2674 
2675  // Easy for inline case, checked at compile-time.
2676  if (sizeof(PageTableEntry) > sizeof(Tuple))
2677  {
2678  page->initZero();
2679  NotVoidType *tuple = page->getInlinePtr(theSafeTupleSize);
2680  *(Tuple *)tuple = val;
2681  return;
2682  }
2683 
2684  bool iszero = isZero(val);
2685 
2686  // In other cases, we may or may not have to unref constant page
2687  if (wasconst)
2688  {
2689  NotVoidType *masked = page->getMaskedPtr();
2690  if (!masked)
2691  {
2692  // Fairly common case: already zero, making zero.
2693  if (iszero)
2694  return;
2695  }
2696  else if (iszero || (page->isShared() && (*(const Tuple*)masked != val)))
2697  {
2698  // No longer need this old constant page
2699  page->decRef();
2700  }
2701  else if (!page->isShared())
2702  {
2703  // Reuse the unshared constant page
2704  *(Tuple*)masked = val;
2705  return;
2706  }
2707  else
2708  {
2709  // Already equal; nothing to do
2710  UT_ASSERT_P(*(const Tuple*)masked == val);
2711  return;
2712  }
2713  }
2714 
2715  if (iszero)
2716  {
2717  page->initZero();
2718  return;
2719  }
2720 
2721  // Need to allocate new constant page
2722  PageTableEntry newpage;
2723  newpage.alloc(UT_PageOff(1), theSafeTupleSize);
2724  NotVoidType *tuple = newpage.getFirstPtr();
2725  (*(Tuple*)tuple) = val;
2726  newpage.setConstantBit();
2727  (*page) = newpage;
2728  }
2729 
2730  static void makeConstant(PageTableEntry *page, const NotVoidType &val, exint tuplesize = TSIZE)
2731  {
2732  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2733  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
2734  UT_ASSERT_P(!PAGESHARDENED);
2735  UT_ASSERT_P(tuplesize >= 1);
2736 
2737  if (PAGESHARDENED)
2738  return;
2739 
2740  // Unref existing non-constant page
2741  bool wasconst = page->isConstant();
2742  if (!wasconst)
2743  page->decRef();
2744 
2745  // Easy for inline case, partly checked at compile-time.
2746  if (PageTableEntry::typeFitsInline(tuplesize))
2747  {
2748  page->initZero();
2749  NotVoidType *tuple = page->getInlinePtr(tuplesize);
2750  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2751  tuple[i] = val;
2752  return;
2753  }
2754 
2755  bool iszero = isZero(val);
2756 
2757  // In other cases, we may or may not have to unref constant page
2758  if (wasconst)
2759  {
2760  NotVoidType *masked = page->getMaskedPtr();
2761  if (!masked)
2762  {
2763  // Fairly common case: already zero, making zero.
2764  if (iszero)
2765  return;
2766  }
2767  else if (iszero)
2768  {
2769  // No longer need this old constant page
2770  page->decRef();
2771  }
2772  else if (page->isShared())
2773  {
2774  bool equal = true;
2775  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2776  {
2777  if (masked[i] != val)
2778  {
2779  equal = false;
2780  break;
2781  }
2782  }
2783 
2784  if (equal)
2785  {
2786  // Already equal; nothing to do
2787  return;
2788  }
2789 
2790  // No longer need this old constant page
2791  page->decRef();
2792  }
2793  else
2794  {
2795  // Reuse the unshared constant page
2796  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2797  masked[i] = val;
2798  return;
2799  }
2800  }
2801 
2802  if (iszero)
2803  {
2804  page->initZero();
2805  return;
2806  }
2807 
2808  // Need to allocate new constant page
2809  PageTableEntry newpage;
2810  newpage.alloc(UT_PageOff(1), tuplesize);
2811  NotVoidType *tuple = newpage.getFirstPtr();
2812  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2813  tuple[i] = val;
2814  newpage.setConstantBit();
2815  (*page) = newpage;
2816  }
2817 
2818  static void makeConstant(PageTableEntry *page, const NotVoidType *values, exint tuplesize)
2819  {
2820  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2821  UT_ASSERT_P(TSIZE == -1 || TSIZE == tuplesize);
2822  UT_ASSERT_P(!PAGESHARDENED);
2823  UT_ASSERT_P(tuplesize >= 1);
2824 
2825  if (PAGESHARDENED)
2826  return;
2827 
2828  // Unref existing non-constant page
2829  bool wasconst = page->isConstant();
2830  if (!wasconst)
2831  page->decRef();
2832 
2833  // Easy for inline case, partly checked at compile-time.
2834  if (PageTableEntry::typeFitsInline(tuplesize))
2835  {
2836  page->initZero();
2837  NotVoidType *tuple = page->getInlinePtr(tuplesize);
2838  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2839  tuple[i] = values[i];
2840  return;
2841  }
2842 
2843  bool iszero = isZero(values, (TSIZE >= 0 ? TSIZE : tuplesize));
2844 
2845  // In other cases, we may or may not have to unref constant page
2846  if (wasconst)
2847  {
2848  NotVoidType *masked = page->getMaskedPtr();
2849  if (!masked)
2850  {
2851  // Fairly common case: already zero, making zero.
2852  if (iszero)
2853  return;
2854  }
2855  else if (iszero)
2856  {
2857  // No longer need this old constant page
2858  page->decRef();
2859  }
2860  else if (page->isShared())
2861  {
2862  bool equal = true;
2863  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2864  {
2865  if (masked[i] != values[i])
2866  {
2867  equal = false;
2868  break;
2869  }
2870  }
2871 
2872  if (equal)
2873  {
2874  // Already equal; nothing to do
2875  return;
2876  }
2877 
2878  // No longer need this old constant page
2879  page->decRef();
2880  }
2881  else
2882  {
2883  // Reuse the unshared constant page
2884  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2885  masked[i] = values[i];
2886  return;
2887  }
2888  }
2889 
2890  if (iszero)
2891  {
2892  page->initZero();
2893  return;
2894  }
2895 
2896  // Need to allocate new constant page
2897  PageTableEntry newpage;
2898  newpage.alloc(UT_PageOff(1), tuplesize);
2899  NotVoidType *tuple = newpage.getFirstPtr();
2900  for (exint i = 0; i < (TSIZE >= 0 ? TSIZE : tuplesize); ++i)
2901  tuple[i] = values[i];
2902  newpage.setConstantBit();
2903  (*page) = newpage;
2904  }
2905 
2906  /// This replaces dest with src. It references if possible and keeps
2907  /// dest constant if easy. If desttuplesize > srctuplesize, it only
2908  /// replaces the first srctuplesize components.
2909  template<typename SrcType>
2910  static void replacePage(PageTableEntry *dest, const typename SrcType::PageTableEntry *src, exint desttuplesize, exint srctuplesize, UT_PageOff destpagesize, UT_PageOff destpagecapacity);
2911 
2912  /// This replaces part of dest with part of src. It keeps
2913  /// dest constant if easy. If desttuplesize > srctuplesize, it only
2914  /// replaces the first srctuplesize components.
2915  /// This will *not* check to see if the whole page is being replaced,
2916  /// so if you want the src page to be referenced, or overwrite a
2917  /// constant value, when writing the whole page, use replacePage.
2918  template<typename SrcType>
2919  static void copyPartialPage(PageTableEntry *dest, const typename SrcType::PageTableEntry *src, exint desttuplesize, exint srctuplesize, UT_PageOff destoff, UT_PageOff srcoff, UT_PageOff ntuples, UT_PageOff destpagecapacity);
2920 
2921  /// This version of makeConstant copies a const page onto this page,
2922  /// converting type if necessary, and supporting a different tuple size,
2923  /// so long as the destination size either isn't larger or is constant.
2924  /// NOTE: This would be templated on the actual templates of UT_PageArray,
2925  /// but somehow, Visual C++ can't deduce the type, so the template type
2926  /// has to be specified by the caller, and one argument is much easier
2927  /// to specify on every call than 4.
2928  template<typename SrcType>
2929  static void makeConstantFrom(PageTableEntry *dest, const typename SrcType::PageTableEntry *src, exint desttuplesize, exint srctuplesize);
2930 
2931  /// This is just a helper function for writing a single source tuple (or nullptr) to a block of tuple data, with conversion.
2932  /// NOTE: destpagedata must point to an *UNSHARED* page's data.
2933  template<typename SrcNotVoidType>
2934  static void fillNonConstWithConst(NotVoidType *destpagedata, NotVoidType *destpageend, const SrcNotVoidType *stuple, exint mintuplesize, exint desttupleextra);
2935 
2936  /// This is just a helper function for copying a non-constant source page to a non-constant destination page, with conversion.
2937  /// NOTE: destpagedata must point to an *UNSHARED* page's data.
2938  /// NOTE: The two data ranges must be *NON-OVERLAPPING*.
2939  template<typename SrcNotVoidType>
2940  static void copyNonConst(NotVoidType *destpagedata, const SrcNotVoidType *srcpagedata, exint desttuplesize, exint srctuplesize, UT_PageOff ntuples);
2941 
2942  /// This is just a helper function for checking whether two constant pages are equal, up to
2943  /// the smaller of the two, to see if writing is necessary.
2944  template<typename SrcNotVoidType>
2945  static bool isEqualConst(const NotVoidType *tuple, const SrcNotVoidType *stuple, exint mintuplesize);
2946 
2947  static void hardenConstantPage(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize = TSIZE)
2948  {
2949  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
2950  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2951  UT_ASSERT_P(page->isConstant());
2952 
2953  PageTableEntry newpage;
2954  newpage.alloc(pagecapacity, tuplesize);
2955  NotVoidType *dest = newpage.getFirstPtr();
2956  bool isinline = PageTableEntry::typeFitsInline(tuplesize);
2957  if (isinline ? page->isConstantAndZeroSafe() : page->isConstantZero())
2958  {
2959  memset(dest, 0, (sizeof(NotVoidType)*(TSIZE >= 0 ? TSIZE : tuplesize))*pagecapacity);
2960  }
2961  else
2962  {
2963  const NotVoidType *src;
2964  if (isinline)
2965  src = page->getInlinePtr(tuplesize);
2966  else
2967  src = page->getMaskedPtr();
2968 
2969  for (UT_PageOff i = 0; i < pagecapacity; ++i)
2970  {
2971  for (exint component = 0; component < tuplesize; ++component, ++dest)
2972  *dest = src[component];
2973  }
2974 
2975  if (!isinline)
2976  page->decRef();
2977  }
2978  (*page) = newpage;
2979  }
2980 
2981  static void hardenConstantPageNoInit(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize = TSIZE)
2982  {
2983  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
2984  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
2985  UT_ASSERT_P(page->isConstant());
2986 
2987  PageTableEntry newpage;
2988  newpage.alloc(pagecapacity, tuplesize);
2989  // Compile-time check, to speed up case where even one element doesn't fit inline.
2990  bool isinline = PageTableEntry::typeFitsInline(tuplesize);
2991  if (!isinline && !page->isConstantZero())
2992  page->decRef();
2993  (*page) = newpage;
2994  }
2995 
2996  static void hardenSharedPage(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize = TSIZE)
2997  {
2998  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
2999  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
3000  UT_ASSERT_P(!page->isConstant());
3001  // This may technically no longer be shared between checking and calling
3002  // due to other threads decRef'ing the page. It's okay to harden anyway.
3003  //UT_ASSERT_P(page->isShared());
3004 
3005  PageTableEntry newpage;
3006  newpage.alloc(pagecapacity, tuplesize);
3007  NotVoidType *dest = newpage.getFirstPtr();
3008  const NotVoidType *src = page->getFirstPtr();
3009  memcpy(dest, src, (sizeof(NotVoidType)*(TSIZE >= 0 ? TSIZE : tuplesize))*pagecapacity);
3010  page->decRef();
3011  (*page) = newpage;
3012  }
3013 
3014  static void hardenSharedPageNoInit(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize = TSIZE)
3015  {
3016  UT_ASSERT_P(TSIZE == -1 || tuplesize == TSIZE);
3017  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
3018  UT_ASSERT_P(!page->isConstant());
3019  // This may technically no longer be shared between checking and calling
3020  // due to other threads decRef'ing the page. It's okay to harden anyway.
3021  //UT_ASSERT_P(page->isShared());
3022 
3023  PageTableEntry newpage;
3024  newpage.alloc(pagecapacity, tuplesize);
3025  page->decRef();
3026  (*page) = newpage;
3027  }
3028 
3030  {
3031  public:
3034 
3036  {
3037  UT_ASSERT_P(myCapacity > IDX_T(0));
3038  return reinterpret_cast<PageTableEntry*>(this+1);
3039  }
3041  {
3042  UT_ASSERT_P(myCapacity > IDX_T(0));
3043  return reinterpret_cast<const PageTableEntry *>(this+1);
3044  }
3045  // NOTE: Clang sometimes decides to ignore const references and pass by
3046  // value, so we have to return by pointer, in case the page
3047  // is constant and inlined, so that the address of the constant
3048  // value is accessible through this function.
3049  // Yes, many cases don't rely on this, but to reduce the risk of
3050  // bugs, the only way to access page pointers in a const PageTable
3051  // is to call this to get the page pointer address and explicitly
3052  // dereference, if valid in the given circumstance.
3054  {
3055  UT_ASSERT_P(myCapacity > IDX_T(i << THEPAGEBITS));
3056  return reinterpret_cast<const PageTableEntry *>(this+1) + i;
3057  }
3059  {
3060  UT_ASSERT_P(myCapacity > IDX_T(i << THEPAGEBITS));
3061  return reinterpret_cast<PageTableEntry*>(this+1) + i;
3062  }
3064  {
3065  UT_ASSERT_P(myRefCount.relaxedLoad() > 0);
3066  return myRefCount.relaxedLoad() != 1;
3067  }
3069  {
3070  return myRefCount.relaxedLoad();
3071  }
3072  /// NOTE: This is the size of the full array, not the number of pages.
3073  SYS_FORCE_INLINE IDX_T size() const
3074  {
3075  return mySize;
3076  }
3077  /// NOTE: This is the capacity of the full array, not the capacity of pages.
3079  {
3080  return myCapacity;
3081  }
3082  /// NOTE: This will *assume* the capacity is large enough! The caller *must* check!
3083  /// NOTE: This is the size of the full array, not the number of pages.
3084  SYS_FORCE_INLINE void setSize(IDX_T newsize)
3085  {
3086  UT_ASSERT_P(newsize <= capacity());
3087  UT_ASSERT_MSG_P(myRefCount.relaxedLoad() == 1, "The table must already be hardened before we modify it!");
3088  if (PAGESHARDENED && newsize > mySize)
3089  {
3090  UT_ASSERT_P(TSIZE >= 0);
3091  // Need to ensure pages between mySize and newsize are hardened.
3092  // Pages containing offsets less than or equal to mySize are
3093  // already guaranteed to be hardened.
3094  UT_PageNum startpage = numPages(mySize);
3095  UT_PageNum endpage = numPages(newsize);
3096  UT_PageOff pagecapacity(thePageSize);
3097  if (endpage == UT_PageNum(1) && exint(capacity()) < thePageSize)
3098  pagecapacity = capacity();
3099  PageTableEntry *page = getPPage(startpage);
3100  for (UT_PageNum p = startpage; p < endpage; ++p, ++page)
3101  {
3102  if (page->isConstant())
3103  hardenConstantPage(page, pagecapacity);
3104  else if (page->isShared())
3105  hardenSharedPage(page, pagecapacity);
3106  }
3107  }
3108  mySize = newsize;
3109  }
3110  /// NOTE: This will *assume* the capacity is large enough! The caller *must* check!
3111  /// NOTE: This is the size of the full array, not the number of pages.
3112  SYS_FORCE_INLINE void setSize(IDX_T newsize, exint tuplesize)
3113  {
3114  UT_ASSERT_P(newsize <= capacity());
3115  UT_ASSERT_MSG_P(myRefCount.relaxedLoad() == 1, "The table must already be hardened before we modify it!");
3116  if (PAGESHARDENED && newsize > mySize)
3117  {
3118  // Need to ensure pages between mySize and newsize are hardened.
3119  // Pages containing offsets less than or equal to mySize are
3120  // already guaranteed to be hardened.
3121  UT_PageNum startpage = numPages(mySize);
3122  UT_PageNum endpage = numPages(newsize);
3123  UT_PageOff pagecapacity(thePageSize);
3124  if (endpage == UT_PageNum(1) && exint(capacity()) < thePageSize)
3125  pagecapacity = capacity();
3126  PageTableEntry *page = getPPage(startpage);
3127  for (UT_PageNum p = startpage; p < endpage; ++p, ++page)
3128  {
3129  if (page->isConstant())
3130  hardenConstantPage(page, pagecapacity, tuplesize);
3131  else if (page->isShared())
3132  hardenSharedPage(page, pagecapacity, tuplesize);
3133  }
3134  }
3135  mySize = newsize;
3136  }
3137  void fill(IDX_T start, IDX_T end, const NotVoidType &val);
3138  void fill(IDX_T start, IDX_T end, NotVoidType val, exint tuplesize);
3139  void fill(IDX_T start, IDX_T end, const NotVoidType *values, exint tuplesize);
3140  void fill(IDX_T start, IDX_T end, const UT_FixedVector<NotVoidType,theSafeTupleSize> &val);
3141 
3142  void incRef()
3143  {
3144  myRefCount.add(1);
3145  }
3146  void decRef(exint tuplesize)
3147  {
3148  const int64 new_count = myRefCount.add(-1);
3149  if (new_count != 0)
3150  {
3151  UT_ASSERT_P(new_count > 0);
3152  return;
3153  }
3154 
3155  UT_ASSERT_P(!(SYSisSame<DATA_T, void>()));
3156 
3157  const size_t bytesize = sizeof(NotVoidType)*((TSIZE >= 0) ? TSIZE : tuplesize);
3158 
3159  // If we're deleting the table, first, decRef all pages.
3160  UT_PageNum npages = numPages(myCapacity);
3161  PageTableEntry *page = getFirstPage();
3162  for (UT_PageNum p = 0; p < npages; ++p, ++page)
3163  {
3164  if (page->isRefd(bytesize))
3165  page->decRef();
3166  }
3167  free(this);
3168 #if 0
3169  printf("Free table %d pages (case 0); %d tables total\n",int(npages), int(thePageTableCount.add(-1)));
3170  fflush(stdout);
3171 #endif
3172  }
3173 
3174  void hardenAllPages(IDX_T start, IDX_T end, exint tuplesize = TSIZE)
3175  {
3176  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
3177  UT_ASSERT_P(tuplesize >= 0);
3178  UT_PageNum startpage = pageNum(start);
3179  UT_PageNum endpage = numPages(end);
3180  UT_PageOff pagecapacity(thePageSize);
3181  if (endpage == UT_PageNum(1) && exint(capacity()) < thePageSize)
3182  pagecapacity = capacity();
3183  PageTableEntry *page = getPPage(startpage);
3184  for (UT_PageNum p = startpage; p < endpage; ++p, ++page)
3185  {
3186  if (page->isConstant())
3187  hardenConstantPage(page, pagecapacity, tuplesize);
3188  else if (page->isShared())
3189  hardenSharedPage(page, pagecapacity, tuplesize);
3190  }
3191  }
3192 
3193  void tryCompressAllPages(IDX_T start, IDX_T end, exint tuplesize = TSIZE)
3194  {
3195  UT_ASSERT_P(tuplesize >= 0);
3196  UT_PageNum startpage = pageNum(start);
3197  UT_PageNum endpage = numPages(end);
3198  UT_PageNum npages = numPages(mySize);
3199  PageTableEntry *page = getPPage(startpage);
3200  for (UT_PageNum p = startpage; p < endpage; ++p, ++page)
3201  {
3202  if (page->isConstant())
3203  continue;
3204 
3205  UT_PageOff pagesize = thePageSize;
3206  if (p+1 >= npages)
3207  {
3208  if (p+1 == npages)
3209  {
3210  UT_PageOff pagesizetemp = pageOff(mySize);
3211  if (pagesizetemp != UT_PageOff(0))
3212  pagesize = pagesizetemp;
3213  }
3214  else
3215  pagesize = UT_PageOff(0);
3216  }
3217  page->tryCompressPage(pagesize, tuplesize);
3218  }
3219  }
3220 
3221  void tryCompressPage(UT_PageNum pagenum, exint tuplesize = TSIZE)
3222  {
3223  UT_ASSERT_P(tuplesize >= 0);
3224  PageTableEntry *page = getPPage(pagenum);
3225  if (page->isConstant())
3226  return;
3227 
3228  UT_PageOff pagesize = thePageSize;
3229  UT_PageNum npages = numPages(mySize);
3230  if (pagenum+1 >= npages)
3231  {
3232  if (pagenum+1 == npages)
3233  {
3234  UT_PageOff pagesizetemp = pageOff(mySize);
3235  if (pagesizetemp != UT_PageOff(0))
3236  pagesize = pagesizetemp;
3237  }
3238  else
3239  pagesize = UT_PageOff(0);
3240  }
3241  page->tryCompressPage(pagesize, tuplesize);
3242  }
3243 
3244  static ThisType *alloc(UT_PageNum npages)
3245  {
3246  UT_ASSERT_MSG_P(npages > UT_PageNum(0), "Shouldn't have a zero-page page table; nullptr should be used instead.");
3247  ThisType *p = (ThisType *)malloc(sizeof(ThisType) + npages*sizeof(PageTableEntry));
3248  //UT_ASSERT_P(_CrtIsValidHeapPointer(p));
3249  p->myRefCount.relaxedStore(1);
3250 #if 0
3251  printf("Alloc table %d pages; %d tables total\n",int(npages), int(thePageTableCount.add(1)));
3252  fflush(stdout);
3253 #endif
3254  return p;
3255  }
3256  static ThisType *harden(ThisType *pages, IDX_T newcapacity, exint tuplesize=TSIZE)
3257  {
3258  UT_ASSERT_P(!(SYSisSame<DATA_T,void>()));
3259 
3260  // If we're setting capacity to zero, free the table.
3261  if (newcapacity == IDX_T(0))
3262  {
3263  if (pages)
3264  pages->decRef(tuplesize);
3265  return nullptr;
3266  }
3267 
3268  UT_ASSERT_P(tuplesize >= 0);
3269  UT_ASSERT_MSG_P(newcapacity == roundUpCapacity(newcapacity), "Caller should already have adjusted capacity");
3270 
3271  bool wasshared = pages && pages->isShared();
3272  UT_PageNum npages = numPages(newcapacity);
3273  IDX_T oldcapacity = pages ? pages->capacity() : IDX_T(0);
3274  UT_PageNum oldnpages = numPages(oldcapacity);
3275 
3276  // Caller must force capacity to be on a page boundary if more than one page, else power of 2.
3277  UT_ASSERT_P((newcapacity < IDX_T(thePageSize) && SYSisPow2(exint(newcapacity))) || newcapacity == IDX_T(exint(npages)<<thePageBits));
3278 
3279  const bool nonconstfirst = pages && !pages->getFirstPage()->isConstant();
3280 
3281  if (npages == UT_PageNum(1) && oldnpages == UT_PageNum(1) && !wasshared)
3282  {
3283  // Can reuse existing page table if we're staying at one page, but changing its capacity.
3284 
3285  // NOTE: The "newcapacity != oldcapacity" will almost always be true here, but
3286  // we need to make absolutely sure that we don't realloc a shared page when just
3287  // calling hardenTable()! If we didn't have this check, it could happen, because
3288  // between the reference count check in hardenTable() could return 2, and then
3289  // before the pages->isShared() call above, the reference count drops to 1,
3290  // so we have an already hardened table, but we can identify that we don't need
3291  // to do anything because the capacity won't be changing.
3292  // The reason we can't have hardenTable() realloc a shared page is that in
3293  // GA_PrimitiveList, it needs to incDataRef() on all GA_OffsetLists in a shared
3294  // page before ever hardening one.
3295  if (nonconstfirst && newcapacity != oldcapacity)
3296  {
3297  // Just need to realloc the page.
3298  UT_PageOff newpagecapacity = UT_PageOff(exint(newcapacity));
3299  UT_PageOff sizetocopy = SYSmin(UT_PageOff(exint(pages->mySize)), newpagecapacity);
3300  PageTableEntry *page = pages->getFirstPage();
3301  page->realloc(sizetocopy, newpagecapacity, tuplesize);
3302  }
3303  // Don't need to change anything but capacity (and possibly size)
3304  // if constant page, even if it's an allocated constant page.
3305 
3306  pages->myCapacity = newcapacity;
3307  if (pages->mySize > newcapacity)
3308  pages->mySize = newcapacity;
3309  return pages;
3310  }
3311 
3312  PageTable *table = PageTable::alloc(npages);
3313  table->myCapacity = newcapacity;
3314  if (pages)
3315  {
3316  UT_ASSERT_P(pages->getRefCount() > 0 && table->getRefCount() > 0);
3317  UT_ASSERT_P(pages->capacity() > IDX_T(0));
3318  UT_ASSERT_P(oldnpages > UT_PageNum(0));
3319  const bool wassmallfirstpage = pages->capacity() < IDX_T(thePageSize);
3320  const bool willbesmallfirstpage = newcapacity < IDX_T(thePageSize);
3321 
3322  UT_PageNum minnpages = SYSmin(npages, oldnpages);
3323  table->mySize = pages->mySize;
3324  memcpy(table->getFirstPage(), pages->getFirstPage(), (sizeof(PageTableEntry))*minnpages);
3325 
3326  UT_ASSERT_P(!(SYSisSame<DATA_T, void>()));
3327 
3328  const size_t bytesize = sizeof(NotVoidType)*((TSIZE >= 0) ? TSIZE : tuplesize);
3329 
3330  if (wasshared)
3331  {
3332  // Increment all of the reference counts of reused pages,
3333  // if we're unsharing a table.
3334  PageTableEntry *page = table->getFirstPage();
3335  for (UT_PageNum p = 0; p < minnpages; ++p, ++page)
3336  {
3337  if (page->isRefd(bytesize))
3338  page->incRef();
3339  }
3340  pages->decRef(tuplesize);
3341  }
3342  else
3343  {
3344  // pages isn't shared, so we can just decRef() any pages no
3345  // longer referenced and free pages. This avoids calling
3346  // incRef on all kept pages and decRef on all pages like the
3347  // above would do if pages had its reference count go to
3348  // zero.
3349  PageTableEntry *page = pages->getFirstPage() + npages;
3350  for (UT_PageNum p = npages; p < oldnpages; ++p, ++page)
3351  {
3352  if (page->isRefd(bytesize))
3353  page->decRef();
3354  }
3355  UT_ASSERT_MSG_P(!pages->isShared(), "This should still be unshared.");
3356  free(pages);
3357 #if 0
3358  printf("Free table %d pages (case 1); %d tables total\n",int(oldnpages), int(thePageTableCount.add(-1)));
3359  fflush(stdout);
3360 #endif
3361  }
3362  // Can't refer to pages beyond this point!
3363  UT_IF_ASSERT_P(pages = nullptr;)
3364 
3365  if (table->mySize > newcapacity)
3366  table->mySize = newcapacity;
3367 
3368  // NOTE: The "newcapacity != oldcapacity" is to make absolutely
3369  // sure that we don't realloc a shared page when just
3370  // calling hardenTable()! This is because in
3371  // GA_PrimitiveList, it needs to incDataRef() on all
3372  // GA_OffsetLists in a shared page before ever hardening one.
3373  if (nonconstfirst && (wassmallfirstpage || willbesmallfirstpage) && newcapacity != oldcapacity)
3374  {
3375  UT_PageOff newpagecapacity = SYSmin(UT_PageOff(exint(newcapacity)), UT_PageOff(thePageSize));
3376  UT_PageOff sizetocopy = SYSmin(UT_PageOff(exint(table->mySize)), newpagecapacity);
3377  PageTableEntry *page = table->getFirstPage();
3378  page->realloc(sizetocopy, newpagecapacity, tuplesize);
3379  }
3380  }
3381  else
3382  {
3383  oldnpages = 0;
3384  table->mySize = IDX_T(0);
3385  }
3386 
3387  // Set any new pages to be constant zero.
3388  PageTableEntry *page = table->getFirstPage() + oldnpages;
3389  for (UT_PageNum p = oldnpages; p < npages; ++p, ++page)
3390  {
3391  page->initZero();
3392  }
3393 
3394  if (PAGESHARDENED && wasshared)
3395  {
3396  if (TSIZE >= 1)
3397  table->hardenAllPages(IDX_T(0), table->mySize);
3398  else
3399  table->hardenAllPages(IDX_T(0), table->mySize, tuplesize);
3400  }
3401 
3402  return table;
3403  }
3404 
3405  /// Atomically decrement the reference count iff the page is shared.
3406  /// See the description of decRefIffSharedPage() above for
3407  /// why this function exists.
3409  {
3410  return decCounterIffAbove1(myRefCount);
3411  }
3412 
3413  private:
3414  // The constructor/destructor should never be called, since the class
3415  // can only be allocated with malloc.
3416  PageTable() { UT_ASSERT_P(0); }
3417  PageTable(const PageTable &) { UT_ASSERT_P(0); }
3418  ~PageTable() { UT_ASSERT_P(0); }
3419 
3420  SYS_AtomicCounter myRefCount;
3421  IDX_T mySize;
3422  IDX_T myCapacity;
3423  };
3424 
3425  /// NOTE: These have to be below the PageTable class definition, because
3426  /// for no apparent reason, even though you can have a regular local
3427  /// variable of type PageTable before, you can't have a function
3428  /// parameter of type PageTable before. *sigh*
3429  /// @{
3430  static void hardenConstantPage(PageTable *pages, PageTableEntry *page)
3431  {
3432  const exint arraycapacity(exint(pages->capacity()));
3433  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3434  hardenConstantPage(page, pagecapacity);
3435  }
3436  static void hardenConstantPageNoInit(PageTable *pages, PageTableEntry *page)
3437  {
3438  const exint arraycapacity(exint(pages->capacity()));
3439  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3440  hardenConstantPageNoInit(page, pagecapacity);
3441  }
3442  static void hardenConstantPage(PageTable *pages, PageTableEntry *page, exint tuplesize)
3443  {
3444  const exint arraycapacity(exint(pages->capacity()));
3445  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3446  hardenConstantPage(page, pagecapacity, tuplesize);
3447  }
3448  static void hardenConstantPageNoInit(PageTable *pages, PageTableEntry *page, exint tuplesize)
3449  {
3450  const exint arraycapacity(exint(pages->capacity()));
3451  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3452  hardenConstantPageNoInit(page, pagecapacity, tuplesize);
3453  }
3454  static void hardenSharedPage(PageTable *pages, PageTableEntry *page)
3455  {
3456  const exint arraycapacity(exint(pages->capacity()));
3457  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3458  hardenSharedPage(page, pagecapacity);
3459  }
3460  static void hardenSharedPageNoInit(PageTable *pages, PageTableEntry *page)
3461  {
3462  const exint arraycapacity(exint(pages->capacity()));
3463  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3464  hardenSharedPageNoInit(page, pagecapacity);
3465  }
3466  static void hardenSharedPage(PageTable *pages, PageTableEntry *page, exint tuplesize)
3467  {
3468  const exint arraycapacity(exint(pages->capacity()));
3469  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3470  hardenSharedPage(page, pagecapacity, tuplesize);
3471  }
3472  static void hardenSharedPageNoInit(PageTable *pages, PageTableEntry *page, exint tuplesize)
3473  {
3474  const exint arraycapacity(exint(pages->capacity()));
3475  UT_PageOff pagecapacity((arraycapacity < thePageSize) ? arraycapacity : thePageSize);
3476  hardenSharedPageNoInit(page, pagecapacity, tuplesize);
3477  }
3478  /// @}
3479 
3480  void forceHardenTable(IDX_T newcapacity)
3481  {
3482  if constexpr (SYSisSame<DATA_T,void>())
3483  {
3484  const UT_Storage storage = myImpl.getStorage();
3485  switch (storage)
3486  {
3487  case UT_Storage::INVALID:
3488  UT_ASSERT_MSG_P(0, "Can't have a non-numeric type with a void DATA_T.");
3489  return;
3490  case UT_Storage::INT8:
3491  castType<int8>().forceHardenTable(newcapacity); return;
3492  case UT_Storage::UINT8:
3493  castType<uint8>().forceHardenTable(newcapacity); return;
3494  case UT_Storage::INT16:
3495  castType<int16>().forceHardenTable(newcapacity); return;
3496  case UT_Storage::INT32:
3497  castType<int32>().forceHardenTable(newcapacity); return;
3498  case UT_Storage::INT64:
3499  castType<int64>().forceHardenTable(newcapacity); return;
3500  case UT_Storage::REAL16:
3501  castType<fpreal16>().forceHardenTable(newcapacity); return;
3502  case UT_Storage::REAL32:
3503  castType<fpreal32>().forceHardenTable(newcapacity); return;
3504  case UT_Storage::REAL64:
3505  castType<fpreal64>().forceHardenTable(newcapacity); return;
3506  }
3507  return;
3508  }
3509 
3510  PageTable *&pages = myImpl.getPages();
3511  pages = PageTable::harden(pages, newcapacity, myImpl.getTupleSize());
3512  }
3513 
3514 protected:
3515  template<typename T>
3517  static bool isZero(const T &val)
3518  {
3519  // TODO: Find a way to check for zero that will still compile, but
3520  // that avoids the mess of dealing with memset for POD types.
3521  T zero;
3522  memset(&zero, 0, sizeof(T));
3523  return (val == zero);
3524  }
3525 
3526  template<typename T>
3528  static bool isZero(const T *val,exint tuplesize)
3529  {
3530  UT_ASSERT_P(tuplesize >= 1);
3531  if (tuplesize == 0)
3532  return true;
3533 
3534  // TODO: Find a way to check for zero that will still compile, but
3535  // that avoids the mess of dealing with memset for POD types.
3536  T zero;
3537  memset(&zero, 0, sizeof(T));
3538 
3539  bool iszero = (val[0] == zero);
3540  for (exint i = 1; i < tuplesize; ++i)
3541  iszero &= (val[i] == zero);
3542  return iszero;
3543  }
3544 
3545  template<typename T0,typename T1>
3547  static bool isEqual(const T0 *a,const T1 *b,exint tuplesize)
3548  {
3549  UT_ASSERT_P(tuplesize >= 1);
3550  UT_ASSERT_P(a != nullptr && b != nullptr);
3551 
3552  for (exint i = 0; i < tuplesize; ++i)
3553  {
3554  if (a[i] != b[i])
3555  return false;
3556  }
3557  return true;
3558  }
3559 
3560  template<typename DEST_TYPE,typename SRC_TYPE,exint SRC_TSIZE>
3563  {
3564  if constexpr (SYSisSame<DEST_TYPE,SRC_TYPE>())
3566  else
3567  {
3569  for (exint i = 0; i < SRC_TSIZE; ++i)
3570  dest[i] = UTconvertStorage<DEST_TYPE>(src[i]);
3571  return UT_FixedVector<DEST_TYPE, SRC_TSIZE>{ dest };
3572  }
3573  }
3574 
3575 private:
3576  /// This class wraps the data of UT_PageArray, just so that if
3577  /// the tuple size and data type are known at compile-time, it doesn't
3578  /// need to store that information as an int and a UT_Storage.
3579  /// myPages is the same in all of them, but can't be extracted out,
3580  /// because the C++ standard, unfortunately, forbids 0-byte classes.
3581  /// @{
3582  struct ImplPageData
3583  {
3584  PageTable *myPages;
3585  };
3586 
3587  template<exint ITSIZE, typename IDATA_T>
3588  class Impl :
3589  private ImplPageData,
3590  private UT_TupleType<ITSIZE, IDATA_T>
3591  {
3592  public:
3593  SYS_FORCE_INLINE const PageTable *getPages() const { return ImplPageData::myPages; }
3594  SYS_FORCE_INLINE PageTable *&getPages() { return ImplPageData::myPages; }
3599  };
3600 
3601  Impl<TSIZE,DATA_T> myImpl;
3602  /// @}
3603 };
3604 
3605 #endif
void hardenAllPages(IDX_T start, IDX_T end, exint tuplesize=TSIZE)
SYS_FORCE_INLINE NotVoidType operator()(IDX_T i, exint component=0) const
Read element i's specified component (default component 0).
Definition: UT_PageArray.h:444
type
Definition: core.h:556
static void hardenConstantPageNoInit(PageTable *pages, PageTableEntry *page)
GLint first
Definition: glcorearb.h:405
static void copyNonConst(NotVoidType *destpagedata, const SrcNotVoidType *srcpagedata, exint desttuplesize, exint srctuplesize, UT_PageOff ntuples)
SYS_FORCE_INLINE bool isPageConstant(UT_PageNum pagenum) const
Returns true iff the specified page is constant-compressed.
static void hardenConstantPage(PageTable *pages, PageTableEntry *page, exint tuplesize)
SYS_FORCE_INLINE void opVector(IDX_T i, const UT_FixedVector< SRC_DATA_T, SRC_TSIZE > &v)
Definition: UT_PageArray.h:709
static SYS_FORCE_INLINE const NotVoidType * getConstantPtr(const PageTableEntry *page, exint component=0, exint tuplesize=TSIZE)
void SecondGuess
Definition: UT_Storage.h:91
SYS_FORCE_INLINE void setSize(IDX_T newsize)
static SYS_FORCE_INLINE bool decCounterIffAbove1(SYS_AtomicCounter &counter)
int int32
Definition: SYS_Types.h:39
SYS_FORCE_INLINE const NotVoidType * getInlinePtr(exint tuplesize) const
Returns the data pointer, if an inline constant page.
SYS_FORCE_INLINE void * getFirstPtrVoid()
Returns the data pointer, if not a constant page.
UT_Storage
Definition: UT_Storage.h:28
static void hardenSharedPageNoInit(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize=TSIZE)
void hardenAllPagesUnknownType(IDX_T start, IDX_T end)
void tryCompressPage(UT_PageOff pagesize, exint tuplesize=TSIZE)
static ThisType * harden(ThisType *pages, IDX_T newcapacity, exint tuplesize=TSIZE)
static void hardenSharedPage(PageTable *pages, PageTableEntry *page, exint tuplesize)
SYS_FORCE_INLINE bool isArrayZero() const
Returns true iff the entire array is constant & zero.
static const UT_Storage theStorage
Definition: UT_PageArray.h:246
static SYS_FORCE_INLINE NotVoidType getConstant(const PageTableEntry *const page, exint component, exint tuplesize=TSIZE)
NotVoid< DATA_T >::type NotVoidType
Definition: UT_PageArray.h:238
UT_PageArray(const ThisType &that)
Definition: UT_PageArray.h:304
void setTupleSize(exint newtuplesize, const UT_Defaults &v)
static ThisType * alloc(UT_PageNum npages)
void tryCompressPage(UT_PageNum pagenum, exint tuplesize=TSIZE)
SYS_FORCE_INLINE const NotVoidType * getMaskedPtr() const
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
GLboolean * data
Definition: glcorearb.h:131
void copy(IDX_T dest, IDX_T src)
Copies a single element from src to dest within this.
void tryCompressAllPagesUnknownType(IDX_T start, IDX_T end)
const GLdouble * v
Definition: glcorearb.h:837
auto printf(const S &fmt, const T &...args) -> int
Definition: printf.h:670
uint128_t uintptr_t
Definition: format.h:479
SYS_FORCE_INLINE ~UT_PageArray()
Definition: UT_PageArray.h:318
SYS_FORCE_INLINE bool operator!=(const ThisType &that) const
NOTE: This just does a shallow comparison.
void tryCompressAllPages(IDX_T start, IDX_T end, exint tuplesize=TSIZE)
GLuint start
Definition: glcorearb.h:475
constexpr const T * data() const noexcept
SYS_FORCE_INLINE void clear()
Definition: UT_PageArray.h:324
SYS_FORCE_INLINE ThisType & operator=(const UT_PageArray< SRC_DATA_T, SRC_TSIZE, SRC_TABLEHARDENED, SRC_PAGESHARDENED, THEPAGEBITS, IDX_T > &that)
Definition: UT_PageArray.h:382
static void hardenSharedPageNoInit(PageTable *pages, PageTableEntry *page, exint tuplesize)
void opVectorUnknownType(IDX_T i, const UT_FixedVector< SRC_DATA_T, SRC_TSIZE > &v)
SYS_FORCE_INLINE IDX_T size() const
Definition: UT_PageArray.h:842
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
Definition: SYS_Types.h:136
constexpr UT_FixedVector< SYS_FixedArrayElement_t< TS >, SYS_FixedArraySize_v< TS > > UTmakeFixedVector(const TS &as) noexcept
SYS_FORCE_INLINE void add(IDX_T i, SRC_DATA_T v)
component == 0 in this version
Definition: UT_PageArray.h:592
UT_FixedVector< DEST_DATA_T, DEST_TSIZE > getVectorUnknownSize(IDX_T i) const
SYS_FORCE_INLINE void relaxedStore(T val)
SYS_FORCE_INLINE bool isShared() const
void countMemory(UT_MemoryCounter &counter, bool inclusive) const
int64 exint
Definition: SYS_Types.h:125
SYS_FORCE_INLINE PageTableEntry * getFirstPage()
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
static void hardenSharedPageNoInit(PageTable *pages, PageTableEntry *page)
void setConstant(IDX_T start, IDX_T end, NotVoidType v)
void opUnknownType(IDX_T i, exint component, SRC_DATA_T v)
exint UT_PageNum
Definition: UT_PageArray.h:34
SYS_FORCE_INLINE const PageTableEntry * getPPage(UT_PageNum i) const
static void hardenConstantPage(PageTable *pages, PageTableEntry *page)
IMATH_HOSTDEVICE constexpr bool equal(T1 a, T2 b, T3 t) IMATH_NOEXCEPT
Definition: ImathFun.h:105
SYS_FORCE_INLINE UT_PageArray< DATA_T, TSIZE, true, true, THEPAGEBITS, IDX_T > & hardenAllPages(IDX_T start=IDX_T(0), IDX_T end=IDX_T(-1))
static const exint theTupleSize
Definition: UT_PageArray.h:247
SYS_FORCE_INLINE UT_PageArray< DEST_DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T >::PageTableEntry * castType()
SYS_FORCE_INLINE exint getTupleSize() const
DEST_DATA_T getUnknownType(IDX_T i, exint component=0) const
SYS_SelectType< T, float, SYS_IsSame< T, void >::value >::type type
Definition: UT_PageArray.h:236
#define UT_ASSERT_MSG_P(ZZ,...)
Definition: UT_Assert.h:167
SYS_FORCE_INLINE IDX_T size() const
NOTE: This is the size of the full array, not the number of pages.
SYS_FORCE_INLINE UT_FixedVector< DEST_DATA_T, DEST_TSIZE > getVector(IDX_T i) const
Definition: UT_PageArray.h:497
#define UT_IF_ASSERT_P(ZZ)
Definition: UT_Assert.h:191
SYS_FORCE_INLINE void set(IDX_T i, SRC_DATA_T v)
component == 0 in this version
Definition: UT_PageArray.h:586
SYS_FORCE_INLINE UT_Storage getStorage() const
static SYS_FORCE_INLINE IDX_T roundUpCapacity(IDX_T capacity)
Definition: UT_PageArray.h:863
SYS_FORCE_INLINE bool isTableHardened() const
Returns true iff the table isn't shared with any other UT_PageArray's.
float fpreal32
Definition: SYS_Types.h:200
SYS_FORCE_INLINE exint getRefCount() const
SYS_FORCE_INLINE UT_PageArray< DATA_T, DEST_TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T > & castTupleSize()
bool hasNanInRange(IDX_T start, IDX_T end) const
void setVectorRange(IDX_T deststart, IDX_T nelements, const UT_FixedVector< SRC_DATA_T, SRC_TSIZE > *src)
UT_PageArray(const UT_PageArray< SRC_DATA_T, SRC_TSIZE, SRC_TABLEHARDENED, SRC_PAGESHARDENED, THEPAGEBITS, IDX_T > &that)
Definition: UT_PageArray.h:311
UT_PageArray(exint tuplesize, UT_Storage storage)
Definition: UT_PageArray.h:288
void tryCompressPageUnknownType(UT_PageNum pagenum)
void decRef(exint tuplesize)
SYS_FORCE_INLINE bool isShared() const
void fill(IDX_T start, IDX_T end, const NotVoidType &val)
static bool isEqualConst(const NotVoidType *tuple, const SrcNotVoidType *stuple, exint mintuplesize)
SYS_FORCE_INLINE void setConstantBit()
double fpreal64
Definition: SYS_Types.h:201
#define UT_ASSERT_MSG(ZZ,...)
Definition: UT_Assert.h:168
unsigned char uint8
Definition: SYS_Types.h:36
UT_FixedVector< DEST_DATA_T, DEST_TSIZE > getVectorMismatchedSize(IDX_T i) const
UT_FixedVector< DEST_DATA_T, DEST_TSIZE > getVectorUnknownType(IDX_T i) const
SYS_FORCE_INLINE exint getRefCount() const
SYS_FORCE_INLINE bool isConstantZero() const
This is only valid to call if the type doesn't fit inline.
SYS_FORCE_INLINE void setSize(IDX_T newsize, exint tuplesize)
static SYS_FORCE_INLINE UT_PageOff pageOff(IDX_T i)
UT_PageArray(UT_Storage storage)
Definition: UT_PageArray.h:277
SYS_FORCE_INLINE void tryCompressPage(UT_PageNum pagenum)
SYS_FORCE_INLINE ThisType & operator=(const ThisType &that)
Definition: UT_PageArray.h:374
static SYS_FORCE_INLINE bool typeFitsInline(exint tuplesize=TSIZE)
This is only valid to call when DATA_T is non-void.
SYS_FORCE_INLINE const PageTableEntry * getFirstPage() const
SYS_FORCE_INLINE void setPageConstant(UT_PageNum pagenum, const UT_FixedVector< SRC_DATA_T, theSafeTupleSize > &val)
static SYS_FORCE_INLINE UT_PageNum numPages(IDX_T nelements)
static SYS_FORCE_INLINE bool isEqual(const T0 *a, const T1 *b, exint tuplesize)
SYS_FORCE_INLINE void getRange(IDX_T srcstart, IDX_T nelements, T *dest) const
SYS_FORCE_INLINE bool isRefd(exint tuplebytes) const
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:164
SYS_FORCE_INLINE bool operator==(const ThisType &that) const
NOTE: This just does a shallow comparison.
SYS_FORCE_INLINE void setPageConstant(UT_PageNum pagenum, const NotVoidType &val)
SYS_FORCE_INLINE IDX_T capacity() const
NOTE: This is the capacity of the full array, not the capacity of pages.
static SYS_FORCE_INLINE bool isZero(const T &val)
SYS_FORCE_INLINE IDX_T capacity() const
Definition: UT_PageArray.h:850
#define SYS_FALLTHROUGH
Definition: SYS_Compiler.h:61
GLuint GLuint end
Definition: glcorearb.h:475
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
T compare_swap(T expected, T desired)
SYS_FORCE_INLINE NotVoidType * hardenPage(UT_PageNum pagenum)
UT_FixedVector< NotVoidType, theSafeTupleSize > Tuple
DATA_T DataType
Definition: UT_PageArray.h:245
SYS_FORCE_INLINE void op(IDX_T i, exint component, SRC_DATA_T v)
Definition: UT_PageArray.h:612
SYS_FORCE_INLINE NotVoidType * getInlinePtr(exint tuplesize)
Returns the data pointer, if an inline constant page.
void setCapacity(IDX_T newcapacity)
Definition: UT_PageArray.h:878
long long int64
Definition: SYS_Types.h:116
void clearAndDestroy()
Definition: UT_PageArray.h:330
SYS_FORCE_INLINE void * getMaskedPtrVoid()
static SYS_FORCE_INLINE UT_PageOff pageStart(IDX_T page)
UT_PageArray(exint tuplesize)
Definition: UT_PageArray.h:265
SYS_FORCE_INLINE const void * getFirstPtrVoidUnsafe() const
static void copyPartialPage(PageTableEntry *dest, const typename SrcType::PageTableEntry *src, exint desttuplesize, exint srctuplesize, UT_PageOff destoff, UT_PageOff srcoff, UT_PageOff ntuples, UT_PageOff destpagecapacity)
SYS_FORCE_INLINE bool isConstantAndZeroSafe() const
signed char int8
Definition: SYS_Types.h:35
void setPageConstantUnknownType(UT_PageNum pagenum, const UT_FixedVector< SRC_DATA_T, theSafeTupleSize > &val)
SYS_FORCE_INLINE bool decRefIffShared(exint tuplesize=TSIZE)
static void makeConstant(PageTableEntry *page, const NotVoidType *values, exint tuplesize)
SYS_FORCE_INLINE NotVoidType * hardenPageNoInit(UT_PageNum pagenum)
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
SYS_FORCE_INLINE void tryCompressAllPages(IDX_T start=IDX_T(0), IDX_T end=IDX_T(-1))
static const exint thePageBits
Definition: UT_PageArray.h:249
SYS_FORCE_INLINE void add(IDX_T i, exint component, SRC_DATA_T v)
Definition: UT_PageArray.h:607
const NotVoidType * getPageData(UT_PageNum pagenum) const
exint UT_PageOff
Definition: UT_PageArray.h:35
static const exint theSafeTupleSize
Definition: UT_PageArray.h:248
SYS_FORCE_INLINE void incRef()
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
void realloc(UT_PageOff sizetocopy, UT_PageOff newpagecapacity, exint tuplesize=TSIZE)
static void makeConstantFrom(PageTableEntry *dest, const typename SrcType::PageTableEntry *src, exint desttuplesize, exint srctuplesize)
PageTableEntry ThisType
SYS_FORCE_INLINE void setPageConstant(UT_PageNum pagenum, const SRC_DATA_T *values)
SYS_FORCE_INLINE void addVector(IDX_T i, const TS &as)
Definition: UT_PageArray.h:699
SYS_FORCE_INLINE T relaxedLoad() const
void moveRange(IDX_T srcstart, IDX_T deststart, IDX_T nelements)
static const exint thePageMask
Definition: UT_PageArray.h:251
static void hardenConstantPageNoInit(PageTable *pages, PageTableEntry *page, exint tuplesize)
GLsizeiptr size
Definition: glcorearb.h:664
static const uintptr_t theConstantPageBit
SYS_FORCE_INLINE int64 getMemoryUsage(exint tuplebytes) const
SYS_AtomicInt< int32 > SYS_AtomicCounter
SYS_FORCE_INLINE const UT_PageArray< DEST_DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T > & castType() const
void setSize(IDX_T newsize)
Definition: UT_PageArray.h:902
SYS_FORCE_INLINE const void * getMaskedPtrVoid() const
SYS_FORCE_INLINE const UT_PageArray< DEST_DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T >::PageTableEntry * castType() const
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
void swapRange(IDX_T astart, IDX_T bstart, IDX_T nelements)
SYS_FORCE_INLINE void * getFirstPtrVoidUnsafe()
short int16
Definition: SYS_Types.h:37
bool decRefIffSharedPage(UT_PageNum pagenum)
void setPageConstantUnknownType(UT_PageNum pagenum, const SRC_DATA_T *values)
SYS_FORCE_INLINE PageTableEntry * getPPage(UT_PageNum i)
void setStorage(const UT_Storage newstorage)
IMATH_HOSTDEVICE constexpr bool iszero(T a, T t) IMATH_NOEXCEPT
Definition: ImathFun.h:98
SYS_FORCE_INLINE void set(IDX_T i, exint component, SRC_DATA_T v)
Definition: UT_PageArray.h:602
static void hardenConstantPageNoInit(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize=TSIZE)
static void fillNonConstWithConst(NotVoidType *destpagedata, NotVoidType *destpageend, const SrcNotVoidType *stuple, exint mintuplesize, exint desttupleextra)
SYS_FORCE_INLINE NotVoidType * getMaskedPtr()
bool decRefIffSharedTable()
SYS_FORCE_INLINE const UT_PageArray< DATA_T, DEST_TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T > & castTupleSize() const
SYS_FORCE_INLINE bool isConstant() const
This is always valid to call.
SYS_FORCE_INLINE void setRange(IDX_T deststart, IDX_T nelements, const T *src)
static void hardenSharedPage(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize=TSIZE)
SYS_FORCE_INLINE const void * getFirstPtrVoid() const
Returns the data pointer, if not a constant page.
GLuint GLfloat * val
Definition: glcorearb.h:1608
static SYS_FORCE_INLINE NotVoidType * getConstantPtr(PageTableEntry *page, exint component=0, exint tuplesize=TSIZE)
static SYS_FORCE_INLINE void makeConstant(PageTableEntry *page, const UT_FixedVector< NotVoidType, theSafeTupleSize > &val)
static SYS_FORCE_INLINE UT_FixedVector< DEST_TYPE, SRC_TSIZE > convertVectorStorage(const UT_FixedVector< SRC_TYPE, SRC_TSIZE > &src)
static void makeConstant(PageTableEntry *page, const NotVoidType &val, exint tuplesize=TSIZE)
SYS_FORCE_INLINE UT_PageArray< DATA_T, TSIZE, true, PAGESHARDENED, THEPAGEBITS, IDX_T > & hardenTable()
static const exint thePageSize
Definition: UT_PageArray.h:250
static SYS_FORCE_INLINE bool isZero(const T *val, exint tuplesize)
SYS_FORCE_INLINE void alloc(UT_PageOff nelements, exint tuplesize=TSIZE)
T add(T val)
Atomically adds val to myValue, returning the new value of myValue.
static void hardenConstantPage(PageTableEntry *page, UT_PageOff pagecapacity, exint tuplesize=TSIZE)
int64 getMemoryUsage(bool inclusive) const
static SYS_FORCE_INLINE UT_PageNum pageNum(IDX_T i)
UT_PageArray< DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T > ThisType
Definition: UT_PageArray.h:244
static void hardenSharedPage(PageTable *pages, PageTableEntry *page)
void setCapacityIfNeeded(IDX_T newcapacity)
Definition: UT_PageArray.h:891
SYS_FORCE_INLINE void setVector(IDX_T i, const TS &as)
Definition: UT_PageArray.h:693
void getVectorRange(IDX_T srcstart, IDX_T nelements, UT_FixedVector< DEST_DATA_T, DEST_TSIZE > *dest) const
void forceHardenTable(IDX_T newcapacity)
SYS_FORCE_INLINE int64 getGuaranteedInt(IDX_T i) const
SYS_FORCE_INLINE NotVoidType * unshareConstantPage(UT_PageNum pagenum)
#define SYSmin(a, b)
Definition: SYS_Math.h:1953
SYS_FORCE_INLINE bool isPageHard(UT_PageNum pagenum) const
UT_PageArray< DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T > ParentType
SYS_FORCE_INLINE constexpr int UTstorageSize(UT_Storage storage)
Returns the number of bytes in the given storage type.
Definition: UT_Storage.h:57
SYS_FORCE_INLINE void initZero()
SYS_FORCE_INLINE const NotVoidType * getFirstPtr() const
Returns the data pointer, if not a constant page.
SYS_FORCE_INLINE NotVoidType * getFirstPtr()
Returns the data pointer, if not a constant page.
void setPageConstantMismatchedType(UT_PageNum pagenum, const SRC_DATA_T *values)
void replace(const UT_PageArray< SRC_DATA_T, SRC_TSIZE, SRC_TABLEHARDENED, SRC_PAGESHARDENED, THEPAGEBITS, IDX_T > &that)
Definition: UT_PageArray.h:391
Definition: format.h:1821
SYS_FORCE_INLINE NotVoidType & operator()(IDX_T i, exint component=0)
Definition: UT_PageArray.h:543
NotVoidType * getPageData(UT_PageNum pagenum)
SYS_FORCE_INLINE void decRef()
static void replacePage(PageTableEntry *dest, const typename SrcType::PageTableEntry *src, exint desttuplesize, exint srctuplesize, UT_PageOff destpagesize, UT_PageOff destpagecapacity)
SYS_FORCE_INLINE UT_PageArray< DEST_DATA_T, TSIZE, TABLEHARDENED, PAGESHARDENED, THEPAGEBITS, IDX_T > & castType()
GLenum src
Definition: glcorearb.h:1793