opennurbs_lookup.cpp
Go to the documentation of this file.
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 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 static bool IdIsNotZero(const ON_UUID* id)
00020 {
00021   // This is a fast static function to check for
00022   // zero ids.  The caller always checks that
00023   // id is not null.
00024   const unsigned char* p = (const unsigned char*)id;
00025   unsigned int i = (unsigned int)sizeof(ON_UUID);
00026   while (i--) 
00027   {
00028     if (0 != *p++) 
00029       return true;
00030   }
00031   return false;
00032 }
00033 
00034 static ON__UINT16 IdCRC(const ON_UUID* id)
00035 {
00036   // This is a private member function and the caller
00037   // insures the id pointer is not null.
00038   static const ON__UINT16 ON_CRC16_CCITT_TABLE[256] =
00039    {0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,0x8108,0x9129,0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF,
00040     0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,
00041     0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,
00042     0x3653,0x2672,0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,
00043     0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B,
00044     0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,0xDBFD,0xCBDC,0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,
00045     0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41,0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,
00046     0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,
00047     0x9188,0x81A9,0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046,0x6067,
00048     0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256,
00049     0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,0x34E2,0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,
00050     0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E,0xC71D,0xD73C,0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,
00051     0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3,
00052     0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1,0x1AD0,0x2AB3,0x3A92,
00053     0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,
00054     0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0};
00055 
00056   const unsigned char* b = (const unsigned char*)id;
00057   ON__UINT16 current_remainder;
00058   ON__UINT16 r1;
00059 
00060   // crc calculation loop unrolled for speed
00061 
00062   //current_remainder=0;
00063   //r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00064   //current_remainder = (current_remainder << 8) ^ (*b++);
00065   //current_remainder ^= r1;  
00066   //r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00067   //current_remainder = (current_remainder << 8) ^ (*b++);
00068   //current_remainder ^= r1;  
00069 
00070   // The commented out lines above reduce to these two lines.
00071   current_remainder = *b++; 
00072   current_remainder = (current_remainder << 8) ^ (*b++);
00073 
00074   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00075   current_remainder = (current_remainder << 8) ^ (*b++);
00076   current_remainder ^= r1;  
00077   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00078   current_remainder = (current_remainder << 8) ^ (*b++);
00079   current_remainder ^= r1;  
00080   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00081   current_remainder = (current_remainder << 8) ^ (*b++);
00082   current_remainder ^= r1;  
00083   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00084   current_remainder = (current_remainder << 8) ^ (*b++);
00085   current_remainder ^= r1;  
00086   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00087   current_remainder = (current_remainder << 8) ^ (*b++);
00088   current_remainder ^= r1;  
00089   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00090   current_remainder = (current_remainder << 8) ^ (*b++);
00091   current_remainder ^= r1;  
00092   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00093   current_remainder = (current_remainder << 8) ^ (*b++);
00094   current_remainder ^= r1;  
00095   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00096   current_remainder = (current_remainder << 8) ^ (*b++);
00097   current_remainder ^= r1;  
00098   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00099   current_remainder = (current_remainder << 8) ^ (*b++);
00100   current_remainder ^= r1;  
00101   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00102   current_remainder = (current_remainder << 8) ^ (*b++);
00103   current_remainder ^= r1;  
00104   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00105   current_remainder = (current_remainder << 8) ^ (*b++);
00106   current_remainder ^= r1;  
00107   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00108   current_remainder = (current_remainder << 8) ^ (*b++);
00109   current_remainder ^= r1;  
00110   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00111   current_remainder = (current_remainder << 8) ^ (*b++);
00112   current_remainder ^= r1;  
00113   r1 = ON_CRC16_CCITT_TABLE[(current_remainder & ((ON__UINT16)0xff00))>>8];
00114   current_remainder = (current_remainder << 8) ^ (*b);
00115   current_remainder ^= r1;  
00116 
00117   return current_remainder;
00118 }
00119 
00120 ON_SerialNumberMap::ON_SerialNumberMap(ON_MEMORY_POOL* pool)
00121 {
00122   m_maxsn = 0;
00123   m_reserved = 0;
00124   m_pool = pool;
00125   m_sn_count = 0;
00126   m_sn_purged = 0;
00127   m_snblk_list = 0;
00128   m_snblk_list_capacity = 0;
00129   m_snblk_list_count = 0;
00130   m_e_blk = 0;
00131   m_sn_block0.EmptyBlock();
00132   m_bHashTableIsValid = true;
00133   m_active_id_count = 0;
00134   memset(&m_inactive_id,0,sizeof(m_inactive_id));
00135   memset(m_hash_table,0,sizeof(m_hash_table));
00136 }
00137 
00138 ON_SerialNumberMap::~ON_SerialNumberMap()
00139 {
00140   EmptyList();
00141 }
00142 
00143 void ON_SerialNumberMap::EmptyList()
00144 {
00145   m_maxsn = 0;
00146   m_sn_count = 0;
00147   m_sn_purged = 0;
00148   m_sn_block0.EmptyBlock();
00149   if (m_snblk_list)
00150   {
00151     size_t i = m_snblk_list_capacity;
00152     while(i--)
00153     {
00154       if ( 0 != m_snblk_list[i] )
00155         onfree(m_snblk_list[i]);
00156     }
00157     onfree(m_snblk_list);
00158     m_snblk_list = 0;
00159     m_snblk_list_capacity = 0;
00160     m_snblk_list_count = 0;
00161   }
00162   m_bHashTableIsValid = true;
00163   m_active_id_count = 0;
00164   memset(m_hash_table,0,sizeof(m_hash_table));
00165 }
00166 
00167 
00168 void ON_SerialNumberMap::SN_BLOCK::EmptyBlock()
00169 {
00170   m_count = 0;
00171   m_purged = 0;
00172   m_sorted = 1;
00173   m_sn0 = 0;
00174   m_sn1 = 0;
00175 }
00176 
00177 void ON_SerialNumberMap::SN_BLOCK::CullBlockHelper()
00178 {
00179   // Search the m_an[] array for elements whose m_u_type
00180   // value is zero and remove them from the array.
00181   //
00182   // This is a high speed helper function.  
00183   // The calling function must verfy m_purged > 0.
00184   //
00185   // This function removes all m_sn[] elements
00186   // that have 0 == m_sn_active.
00187 
00188   size_t i, j;
00189   for (i = 0; i < m_count; i++ )
00190   {
00191     if ( 0 == m_sn[i].m_sn_active )
00192     {
00193       for ( j = i+1; j < m_count; j++ )
00194       {
00195         if ( m_sn[j].m_sn_active )
00196         {
00197           m_sn[i++] = m_sn[j];
00198         }
00199       }
00200       if ( 0 == i )
00201       {
00202         EmptyBlock();
00203       }
00204       else
00205       {
00206         m_count = i;
00207         m_purged = 0;
00208         if ( m_sorted )
00209         {
00210           m_sn0 = m_sn[0].m_sn;
00211           m_sn1 = m_sn[m_count-1].m_sn;
00212         }
00213       }
00214       break;
00215     }
00216   }
00217 }
00218 
00219 
00220 
00221 /*
00222 The defines and #include generates a fast sorting function
00223 static void ON_qsort_SN_ELEMENT( struct ON_SerialNumberMap::SN_ELEMENT* base, size_t nel );
00224 */
00225 
00226 #define ON_SORT_TEMPLATE_COMPARE compare_SN_ELEMENT_sn
00227 #define ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS
00228 #define ON_SORT_TEMPLATE_STATIC_FUNCTION
00229 #define ON_SORT_TEMPLATE_TYPE struct ON_SerialNumberMap::SN_ELEMENT
00230 #define ON_QSORT_FNAME ON_qsort_SN_ELEMENT
00231 
00232 static int ON_SORT_TEMPLATE_COMPARE(const struct ON_SerialNumberMap::SN_ELEMENT * a, const struct ON_SerialNumberMap::SN_ELEMENT * b)
00233 {
00234   unsigned int asn, bsn;
00235   return ( ( (asn = a->m_sn) < (bsn = b->m_sn) ) ? -1 : (asn > bsn) ? 1 : 0 );
00236 }
00237 
00238 #include "pcl/surface/3rdparty/opennurbs/opennurbs_qsort_template.h"
00239 
00240 #undef ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS
00241 #undef ON_SORT_TEMPLATE_STATIC_FUNCTION
00242 #undef ON_SORT_TEMPLATE_TYPE
00243 #undef ON_QSORT_FNAME
00244 #undef ON_SORT_TEMPLATE_COMPARE
00245 
00246 
00247 void ON_SerialNumberMap::SN_BLOCK::SortBlockHelper()
00248 {
00249   // Sort m_sn[] by serial number.
00250   //
00251   // This is a high speed helper function.  
00252   //
00253   // The calling function verify:
00254   //   m_sorted is zero
00255   //   m_purged is zero
00256   //   m_count > 0
00257   //
00258   // The array m_sn[] is almost always nearly sorted,
00259   // so the sort used here should efficiently
00260   // handle almost sorted arrays. In the past,
00261   // heap sort was the best choice, but the qsort()
00262   // in VS 2010 is now a better choice than heap sort.
00263    
00264   if ( m_count > 1 )
00265   {
00266 #if 1
00267     // Quick sort
00268     ON_qsort_SN_ELEMENT(m_sn, m_count);
00269 #else
00270     // Heap sort
00271     size_t i, j, k, i_end;
00272     struct SN_ELEMENT e_tmp;
00273     struct SN_ELEMENT* e;
00274 
00275     e = m_sn;
00276 
00277     k = m_count >> 1;
00278     i_end = m_count-1;
00279     for (;;) 
00280     {
00281       if (k)
00282       {
00283         --k;
00284         e_tmp = e[k];
00285       } 
00286       else 
00287       {
00288         e_tmp = e[i_end];
00289         e[i_end] = e[0];
00290         if (!(--i_end)) 
00291         {
00292           e[0] = e_tmp;
00293           break;
00294         }
00295       }
00296 
00297       i = k;
00298       j = (k<<1) + 1;
00299       while (j <= i_end)
00300       {
00301         if (j < i_end && e[j].m_sn < e[j + 1].m_sn)
00302           j++;
00303         if (e_tmp.m_sn < e[j].m_sn)
00304         {
00305           e[i] = e[j];
00306           i = j;
00307           j = (j<<1) + 1;
00308         } 
00309         else 
00310           j = i_end + 1;
00311       }
00312       e[i] = e_tmp;
00313     }
00314 #endif
00315     m_sn0 = m_sn[0].m_sn;
00316     m_sn1 = m_sn[m_count-1].m_sn;
00317   }
00318   else
00319   {
00320     m_sn0 = m_sn1 = ((1 == m_count) ? m_sn[0].m_sn : 0);
00321   }
00322   m_sorted = 1;
00323 }
00324 
00325 
00326 static bool ON_SerialNumberMap_IsNotValidBlock()
00327 {
00328   return ON_IsNotValid(); // <- Good place for a debugger breakpoint
00329 }
00330 
00331 bool ON_SerialNumberMap::SN_BLOCK::IsValidBlock(ON_TextLog* textlog, 
00332                                                 struct SN_ELEMENT*const* hash_table,
00333                                                 size_t* active_id_count) const
00334 {
00335   unsigned int sn0, sn;
00336   size_t i, pc, aidcnt;
00337 
00338   if ( m_count > SN_BLOCK_CAPACITY )
00339   {
00340     if (textlog)
00341       textlog->Print("SN_BLOCK m_count = %u (should be >=0 and <%u).\n",
00342                       m_count,SN_BLOCK_CAPACITY);
00343     return ON_SerialNumberMap_IsNotValidBlock();
00344   }
00345 
00346   if ( m_purged > m_count )
00347   {
00348     if (textlog)
00349       textlog->Print("SN_BLOCK m_purged = %u (should be >0 and <=%u).\n",
00350                       m_purged,m_count);
00351     return ON_SerialNumberMap_IsNotValidBlock();
00352   }
00353 
00354   if ( m_count < 2 && 1 != m_sorted )
00355   {
00356     if (textlog)
00357       textlog->Print("SN_BLOCK m_count = %u but m_sorted is not 1.\n",m_count);
00358     return ON_SerialNumberMap_IsNotValidBlock();
00359   }
00360 
00361   if ( 0 == m_count )
00362   {
00363     if ( 0 != m_sn0 )
00364     {
00365       if (textlog)
00366         textlog->Print("SN_BLOCK m_count = 0 but m_sn0 != 0.\n");
00367       return ON_SerialNumberMap_IsNotValidBlock();
00368     }
00369     if ( 0 != m_sn1 )
00370     {
00371       if (textlog)
00372         textlog->Print("SN_BLOCK m_count = 0 but m_sn1 != 0.\n");
00373       return ON_SerialNumberMap_IsNotValidBlock();
00374     }
00375     return true;
00376   }
00377 
00378   if ( m_sn1 < m_sn0 )
00379   {
00380     if (textlog)
00381       textlog->Print("SN_BLOCK m_sn1 < m_sn0.\n");
00382     return ON_SerialNumberMap_IsNotValidBlock();
00383   }
00384 
00385   if ( m_count > m_purged )
00386   {
00387     if ( m_sn1 - m_sn0 < m_count-m_purged-1 )
00388     {
00389       if (textlog)
00390         textlog->Print("SN_BLOCK m_sn1 < m_sn0.\n");
00391       return ON_SerialNumberMap_IsNotValidBlock();
00392     }
00393   }
00394 
00395   sn0 = 0;
00396   pc = 0;
00397   aidcnt = 0;
00398   for (i = 0; i < m_count; i++)
00399   {
00400     // validate m_sn_active and m_id_active flags
00401     if (0 == m_sn[i].m_sn_active)
00402     {
00403       // The element serial number is not active.
00404       // The id must also be not active.
00405       pc++;
00406       if ( 0 != m_sn[i].m_id_active )
00407       {
00408         if (textlog)
00409           textlog->Print("SN_BLOCK m_sn[%d].m_sn_active = 0 but m_id_active != 0.\n",i);
00410         return ON_SerialNumberMap_IsNotValidBlock();
00411       }
00412     }
00413     else if ( 0 != m_sn[i].m_id_active )
00414     {
00415       // The element has active serial number and active id.
00416       // It must have a nonzero m_id and be in the hash table.
00417       aidcnt++;
00418       if ( IdIsNotZero(&m_sn[i].m_id) )
00419       {
00420         const SN_ELEMENT* e;
00421         for (e = hash_table[IdCRC(&m_sn[i].m_id) % ID_HASH_TABLE_COUNT]; 0!=e; e = e->m_next)
00422         {
00423           if ( e == &m_sn[i] )
00424           {
00425             // found m_sn[i] in the hash table
00426             break;
00427           }
00428         }
00429         if ( 0 == e )
00430         {
00431           // m_sn[i] is not in the hash table but m_id_active indicates
00432           // it should be.
00433           if (textlog)
00434             textlog->Print("SN_BLOCK m_sn[%d].m_id_active != 0 but the element is not in m_hash_table[].\n",i);
00435           return ON_SerialNumberMap_IsNotValidBlock();
00436         }
00437       }
00438       else 
00439       {
00440         if (textlog)
00441           textlog->Print("SN_BLOCK m_sn[%d].m_id_active != 0 but m_id = 0.\n",i);
00442         return ON_SerialNumberMap_IsNotValidBlock();
00443       }
00444     }
00445 
00446     // verify the serial number is in the range m_sn0 to m_sn1
00447     sn = m_sn[i].m_sn;
00448     if ( sn < m_sn0 )
00449     {
00450       if (textlog)
00451         textlog->Print("SN_BLOCK m_sn[%d] < m_sn0.\n",i);
00452       return ON_SerialNumberMap_IsNotValidBlock();
00453     }
00454     if ( sn > m_sn1 )
00455     {
00456       if (textlog)
00457         textlog->Print("SN_BLOCK m_sn[%d] > m_sn1.\n",i);
00458       return ON_SerialNumberMap_IsNotValidBlock();
00459     }
00460 
00461     if ( m_sorted )
00462     {
00463       // Verify this sn is bigger than the previous one
00464       if (sn <= sn0 )
00465       {
00466         if (textlog)
00467           textlog->Print("SN_BLOCK m_sn[%d] > m_sn[%d].\n",i,i-1);
00468         return ON_SerialNumberMap_IsNotValidBlock();
00469       }
00470       sn0 = sn;
00471     }
00472   }
00473 
00474   if ( pc != m_purged )
00475   {
00476     if (textlog)
00477       textlog->Print("SN_BLOCK m_purged = %u (should be %u)\n",m_purged,pc);
00478     return ON_SerialNumberMap_IsNotValidBlock();
00479   }
00480 
00481   // Update the active id count to include
00482   // the active ids from this block.
00483   if ( 0 != active_id_count )
00484     *active_id_count += aidcnt;
00485 
00486   return true;
00487 }
00488 
00489 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::SN_BLOCK::BinarySearchBlockHelper(unsigned int sn)
00490 {
00491   // Perform a binary search on the serial number values in the m_sn[] array.
00492   //
00493   // This is a high speed helper function.  
00494   //
00495   // The calling function verify:
00496   //   m_sn[] is sorted by serial number (1 == m_sorted)
00497   //   m_count > 0
00498   //   m_sn0 <= sn <= m_sn1.
00499 
00500   size_t i, j;
00501   struct SN_ELEMENT* e;
00502   unsigned int midsn;
00503 
00504   i = m_count;
00505   e = m_sn;
00506   while (i > 0 )
00507   {
00508     midsn = e[(j=i/2)].m_sn;
00509     if ( sn < midsn )
00510     {
00511       i = j;
00512     }
00513     else if ( sn > midsn )
00514     {
00515       j++;
00516       e += j;
00517       i -= j;
00518     }
00519     else 
00520     {
00521       return e + j;
00522     }
00523   }
00524   return 0;
00525 }
00526 
00527 void ON_SerialNumberMap::UpdateMaxSNHelper()
00528 {
00529   m_maxsn = (m_snblk_list_count > 0) ? m_snblk_list[m_snblk_list_count-1]->m_sn1 : 0;
00530   if ( m_maxsn < m_sn_block0.m_sn1 )
00531     m_maxsn = m_sn_block0.m_sn1;
00532 }
00533 
00534 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindElementHelper(unsigned int sn)
00535 {
00536   struct SN_BLOCK** eblk_array;
00537   struct SN_BLOCK* eblk;
00538   size_t i, j;
00539 
00540   if ( m_maxsn < sn )
00541     return 0; // happens almost every time an object is added to the doc
00542 
00543   if ( sn <= 0 )
00544     return 0;
00545 
00546   // First check m_sn_block0. For small models this
00547   // is the only place we need to look.
00548   if ( sn <= m_sn_block0.m_sn1 && m_sn_block0.m_sn0 <= sn )
00549   {
00550     SN_ELEMENT* e;
00551 
00552     m_e_blk = &m_sn_block0;
00553     if ( !m_sn_block0.m_sorted )
00554     {
00555       // m_sn_block0.m_sn[] needs to be sorted
00556       //
00557       // This is a rare occurance.  It only happens
00558       // when commands add new objects in an order that
00559       // is different from that in which the CRhinoObject
00560       // class constructor was called.  In testing,
00561       // I could not find a real command that did this
00562       // and had to write TestAddBackwards to test this code.
00563       if ( m_sn_block0.m_purged > 0 )
00564       {
00565         InvalidateHashTableHelper();
00566         m_sn_count -= m_sn_block0.m_purged;
00567         m_sn_purged -= m_sn_block0.m_purged;
00568         m_sn_block0.CullBlockHelper();
00569         UpdateMaxSNHelper();
00570       }
00571       if ( m_sn_block0.m_count > 0 )
00572       {
00573         InvalidateHashTableHelper();
00574         m_sn_block0.SortBlockHelper();
00575       }
00576       e = ( sn <= m_sn_block0.m_sn1 && m_sn_block0.m_sn0 <= sn )
00577         ? m_sn_block0.BinarySearchBlockHelper(sn)
00578         : 0;
00579     }
00580     else if ( m_sn_block0.m_purged > m_sn_block0.m_count/SN_PURGE_RATIO )
00581     {
00582       InvalidateHashTableHelper();
00583       m_sn_count -= m_sn_block0.m_purged;
00584       m_sn_purged -= m_sn_block0.m_purged;
00585       m_sn_block0.CullBlockHelper();
00586       UpdateMaxSNHelper();
00587       e = ( sn <= m_sn_block0.m_sn1 && m_sn_block0.m_sn0 <= sn )
00588         ? m_sn_block0.BinarySearchBlockHelper(sn)
00589         : 0;
00590     }
00591     else
00592     {
00593       e = m_sn_block0.BinarySearchBlockHelper(sn);
00594     }
00595     if (e)
00596       return e;
00597   }
00598 
00599   // look in the blocks kept in the m_sn_list[] array.
00600   // The m_sn[] arrays in these blocks are always sorted.
00601   // In addition the blocks are disjoint and stored in
00602   // the m_sn_list[] array in sorted order.
00603   if ( 0 == (i = m_snblk_list_count) )
00604   {
00605     return 0;
00606   }
00607   eblk_array = m_snblk_list;
00608   while (i > 0 )
00609   {
00610     eblk = eblk_array[(j=i/2)];
00611 
00612     // The culling code is here rather than
00613     // in the binary search clause so the
00614     // entire ON_SerialNumberMap tends to
00615     // be tidy.
00616     if ( eblk->m_purged > eblk->m_count/SN_PURGE_RATIO )
00617     {
00618       // cull purged items from eblk
00619       InvalidateHashTableHelper();
00620       m_sn_count -= eblk->m_purged;
00621       m_sn_purged -= eblk->m_purged;
00622       eblk->CullBlockHelper();
00623       if ( 0 == eblk->m_count )
00624       {
00625         // put empty block at the end of the list
00626         for( j += ((eblk_array-m_snblk_list)+1); j < m_snblk_list_count; j++ )
00627         {
00628           m_snblk_list[j-1] = m_snblk_list[j];
00629         }
00630         m_snblk_list_count--;
00631         m_snblk_list[m_snblk_list_count] = eblk;
00632         i--;
00633         UpdateMaxSNHelper();
00634         continue;
00635       }
00636       UpdateMaxSNHelper();
00637     }
00638 
00639     if ( sn < eblk->m_sn0 )
00640     {
00641       i = j;
00642     }
00643     else if ( sn > eblk->m_sn1 )
00644     {
00645       j++;
00646       eblk_array += j;
00647       i -= j;
00648     }
00649     else 
00650     {
00651       m_e_blk = eblk;
00652       return eblk->BinarySearchBlockHelper(sn);
00653     }
00654   }
00655 
00656   return 0;
00657 }
00658 
00659 size_t ON_SerialNumberMap::ActiveSerialNumberCount() const
00660 {
00661   return m_sn_count - m_sn_purged;
00662 }
00663 
00664 size_t ON_SerialNumberMap::ActiveIdCount() const
00665 {
00666   return m_active_id_count;
00667 }
00668 
00669 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FirstElement() const
00670 {
00671   struct SN_ELEMENT* e=0;
00672   size_t i,j;
00673 
00674   // The first element is likely to be m_snblk_list[0]->m_sn[0]
00675   // so start looking there.
00676   for(i = 0; i < m_snblk_list_count; i++)
00677   {
00678     if ( m_snblk_list[i]->m_count > m_snblk_list[i]->m_purged )
00679     {
00680       for ( j = 0; j < m_snblk_list[i]->m_count; j++ )
00681       {
00682         if ( m_snblk_list[i]->m_sn[j].m_sn_active )
00683         {
00684           e = &m_snblk_list[i]->m_sn[j];
00685           break;
00686         }
00687       }
00688       break;
00689     }
00690   }
00691 
00692   if ( m_sn_block0.m_count > m_sn_block0.m_purged 
00693        && (!e || m_sn_block0.m_sn0 < e->m_sn) 
00694      )
00695   {
00696     // It's possible the element is in m_sn_block0.
00697     if ( m_sn_block0.m_purged > 0 )
00698     {
00699       // remove purged elements from m_sn_block0.
00700       const_cast<ON_SerialNumberMap*>(this)->InvalidateHashTableHelper();
00701       const_cast<ON_SerialNumberMap*>(this)->m_sn_count -= m_sn_block0.m_purged;
00702       const_cast<ON_SerialNumberMap*>(this)->m_sn_purged -= m_sn_block0.m_purged;
00703       const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.CullBlockHelper();
00704     }
00705     if ( !m_sn_block0.m_sorted )
00706     {
00707       // sort elements in m_sn_block0.
00708       const_cast<ON_SerialNumberMap*>(this)->InvalidateHashTableHelper();
00709       const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.SortBlockHelper();      
00710     }
00711     if ( !e || m_sn_block0.m_sn0 <  e->m_sn )
00712     {
00713       // first element in m_sn_block0 is the
00714       // one with the smallest serial number.
00715       e = const_cast<struct SN_ELEMENT*>(&m_sn_block0.m_sn[0]);
00716     }
00717   }
00718   return e;
00719 }
00720 
00721 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::LastElement() const
00722 {
00723   struct SN_ELEMENT* e=0;
00724   size_t i,j;
00725 
00726   // Last element is likely to be m_sn_block0.m_sn[m_sn_block0.m_count-1]
00727   // so start looking there.
00728   if ( m_sn_block0.m_count > m_sn_block0.m_purged )
00729   {
00730     if ( m_sn_block0.m_purged > 0 )
00731     {
00732       // remove purged elements from m_sn_block0
00733       const_cast<ON_SerialNumberMap*>(this)->InvalidateHashTableHelper();
00734       const_cast<ON_SerialNumberMap*>(this)->m_sn_count -= m_sn_block0.m_purged;
00735       const_cast<ON_SerialNumberMap*>(this)->m_sn_purged -= m_sn_block0.m_purged;
00736       const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.CullBlockHelper();
00737     }
00738     if ( !m_sn_block0.m_sorted )
00739     {
00740       // sort m_sn_block0
00741       const_cast<ON_SerialNumberMap*>(this)->InvalidateHashTableHelper();
00742       const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.SortBlockHelper();      
00743     }
00744     e = const_cast<struct SN_ELEMENT*>(&m_sn_block0.m_sn[m_sn_block0.m_count-1]);
00745   }
00746 
00747   i = m_snblk_list_count;
00748   while(i--)
00749   {
00750     if ( m_snblk_list[i]->m_count > m_snblk_list[i]->m_purged )
00751     {
00752       if (e && e->m_sn > m_snblk_list[i]->m_sn1 )
00753         break;
00754       j = m_snblk_list[i]->m_count;
00755       while(j--)
00756       {
00757         if ( m_snblk_list[i]->m_sn[j].m_sn_active )
00758         {
00759           e = &m_snblk_list[i]->m_sn[j];
00760           break;
00761         }
00762       }
00763       break;
00764     }
00765   }
00766 
00767   return e;
00768 }
00769 
00770 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindSerialNumber(unsigned int sn) const
00771 {
00772   struct SN_ELEMENT* e = const_cast<ON_SerialNumberMap*>(this)->FindElementHelper(sn);
00773   return ( (e && e->m_sn_active) ? e : 0);
00774 }
00775 
00776 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::FindId(ON_UUID id) const
00777 {
00778   struct SN_ELEMENT* e = 0;
00779   size_t i;
00780 
00781   if ( m_active_id_count > 0 )
00782   {
00783     i = HashIndex(&id);
00784     if ( 0 != i || IdIsNotZero(&id) )
00785     {
00786       if ( !m_bHashTableIsValid )
00787       {
00788         const_cast<ON_SerialNumberMap*>(this)->BuildHashTableHelper();
00789       }
00790       e = m_hash_table[i];
00791       while(e)
00792       {
00793         if ( 0 == memcmp(&e->m_id,&id,sizeof(e->m_id)) )
00794           break;
00795         e = e->m_next;
00796       }
00797     }
00798   }
00799   return e;
00800 }
00801 
00802 
00803 size_t ON_SerialNumberMap::GetElements(
00804         unsigned int sn0,
00805         unsigned int sn1, 
00806         size_t max_count,
00807         ON_SimpleArray<SN_ELEMENT>& elements
00808         ) const
00809 {
00810   size_t i,j,k,c;
00811   const SN_ELEMENT *ei, *ek;
00812 
00813   const int elements_count0 = elements.Count();
00814 
00815   if ( sn1 < sn0 || max_count <= 0 || m_sn_count <= m_sn_purged )
00816     return 0;
00817 
00818   if ( sn0+3 <= sn1 )
00819   {
00820     elements.Reserve(elements_count0+3);
00821     while ( sn0 <= sn1 )
00822     {
00823       ei = const_cast<ON_SerialNumberMap*>(this)->FindElementHelper(sn0++);
00824       if ( ei && ei->m_sn_active )
00825         elements.Append(*ei);
00826     }
00827     return (elements.Count() - elements_count0); 
00828   }
00829 
00830   ek = 0;
00831   k = 0;
00832   for ( j = 0; j < m_snblk_list_count; j++ )
00833   {
00834     if ( m_snblk_list[j]->m_sn1 < sn0 )
00835       continue;
00836     if ( sn1 < m_snblk_list[j]->m_sn0 )
00837       break;
00838 
00839     k = m_snblk_list[j]->m_count; // always > 0
00840     ek = &m_snblk_list[j]->m_sn[0];
00841     while ( ek->m_sn < sn0 || !ek->m_sn_active )
00842     {
00843       if ( --k )
00844       {
00845         if ((++ek)->m_sn > sn1)
00846         {
00847           ek = 0;
00848           break;
00849         }
00850       }
00851       else if ( ++j < m_snblk_list_count && m_snblk_list[j]->m_sn0 <= sn1 )
00852       {
00853         k = m_snblk_list[j]->m_count; // always > 0
00854         ek = &m_snblk_list[j]->m_sn[0];
00855       }
00856       else
00857       {
00858         ek = 0;
00859         break;
00860       }
00861     }
00862     if ( ek && ek->m_sn > sn1 )
00863       ek = 0;
00864     break;
00865   }
00866 
00867   // set c = estimate of number of items in m_snblk_list[]
00868   if ( ek )
00869   {
00870     c = m_snblk_list[j]->ActiveElementEstimate(ek->m_sn,sn1);
00871     for ( i = j+1; i < m_snblk_list_count && m_snblk_list[i]->m_sn0 <= sn1; i++ )
00872     {
00873       c += m_snblk_list[j]->ActiveElementEstimate(ek->m_sn,sn1);
00874     }
00875   }
00876   else
00877     c = 0;
00878 
00879   // determine where to begin searching in m_sn_block0
00880   i = 0;
00881   ei = 0;
00882   if (    m_sn_block0.m_count > m_sn_block0.m_purged
00883        && sn1 >= m_sn_block0.m_sn0
00884        && sn0 <= m_sn_block0.m_sn1
00885        && !m_sn_block0.m_sorted
00886      )
00887   {
00888     if ( !m_sn_block0.m_sorted )
00889     {
00890       if ( m_sn_block0.m_purged > 0 )
00891       {
00892         const_cast<ON_SerialNumberMap*>(this)->InvalidateHashTableHelper();
00893         const_cast<ON_SerialNumberMap*>(this)->m_sn_count -= m_sn_block0.m_purged;
00894         const_cast<ON_SerialNumberMap*>(this)->m_sn_purged -= m_sn_block0.m_purged;
00895         const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.CullBlockHelper();
00896         const_cast<ON_SerialNumberMap*>(this)->UpdateMaxSNHelper();
00897       }
00898       if ( m_sn_block0.m_count > 0 )
00899       {
00900         const_cast<ON_SerialNumberMap*>(this)->InvalidateHashTableHelper();
00901         const_cast<ON_SerialNumberMap*>(this)->m_sn_block0.SortBlockHelper();
00902         if ( sn1 >= m_sn_block0.m_sn0 && sn0 <= m_sn_block0.m_sn1 )
00903         {
00904           i = m_sn_block0.m_count;
00905           ei = &m_sn_block0.m_sn[0];
00906         }
00907       }
00908     }
00909     else
00910     {
00911       i = m_sn_block0.m_count;
00912       ei = &m_sn_block0.m_sn[0];
00913       while ( ei->m_sn < sn0 || !ei->m_sn_active )
00914       {
00915         if ( --i )
00916           ei++;
00917         else
00918         {
00919           ei = 0;
00920           break;
00921         }
00922       }
00923       if ( ei && ei->m_sn > sn1 )
00924       {
00925         ei = 0;
00926       }
00927     }
00928   }
00929 
00930   // adjust c = estimate of number of items in m_snblk_list[]
00931   if ( ei )
00932     c += m_sn_block0.ActiveElementEstimate(ei->m_sn,sn1);
00933   if (c > (sn1-sn0+1) )
00934     c = (sn1-sn0+1);
00935   if ( c > 2*4096 )
00936     c = 2*4096;
00937 
00938   // reserve room for elements so we don't thrash memory
00939   // while growing a large dynamic array.
00940   elements.Reserve(elements.Count()+((int)c));
00941 
00942   // Add appropriate elements to elements[] array.
00943   while (ei || ek)
00944   {
00945     if (ei && (!ek || ei->m_sn < ek->m_sn) )
00946     {
00947       if ( ei->m_sn_active )
00948         elements.Append(*ei);
00949       if ( --i )
00950       {
00951         if ( (++ei)->m_sn > sn1 )
00952         {
00953           ei = 0;
00954         }
00955       }
00956       else
00957       {
00958         ei = 0;
00959       }
00960     }
00961     else 
00962     {
00963       if ( ek->m_sn_active )
00964         elements.Append(*ei);
00965       if ( --k )
00966       {
00967         if ( (++ek)->m_sn > sn1 )
00968         {
00969           ek = 0;
00970         }
00971       }
00972       else if (++j < m_snblk_list_count && sn1 <= m_snblk_list[j]->m_sn0 )
00973       {
00974         k = m_snblk_list[j]->m_count; // always > 0
00975         ek = &m_snblk_list[j]->m_sn[0];
00976       }
00977       else 
00978       {
00979         ek = 0;
00980       }
00981     }
00982   }
00983   
00984   return (elements.Count() - elements_count0);
00985 }
00986 
00987 struct ON_SerialNumberMap::SN_ELEMENT* 
00988 ON_SerialNumberMap::RemoveSerialNumberAndId(unsigned int sn)
00989 {
00990   struct SN_ELEMENT* e = FindElementHelper(sn);
00991   if ( e && e->m_sn_active )
00992   {
00993     if ( e->m_id_active )
00994     {
00995       if ( m_bHashTableIsValid )
00996       {
00997         // Hash table is valid - remove the element from the table
00998         size_t i = HashIndex(&e->m_id);
00999         struct SN_ELEMENT* prev = 0;
01000         struct SN_ELEMENT* h;
01001         for ( h = m_hash_table[i]; h; h = h->m_next )
01002         {
01003           if (h == e)
01004           {
01005             if ( prev )
01006             {
01007               prev->m_next = e->m_next;
01008             }
01009             else
01010             {
01011               m_hash_table[i] = e->m_next;
01012             }
01013             break;
01014           }
01015           prev = h;
01016         }
01017       }
01018       e->m_next = 0;
01019       e->m_id_active = 0;
01020       if ( m_active_id_count > 0 )
01021       {
01022         m_active_id_count--;
01023       }
01024       else
01025       {
01026         ON_ERROR("ON_SerialNumberMap - m_active_id_count corruption");
01027       }
01028 
01029       // save this id.  When objects are replaced, this id will
01030       // be added back and saving it in m_inactive_id will 
01031       // prevent having to check for it in the hash table.
01032       m_inactive_id = e->m_id;
01033     }
01034 
01035     e->m_sn_active = 0;
01036     m_sn_purged++;
01037     if ( m_e_blk->m_count == ++m_e_blk->m_purged )
01038     {
01039       // every item in the block is purged
01040       if ( m_e_blk == &m_sn_block0 )
01041       {
01042         // Every element in m_sn_block0 is purged.
01043         // Empyt m_sn_block0.
01044         m_sn_count -= m_sn_block0.m_count;
01045         m_sn_purged -= m_sn_block0.m_count;
01046         m_sn_block0.EmptyBlock();
01047       }
01048       else if ( m_e_blk->m_count > 1 )
01049       {
01050         // m_e_blk is in m_sn_list[] and every element
01051         // in m_e_blk is purged. Remove all but
01052         // except one of these elements.
01053         // Note: We cannot empty blocks in the m_sn_list[] because
01054         //       this class has code that assumes the blocks
01055         //       in m_sn_list[] have m_count >= 1.  This makes
01056         //       the class generally faster.  There is code in 
01057         //       FindElementHelper() the keeps the m_sn_list[]
01058         //       blocks relatively tidy.
01059         m_sn_count  -= (m_e_blk->m_count-1);
01060         m_sn_purged -= (m_e_blk->m_count-1);
01061         m_e_blk->m_count = 1;
01062         m_e_blk->m_purged = 1;
01063         m_e_blk->m_sn0 = m_e_blk->m_sn1 = m_e_blk->m_sn[0].m_sn;
01064       }
01065     }
01066     return e;
01067   }
01068 
01069   return 0;
01070 }
01071 
01072 struct ON_SerialNumberMap::SN_ELEMENT* 
01073 ON_SerialNumberMap::RemoveId(unsigned int sn, ON_UUID id)
01074 {
01075   struct SN_ELEMENT* e=0;
01076   struct SN_ELEMENT* prev;
01077   size_t i;
01078   if ( m_active_id_count > 0 )
01079   {
01080     i = HashIndex(&id);
01081     if ( i != 0 || IdIsNotZero(&id) )
01082     {
01083       if ( !m_bHashTableIsValid && sn > 0 )
01084       {
01085         // We can avoid rebuilding the hash table.
01086         // Use the serial number to find the element.
01087         e = FindSerialNumber(sn);
01088         if (e)
01089         {
01090           if (e->m_id_active && 0 == memcmp(&e->m_id,&id,sizeof(e->m_id)) )
01091           {
01092             e->m_next = 0;
01093             e->m_id_active = 0;
01094             m_active_id_count--;
01095             m_inactive_id = e->m_id;
01096           }
01097           else
01098           {
01099             // id is not active
01100             e = 0;
01101           }
01102         }
01103       }
01104       else
01105       {
01106         BuildHashTableHelper();
01107         prev = 0;
01108         for( e = m_hash_table[i]; e; e = e->m_next )
01109         {
01110           if ( 0 == memcmp(&e->m_id,&id,sizeof(e->m_id)) )
01111           {
01112             if ( prev )
01113               prev->m_next = e->m_next;
01114             else
01115               m_hash_table[i] = e->m_next;
01116             e->m_next = 0;
01117             e->m_id_active = 0;
01118             m_active_id_count--;
01119             m_inactive_id = e->m_id;
01120             break;
01121           }
01122           prev = e;
01123         }
01124       }
01125     }
01126   }
01127   return e;
01128 }
01129 
01130 
01131 int ON_SerialNumberMap::SN_BLOCK::CompareMaxSN(const void* a, const void* b)
01132 {
01133   const unsigned int sna = (*((const SN_BLOCK*const*)a))->m_sn1;
01134   const unsigned int snb = (*((const SN_BLOCK*const*)b))->m_sn1;
01135   if ( sna < snb )
01136   {
01137     return (0 == sna) ? 1 : -1;
01138   }
01139   if ( snb < sna )
01140   {
01141     return (0 == snb) ? -1 : 1;
01142   }
01143   return 0;
01144 }
01145 
01146 size_t ON_SerialNumberMap::SN_BLOCK::ActiveElementEstimate(unsigned int sn0, unsigned int sn1) const
01147 {
01148   size_t c = m_count-m_purged;
01149   if ( c > 0 )
01150   {
01151     if ( sn0 < m_sn0 )
01152       sn0 = m_sn0;
01153     if ( sn1 > m_sn1 )
01154       sn1 = m_sn1;
01155     sn1 -= sn0;
01156     sn1++;
01157     if (c > sn1)
01158       c = sn1;
01159   }
01160   return c;
01161 }
01162 
01163 static bool ON_SerialNumberMap_IsNotValid()
01164 {
01165   return ON_IsNotValid(); // <- Good place for a debugger breakpoint
01166 }
01167 
01168 bool ON_SerialNumberMap::IsValid(ON_TextLog* textlog) const
01169 {
01170   size_t i, c, pc, aic;
01171 
01172   aic = 0;
01173 
01174   const_cast<ON_SerialNumberMap*>(this)->BuildHashTableHelper();
01175 
01176   if ( !m_sn_block0.IsValidBlock(textlog,m_hash_table,&aic) )
01177   {
01178     if ( textlog )
01179       textlog->Print("m_sn_block0 is not valid\n");
01180     return ON_SerialNumberMap_IsNotValid();
01181   }
01182 
01183   c = m_sn_block0.m_count;
01184   pc = m_sn_block0.m_purged;
01185 
01186   for ( i = 0; i < m_snblk_list_count; i++ )
01187   {
01188     if ( 0 == m_snblk_list[i]->m_count )
01189     {
01190       if ( textlog )
01191         textlog->Print("m_snblk_list[%d] is empty\n",i);
01192       return ON_SerialNumberMap_IsNotValid();
01193     }
01194     if ( 1 != m_snblk_list[i]->m_sorted )
01195     {
01196       if ( textlog )
01197         textlog->Print("m_snblk_list[%d] is not sorted\n",i);
01198       return ON_SerialNumberMap_IsNotValid();
01199     }
01200     if ( !m_snblk_list[i]->IsValidBlock(textlog,m_hash_table,&aic) )
01201     {
01202       if ( textlog )
01203         textlog->Print("m_snblk_list[%d] is not valid\n",i);
01204       return ON_SerialNumberMap_IsNotValid();
01205     }
01206     c += m_snblk_list[i]->m_count;
01207     pc += m_snblk_list[i]->m_purged;
01208     if ( i>0 && m_snblk_list[i]->m_sn0 <= m_snblk_list[i-1]->m_sn1 )
01209     {
01210       if ( textlog )
01211         textlog->Print("m_snblk_list[%d]->m_sn0 <= m_snblk_list[%d]->m_sn1\n",i,i-1);
01212       return ON_SerialNumberMap_IsNotValid();
01213     }
01214   }
01215 
01216   if ( c != m_sn_count )
01217   {
01218     if ( textlog )
01219       textlog->Print("m_sn_count=%d (should be %d) is not correct\n",m_sn_count,c);
01220     return ON_SerialNumberMap_IsNotValid();
01221   }
01222 
01223   if ( pc != m_sn_purged )
01224   {
01225     if ( textlog )
01226       textlog->Print("m_sn_purged=%d (should be %d) is not correct\n",m_sn_purged,pc);
01227     return ON_SerialNumberMap_IsNotValid();
01228   }
01229 
01230   if ( m_active_id_count != aic )
01231   {
01232     if ( textlog )
01233       textlog->Print("m_active_id_count=%d (should be %d) is not correct\n",m_active_id_count,aic);
01234     return ON_SerialNumberMap_IsNotValid();
01235   }
01236 
01237   if ( m_active_id_count + m_sn_purged > m_sn_count )
01238   {
01239     if ( textlog )
01240       textlog->Print("m_active_id_count=%d > %d = (m_sn_count-m_sn_purged)\n",m_active_id_count,m_sn_count-m_sn_purged);
01241     return ON_SerialNumberMap_IsNotValid();
01242   }
01243 
01244   c = 0;
01245   for ( i = 0; i < ID_HASH_TABLE_COUNT; i++ )
01246   {
01247     const SN_ELEMENT* e;
01248     for (e = m_hash_table[i]; e && c <= m_active_id_count; e = e->m_next)
01249     {
01250       c++;
01251     }
01252   }
01253 
01254   if ( c > m_active_id_count )
01255   {
01256     if ( textlog )
01257       textlog->Print("m_hash_table[] linked lists have too many elements.\n");
01258     return ON_SerialNumberMap_IsNotValid();
01259   }
01260 
01261   return true;
01262 }
01263 
01264 size_t ON_SerialNumberMap::GarbageCollectMoveHelper(ON_SerialNumberMap::SN_BLOCK* dst,ON_SerialNumberMap::SN_BLOCK* src)
01265 {
01266   // This helper is used by GarbageCollectHelper and moves
01267   // as many entries as possible from src to dst.  
01268   // Returns: the number of entries transfered.
01269   size_t i,j,n;
01270   if ( src && dst )
01271   {
01272     n = SN_BLOCK_CAPACITY - dst->m_count;
01273     if ( src->m_count < n )
01274       n = src->m_count;
01275     if ( n > 0 )
01276     {
01277       if ( 0 == dst->m_count )
01278       {
01279         dst->EmptyBlock();
01280       }
01281       if ( 0 == src->m_sorted )
01282       {
01283         dst->m_sorted = 0;
01284         if ( 0 == dst->m_count )
01285         {
01286           dst->m_sn0 = src->m_sn0;
01287           dst->m_sn1 = src->m_sn1;
01288         }
01289       }
01290       memcpy(&dst->m_sn[dst->m_count],&src->m_sn[0],n*sizeof(src->m_sn[0]));
01291       dst->m_count += n;
01292       if ( dst->m_sorted )
01293       {
01294         dst->m_sn0 = dst->m_sn[0].m_sn; // set m_sn0 because input dst could have count 0
01295         dst->m_sn1 = dst->m_sn[dst->m_count-1].m_sn;
01296       }
01297       else 
01298       {
01299         if ( dst->m_sn0 > src->m_sn0 )
01300           dst->m_sn0 = src->m_sn0;
01301         if ( dst->m_sn1 < src->m_sn1 )
01302           dst->m_sn1 = src->m_sn1;
01303       }
01304       i = 0; j = n;
01305       while ( j < src->m_count )
01306       {
01307         src->m_sn[i++] = src->m_sn[j++];
01308       }
01309       src->m_count = i;
01310       if ( src->m_count > 0 )
01311       {
01312         if ( src->m_sorted )
01313           src->m_sn0 = src->m_sn[0].m_sn;
01314       }
01315       else
01316       {
01317         src->EmptyBlock();
01318       }
01319     }
01320   }
01321   else
01322   {
01323     n = 0;
01324   }
01325   return n;
01326 }
01327 
01328 void ON_SerialNumberMap::GarbageCollectHelper()
01329 {
01330   size_t i,j,k,n;
01331 
01332   // This is a helper function.  The caller
01333   // should check that SN_BLOCK_CAPACITY == m_sn_block0.m_count
01334   // before calling it.
01335   InvalidateHashTableHelper();
01336 
01337   if ( m_sn_block0.m_purged > 0 )
01338   {
01339     m_sn_count -= m_sn_block0.m_purged;
01340     m_sn_purged -= m_sn_block0.m_purged;
01341     m_sn_block0.CullBlockHelper();
01342     if ( !m_sn_block0.m_sorted )
01343       m_sn_block0.SortBlockHelper();
01344     if ( 0 == m_snblk_list_count )
01345       m_maxsn = m_sn_block0.m_sn1; 
01346     if ( m_sn_block0.m_count < 7*(SN_BLOCK_CAPACITY/8) )
01347       return;
01348   }
01349   else if ( !m_sn_block0.m_sorted )
01350   {
01351     m_sn_block0.SortBlockHelper();
01352     if ( 0 == m_snblk_list_count )
01353       m_maxsn = m_sn_block0.m_sn1; 
01354   }
01355 
01356   // Remove all purged serial numbers from every block
01357   // and make sure every block is sorted.
01358   m_sn_purged = 0;
01359   m_sn_count = m_sn_block0.m_count;
01360   i = m_snblk_list_count;
01361   while (i--)
01362   {
01363     if ( m_snblk_list[i]->m_purged > 0 )
01364     {
01365       // The next call may empty m_snblk_list[i].
01366       m_snblk_list[i]->CullBlockHelper();
01367     }
01368     m_sn_count += m_snblk_list[i]->m_count;
01369   }
01370 
01371   // Put empty blocks at the end of the list
01372   for ( i = 0; i < m_snblk_list_count; i++ )
01373   {
01374     if ( 0 == m_snblk_list[i]->m_count )
01375     {
01376       // m_snblk_list[i] is empty ...
01377       for(j = i+1; j < m_snblk_list_count; j++ )
01378       {
01379         if ( m_snblk_list[j]->m_count > 0 )
01380         {
01381           // ... and m_snblk_list[j] is not empty
01382           ON_qsort(m_snblk_list+i,m_snblk_list_count-i,sizeof(*m_snblk_list),SN_BLOCK::CompareMaxSN);
01383           break;
01384         }
01385       }
01386       while ( m_snblk_list_count > 0 && 0 == m_snblk_list[m_snblk_list_count-1]->m_count )
01387         m_snblk_list_count--;
01388       break;
01389     }
01390   }
01391 
01392   if (    m_snblk_list_count > 0 
01393        && m_snblk_list[m_snblk_list_count-1]->m_sn1 > m_sn_block0.m_sn0 
01394      )
01395   {
01396     // Merge the serial number lists so the blocks in m_sn_list[]
01397     // have the lowest serial numbers and m_sn_block0.m_sn[] contains
01398     // the largest.
01399     size_t snarray_count = 0;
01400     struct SN_ELEMENT* snarray = (struct SN_ELEMENT*)onmalloc_from_pool(m_pool,2*SN_BLOCK_CAPACITY*sizeof(snarray[0]));
01401     for ( i = 0; i < m_snblk_list_count && m_sn_block0.m_count > 0; i++ )
01402     {
01403       if ( m_snblk_list[i]->m_sn1 < m_sn_block0.m_sn0 )
01404         continue;
01405 
01406       // Move some entries in m_sn_block0.m_sn[] 
01407       // to m_snblk_list[i]->m_sn[].
01408       SN_BLOCK* blk = m_snblk_list[i];
01409       const unsigned int sn1 = (i < m_snblk_list_count-1) 
01410                              ? m_snblk_list[i+1]->m_sn0 
01411                              : (m_sn_block0.m_sn1+1);
01412       snarray_count = j = k = 0;
01413       while(j < blk->m_count && k < m_sn_block0.m_count )
01414       {
01415         if ( blk->m_sn[j].m_sn < m_sn_block0.m_sn[k].m_sn )
01416         {
01417           snarray[snarray_count++] = blk->m_sn[j++];
01418         }
01419         else if ( m_sn_block0.m_sn[k].m_sn < sn1 )
01420         {
01421           snarray[snarray_count++] = m_sn_block0.m_sn[k++];
01422         }
01423         else
01424         {
01425           // If m_sn_block0.m_sn[m_sn_block0.m_count-1].m_sn is the largest
01426           // value, then we should get j == blk->m_count exit.
01427           // If blk->m_sn[blk->m_count-1].m_sn is the largest value,
01428           // then we should get k == m_sn_block0.m_count and exit.
01429           ON_ERROR("Bogus information - should never get here");
01430           break;
01431         }
01432       }
01433       n = blk->m_count-j;
01434       if ( n > 0 )
01435       {
01436         memcpy(&snarray[snarray_count],&blk->m_sn[j],n*sizeof(snarray[0]));
01437         snarray_count += n;
01438       }
01439       else
01440       {
01441         while ( k < m_sn_block0.m_count && m_sn_block0.m_sn[k].m_sn < sn1 )
01442           snarray[snarray_count++] = m_sn_block0.m_sn[k++];
01443       }
01444       n = (snarray_count > SN_BLOCK_CAPACITY) 
01445         ? SN_BLOCK_CAPACITY 
01446         : snarray_count;
01447       if ( k < m_sn_block0.m_count )
01448       {
01449         memcpy(&snarray[snarray_count],
01450                &m_sn_block0.m_sn[k],
01451                (m_sn_block0.m_count-k)*sizeof(snarray[0])
01452                );
01453         snarray_count += (m_sn_block0.m_count-k);
01454       }
01455       blk->m_count = n;
01456       memcpy(&blk->m_sn[0],snarray,blk->m_count*sizeof(blk->m_sn[0]));
01457       blk->m_sn0 = blk->m_sn[0].m_sn;
01458       blk->m_sn1 = blk->m_sn[blk->m_count-1].m_sn;
01459       if ( snarray_count > n )
01460       {
01461         // put the end of snarray[] (the largest serial numbers)
01462         // in m_sn_block0.m_sn[].
01463         m_sn_block0.m_count = (snarray_count-n);
01464         memcpy(&m_sn_block0.m_sn[0],
01465                &snarray[n],
01466                m_sn_block0.m_count*sizeof(m_sn_block0.m_sn[0])
01467                );
01468         m_sn_block0.m_sn0 = m_sn_block0.m_sn[0].m_sn;
01469         m_sn_block0.m_sn1 = m_sn_block0.m_sn[m_sn_block0.m_count-1].m_sn;
01470       }
01471       else
01472       {
01473         m_sn_block0.EmptyBlock();
01474       }
01475     }
01476     onfree(snarray);
01477     snarray = 0;
01478   }
01479 
01480   // Compact the blocks in m_sn_list[]
01481   for ( i = 0; i < m_snblk_list_count; i++ )
01482   {
01483     for ( j = i+1; j < m_snblk_list_count; j++ )
01484     {
01485       if ( m_snblk_list[i]->m_count >= SN_BLOCK_CAPACITY )
01486         break;
01487       GarbageCollectMoveHelper(m_snblk_list[i],m_snblk_list[j]);
01488     }
01489   }
01490   while ( m_snblk_list_count > 0 && 0 == m_snblk_list[m_snblk_list_count-1]->m_count )
01491   {
01492     m_snblk_list_count--;
01493   }
01494 
01495   // Make sure m_sn_block0.m_an[] is has plenty of room
01496   if ( m_sn_block0.m_count > SN_BLOCK_CAPACITY/4 )
01497   {
01498     if ( m_snblk_list_count > 0 )
01499     {
01500       GarbageCollectMoveHelper(m_snblk_list[m_snblk_list_count-1],&m_sn_block0);
01501     }
01502     if ( m_sn_block0.m_count > SN_BLOCK_CAPACITY/2 )
01503     {
01504       // Need to add a new block to m_snblk_list[]
01505       if ( m_snblk_list_count == m_snblk_list_capacity )
01506       {
01507         // Add room to store more pointers to blocks in the m_sn_list[]
01508         i = m_snblk_list_capacity;
01509         m_snblk_list_capacity += 32;
01510         n = m_snblk_list_capacity*sizeof(m_snblk_list[0]);
01511         m_snblk_list = (SN_BLOCK**)((0 == m_snblk_list)
01512                      ? onmalloc_from_pool(m_pool,n)
01513                      : onrealloc(m_snblk_list,n));
01514         while ( i < m_snblk_list_capacity )
01515           m_snblk_list[i++] = 0;
01516       }
01517       if ( 0 == m_snblk_list[m_snblk_list_count] )
01518       {
01519         // add room to store at more serial numbers
01520         m_snblk_list[m_snblk_list_count] = (SN_BLOCK*)onmalloc_from_pool(m_pool,sizeof(*(m_snblk_list[m_snblk_list_count])));
01521       }
01522       *m_snblk_list[m_snblk_list_count++] = m_sn_block0;
01523       m_sn_block0.EmptyBlock();
01524     }
01525   }
01526 }
01527 
01528 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::AddSerialNumber(unsigned int sn)
01529 {
01530   if ( sn <= 0 )
01531     return 0;
01532   struct SN_ELEMENT* e = FindElementHelper(sn);
01533   if ( e )
01534   {
01535     if ( 0 == e->m_sn_active )
01536     {
01537       m_sn_purged--;
01538       m_e_blk->m_purged--;
01539       memset(e,0,sizeof(*e));
01540       e->m_sn = sn;
01541       e->m_sn_active = 1;
01542     }
01543   }
01544   else
01545   {
01546     if ( SN_BLOCK_CAPACITY == m_sn_block0.m_count )
01547     {
01548       // make room in m_sn_block0 for the new serial number
01549       GarbageCollectHelper();    
01550     }
01551 
01552     if ( 0 == m_sn_block0.m_count )
01553     {
01554       m_sn_block0.m_sn0 = m_sn_block0.m_sn1 = sn;
01555       m_sn_block0.m_sorted = 1;
01556     }
01557     else
01558     {
01559       if ( sn > m_sn_block0.m_sn1 )
01560       {
01561         m_sn_block0.m_sn1 = sn;
01562       }
01563       else
01564       {
01565         if ( sn < m_sn_block0.m_sn0 )
01566           m_sn_block0.m_sn0 = sn;
01567         m_sn_block0.m_sorted = 0;
01568       }        
01569     }
01570     if ( sn > m_maxsn )
01571       m_maxsn = sn;
01572     m_sn_count++;
01573     e = &m_sn_block0.m_sn[m_sn_block0.m_count++];
01574     memset(e,0,sizeof(*e));
01575     e->m_sn = sn;
01576     e->m_sn_active = 1;
01577   }
01578 
01579   return e;
01580 }
01581 
01582 struct ON_SerialNumberMap::SN_ELEMENT* ON_SerialNumberMap::AddSerialNumberAndId(unsigned int sn,ON_UUID id)
01583 {
01584   struct SN_ELEMENT* e = AddSerialNumber(sn);
01585   size_t i;
01586 
01587   if ( 0 != e && 0 == e->m_id_active )
01588   {
01589     if ( IdIsNotZero(&id) )
01590     {
01591       if (    m_active_id_count > 0 
01592            && 0 != memcmp(&m_inactive_id,&id,sizeof(m_inactive_id)) 
01593          )
01594       {
01595         // Need to determine if id is already in use.
01596         BuildHashTableHelper();
01597         i = HashIndex(&id);
01598         for(const struct SN_ELEMENT* h = m_hash_table[i];h;h = h->m_next)
01599         {
01600           if ( 0 == memcmp(&h->m_id,&id,sizeof(h->m_id)) )
01601           {
01602             // This id is already in use. Create a new id.
01603             ON_CreateUuid(id);
01604             break;
01605           }
01606         }
01607       }        
01608     }
01609     else
01610     {
01611       // The input id was zero. Create a new id.
01612       ON_CreateUuid(id);
01613     }
01614 
01615     // Add id to the hash table
01616     e->m_id = id;
01617     e->m_id_active = 1;
01618     if ( m_bHashTableIsValid )
01619     {
01620       // the table isn't dirty - go ahead
01621       // and add the element.  If the table
01622       // is dirty, it will get rebuilt before
01623       // it needs to be used and this element
01624       // will be included at that time.
01625       i = HashIndex(&id);
01626       e->m_next = m_hash_table[i];
01627       m_hash_table[i] = e;
01628     }
01629     m_active_id_count++;
01630     memset(&m_inactive_id,0,sizeof(m_inactive_id));
01631   }
01632 
01633   return e;
01634 }
01635 
01636 size_t ON_SerialNumberMap::HashIndex(const ON_UUID* id) const
01637 {
01638   // this is a private member function and the caller
01639   // insures the id pointer is not null.
01640   return (IdCRC(id) % ID_HASH_TABLE_COUNT);
01641 }
01642 
01643 void ON_SerialNumberMap::InvalidateHashTableHelper()
01644 {
01645   // This helper function is called when the memory
01646   // locations of SN_ELEMENTs are going to change.
01647   // and the pointers saved in m_hash_table[] and
01648   // SN_ELEMENT::next may become invalid. When a
01649   // valid m_hash_table[] is needed at a later time,
01650   // BuildHashTableHelper() will restore it.
01651   if ( m_bHashTableIsValid )
01652   {
01653     m_bHashTableIsValid = false;
01654     memset(m_hash_table,0,sizeof(m_hash_table));
01655   }
01656 }
01657 
01658 bool ON_SerialNumberMap::RemoveBlockFromHashTableHelper(const struct ON_SerialNumberMap::SN_BLOCK* blk)
01659 {
01660   // quickly remove every id in the block from the hash table.
01661   // This is done when the block is small compared to the
01662   // size of the hash table and removing the elements from
01663   // the hash table, rearranging the block, and then adding
01664   // the elements back into the hash table is faster than
01665   // simply rebuilding the hash table.
01666   //
01667   // The 128 multiplier is determined as follows:
01668   // - C = average number of elements with the same hash index
01669   //     = m_active_id_count/ID_HASH_TABLE_COUNT.
01670   // - D = cost of destroying hash table = time to memset the table
01671   //       to zero.
01672   // - H = cost of calculating the hash table index of an id.
01673   // - A = The cost of adding an element to the hash table is
01674   //       H + two pointer assignments with is essentially H.
01675   // - F = The average cost of finding an element in the hash
01676   //       table is H plus going through half the linked list
01677   //       of elements at that hash index (C/2 pointer dereferences).
01678   // - B = number of elements in a block.
01679   // - I = (number times a valid hash table is destroyed)/
01680   //       (number of times the hash table is rebuilt).
01681   // - R = rebuild cost = A*m_active_id_count.
01682   // - Keeping the the has table up to date only makes sense if
01683   //   R > I*(A+F)*active_id_count
01684   //   it is needed often enough to justify ...
01685   bool rc = false;
01686   if ( m_bHashTableIsValid && m_active_id_count > 128*blk->m_count )
01687   {
01688     const SN_ELEMENT* e;
01689     SN_ELEMENT* h;
01690     SN_ELEMENT* prev;
01691     size_t i, hash_i;
01692     rc = true;
01693     for (e = blk->m_sn, i = blk->m_count; i--; e++)
01694     {
01695       if ( e->m_id_active )
01696       {
01697         prev = 0;
01698         hash_i = HashIndex(&e->m_id);
01699         for (h = m_hash_table[hash_i];h;h=h->m_next)
01700         {
01701           if (h == e)
01702           {
01703             // Do not change value of SN_ELEMENT's m_id_active.
01704             // This value is needed by AddBlockToHashTableHelper()
01705             // when it add the element back in.
01706             m_active_id_count--;
01707             if (prev)
01708               prev->m_next = h->m_next;
01709             else
01710               m_hash_table[hash_i] = h->m_next;
01711             break;
01712           }
01713           prev = h;
01714         }
01715       }
01716     }
01717   }
01718   return rc;
01719 }
01720 
01721 void ON_SerialNumberMap::AddBlockToHashTableHelper(struct ON_SerialNumberMap::SN_BLOCK* blk)
01722 {
01723   if ( m_bHashTableIsValid )
01724   {
01725     SN_ELEMENT* e;
01726     size_t i, hash_i;
01727     for (e = blk->m_sn, i = blk->m_count; i--; e++)
01728     {
01729       if ( e->m_id_active )
01730       {
01731         hash_i = HashIndex(&e->m_id);
01732         e->m_next = m_hash_table[hash_i];
01733         m_hash_table[hash_i] = e;
01734       }
01735     }
01736   }
01737 }
01738 
01739 
01740 void ON_SerialNumberMap::BuildHashTableHelper()
01741 {
01742   // This function is called when code requires
01743   // m_hash_table[] to be valid so an id can be looked up.
01744   if ( !m_bHashTableIsValid )
01745   {
01746     // The hash table is dirty because elements
01747     // have moved in memory.  Rebuild it putting
01748     // the newest elements first.
01749     m_bHashTableIsValid = true;
01750     if (m_active_id_count > 0)
01751     {
01752       struct SN_BLOCK* blk;
01753       struct SN_ELEMENT* e;
01754       size_t snblk_i, j, hash_i;
01755       for ( snblk_i = 0; snblk_i < m_snblk_list_count; snblk_i++ )
01756       {
01757         blk = m_snblk_list[snblk_i];
01758         if ( blk->m_purged < blk->m_count )
01759         {
01760           e = &blk->m_sn[0];
01761           for(j = blk->m_count; j--; e++ )
01762           {
01763             if ( e->m_id_active )
01764             {
01765               hash_i = HashIndex(&e->m_id);
01766               e->m_next = m_hash_table[hash_i];
01767               m_hash_table[hash_i] = e;
01768             }
01769             else
01770             {
01771               e->m_next = 0;
01772             }
01773           }
01774         }
01775       }
01776 
01777       blk = &m_sn_block0;
01778       if ( blk->m_purged < blk->m_count )
01779       {
01780         e = &blk->m_sn[0];
01781         for(j = blk->m_count; j--; e++ )
01782         {
01783           if ( e->m_id_active )
01784           {
01785             hash_i = HashIndex(&e->m_id);
01786             e->m_next = m_hash_table[hash_i];
01787             m_hash_table[hash_i] = e;
01788           }
01789           else
01790           {
01791             e->m_next = 0;
01792           }
01793         }
01794       }
01795     }
01796   }
01797 }
01798 
01799 
01800 void ON_SerialNumberMap::SN_ELEMENT::Dump(ON_TextLog& text_log) const
01801 {
01802   text_log.Print("m_id = ");
01803   text_log.Print(m_id);
01804   text_log.Print("\nm_sn = %d\n",m_sn);
01805   text_log.Print("m_sn_active = %d\n",m_sn_active);
01806   text_log.Print("m_id_active = %d\n",m_id_active);
01807 }
01808 
01809 
01810 void ON_SerialNumberMap::SN_BLOCK::Dump(ON_TextLog& text_log) const
01811 {
01812   text_log.Print("m_count = %d\n",m_count);
01813   text_log.Print("m_purged = %d\n",m_purged);
01814   text_log.Print("m_sorted = %d\n",m_sorted);
01815   text_log.Print("m_sn0 = %d\n",m_sn0);
01816   text_log.Print("m_sn1 = %d\n",m_sn1);
01817   if ( m_count > 0 )
01818   {
01819     text_log.Print("m_sn[0]\n");
01820     text_log.PushIndent();
01821     m_sn[0].Dump(text_log);
01822     text_log.PopIndent();
01823     if ( m_count > 1 )
01824     {
01825       text_log.Print("m_sn[%d]\n",m_count-1);
01826       text_log.PushIndent();
01827       m_sn[m_count-1].Dump(text_log);
01828       text_log.PopIndent();
01829     }
01830   }
01831 }
01832 
01833 void ON_SerialNumberMap::Dump(ON_TextLog& text_log) const
01834 {
01835   text_log.Print("m_maxsn = %d\n",m_maxsn);
01836   text_log.Print("m_sn_count = %d\n",m_sn_count);
01837   text_log.Print("m_sn_purged = %d\n",m_sn_purged);
01838   text_log.Print("m_active_id_count = %d\n",m_sn_purged);
01839   text_log.Print("m_bHashTableIsValid = %d\n",m_bHashTableIsValid);
01840   text_log.Print("m_snblk_list_capacity = %d\n",m_snblk_list_capacity);
01841   text_log.Print("m_snblk_list_count = %d\n",m_snblk_list_count);
01842   
01843   text_log.Print("m_sn_block0\n");
01844   text_log.PushIndent();
01845   m_sn_block0.Dump(text_log);
01846   text_log.PopIndent();
01847 
01848   for ( size_t i = 0; i < m_snblk_list_count; i++ )
01849   {
01850     text_log.Print("m_snblk_list[%d]\n",i);
01851     text_log.PushIndent();
01852     m_snblk_list[i]->Dump(text_log);
01853     text_log.PopIndent();
01854   }
01855 }


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:27:01