opennurbs_fsp.h
1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 #if !defined(OPENNURBS_FSP_INC_)
17 #define OPENNURBS_FSP_INC_
18 
19 class ON_CLASS ON_FixedSizePool
20 {
21 public:
24 
25 #if defined(ON_HAS_RVALUEREF)
27  ON_FixedSizePool& operator=(ON_FixedSizePool&&);
28 #endif
29 
30  /*
31  Description:
32  Create a fixed size memory pool.
33  Parameters:
34  sizeof_element - [in]
35  number of bytes in each element. This parameter must be greater than zero.
36  In general, use sizeof(element type). If you pass a "raw" number as
37  sizeof_element, then be certain that it is the right size to insure the
38  fields in your elements will be properly aligned.
39  element_count_estimate - [in] (0 = good default)
40  If you know how many elements you will need, pass that number here.
41  It is better to slightly overestimate than to slightly underestimate.
42  If you do not have a good estimate, then use zero.
43  block_element_capacity - [in] (0 = good default)
44  If block_element_capacity is zero, Create() will calculate a block
45  size that is efficent for most applications. If you are an expert
46  user and want to specify the number of elements per block,
47  then pass the number of elements per block here. When
48  block_element_capacity > 0 and element_count_estimate > 0, the first
49  block will have a capacity of at least element_count_estimate; in this
50  case do not ask for extraordinarly large amounts of contiguous heap.
51  Remarks:
52  You must call Create() on an unused ON_FixedSizePool or call Destroy()
53  before calling create.
54  Returns:
55  True if successful and the pool can be used.
56  */
57  bool Create(
58  size_t sizeof_element,
59  size_t element_count_estimate,
60  size_t block_element_capacity
61  );
62 
63  /*
64  Returns:
65  Size of the elements in this pool.
66  */
67  size_t SizeofElement() const;
68 
69  /*
70  Returns:
71  A pointer to sizeof_element bytes. The memory is zeroed.
72  */
73  void* AllocateElement();
74 
75  /*
76  Returns:
77  A pointer to sizeof_element bytes. The values in the returned block are undefined.
78  */
79  void* AllocateDirtyElement();
80 
81  /*
82  Description:
83  Return an element to the pool.
84  Parameters:
85  p - [in]
86  A pointer returned by AllocateElement().
87  It is critical that p be from this pool and that
88  you return a pointer no more than one time.
89  Remarks:
90  If you find the following remarks confusing, but you really want to use
91  ReturnElement(), then here are some simple guidelines.
92  1) SizeofElement() must be >= 16
93  2) SizeofElement() must be a multiple of 8.
94  3) Do not use FirstElement() and NextElement() to iterate through
95  the pool.
96 
97  If 1 to 3 don't work for you, then you need to understand the following
98  information before using ReturnElement().
99 
100  ON_FixedMemoryPool uses the first sizeof(void*) bytes of the
101  returned element for bookkeeping purposes. Therefore, if you
102  are going to use ReturnElement(), then SizeofElement() must be
103  at least sizeof(void*). If you are using a platform that requires
104  pointers to be aligned on sizeof(void*) boundaries, then
105  SizeofElement() must be a multiple of sizeof(void*).
106  If you are going to use ReturnElement() and then use FirstElement()
107  and NextElement() to iterate through the list of elements, then you
108  need to set a value in the returned element to indicate that it
109  needs to be skipped during the iteration. This value cannot be
110  located in the fist sizeof(void*) bytes of the element. If the
111  element is a class with a vtable, you cannot call a virtual
112  function on a returned element because the vtable pointer is
113  trashed when ReturnElement() modifies the fist sizeof(void*) bytes.
114  */
115  void ReturnElement(void* p);
116 
117  /*
118  Description:
119  Return all allocated elements to the pool. No heap is freed and
120  the pool remains initialized and ready for AllocateElement()
121  to be called.
122  */
123  void ReturnAll();
124 
125  /*
126  Description:
127  Destroy the pool and free all the heap. The pool cannot be used again
128  until Create() is called.
129  */
130  void Destroy();
131 
132  /*
133  Returns:
134  Number of active elements. (Elements that have been returned are not active.)
135  */
136  size_t ActiveElementCount() const;
137 
138  /*
139  Returns:
140  Total number of elements = number of active elements + number of returned elements.
141  */
142  size_t TotalElementCount() const;
143 
144  /*
145  Description:
146  Get the i-th elment in the fixed size pool.
147  Parameters:
148  element_index - [in]
149  Returns:
150  A pointer to the element with the specified index.
151  The first element has element_index = 0 and is the element
152  returned by the first call to AllocateElement().
153  The last element has element_index = ElementCount()-1.
154  If element_index is out of range, nullptr is returned.
155  Remarks:
156  It is faster to use ON_FixedSizePoolIterator.FirstElement() and
157  ON_FixedSizePoolIterator.NextElement() to iterate through the
158  entire list of elements. This function is relatively
159  efficient when there are a few large blocks in the pool
160  or element_index is small compared to the number of elements
161  in the first few blocks.
162 
163  If ReturnElement() is not used or no AllocateElement() calls
164  are made after any use of ReturnElement(), then the i-th
165  element is the one returned by the (i+1)-th call to
166  AllocateElement()
167  */
168  void* Element(
169  size_t element_index
170  ) const;
171 
172 
173  /*
174  Description:
175  Get the fixed size pool index of an element.
176  Parameters:
177  element_pointer - [in]
178  Returns:
179  An index >= 0 and < ON_MAX_SIZE_T if the element_pointer
180  points to an element managed by the this fixed size pool.
181  ON_MAX_SIZE_T otherwise.
182  Remarks:
183  It is faster to use ON_FixedSizePoolIterator.FirstElement() and
184  ON_FixedSizePoolIterator.NextElement() to iterate through the
185  entire list of elements. This function is relatively
186  efficient when there are a few large blocks in the pool
187  or element_pointer is an element in the first few blocks.
188 
189  If ReturnElement() is not used or no AllocateElement() calls
190  are made after any use of ReturnElement(), then the i-th
191  element is the one returned by the (i+1)-th call to
192  AllocateElement().
193  */
194  size_t ElementIndex(
195  const void* element_pointer
196  ) const;
197 
198  /*
199  Description:
200  If you are certain that all elements in hte pool (active and returned)
201  have an unsigned id that is unique and increasing, then you may use
202  this function to find them.
203  Parameters:
204  id_offset - [in]
205  offset into the element where the id is stored.
206  id - [in]
207  id to search for
208  */
209  void* ElementFromId(
210  size_t id_offset,
211  unsigned int id
212  ) const;
213 
214 public:
215  // Primarily used for debugging
216  bool IsValid() const;
217 
218 private:
219  friend class ON_FixedSizePoolIterator;
220 
221  void* m_first_block;
222 
223  // ReturnElement() adds to the m_al_element stack.
224  // AllocateElement() will use the stack before using m_al_element_array[]
225  void* m_al_element_stack;
226 
227  void* m_al_block; // current element allocation block.
228  // m_al_element_array[] is in m_al_block and has length m_al_count.
229  void* m_al_element_array;
230  size_t m_al_count;
231  size_t m_sizeof_element;
232  size_t m_block_element_count; // block element count
233  size_t m_active_element_count; // number of active elements
234  size_t m_total_element_count; // total number of elements (active + returned)
235 
236 private:
237  // returns capacity of elements in existing block
238  size_t BlockElementCapacity( const void* block ) const;
240  // returns number of allocated of elements in existing block
241  size_t BlockElementCount( const void* block ) const;
242 
243 private:
244  // prohibit copy construction and operator=.
245  ON_FixedSizePool(const ON_FixedSizePool&) = delete;
246  ON_FixedSizePool& operator=(const ON_FixedSizePool&) = delete;
247 };
248 
249 class ON_CLASS ON_FixedSizePoolIterator
250 {
251 public:
253  ON_FixedSizePoolIterator( const class ON_FixedSizePool& fsp );
254 
255  const class ON_FixedSizePool* FixedSizePool();
256 
257  void Create(const ON_FixedSizePool* fsp);
258 
259  /*
260  Description:
261  Get the first element when iterating through the list of elements.
262  Parameters:
263  element_index - [in]
264  If you use the version of FirstElement() that has an
265  element_index parameter, then the iteration begins at
266  that element.
267  Example:
268  The loop will iteratate through all the elements returned from
269  AllocateElement(), including any that have be returned to the pool
270  using ReturnElement().
271 
272  // iterate through all elements in the pool
273  // This iteration will go through TotalElements() items.
274  for ( void* p = FirstElement(); 0 != p; p = NextElement() )
275  {
276  // If you are not using ReturnElement(), then you may process
277  // "p" immediately. If you have used ReturnElement(), then you
278  // must check some value in p located after the first sizeof(void*)
279  // bytes to see if p is active.
280  if ( p is not active )
281  continue;
282 
283  ... process p
284  }
285 
286  Returns:
287  The first element when iterating through the list of elements.
288  Remarks:
289  FirstElement() and NextElement() will return elements that have
290  been returned to the pool using ReturnElement(). If you use
291  ReturnElement(), then be sure to mark the element so it can be
292  identified and skipped.
293 
294  Do not make any calls to FirstBlock() or NextBlock() when using
295  FirstElement() and NextElement() to iteratate through elements.
296  */
297  void* FirstElement();
298  void* FirstElement( size_t element_index );
299 
300  /*
301  Description:
302  Get the next element when iterating through the list of elements.
303  If FirstElement() is not called, then the first call to
304  NextElement() returns the first element.
305  Example:
306  See the FirstElement() documentation.
307  Returns:
308  The next element when iterating through the list of elements.
309  Remarks:
310  FirstElement() and NextElement() will return elements that have
311  been returned to the pool using ReturnElement(). If you use
312  ReturnElement(), then be sure to mark the element so it can be
313  identified and skipped.
314 
315  Do not make any calls to FirstBlock() or NextBlock() when using
316  FirstElement() and NextElement() to iteratate through elements.
317  */
318  void* NextElement();
319 
320  /*
321  Returns:
322  The most recently returned value from a call to FirstElement()
323  or NextElement().
324  Remarks:
325  Do not make any calls to FirstBlock() or NextBlock() when using
326  FirstElement() and NextElement() to iteratate through elements.
327  */
328  void* CurrentElement() const;
329 
330  /*
331  Description:
332  Sets the state of the iterator to the initial state that
333  exists after construction. This is useful if the iterator
334  has been used the get one or more elements and then
335  the referenced fixed size pool is modified or code wants
336  to begin iteration again a used a call to NextElement()
337  to return the first element.
338  */
339  void Reset();
340 
341 
342  /*
343  Description:
344  Get a pointer to the first element in the first block.
345  Parameters:
346  block_element_count - [out] (can be null)
347  If not null, the number of elements allocated from the
348  first block is returned in block_element_count.
349  Note that if you have used ReturnElement(), some
350  of these elemements may have been returned.
351  Example:
352  The loop will iteratate through all the blocks.
353 
354  // iterate through all blocks in the pool
355  size_t block_element_count = 0;
356  for ( void* p = FirstBlock(&block_element_count);
357  0 != p;
358  p = NextBlock(&block_element_count)
359  )
360  {
361  ElementType* e = (ElementType*)p;
362  for ( size_t i = 0;
363  i < block_element_count;
364  i++, e = ((const char*)e) + SizeofElement()
365  )
366  {
367  ...
368  }
369  }
370 
371  Returns:
372  The first block when iterating the list of blocks.
373  Remarks:
374  The heap for a fixed size memory pool is simply a linked
375  list of blocks. FirstBlock() and NextBlock() can be used
376  to iterate through the list of blocks.
377 
378  Do not make any calls to FirstElement() or NextElement() when using
379  FirstBlock() and NextBlock() to iteratate through blocks.
380  */
381  void* FirstBlock( size_t* block_element_count );
382 
383  /*
384  Description:
385  Get the next block when iterating through the blocks.
386  Parameters:
387  block_element_count - [out] (can be null)
388  If not null, the number of elements allocated from the
389  block is returned in block_element_count. Note that if
390  you have used ReturnElement(), some of these elemements
391  may have been returned.
392  Example:
393  See the FirstBlock() documentation.
394  Returns:
395  The next block when iterating through the blocks.
396  Remarks:
397  Do not make any calls to FirstElement() or NextElement() when using
398  FirstBlock() and NextBlock() to iteratate through blocks.
399  */
400  void* NextBlock( size_t* block_element_count );
401 
402 private:
403  const class ON_FixedSizePool* m_fsp;
404  void* m_it_block;
405  void* m_it_element;
406 };
407 
408 
409 template <class T> class ON_SimpleFixedSizePool : private ON_FixedSizePool
410 {
411 public:
412  // construction ////////////////////////////////////////////////////////
413 
416 
417  /*
418  Description:
419  Create a fixed size memory pool.
420  Parameters:
421  element_count_estimate - [in] (0 = good default)
422  If you know how many elements you will need, pass that number here.
423  It is better to slightly overestimate than to slightly underestimate.
424  If you do not have a good estimate, then use zero.
425  block_element_count - [in] (0 = good default)
426  If block_element_count is zero, Create() will calculate a block
427  size that is efficent for most applications. If you are an expert
428  user and want to specify the number of blocks, then pass the number
429  of elements per block here. When block_element_count > 0 and
430  element_count_estimate > 0, the first block will be large enough
431  element_count_estimate*sizeof(T) bytes; in this case do not
432  ask for extraordinarly large amounts of contiguous heap.
433  Remarks:
434  You must call Create() on an unused ON_FixedSizePool or call Destroy()
435  before calling create.
436  Returns:
437  True if successful and the pool can be used.
438  */
439  bool Create(
440  size_t element_count_estimate,
441  size_t block_element_count
442  );
443 
444  /*
445  Returns:
446  Size of the elements in this pool.
447  */
448  size_t SizeofElement() const;
449 
450  /*
451  Returns:
452  A pointer to sizeof_element bytes. The memory is zeroed.
453  */
454  T* AllocateElement();
455 
456  /*
457  Description:
458  Return an element to the pool.
459  Parameters:
460  p - [in]
461  A pointer returned by AllocateElement().
462  It is critical that p be from this pool and that
463  you return a pointer no more than one time.
464  Remarks:
465  If you find the following remarks confusing, but you really want to use
466  ReturnElement(), then here are some simple guidelines.
467  1) SizeofElement() must be >= 16
468  2) SizeofElement() must be a multiple of 8.
469  3) Do not use FirstElement() and NextElement() to iterate through
470  the pool.
471 
472  If 1 to 3 don't work for you, then you need to understand the following
473  information before using ReturnElement().
474 
475  ON_FixedMemoryPool uses the first sizeof(void*) bytes of the
476  returned element for bookkeeping purposes. Therefore, if you
477  are going to use ReturnElement(), then SizeofElement() must be
478  at least sizeof(void*). If you are using a platform that requires
479  pointers to be aligned on sizeof(void*) boundaries, then
480  SizeofElement() must be a multiple of sizeof(void*).
481  If you are going to use ReturnElement() and then use FirstElement()
482  and NextElement() to iterate through the list of elements, then you
483  need to set a value in the returned element to indicate that it
484  needs to be skipped during the iteration. This value cannot be
485  located in the fist sizeof(void*) bytes of the element. If the
486  element is a class with a vtable, you cannot call a virtual
487  function on a returned element because the vtable pointer is
488  trashed when ReturnElement() modifies the fist sizeof(void*) bytes.
489  */
490  void ReturnElement(T* p);
491 
492  /*
493  Description:
494  Return all allocated elements to the pool. No heap is freed and
495  the pool remains initialized and ready for AllocateElement()
496  to be called.
497  */
498  void ReturnAll();
499 
500  /*
501  Description:
502  Destroy the pool and free all the heap. The pool cannot be used again
503  until Create() is called.
504  */
505  void Destroy();
506 
507  /*
508  Returns:
509  Number of active elements. (Elements that have been returned are not active.)
510  */
511  size_t ActiveElementCount() const;
512 
513  /*
514  Returns:
515  Total number of elements = number of active elements + number of returned elements.
516  */
517  size_t TotalElementCount() const;
518 
519  /*
520  Description:
521  Get the i-th elment in the pool.
522  Parameters:
523  element_index - [in]
524  Returns:
525  A pointer to the i-th element. The first element has index = 0
526  and is the element returned by the first call to AllocateElement().
527  The last element has index = ElementCount()-1.
528  If i is out of range, null is returned.
529  Remarks:
530  It is faster to use FirstElement() and NextElement() to iterate
531  through the entire list of elements. This function is relatively
532  efficient when there are a few large blocks in the pool
533  or element_index is small compared to the number of elements
534  in the first few blocks.
535 
536  If ReturnElement() is not used or AllocateElement() calls to
537  are made after any use of ReturnElement(), then the i-th
538  element is the one returned by the (i+1)-th call to
539  AllocateElement().
540  */
541  T* Element(size_t element_index) const;
542 
543  size_t ElementIndex(
544  T*
545  ) const;
546 
547 private:
548  // prohibit copy construction and operator=.
551 };
552 
553 template <class T> class ON_SimpleFixedSizePoolIterator : private ON_FixedSizePoolIterator
554 {
555 public:
558 
559  /*
560  Description:
561  Get the first element when iterating through the list of elements.
562  Parameters:
563  element_index - [in]
564  If you use the version of FirstElement() that has an
565  element_index parameter, then the iteration begins at
566  that element.
567  Example:
568  The loop will iteratate through all the elements returned from
569  AllocateElement(), including any that have be returned to the pool
570  using ReturnElement().
571 
572  // iterate through all elements in the pool
573  // This iteration will go through TotalElements() items.
574  for ( void* p = FirstElement(); 0 != p; p = NextElement() )
575  {
576  // If you are not using ReturnElement(), then you may process
577  // "p" immediately. If you have used ReturnElement(), then you
578  // must check some value in p located after the first sizeof(void*)
579  // bytes to see if p is active.
580  if ( p is not active )
581  continue;
582 
583  ... process p
584  }
585 
586  Returns:
587  The first element when iterating through the list of elements.
588  Remarks:
589  FirstElement() and NextElement() will return elements that have
590  been returned to the pool using ReturnElement(). If you use
591  ReturnElement(), then be sure to mark the element so it can be
592  identified and skipped.
593 
594  Do not make any calls to FirstBlock() or NextBlock() when using
595  FirstElement() and NextElement() to iteratate through elements.
596  */
597  T* FirstElement();
598  T* FirstElement( size_t element_index );
599 
600  /*
601  Description:
602  Get the next element when iterating through the list of elements.
603  If FirstElement() is not called, then the first call to
604  NextElement() returns the first element.
605  Example:
606  See the FirstElement() documentation.
607  Returns:
608  The next element when iterating through the list of elements.
609  Remarks:
610  FirstElement() and NextElement() will return elements that have
611  been returned to the pool using ReturnElement(). If you use
612  ReturnElement(), then be sure to mark the element so it can be
613  identified and skipped.
614 
615  Do not make any calls to FirstBlock() or NextBlock() when using
616  FirstElement() and NextElement() to iteratate through elements.
617  */
618  T* NextElement();
619 
620  /*
621  Returns:
622  The most recently returned value from a call to FirstElement()
623  or NextElement().
624  Remarks:
625  Do not make any calls to FirstBlock() or NextBlock() when using
626  FirstElement() and NextElement() to iteratate through elements.
627  */
628  T* CurrentElement();
629 
630  /*
631  Description:
632  Sets the state of the iterator to the initail state that
633  exists after construction. This is useful if the iterator
634  has been used the get one or more elements and then
635  the referenced fixed size pool is modified or code wants
636  to begin iteration again a used a call to NextElement()
637  to return the first element.
638  */
639  void Reset();
640 
641 
642  /*
643  Description:
644  Get a pointer to the first element in the first block.
645  Parameters:
646  block_element_count - [out] (can be null)
647  If not null, the number of elements allocated from the
648  first block is returned in block_element_count.
649  Note that if you have used ReturnElement(), some
650  of these elemements may have been returned.
651  Example:
652  The loop will iteratate through all the blocks.
653 
654  // iterate through all blocks in the pool
655  size_t block_element_count = 0;
656  for ( void* p = FirstBlock(&block_element_count);
657  0 != p;
658  p = NextBlock(&block_element_count)
659  )
660  {
661  ElementType* e = (ElementType*)p;
662  for ( size_t i = 0;
663  i < block_element_count;
664  i++, e = ((const char*)e) + SizeofElement()
665  )
666  {
667  ...
668  }
669  }
670 
671  Returns:
672  The first block when iterating the list of blocks.
673  Remarks:
674  The heap for a fixed size memory pool is simply a linked
675  list of blocks. FirstBlock() and NextBlock() can be used
676  to iterate through the list of blocks.
677 
678  Do not make any calls to FirstElement() or NextElement() when using
679  FirstBlock() and NextBlock() to iteratate through blocks.
680  */
681  T* FirstBlock( size_t* block_element_count );
682 
683  /*
684  Description:
685  Get the next block when iterating through the blocks.
686  Parameters:
687  block_element_count - [out] (can be null)
688  If not null, the number of elements allocated from the
689  block is returned in block_element_count. Note that if
690  you have used ReturnElement(), some of these elemements
691  may have been returned.
692  Example:
693  See the FirstBlock() documentation.
694  Returns:
695  The next block when iterating through the blocks.
696  Remarks:
697  Do not make any calls to FirstElement() or NextElement() when using
698  FirstBlock() and NextBlock() to iteratate through blocks.
699  */
700  T* NextBlock( size_t* block_element_count );
701 
702 private:
703  // no implementation (you can use a copy construtor)
704  class ON_SimpleFixedSizePoolIterator<T>& operator=(const class ON_SimpleFixedSizePoolIterator<T>&);
705 };
706 
707 // definitions of the template functions are in a different file
708 // so that Microsoft's developer studio's autocomplete utility
709 // will work on the template functions.
710 #include "opennurbs_fsp_defs.h"
711 
712 #endif
713 
Definition: opennurbs_fsp.h:19
Definition: opennurbs_fsp.h:411
Definition: opennurbs_fsp.h:548
Definition: opennurbs_fsp.h:239
bool Create(size_t sizeof_element, size_t element_count_estimate, size_t block_element_capacity)
Create a fixed size memory pool.