00001 /* $NoKeywords: $ */ 00002 /* 00003 // 00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. 00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert 00006 // McNeel & Associates. 00007 // 00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. 00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF 00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED. 00011 // 00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>. 00013 // 00015 */ 00016 00017 #if !defined(OPENNURBS_MAP_INC_) 00018 #define OPENNURBS_MAP_INC_ 00019 00020 /* 00021 Description: 00022 ON_SerialNumberMap provides a way to map set of unique 00023 serial number - uuid pairs to application defined values 00024 so that adding, finding and removing serial numbers is 00025 fast and efficient. The class is designed to handle 00026 several millions of unique serial numbers. There are no 00027 restrictions on what order numbers are added and removed. 00028 The minimum memory footprint is less than 150KB and doesn't 00029 increase until you have more than 8000 serial numbers. 00030 It is possible to have an active serial number and an 00031 inactive id. 00032 */ 00033 class ON_CLASS ON_SerialNumberMap 00034 { 00035 public: 00036 ON_SerialNumberMap( ON_MEMORY_POOL* pool = 0 ); 00037 ~ON_SerialNumberMap(); 00038 00039 struct MAP_VALUE 00040 { 00041 ON__UINT32 m_u_type; 00042 union 00043 { 00044 void* ptr; 00045 unsigned int ui; 00046 int i; 00047 } m_u; 00048 }; 00049 00050 struct SN_ELEMENT 00051 { 00053 // 00054 // ID 00055 // 00056 ON_UUID m_id; 00057 struct SN_ELEMENT* m_next; // id hash table linked list 00058 00060 // 00061 // Serial number: 00062 // 00063 unsigned int m_sn; 00064 00066 // 00067 // Status flags: 00068 // 00069 // If m_id_active is 1, then m_sn_active must be 1. 00070 // If m_sn_active = 1, then m_id_active can be 0 or 1. 00071 // 00072 unsigned char m_sn_active; // 1 = serial number is active 00073 unsigned char m_id_active; // 1 = id is active 00074 unsigned char m_reserved1; 00075 unsigned char m_reserved2; 00076 00078 // 00079 // User information: 00080 // 00081 // ON_SerialNumberMap does not use the m_value field. 00082 // When a new element is added, m_value is memset to 00083 // zero. Other than that, m_value is not changed by 00084 // this class. The location of m_value in memory, 00085 // (&m_value) may change at any time. 00086 struct MAP_VALUE m_value; 00087 00088 void Dump(ON_TextLog&) const; 00089 }; 00090 00091 /* 00092 Returns: 00093 Number of active serial numbers in the list. 00094 */ 00095 size_t ActiveSerialNumberCount() const; 00096 00097 /* 00098 Returns: 00099 Number of active ids in the list. This number 00100 is less than or equal to ActiveSerialNumberCount(). 00101 */ 00102 size_t ActiveIdCount() const; 00103 00104 /* 00105 Returns: 00106 The active element with the smallest serial number, 00107 or null if the list is empty. 00108 Restrictions: 00109 The returned pointer may become invalid after any 00110 subsequent calls to any function in this class. 00111 If you need to save information in the returned 00112 SN_ELEMENT for future use, you must copy the 00113 information into storage you are managing. 00114 00115 You may change the value of the SN_ELEMENT's m_value 00116 field. You must NEVER change any other SN_ELEMENT 00117 fields or you will break searching and possibly cause 00118 crashes. 00119 */ 00120 struct SN_ELEMENT* FirstElement() const; 00121 00122 /* 00123 Returns: 00124 The active element with the biggest serial number, 00125 or null if the list is empty. 00126 Restrictions: 00127 The returned pointer may become invalid after any 00128 subsequent calls to any function in this class. 00129 If you need to save information in the returned 00130 SN_ELEMENT for future use, you must copy the 00131 information into storage you are managing. 00132 00133 You may change the value of the SN_ELEMENT's m_value 00134 field. You must NEVER change any other SN_ELEMENT 00135 fields or you will break searching and possibly cause 00136 crashes. 00137 */ 00138 struct SN_ELEMENT* LastElement() const; 00139 00140 /* 00141 Parameters: 00142 sn - [in] serial number to search for. 00143 Returns: 00144 If the serial number is active, a pointer to 00145 its element is returned. 00146 Restrictions: 00147 The returned pointer may become invalid after any 00148 subsequent calls to any function in this class. 00149 If you need to save information in the returned 00150 SN_ELEMENT for future use, you must copy the 00151 information into storage you are managing. 00152 00153 You may change the value of the SN_ELEMENT's m_value 00154 field. You must NEVER change any other SN_ELEMENT 00155 fields or you will break searching and possibly cause 00156 crashes. 00157 */ 00158 struct SN_ELEMENT* FindSerialNumber(unsigned int sn) const; 00159 00160 /* 00161 Parameters: 00162 id - [in] id number to search for. 00163 Returns: 00164 If the id is active, a pointer to 00165 its element is returned. 00166 Restrictions: 00167 The returned pointer may become invalid after any 00168 subsequent calls to any function in this class. 00169 If you need to save information in the returned 00170 SN_ELEMENT for future use, you must copy the 00171 information into storage you are managing. 00172 00173 You may change the value of the SN_ELEMENT's m_value 00174 field. You must NEVER change any other SN_ELEMENT 00175 fields or you will break searching and possibly cause 00176 crashes. 00177 */ 00178 struct SN_ELEMENT* FindId(ON_UUID) const; 00179 00180 /* 00181 Description: 00182 Add a serial number to the map. 00183 Parameters: 00184 sn - [in] serial number to add. 00185 Returns: 00186 If the serial number is valid (>0), a pointer to its 00187 element is returned. When a new element is added, 00188 every byte of the m_value field is set to 0. 00189 If the serial number was already active, its element is 00190 also returned. If you need to distinguish between new 00191 and previously existing elements, then change 00192 m_value.m_u_type to something besides 0 after you add 00193 a new serial number. The id associated with this 00194 serial number will be zero and cannot be found using 00195 FindId(). 00196 Restrictions: 00197 The returned pointer may become invalid after any 00198 subsequent calls to any function in this class. 00199 If you need to save information in the returned 00200 SN_ELEMENT for future use, you must copy the 00201 information into storage you are managing. 00202 00203 You may change the value of the SN_ELEMENT's m_value 00204 field. You must NEVER change any other SN_ELEMENT 00205 fields or you will break searching and possibly cause 00206 crashes. 00207 */ 00208 struct SN_ELEMENT* AddSerialNumber(unsigned int sn); 00209 00210 /* 00211 Parameters: 00212 sn - [in] serial number to add. 00213 id - [in] suggested id to add. If id is zero or 00214 already in use, another id will be assigned 00215 to the element. 00216 Returns: 00217 If the serial number is valid (>0), a pointer to its 00218 element is returned. When a new element is added, 00219 every byte of the m_value field is set to 0. 00220 If the serial number was already active, its element is 00221 also returned. If you need to distinguish between new 00222 and previously existing elements, then change 00223 m_value.m_u_type to something besides 0 after you add 00224 a new serial number. 00225 If the id parameter is zero, then a new uuid is created 00226 and added. If the id parameter is non zero but is active 00227 on another element, a new uuid is created and added. 00228 You can inspect the value of m_id on the returned element 00229 to determine the id AddSerialNumberAndId() assigned to 00230 the element. 00231 Restrictions: 00232 The returned pointer may become invalid after any 00233 subsequent calls to any function in this class. 00234 If you need to save information in the returned 00235 SN_ELEMENT for future use, you must copy the 00236 information into storage you are managing. 00237 00238 You may change the value of the SN_ELEMENT's m_value 00239 field. You must NEVER change any other SN_ELEMENT 00240 fields or you will break searching and possibly cause 00241 crashes. 00242 */ 00243 struct SN_ELEMENT* AddSerialNumberAndId(unsigned int sn, ON_UUID id); 00244 00245 /* 00246 Parameters: 00247 sn - [in] serial number of the element to remove. 00248 Returns: 00249 If the serial number was active, it is removed 00250 and a pointer to its element is returned. If 00251 the element's id was active, the id is also removed. 00252 Restrictions: 00253 The returned pointer may become invalid after any 00254 subsequent calls to any function in this class. 00255 If you need to save information in the returned 00256 SN_ELEMENT for future use, you must copy the 00257 information into storage you are managing. 00258 00259 You may change the value of the SN_ELEMENT's m_value 00260 field. You must NEVER change any other SN_ELEMENT 00261 fields or you will break searching and possibly cause 00262 crashes. 00263 */ 00264 struct SN_ELEMENT* RemoveSerialNumberAndId(unsigned int sn); 00265 00266 /* 00267 Parameters: 00268 sn - [in] If > 0, this is the serial number 00269 of the element with the id. If 0, the 00270 field is ignored. 00271 id - [in] id to search for. 00272 Returns: 00273 If the id was active, it is removed and a pointer 00274 to its element is returned. The element's serial 00275 remains active. To remove both the id and serial number, 00276 use RemoveSerialNumberAndId(). 00277 Restrictions: 00278 The returned pointer may become invalid after any 00279 subsequent calls to any function in this class. 00280 If you need to save information in the returned 00281 SN_ELEMENT for future use, you must copy the 00282 information into storage you are managing. 00283 00284 You may change the value of the SN_ELEMENT's m_value 00285 field. You must NEVER change any other SN_ELEMENT 00286 fields or you will break searching and possibly cause 00287 crashes. 00288 */ 00289 struct SN_ELEMENT* RemoveId(unsigned int sn, ON_UUID id); 00290 00291 /* 00292 Description: 00293 Finds all the elements whose serial numbers are 00294 in the range sn0 <= sn <= sn1 and appends them 00295 to the elements[] array. If max_count > 0, it 00296 specifies the maximum number of elements to append. 00297 Parameters: 00298 sn0 - [in] 00299 Minimum serial number. 00300 sn1 - [in] 00301 Maximum serial number 00302 max_count - [in] 00303 If max_count > 0, this parameter specifies the 00304 maximum number of elements to append. 00305 elements - [out] 00306 Elements are appended to this array 00307 Returns: 00308 Number of elements appended to elements[] array. 00309 Remarks: 00310 When many elements are returned, GetElements() can be 00311 substantially faster than repeated calls to FindElement(). 00312 */ 00313 size_t GetElements( 00314 unsigned int sn0, 00315 unsigned int sn1, 00316 size_t max_count, 00317 ON_SimpleArray<SN_ELEMENT>& elements 00318 ) const; 00319 00320 /* 00321 Description: 00322 Empties the list. 00323 */ 00324 void EmptyList(); 00325 00326 /* 00327 Description: 00328 Returns true if the map is valid. Returns false if the 00329 map is not valid. If an error is found and textlog 00330 is not null, then a description of the problem is sent 00331 to textlog. 00332 Returns: 00333 true if the list if valid. 00334 */ 00335 bool IsValid(ON_TextLog* textlog) const; 00336 00337 void Dump(ON_TextLog& text_log) const; 00338 00339 private: 00340 // prohibit copy construction and operator= 00341 // no implementation 00342 ON_SerialNumberMap(const ON_SerialNumberMap&); 00343 ON_SerialNumberMap& operator=(const ON_SerialNumberMap&); 00344 00345 enum 00346 { 00347 // These numbers are chosen so the ON_SerialNumberMap 00348 // will be computationally efficient for up to 00349 // 10 million entries. 00350 SN_BLOCK_CAPACITY = 8192, 00351 SN_PURGE_RATIO = 16, 00352 ID_HASH_TABLE_COUNT = 8192 00353 }; 00354 00355 struct SN_BLOCK 00356 { 00357 size_t m_count; // used elements in m_sn[] 00358 size_t m_purged; // number of purged elements in m_sn[] 00359 unsigned int m_sorted; // 0 = no, 1 = yes 00360 unsigned int m_sn0; // minimum sn in m_sn[] 00361 unsigned int m_sn1; // maximum sn in m_sn[] 00362 struct SN_ELEMENT m_sn[SN_BLOCK_CAPACITY]; 00363 void EmptyBlock(); 00364 void CullBlockHelper(); 00365 void SortBlockHelper(); 00366 bool IsValidBlock(ON_TextLog* textlog,struct SN_ELEMENT*const* hash_table,size_t* active_id_count) const; 00367 struct SN_ELEMENT* BinarySearchBlockHelper(unsigned int sn); 00368 static int CompareMaxSN(const void*,const void*); 00369 size_t ActiveElementEstimate(unsigned int sn0, unsigned int sn1) const; 00370 void Dump(ON_TextLog&) const; 00371 }; 00372 00373 unsigned int m_maxsn; // largest sn stored anywhere 00374 unsigned int m_reserved; 00375 00376 // All heap used in this class is allocated from this pool. 00377 ON_MEMORY_POOL* m_pool; 00378 00379 // Serial Number list counts 00380 size_t m_sn_count; // total number of elements 00381 size_t m_sn_purged; // total number of purged elements 00382 00383 // ID hash table counts (all ids in the hash table are active) 00384 bool m_bHashTableIsValid; // true if m_hash_table[] is valid 00385 size_t m_active_id_count; // number of active ids in the hash table 00386 ON_UUID m_inactive_id; // frequently and id is removed and 00387 // then added back. m_inactive_id 00388 // records the most recently removed 00389 // id so we don't have to waste time 00390 // searching the hash table for 00391 // an id that is not there. 00392 00393 00394 // The blocks in m_sn_list[] are alwasy sorted, disjoint, 00395 // and in increasing order. m_sn_list is used when 00396 // m_sn_block0.m_sn[] is not large enough. 00397 // The sn list is partitioned into blocks to avoid 00398 // requiring large amounts of contiguous memory for 00399 // situations with millions of serial numbers. 00400 struct SN_BLOCK** m_snblk_list; 00401 size_t m_snblk_list_capacity; // capacity of m_blk_list[] 00402 size_t m_snblk_list_count; // used elements in m_snblk_list[] 00403 00404 // If FindElementHelper() returns a non-null pointer 00405 // to an element, then m_e_blk points to the SN_BLOCK 00406 // that contains the returned element. In all other 00407 // situations the value in m_e_blk is undefined and 00408 // m_e_blk must not be dereferenced. 00409 struct SN_BLOCK* m_e_blk; 00410 00411 // m_sn_block0 is where the new additions are added. 00412 // When serial numbers are not added in increasing 00413 // order, m_sn_block0.m_sn[] may not be sorted. 00414 SN_BLOCK m_sn_block0; 00415 00416 struct SN_ELEMENT* FindElementHelper(unsigned int sn); 00417 void UpdateMaxSNHelper(); 00418 void GarbageCollectHelper(); 00419 size_t GarbageCollectMoveHelper(SN_BLOCK* dst,SN_BLOCK* src); 00420 00421 // When m_bHashTableIsValid is true, then m_hash_table[i] is 00422 // a linked list of elements whose id satisfies 00423 // i = HashIndex(&e->m_id). When m_bHashTableIsValid is false, 00424 // m_hash_table[] is identically zero. 00425 struct SN_ELEMENT* m_hash_table[ID_HASH_TABLE_COUNT]; 00426 size_t HashIndex(const ON_UUID*) const; 00427 void InvalidateHashTableHelper(); // marks table as dirty 00428 bool RemoveBlockFromHashTableHelper(const struct SN_BLOCK* blk); 00429 void AddBlockToHashTableHelper(struct SN_BLOCK* blk); 00430 void BuildHashTableHelper(); // prepares table for use 00431 }; 00432 00433 00434 #endif