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