opennurbs_lookup.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 
17 #if !defined(OPENNURBS_MAP_INC_)
18 #define OPENNURBS_MAP_INC_
19 
20 /*
21 Description:
22  ON_SerialNumberMap provides a way to map set of unique
23  serial number - uuid pairs to application defined values
24  so that adding, finding and removing serial numbers is
25  fast and efficient. The class is designed to handle
26  several millions of unique serial numbers. There are no
27  restrictions on what order numbers are added and removed.
28  The minimum memory footprint is less than 150KB and doesn't
29  increase until you have more than 8000 serial numbers.
30  It is possible to have an active serial number and an
31  inactive id.
32 */
33 class ON_CLASS ON_SerialNumberMap
34 {
35 public:
38 
39  struct MAP_VALUE
40  {
41  ON__UINT32 m_u_type;
42  ON__UINT32 m_u32;
43  union
44  {
45  ON__UINT64 u64;
46  ON__INT64 i64;
47  void* ptr;
48  ON__UINT32 ui[2];
49  ON__INT32 i[2];
50  } m_u;
51  };
52 
53  struct SN_ELEMENT
54  {
55  ////////////////////////////////////////////////////////////
56  //
57  // ID
58  //
59  ON_UUID m_id;
60 
61  ////////////////////////////////////////////////////////////
62  //
63  // Serial number:
64  //
65  ON__UINT64 m_sn;
66 
67  ////////////////////////////////////////////////////////////
68  //
69  // Status flags:
70  //
71  // If m_id_active is 1, then m_sn_active must be 1.
72  // If m_sn_active = 1, then m_id_active can be 0 or 1.
73  ON__UINT8 m_sn_active; // 1 = serial number is active
74  ON__UINT8 m_id_active; // 1 = id is active
75  ON__UINT8 m_reserved1;
76  ON__UINT8 m_reserved2;
77 
78  ON__UINT32 m_id_crc32; // id hash = IdCRC(id)
79 
80  struct SN_ELEMENT* m_next; // id hash table linked list
81 
82  ////////////////////////////////////////////////////////////
83  //
84  // User information:
85  //
86  // ON_SerialNumberMap does not use the m_value field.
87  // When a new element is added, m_value is memset to
88  // zero. Other than that, m_value is not changed by
89  // this class. The location of m_value in memory,
90  // (&m_value) may change at any time.
91  struct MAP_VALUE m_value;
92 
93  void Dump(ON_TextLog&) const;
94  };
95 
96  /*
97  Returns:
98  Number of active serial numbers in the list.
99  */
100  ON__UINT64 ActiveSerialNumberCount() const;
101 
102  /*
103  Returns:
104  Number of active ids in the list. This number
105  is less than or equal to ActiveSerialNumberCount().
106  */
107  ON__UINT64 ActiveIdCount() const;
108 
109  /*
110  Returns:
111  The active element with the smallest serial number,
112  or null if the list is empty.
113  Restrictions:
114  The returned pointer may become invalid after any
115  subsequent calls to any function in this class.
116  If you need to save information in the returned
117  SN_ELEMENT for future use, you must copy the
118  information into storage you are managing.
119 
120  You may change the value of the SN_ELEMENT's m_value
121  field. You must NEVER change any other SN_ELEMENT
122  fields or you will break searching and possibly cause
123  crashes.
124  */
125  struct SN_ELEMENT* FirstElement() const;
126 
127  /*
128  Returns:
129  The active element with the biggest serial number,
130  or null if the list is empty.
131  Restrictions:
132  The returned pointer may become invalid after any
133  subsequent calls to any function in this class.
134  If you need to save information in the returned
135  SN_ELEMENT for future use, you must copy the
136  information into storage you are managing.
137 
138  You may change the value of the SN_ELEMENT's m_value
139  field. You must NEVER change any other SN_ELEMENT
140  fields or you will break searching and possibly cause
141  crashes.
142  */
143  struct SN_ELEMENT* LastElement() const;
144 
145  /*
146  Parameters:
147  sn - [in] serial number to search for.
148  Returns:
149  If the serial number is active, a pointer to
150  its element is returned.
151  Restrictions:
152  The returned pointer may become invalid after any
153  subsequent calls to any function in this class.
154  If you need to save information in the returned
155  SN_ELEMENT for future use, you must copy the
156  information into storage you are managing.
157 
158  You may change the value of the SN_ELEMENT's m_value
159  field. You must NEVER change any other SN_ELEMENT
160  fields or you will break searching and possibly cause
161  crashes.
162  */
163  struct SN_ELEMENT* FindSerialNumber(ON__UINT64 sn) const;
164 
165  /*
166  Parameters:
167  id - [in] id number to search for.
168  Returns:
169  If the id is active, a pointer to
170  its element is returned.
171  Restrictions:
172  The returned pointer may become invalid after any
173  subsequent calls to any function in this class.
174  If you need to save information in the returned
175  SN_ELEMENT for future use, you must copy the
176  information into storage you are managing.
177 
178  You may change the value of the SN_ELEMENT's m_value
179  field. You must NEVER change any other SN_ELEMENT
180  fields or you will break searching and possibly cause
181  crashes.
182  */
183  struct SN_ELEMENT* FindId(ON_UUID) const;
184 
185  /*
186  Description:
187  Add a serial number to the map.
188  Parameters:
189  sn - [in] serial number to add.
190  Returns:
191  If the serial number is valid (>0), a pointer to its
192  element is returned. When a new element is added,
193  every byte of the m_value field is set to 0.
194  If the serial number was already active, its element is
195  also returned. If you need to distinguish between new
196  and previously existing elements, then set an
197  m_value field to something besides 0 after you add
198  a new serial number. The id associated with this
199  serial number will be zero and cannot be found using
200  FindId().
201  Restrictions:
202  The returned pointer may become invalid after any
203  subsequent calls to any function in this class.
204  If you need to save information in the returned
205  SN_ELEMENT for future use, you must copy the
206  information into storage you are managing.
207 
208  You may change the value of the SN_ELEMENT's m_value
209  field. You must NEVER change any other SN_ELEMENT
210  fields or you will break searching and possibly cause
211  crashes.
212  */
213  struct SN_ELEMENT* AddSerialNumber(ON__UINT64 sn);
214 
215  /*
216  Parameters:
217  sn - [in] serial number to add.
218  id - [in] suggested id to add. If id is zero or
219  already in use, another id will be assigned
220  to the element.
221  Returns:
222  If the serial number is valid (>0), a pointer to its
223  element is returned. When a new element is added,
224  every byte of the m_value field is set to 0.
225  If the serial number was already active, its element is
226  also returned. If you need to distinguish between new
227  and previously existing elements, then set an
228  m_value field to something besides 0 after you add
229  a new serial number.
230  If the id parameter is nil, then a new uuid is created
231  and added. If the id parameter is not nil but is active
232  on another element, a new uuid is created and added.
233  You can inspect the value of m_id on the returned element
234  to determine the id AddSerialNumberAndId() assigned to
235  the element.
236 
237  Restrictions:
238  The returned pointer may become invalid after any
239  subsequent calls to any function in this class.
240  If you need to save information in the returned
241  SN_ELEMENT for future use, you must copy the
242  information into storage you are managing.
243 
244  You may change the value of the SN_ELEMENT's m_value
245  field. You must NEVER change any other SN_ELEMENT
246  fields or you will break searching and possibly cause
247  crashes.
248  */
249  struct SN_ELEMENT* AddSerialNumberAndId(ON__UINT64 sn, ON_UUID id);
250 
251  /*
252  Parameters:
253  sn - [in] serial number of the element to remove.
254  Returns:
255  If the serial number was active, it is removed
256  and a pointer to its element is returned. If
257  the element's id was active, the id is also removed.
258  Restrictions:
259  The returned pointer may become invalid after any
260  subsequent calls to any function in this class.
261  If you need to save information in the returned
262  SN_ELEMENT for future use, you must copy the
263  information into storage you are managing.
264 
265  You may change the value of the SN_ELEMENT's m_value
266  field. You must NEVER change any other SN_ELEMENT
267  fields or you will break searching and possibly cause
268  crashes.
269  */
270  struct SN_ELEMENT* RemoveSerialNumberAndId(ON__UINT64 sn);
271 
272  /*
273  Parameters:
274  sn - [in] If > 0, this is the serial number
275  of the element with the id. If 0, the
276  field is ignored.
277  id - [in] id to search for.
278  Returns:
279  If the id was active, it is removed and a pointer
280  to its element is returned. The element's serial
281  remains active. To remove both the id and serial number,
282  use RemoveSerialNumberAndId().
283  Restrictions:
284  The returned pointer may become invalid after any
285  subsequent calls to any function in this class.
286  If you need to save information in the returned
287  SN_ELEMENT for future use, you must copy the
288  information into storage you are managing.
289 
290  You may change the value of the SN_ELEMENT's m_value
291  field. You must NEVER change any other SN_ELEMENT
292  fields or you will break searching and possibly cause
293  crashes.
294  */
295  struct SN_ELEMENT* RemoveId(ON__UINT64 sn, ON_UUID id);
296 
297  /*
298  Description:
299  Finds all the elements whose serial numbers are
300  in the range sn0 <= sn <= sn1 and appends them
301  to the elements[] array. If max_count > 0, it
302  specifies the maximum number of elements to append.
303  Parameters:
304  sn0 - [in]
305  Minimum serial number.
306  sn1 - [in]
307  Maximum serial number
308  max_count - [in]
309  If max_count > 0, this parameter specifies the
310  maximum number of elements to append.
311  elements - [out]
312  Elements are appended to this array
313  Returns:
314  Number of elements appended to elements[] array.
315  Remarks:
316  When many elements are returned, GetElements() can be
317  substantially faster than repeated calls to FindElement().
318  */
319  ON__UINT64 GetElements(
320  ON__UINT64 sn0,
321  ON__UINT64 sn1,
322  ON__UINT64 max_count,
324  ) const;
325 
326  /*
327  Description:
328  Empties the list.
329  */
330  void EmptyList();
331 
332  /*
333  Description:
334  Returns true if the map is valid. Returns false if the
335  map is not valid. If an error is found and textlog
336  is not null, then a description of the problem is sent
337  to textlog.
338  Returns:
339  true if the list if valid.
340  */
341  bool IsValid(
342  bool bBuildHashTable,
343  ON_TextLog* textlog
344  ) const;
345 
346  void Dump(ON_TextLog& text_log) const;
347 
348 private:
349  // prohibit copy construction and operator=
350  // no implementation
351  ON_SerialNumberMap(const ON_SerialNumberMap&) = delete;
352  ON_SerialNumberMap& operator=(const ON_SerialNumberMap&) = delete;
353 
354 private:
355  ON__UINT64 m_maxsn = 0; // largest sn stored anywhere
356 
357 
358  // Serial Number list counts
359  ON__UINT64 m_sn_count = 0; // total number of elements
360  ON__UINT64 m_sn_purged = 0; // total number of purged elements
361 
362  // The blocks in m_sn_list[] are alwasy sorted, disjoint,
363  // and in increasing order. m_sn_list is used when
364  // m_sn_block0.m_sn[] is not large enough.
365  // The sn list is partitioned into blocks to avoid
366  // requiring large amounts of contiguous memory for
367  // situations with millions of serial numbers.
368  ON__UINT64 m_snblk_list_capacity = 0; // capacity of m_blk_list[]
369  ON__UINT64 m_snblk_list_count = 0; // used elements in m_snblk_list[]
370  class ON_SN_BLOCK** m_snblk_list = nullptr;
371 
372  // If FindElementHelper() returns a non-null pointer
373  // to an element, then m_e_blk points to the ON_SN_BLOCK
374  // that contains the returned element. In all other
375  // situations the value in m_e_blk is undefined and
376  // m_e_blk must not be dereferenced.
377  class ON_SN_BLOCK* m_e_blk = nullptr;
378 
379 private:
380  class ON_SN_BLOCK& m_sn_block0;
381 
382 private:
383  struct SN_ELEMENT* FindElementHelper(ON__UINT64 sn);
384  void UpdateMaxSNHelper();
385  void GarbageCollectHelper();
386  ON__UINT64 GarbageCollectMoveHelper(ON_SN_BLOCK* dst,ON_SN_BLOCK* src);
387 
388  ON__UINT8 m_reserved1 = 0;
389  ON__UINT8 m_reserved2 = 0;
390  ON__UINT8 m_reserved3 = 0;
391 
392  // When m_bHashTableIsValid == 1, the id hash table is valid.
393  // Otherwise it is not built or out of date.
394  // When m_bHashTableIsValid and nullptr != m_hash_table,
395  // then m_hash1_count > 0 and m_hash_table[i][j] is a
396  // linked list of elements whose id satisfies
397  // i = e->m_id_crc32 % m_hash_block_count
398  // j = (e->m_id_crc32/ID_HASH_BLOCK_CAPACITY) % ID_HASH_BLOCK_CAPACITY
399  mutable ON__UINT8 m_bHashTableIsValid = 0;
400 
401  mutable ON__UINT32 m_hash_block_count = 0; // number of blocks in m_hash_tableX[]
402  mutable ON__UINT64 m_hash_capacity = 0; // == m_hash_block_count*ID_HASH_BLOCK_CAPACITY
403  // ideally, m_active_id_count/m_hash_capacity is close to 4
404  mutable struct SN_ELEMENT*** m_hash_table_blocks = nullptr;
405 
406  // ID hash table counts (all ids in the hash table are active)
407  ON__UINT64 m_active_id_count = 0; // number of active ids in the hash table
408  ON_UUID m_inactive_id = ON_nil_uuid; // frequently an id is removed and
409  // then added back. m_inactive_id
410  // records the most recently removed
411  // id so we don't have to waste time
412  // searching the hash table for
413  // an id that is not there.
414 
415  void Internal_HashTableInvalidate(); // marks table as dirty
416 
417  bool Internal_HashTableRemoveSerialNumberBlock(
418  const class ON_SN_BLOCK* blk
419  );
420 
421  /*
422  Returns:
423  Number of active ids added to hash table.
424  */
425  ON__UINT64 Internal_HashTableAddSerialNumberBlock(
426  class ON_SN_BLOCK* blk
427  ) const;
428 
429  void Internal_HashTableBuild() const; // prepares table for use
430 
431  struct SN_ELEMENT** Internal_HashTableBlock(
432  ON__UINT32 id_crc32
433  ) const;
434 
435  ON__UINT32 Internal_HashTableBlockIndex(
436  ON__UINT32 id_crc32
437  ) const;
438 
439  static ON__UINT32 Internal_HashTableBlockRowIndex(
440  ON__UINT32 id_crc32
441  );
442 
443  struct SN_ELEMENT* Internal_HashTableFindId(
444  ON_UUID id,
445  ON__UINT32 id_crc32,
446  bool bBuildTableIfNeeded
447  ) const;
448 
449  struct SN_ELEMENT* Internal_HashTableRemoveElement(
450  struct SN_ELEMENT* e,
451  bool bRemoveFromHashBlock
452  );
453 
454  void Internal_HashTableGrow() const;
455 
456  void Internal_HashTableInitialize() const;
457 
458 };
459 
460 
461 #endif
ON__UINT64 m_sn
Definition: opennurbs_lookup.h:65
ON_SerialNumberMap provides a way to map set of unique serial number - uuid pairs to application defi...
Definition: opennurbs_lookup.h:32
ON_UUID is a 16 byte universally unique identifier.
Definition: opennurbs_uuid.h:32
Definition: opennurbs_array.h:36
Definition: opennurbs_lookup.h:38
Definition: opennurbs_lookup.h:52
Definition: opennurbs_textlog.h:20