/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// */ #if !defined(ON_ARRAY_DEFS_INC_) #define ON_ARRAY_DEFS_INC_ // When this file is parsed with /W4 warnings, two bogus warnings // are generated. #pragma ON_PRAGMA_WARNING_PUSH // The ON_ClassArray::DestroyElement template function generates a // C4100: 'x' : unreferenced formal parameter // warning. // This appears to be caused by a bug in the compiler warning code // or the way templates are expanded. This pragma is needed squelch the // bogus warning. #pragma ON_PRAGMA_WARNING_DISABLE_MSC(4100) // The ON_CompareIncreasing and ON_CompareDecreasing templates generate a // C4211: nonstandard extension used : redefined extern to static // warning. Microsoft's compiler appears to have a little trouble // when static functions are declared before they are defined in a // single .cpp file. This pragma is needed squelch the bogus warning. #pragma ON_PRAGMA_WARNING_DISABLE_MSC(4211) // The main reason the definitions of the functions for the // ON_SimpleArray and ON_ClassArray templates are in this separate // file is so that the Microsoft developer studio autocomplete // functions will work on these classes. // // This file is included by opennurbs_array.h in the appropriate // spot. If you need the definitions in the file, then you // should include opennurbs_array.h and let it take care of // including this file. ///////////////////////////////////////////////////////////////////////////////////// // Class ON_SimpleArray<> ///////////////////////////////////////////////////////////////////////////////////// // construction //////////////////////////////////////////////////////// template T* ON_SimpleArray::Realloc(T* ptr,int capacity) { return (T*)onrealloc(ptr,capacity*sizeof(T)); } template ON_SimpleArray::ON_SimpleArray() ON_NOEXCEPT : m_a(nullptr) , m_count(0) , m_capacity(0) {} template ON_SimpleArray::ON_SimpleArray( size_t c ) : m_a(nullptr) , m_count(0) , m_capacity(0) { if ( c > 0 ) SetCapacity( c ); } // Copy constructor template ON_SimpleArray::ON_SimpleArray( const ON_SimpleArray& src ) : m_a(0) , m_count(0) , m_capacity(0) { *this = src; // operator= defined below } template ON_SimpleArray::~ON_SimpleArray() { SetCapacity(0); } template ON_SimpleArray& ON_SimpleArray::operator=( const ON_SimpleArray& src ) { if( this != &src ) { if ( src.m_count <= 0 ) { m_count = 0; } else { if ( m_capacity < src.m_count ) { SetCapacity( src.m_count ); } if ( m_a ) { m_count = src.m_count; memcpy( (void*)(m_a), (void*)(src.m_a), m_count*sizeof(T) ); } } } return *this; } #if defined(ON_HAS_RVALUEREF) // Clone constructor template ON_SimpleArray::ON_SimpleArray( ON_SimpleArray&& src ) ON_NOEXCEPT : m_a(src.m_a) , m_count(src.m_count) , m_capacity(src.m_capacity) { src.m_a = 0; src.m_count = 0; src.m_capacity = 0; } // Clone assignment template ON_SimpleArray& ON_SimpleArray::operator=( ON_SimpleArray&& src ) ON_NOEXCEPT { if( this != &src ) { this->Destroy(); m_a = src.m_a; m_count = src.m_count; m_capacity = src.m_capacity; src.m_a = 0; src.m_count = 0; src.m_capacity = 0; } return *this; } #endif // emergency destroy /////////////////////////////////////////////////// template void ON_SimpleArray::EmergencyDestroy(void) { m_count = 0; m_capacity = 0; m_a = 0; } // query /////////////////////////////////////////////////////////////// template int ON_SimpleArray::Count() const { return m_count; } template unsigned int ON_SimpleArray::UnsignedCount() const { return ((unsigned int)m_count); } template int ON_SimpleArray::Capacity() const { return m_capacity; } template unsigned int ON_SimpleArray::SizeOfArray() const { return ((unsigned int)(m_capacity*sizeof(T))); } template unsigned int ON_SimpleArray::SizeOfElement() const { return ((unsigned int)(sizeof(T))); } template ON__UINT32 ON_SimpleArray::DataCRC(ON__UINT32 current_remainder) const { return ON_CRC32(current_remainder,m_count*sizeof(m_a[0]),m_a); } template T& ON_SimpleArray::operator[]( int i ) { #if defined(ON_DEBUG) if ( i < 0 || i > m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } template T& ON_SimpleArray::operator[]( unsigned int i ) { #if defined(ON_DEBUG) if ( i > (unsigned int)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } template T& ON_SimpleArray::operator[]( ON__INT64 i ) { #if defined(ON_DEBUG) if ( i < 0 || i > (ON__INT64)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } template T& ON_SimpleArray::operator[]( ON__UINT64 i ) { #if defined(ON_DEBUG) if ( i > (ON__UINT64)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } #if defined(ON_RUNTIME_APPLE) template T& ON_SimpleArray::operator[](size_t i ) { #if defined(ON_DEBUG) if ( i > (size_t)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } #endif template const T& ON_SimpleArray::operator[](int i) const { #if defined(ON_DEBUG) if ( i < 0 || i > m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } template const T& ON_SimpleArray::operator[](unsigned int i) const { #if defined(ON_DEBUG) if ( i > (unsigned int)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } template const T& ON_SimpleArray::operator[](ON__INT64 i) const { #if defined(ON_DEBUG) if ( i < 0 || i > ((ON__INT64)m_capacity) ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } template const T& ON_SimpleArray::operator[](ON__UINT64 i) const { #if defined(ON_DEBUG) if ( i > (ON__UINT64)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } #if defined(ON_RUNTIME_APPLE) template const T& ON_SimpleArray::operator[](size_t i) const { #if defined(ON_DEBUG) if ( i > (size_t)m_capacity ) { ON_ERROR("ON_SimpleArray[i]: i out of range."); } #endif return m_a[i]; } #endif template ON_SimpleArray::operator T*() { return (m_count > 0) ? m_a : 0; } template ON_SimpleArray::operator const T*() const { return (m_count > 0) ? m_a : 0; } template T* ON_SimpleArray::Array() { return m_a; } template const T* ON_SimpleArray::Array() const { return m_a; } template T* ON_SimpleArray::KeepArray() { T* p = m_a; m_a = 0; m_count = 0; m_capacity = 0; return p; } template void ON_SimpleArray::SetArray(T* p) { if ( m_a && m_a != p ) onfree(m_a); m_a = p; } template void ON_SimpleArray::SetArray(T* p, int count, int capacity) { if ( m_a && m_a != p ) onfree(m_a); m_a = p; m_count = count; m_capacity = capacity; } template T* ON_SimpleArray::First() { return (m_count > 0) ? m_a : 0; } template const T* ON_SimpleArray::First() const { return (m_count > 0) ? m_a : 0; } template T* ON_SimpleArray::At( int i ) { return (i >= 0 && i < m_count) ? m_a+i : 0; } template T* ON_SimpleArray::At( unsigned int i ) { return (i < (unsigned int)m_count) ? m_a+i : 0; } template const T* ON_SimpleArray::At( int i) const { return (i >= 0 && i < m_count) ? m_a+i : 0; } template const T* ON_SimpleArray::At( unsigned int i) const { return (i < (unsigned int)m_count) ? m_a+i : 0; } template T* ON_SimpleArray::At( ON__INT64 i ) { return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; } template T* ON_SimpleArray::At( ON__UINT64 i ) { return (i < (ON__UINT64)m_count) ? m_a+i : 0; } template const T* ON_SimpleArray::At( ON__INT64 i) const { return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; } template const T* ON_SimpleArray::At( ON__UINT64 i) const { return (i < (ON__UINT64)m_count) ? m_a+i : 0; } template T* ON_SimpleArray::Last() { return (m_count > 0) ? m_a+(m_count-1) : 0; } template const T* ON_SimpleArray::Last() const { return (m_count > 0) ? m_a+(m_count-1) : 0; } // array operations //////////////////////////////////////////////////// template void ON_SimpleArray::Move( int dest_i, int src_i, int ele_cnt ) { // private function for moving blocks of array memory // caller is responsible for updating m_count. if ( ele_cnt <= 0 || src_i < 0 || dest_i < 0 || src_i == dest_i || src_i + ele_cnt > m_count || dest_i > m_count ) return; int capacity = dest_i + ele_cnt; if ( capacity > m_capacity ) { if ( capacity < 2*m_capacity ) capacity = 2*m_capacity; SetCapacity( capacity ); } memmove( &m_a[dest_i], &m_a[src_i], ele_cnt*sizeof(T) ); } template T& ON_SimpleArray::AppendNew() { if ( m_count == m_capacity ) { int new_capacity = NewCapacity(); Reserve( new_capacity ); } memset( (void*)(&m_a[m_count]), 0, sizeof(T) ); return m_a[m_count++]; } template void ON_SimpleArray::Append( const T& x ) { const T* p = &x; if ( m_count == m_capacity ) { const int newcapacity = NewCapacity(); if ( p >= m_a && p < (m_a + m_capacity) ) { // 26 Sep 2005 Dale Lear // x is in the block of memory about to be reallocated. void* temp = onmalloc(sizeof(T)); memcpy(temp, p, sizeof(T)); p = (T*)temp; } Reserve(newcapacity); if (nullptr == m_a) { ON_ERROR("allocation failure"); return; } } m_a[m_count++] = *p; if (p != &x) onfree((void*)p); } template void ON_SimpleArray::Append( int count, const T* buffer ) { if ( count > 0 && nullptr != buffer ) { const size_t sizeof_buffer = count * sizeof(T); void* temp = nullptr; if ( count + m_count > m_capacity ) { int newcapacity = NewCapacity(); if ( newcapacity < count + m_count ) newcapacity = count + m_count; if ( buffer >= m_a && buffer < (m_a + m_capacity) ) { // buffer is in the block of memory about to be reallocated temp = onmalloc(sizeof_buffer); memcpy(temp, buffer, sizeof_buffer); buffer = (const T*)temp; } Reserve( newcapacity ); } memcpy( (void*)(m_a + m_count), (void*)(buffer), sizeof_buffer ); if (nullptr != temp) onfree(temp); m_count += count; } } template void ON_SimpleArray::Prepend( int count, const T* buffer ) { if ( count > 0 && nullptr != buffer ) { const size_t sizeof_buffer = count * sizeof(T); void* temp = nullptr; if ( count + m_count > m_capacity ) { int newcapacity = NewCapacity(); if ( newcapacity < count + m_count ) newcapacity = count + m_count; if ( buffer >= m_a && buffer < (m_a + m_capacity) ) { // buffer is in the block of memory about to be reallocated temp = onmalloc(sizeof_buffer); memcpy(temp, buffer, sizeof_buffer); buffer = (const T*)temp; } Reserve( newcapacity ); } const size_t count0 = (size_t)m_count; const size_t count1 = count0 + ((size_t)count); T* p0 = m_a; T* p = p0 + count0; T* p1 = m_a + count1; while (p > p0) *(--p1) = *(--p); memcpy( (void*)(m_a), (void*)(buffer), sizeof_buffer ); if (nullptr != temp) onfree(temp); m_count = (int)count1; } } template void ON_SimpleArray::Insert( int i, const T& x ) { if( i >= 0 && i <= m_count ) { const T* p = &x; if ( m_count == m_capacity ) { if (&x >= m_a && &x < (m_a + m_capacity)) { // x is in the block of memory about to be reallocated. void* temp = onmalloc(sizeof(T)); memcpy(temp, p, sizeof(T)); p = (T*)temp; } int newcapacity = NewCapacity(); Reserve( newcapacity ); } m_count++; Move( i+1, i, m_count-1-i ); m_a[i] = *p; if (p != &x) onfree((void*)p); } } template void ON_SimpleArray::Remove() { Remove(m_count-1); } template void ON_SimpleArray::Remove( int i ) { if ( i >= 0 && i < m_count ) { Move( i, i+1, m_count-1-i ); m_count--; memset( (void*)(&m_a[m_count]), 0, sizeof(T) ); } } template void ON_SimpleArray::Empty() { if ( m_a ) memset( (void*)(m_a), 0, m_capacity*sizeof(T) ); m_count = 0; } template void ON_SimpleArray::Reverse() { // NOTE: // If anything in "T" depends on the value of this's address, // then don't call Reverse(). T t; int i = 0; int j = m_count-1; for ( /*empty*/; i < j; i++, j-- ) { t = m_a[i]; m_a[i] = m_a[j]; m_a[j] = t; } } template void ON_SimpleArray::Swap( int i, int j ) { if ( i != j ) { const T t(m_a[i]); m_a[i] = m_a[j]; m_a[j] = t; } } template int ON_SimpleArray::Search( const T& key ) const { const T* p = &key; for ( int i = 0; i < m_count; i++ ) { if (!memcmp(p,m_a+i,sizeof(T))) return i; } return -1; } template int ON_SimpleArray::Search( const T* key, int (*compar)(const T*,const T*) ) const { for ( int i = 0; i < m_count; i++ ) { if (!compar(key,m_a+i)) return i; } return -1; } template int ON_SimpleArray::BinarySearch( const T* key, int (*compar)(const T*,const T*) ) const { const T* found = (key&&m_a&&m_count>0) ? (const T*)bsearch( key, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ) : 0; // This worked on a wide range of 32 bit compilers. int rc; if ( 0 != found ) { // Convert "found" pointer to array index. #if defined(ON_COMPILER_MSC1300) rc = ((int)(found - m_a)); #elif 8 == ON_SIZEOF_POINTER // In an ideal world, return ((int)(found - m_a)) would work everywhere. // In practice, this should work any 64 bit compiler and we can hope // the optimzer generates efficient code. const ON__UINT64 fptr = (ON__UINT64)found; const ON__UINT64 aptr = (ON__UINT64)m_a; const ON__UINT64 sz = (ON__UINT64)sizeof(T); const ON__UINT64 i = (fptr - aptr)/sz; rc = (int)i; #else // In an ideal world, return ((int)(found - m_a)) would work everywhere. // In practice, this should work any 32 bit compiler and we can hope // the optimzer generates efficient code. const ON__UINT32 fptr = (ON__UINT32)found; const ON__UINT32 aptr = (ON__UINT32)m_a; const ON__UINT32 sz = (ON__UINT32)sizeof(T); const ON__UINT32 i = (fptr - aptr)/sz; rc = (int)i; #endif } else { // "key" not found rc = -1; } return rc; } template int ON_SimpleArray::BinarySearch( const T* key, int (*compar)(const T*,const T*), int count ) const { if ( count > m_count ) count = m_count; if ( count <= 0 ) return -1; const T* found = (key&&m_a&&m_count>0) ? (const T*)bsearch( key, m_a, count, sizeof(T), (int(*)(const void*,const void*))compar ) : 0; // This worked on a wide range of 32 bit compilers. int rc; if ( 0 != found ) { // Convert "found" pointer to array index. #if defined(ON_COMPILER_MSC1300) rc = ((int)(found - m_a)); #elif 8 == ON_SIZEOF_POINTER // In an ideal world, return ((int)(found - m_a)) would work everywhere. // In practice, this should work any 64 bit compiler and we can hope // the optimzer generates efficient code. const ON__UINT64 fptr = (ON__UINT64)found; const ON__UINT64 aptr = (ON__UINT64)m_a; const ON__UINT64 sz = (ON__UINT64)sizeof(T); const ON__UINT64 i = (fptr - aptr)/sz; rc = (int)i; #else // In an ideal world, return ((int)(found - m_a)) would work everywhere. // In practice, this should work any 32 bit compiler and we can hope // the optimzer generates efficient code. const ON__UINT32 fptr = (ON__UINT32)found; const ON__UINT32 aptr = (ON__UINT32)m_a; const ON__UINT32 sz = (ON__UINT32)sizeof(T); const ON__UINT32 i = (fptr - aptr)/sz; rc = (int)i; #endif } else { // "key" not found rc = -1; } return rc; } template int ON_SimpleArray::InsertInSortedList(const T& e, int (*compar)(const T*, const T*)) { const int count = m_count; if (count < 0) return -1; if (0 == count) { Insert(0, e); return 0; } const unsigned ucount = ((unsigned)count); unsigned i0 = 0; unsigned i1 = ucount; while (i0 < i1) { const unsigned i = (i0 + i1) / 2; const int c = compar(&e, m_a + i); if (c < 0) { i1 = i; } else if (c > 0) { i0 = i + 1; } else { i1 = i; while (i1 + 1 < ucount && 0 == compar(&e, m_a + (i1 + 1))) ++i1; i0 = i1; } } if (i0 <= ucount) { Insert(i0, e); return ((int)i0); } return -1; } template int ON_SimpleArray::InsertInSortedList(const T& e, int (*compar)(const T*, const T*), int count) { if (count > m_count) count = m_count; if (count < 0) return -1; if (0 == count) { Insert(0, e); return 0; } const unsigned ucount = ((unsigned)count); unsigned i0 = 0; unsigned i1 = ucount; while (i0 0) { i0 = i + 1; } else { i1 = i; while (i1 + 1 < ucount && 0 == compar(&e, m_a + (i1 + 1))) ++i1; i0 = i1; } } if (i0 <= ucount) { Insert(i0, e); return ((int)i0); } return -1; } template bool ON_SimpleArray::HeapSort( int (*compar)(const T*,const T*) ) { bool rc = false; if ( m_a && m_count > 0 && compar ) { if ( m_count > 1 ) ON_hsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); rc = true; } return rc; } template bool ON_SimpleArray::QuickSort( int (*compar)(const T*,const T*) ) { bool rc = false; if ( m_a && m_count > 0 && compar ) { if ( m_count > 1 ) ON_qsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); rc = true; } return rc; } template bool ON_SimpleArray::QuickSortAndRemoveDuplicates( int (*compar)(const T*,const T*) ) { bool rc = false; if ( m_a && m_count > 0 && compar ) { if (m_count > 1) { ON_qsort(m_a, m_count, sizeof(T), (int(*)(const void*, const void*))compar); const T* prev_ele = &m_a[0]; int clean_count = 1; for (int i = 1; i < m_count; ++i) { if (0 == compar(prev_ele, &m_a[i])) continue; // duplicate if (i > clean_count) m_a[clean_count] = m_a[i]; prev_ele = &m_a[clean_count]; ++clean_count; } if (clean_count < m_count) { memset( (void*)(&m_a[clean_count]), 0, (m_count-clean_count)*sizeof(T) ); SetCount(clean_count); } } rc = true; } return rc; } template bool ON_SimpleArray::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*) ) const { bool rc = false; if ( m_a && m_count > 0 && compar && index ) { if ( m_count > 1 ) ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); else if ( m_count == 1 ) index[0] = 0; rc = true; } return rc; } template bool ON_SimpleArray::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*,void*),void* p ) const { bool rc = false; if ( m_a && m_count > 0 && compar && index ) { if ( m_count > 1 ) ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*,void*))compar, p ); else if ( m_count == 1 ) index[0] = 0; rc = true; } return rc; } template bool ON_SimpleArray::Permute( const int* index ) { bool rc = false; if ( m_a && m_count > 0 && index ) { int i; T* buffer = (T*)onmalloc(m_count*sizeof(buffer[0])); memcpy( (void*)(buffer), (void*)(m_a), m_count*sizeof(T) ); for (i = 0; i < m_count; i++ ) memcpy( (void*)(m_a+i), (void*)(buffer+index[i]), sizeof(T) ); // must use memcopy and not operator= onfree(buffer); rc = true; } return rc; } template void ON_SimpleArray::Zero() { if ( m_a && m_capacity > 0 ) { memset( (void*)(m_a), 0, m_capacity*sizeof(T) ); } } template void ON_SimpleArray::MemSet( unsigned char value ) { if ( m_a && m_capacity > 0 ) { memset( (void*)(m_a), value, m_capacity*sizeof(T) ); } } // memory managment //////////////////////////////////////////////////// template T* ON_SimpleArray::Reserve( size_t newcap ) { if( (size_t)m_capacity < newcap ) SetCapacity( newcap ); return m_a; } template void ON_SimpleArray::Shrink() { SetCapacity( m_count ); } template void ON_SimpleArray::Destroy() { SetCapacity( 0 ); } // low level memory managment ////////////////////////////////////////// template void ON_SimpleArray::SetCount( int count ) { if ( count >= 0 && count <= m_capacity ) m_count = count; } template T* ON_SimpleArray::SetCapacity( size_t new_capacity ) { if (0 == m_capacity) { // Allow "expert" users of ON_SimpleArray<>.SetArray(*,*,0) to clean up after themselves // and deals with the case when the forget to clean up after themselves. m_a = nullptr; m_count = 0; } // sets capacity to input value int capacity = (new_capacity > 0 && new_capacity < ON_UNSET_UINT_INDEX) ? (int)new_capacity : 0; if ( capacity != m_capacity ) { if( capacity > 0 ) { if ( m_count > capacity ) m_count = capacity; // NOTE: Realloc() does an allocation if the first argument is nullptr. m_a = Realloc( m_a, capacity ); if ( m_a ) { if ( capacity > m_capacity ) { // zero new memory memset( (void*) (m_a + m_capacity), 0, (capacity-m_capacity)*sizeof(T) ); } m_capacity = capacity; } else { // out of memory m_count = m_capacity = 0; } } else if (m_a) { Realloc(m_a,0); m_a = 0; m_count = m_capacity = 0; } } return m_a; } template int ON_SimpleArray::NewCapacity() const { // Note: // This code appears in ON_SimpleArray::NewCapacity() // and ON_ClassArray::NewCapacity(). Changes made to // either function should be made to both functions. // Because this code is template code that has to // support dynamic linking and the code is defined // in a header, I'm using copy-and-paste rather // than a static. // This function returns 2*m_count unless that will // result in an additional allocation of more than // cap_size bytes. The cap_size concept was added in // January 2010 because some calculations on enormous // models were slightly underestimating the initial // Reserve() size and then wasting gigabytes of memory. // cap_size = 128 MB on 32-bit os, 256 MB on 64 bit os const size_t cap_size = 32*sizeof(void*)*1024*1024; if (m_count*sizeof(T) <= cap_size || m_count < 8) return ((m_count <= 2) ? 4 : 2*m_count); // Growing the array will increase the memory // use by more than cap_size. int delta_count = 8 + cap_size/sizeof(T); if ( delta_count > m_count ) delta_count = m_count; return (m_count + delta_count); } template int ON_ClassArray::NewCapacity() const { // Note: // This code appears in ON_SimpleArray::NewCapacity() // and ON_ClassArray::NewCapacity(). Changes made to // either function should be made to both functions. // Because this code is template code that has to // support dynamic linking and the code is defined // in a header, I'm using copy-and-paste rather // than a static. // This function returns 2*m_count unless that will // result in an additional allocation of more than // cap_size bytes. The cap_size concept was added in // January 2010 because some calculations on enormous // models were slightly underestimating the initial // Reserve() size and then wasting gigabytes of memory. // cap_size = 128 MB on 32-bit os, 256 MB on 64 bit os const size_t cap_size = 32*sizeof(void*)*1024*1024; if (m_count*sizeof(T) <= cap_size || m_count < 8) return ((m_count <= 2) ? 4 : 2*m_count); // Growing the array will increase the memory // use by more than cap_size. int delta_count = 8 + cap_size/sizeof(T); if ( delta_count > m_count ) delta_count = m_count; return (m_count + delta_count); } ///////////////////////////////////////////////////////////////////////////////////// // Class ON_ObjectArray<> ///////////////////////////////////////////////////////////////////////////////////// template ON_ObjectArray::ON_ObjectArray() { } template ON_ObjectArray::~ON_ObjectArray() { } template ON_ObjectArray::ON_ObjectArray( const ON_ObjectArray& src ) : ON_ClassArray(src) { } template ON_ObjectArray& ON_ObjectArray::operator=( const ON_ObjectArray& src) { if( this != &src) { ON_ClassArray::operator =(src); } return *this; } #if defined(ON_HAS_RVALUEREF) // Clone constructor template ON_ObjectArray::ON_ObjectArray( ON_ObjectArray&& src ) : ON_ClassArray(std::move(src)) {} // Clone assignment template ON_ObjectArray& ON_ObjectArray::operator=( ON_ObjectArray&& src ) { if( this != &src ) { ON_ClassArray::operator=(std::move(src)); } return *this; } #endif template ON_ObjectArray::ON_ObjectArray( size_t c ) : ON_ClassArray(c) { } template T* ON_ObjectArray::Realloc(T* ptr,int capacity) { T* reptr = (T*)onrealloc(ptr,capacity*sizeof(T)); if ( ptr && reptr && reptr != ptr ) { // The "this->" in this->m_count and this->m_a // are needed for gcc 4 to compile. int i; for ( i = 0; i < this->m_count; i++ ) { reptr[i].MemoryRelocate(); } } return reptr; } ///////////////////////////////////////////////////////////////////////////////////// // Class ON_ClassArray<> ///////////////////////////////////////////////////////////////////////////////////// // construction //////////////////////////////////////////////////////// template T* ON_ClassArray::Realloc(T* ptr,int capacity) { return (T*)onrealloc(ptr,capacity*sizeof(T)); } template ON__UINT32 ON_ObjectArray::DataCRC(ON__UINT32 current_remainder) const { // The "this->" in this->m_count and this->m_a // are needed for gcc 4 to compile. int i; for ( i = 0; i < this->m_count; i++ ) { current_remainder = this->m_a[i].DataCRC(current_remainder); } return current_remainder; } template ON_ClassArray::ON_ClassArray() ON_NOEXCEPT : m_a(nullptr) , m_count(0) , m_capacity(0) {} template ON_ClassArray::ON_ClassArray( size_t c ) : m_a(nullptr) , m_count(0) , m_capacity(0) { if ( c > 0 ) SetCapacity( c ); } // Copy constructor template ON_ClassArray::ON_ClassArray( const ON_ClassArray& src ) : m_a(nullptr) , m_count(0) , m_capacity(0) { *this = src; // operator= defined below } template ON_ClassArray::~ON_ClassArray() { SetCapacity(0); } template ON_ClassArray& ON_ClassArray::operator=( const ON_ClassArray& src ) { int i; if( this != &src ) { if ( src.m_count <= 0 ) { m_count = 0; } else { if ( m_capacity < src.m_count ) { SetCapacity( src.m_count ); } if ( m_a ) { m_count = src.m_count; for ( i = 0; i < m_count; i++ ) { m_a[i] = src.m_a[i]; } } } } return *this; } #if defined(ON_HAS_RVALUEREF) // Clone constructor template ON_ClassArray::ON_ClassArray( ON_ClassArray&& src ) ON_NOEXCEPT : m_a(src.m_a) , m_count(src.m_count) , m_capacity(src.m_capacity) { src.m_a = 0; src.m_count = 0; src.m_capacity = 0; } // Clone assignment template ON_ClassArray& ON_ClassArray::operator=( ON_ClassArray&& src ) ON_NOEXCEPT { if( this != &src ) { // TODO - investigate why we should use std::move(src) // instead of the code below //ON_ClassArray::operator=(std::move(src)); // Then investigate why the change was requested only for class array. // What about the other dynamic array classes? this->Destroy(); m_a = src.m_a; m_count = src.m_count; m_capacity = src.m_capacity; src.m_a = 0; src.m_count = 0; src.m_capacity = 0; } return *this; } #endif // emergency destroy /////////////////////////////////////////////////// template void ON_ClassArray::EmergencyDestroy(void) { m_count = 0; m_capacity = 0; m_a = 0; } // query /////////////////////////////////////////////////////////////// template int ON_ClassArray::Count() const { return m_count; } template unsigned int ON_ClassArray::UnsignedCount() const { return ((unsigned int)m_count); } template int ON_ClassArray::Capacity() const { return m_capacity; } template unsigned int ON_ClassArray::SizeOfArray() const { return ((unsigned int)(m_capacity*sizeof(T))); } template unsigned int ON_ClassArray::SizeOfElement() const { return ((unsigned int)(sizeof(T))); } template T& ON_ClassArray::operator[]( int i ) { #if defined(ON_DEBUG) if ( i < 0 || i > m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } template T& ON_ClassArray::operator[]( ON__INT64 i ) { #if defined(ON_DEBUG) if ( i < 0 || i > (ON__INT64)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } template T& ON_ClassArray::operator[]( unsigned int i ) { #if defined(ON_DEBUG) if ( i > (unsigned int)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } template T& ON_ClassArray::operator[]( ON__UINT64 i ) { #if defined(ON_DEBUG) if ( i > (ON__UINT64)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } #if defined(ON_RUNTIME_APPLE) template T& ON_ClassArray::operator[](size_t i ) { #if defined(ON_DEBUG) if ( i > (size_t)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } #endif template const T& ON_ClassArray::operator[](int i) const { #if defined(ON_DEBUG) if ( i < 0 || i > m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } template const T& ON_ClassArray::operator[](ON__INT64 i) const { #if defined(ON_DEBUG) if ( i < 0 || i > (ON__INT64)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } template const T& ON_ClassArray::operator[](unsigned int i) const { #if defined(ON_DEBUG) if ( i > (unsigned int)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } template const T& ON_ClassArray::operator[](ON__UINT64 i) const { #if defined(ON_DEBUG) if ( i > (ON__UINT64)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } #if defined(ON_RUNTIME_APPLE) template const T& ON_ClassArray::operator[](size_t i) const { #if defined(ON_DEBUG) if ( i > (size_t)m_capacity ) { ON_ERROR("ON_ClassArray[i]: i out of range."); } #endif return m_a[i]; } #endif template ON_ClassArray::operator T*() { return (m_count > 0) ? m_a : 0; } template ON_ClassArray::operator const T*() const { return (m_count > 0) ? m_a : 0; } template T* ON_ClassArray::Array() { return m_a; } template const T* ON_ClassArray::Array() const { return m_a; } template T* ON_ClassArray::KeepArray() { T* p = m_a; m_a = 0; m_count = 0; m_capacity = 0; return p; } template void ON_ClassArray::SetArray(T* p) { if ( m_a && m_a != p ) Destroy(); m_a = p; } template void ON_ClassArray::SetArray(T* p, int count, int capacity) { if ( m_a && m_a != p ) Destroy(); m_a = p; m_count = count; m_capacity = capacity; } template T* ON_ClassArray::First() { return (m_count > 0) ? m_a : 0; } template const T* ON_ClassArray::First() const { return (m_count > 0) ? m_a : 0; } template T* ON_ClassArray::At( int i ) { return (i >= 0 && i < m_count) ? m_a+i : 0; } template T* ON_ClassArray::At( unsigned int i ) { return (i < (unsigned int)m_count) ? m_a+i : 0; } template const T* ON_ClassArray::At( int i) const { return (i >= 0 && i < m_count) ? m_a+i : 0; } template const T* ON_ClassArray::At( unsigned int i) const { return (i < (unsigned int)m_count) ? m_a+i : 0; } template T* ON_ClassArray::At( ON__INT64 i ) { return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; } template T* ON_ClassArray::At( ON__UINT64 i ) { return (i < (ON__UINT64)m_count) ? m_a+i : 0; } template const T* ON_ClassArray::At( ON__INT64 i) const { return (i >= 0 && i < (ON__INT64)m_count) ? m_a+i : 0; } template const T* ON_ClassArray::At( ON__UINT64 i) const { return (i < (ON__UINT64)m_count) ? m_a+i : 0; } template T* ON_ClassArray::Last() { return (m_count > 0) ? m_a+(m_count-1) : 0; } template const T* ON_ClassArray::Last() const { return (m_count > 0) ? m_a+(m_count-1) : 0; } // array operations //////////////////////////////////////////////////// template void ON_ClassArray::Move( int dest_i, int src_i, int ele_cnt ) { // private function for moving blocks of array memory // caller is responsible for updating m_count and managing // destruction/creation. if ( ele_cnt <= 0 || src_i < 0 || dest_i < 0 || src_i == dest_i || src_i + ele_cnt > m_count || dest_i > m_count ) return; int capacity = dest_i + ele_cnt; if ( capacity > m_capacity ) { if ( capacity < 2*m_capacity ) capacity = 2*m_capacity; SetCapacity( capacity ); } // This call to memmove is ok, even when T is a class with a vtable // because the it doesn't change the vtable for the class. // Classes that have back pointers, like ON_UserData, are // handled elsewhere and cannot be in ON_ClassArray<>s. memmove( (void*)(&m_a[dest_i]), (const void*)(&m_a[src_i]), ele_cnt*sizeof(T) ); } template void ON_ClassArray::ConstructDefaultElement(T* p) { // use placement ( new(size_t,void*) ) to construct // T in supplied memory new(p) T; } template void ON_ClassArray::DestroyElement(T& x) { x.~T(); } template T& ON_ClassArray::AppendNew() { if ( m_count == m_capacity ) { int newcapacity = NewCapacity(); Reserve( newcapacity ); } else { // First destroy what's there .. DestroyElement(m_a[m_count]); // and then get a properly initialized element ConstructDefaultElement(&m_a[m_count]); } return m_a[m_count++]; } template void ON_ClassArray::Append( const T& x ) { if ( m_count == m_capacity ) { const int newcapacity = NewCapacity(); if (m_a) { const int s = (int)(&x - m_a); // (int) cast is for 64 bit pointers if ( s >= 0 && s < m_capacity ) { // 26 Sep 2005 Dale Lear // User passed in an element of the m_a[] // that will get reallocated by the call // to Reserve(newcapacity). T temp; // ON_*Array<> templates do not require robust copy constructor. temp = x; // ON_*Array<> templates require a robust operator=. Reserve( newcapacity ); if (nullptr == m_a) { ON_ERROR("allocation failure"); return; } m_a[m_count++] = temp; return; } } Reserve(newcapacity); if (nullptr == m_a) { ON_ERROR("allocation failure"); return; } } m_a[m_count++] = x; } template void ON_ClassArray::Append( int count, const T* p ) { int i; if ( count > 0 && p ) { if ( count + m_count > m_capacity ) { int newcapacity = NewCapacity(); if ( newcapacity < count + m_count ) newcapacity = count + m_count; Reserve( newcapacity ); } for ( i = 0; i < count; i++ ) { m_a[m_count++] = p[i]; } } } // Insert called with a reference uses operator = template void ON_ClassArray::Insert( int i, const T& x ) { if( i >= 0 && i <= m_count ) { if ( m_count == m_capacity ) { int newcapacity = NewCapacity(); Reserve( newcapacity ); } DestroyElement( m_a[m_count] ); m_count++; if ( i < m_count-1 ) { Move( i+1, i, m_count-1-i ); // This call to memset is ok even when T has a vtable // because in-place construction is used later. memset( (void*)(&m_a[i]), 0, sizeof(T) ); ConstructDefaultElement( &m_a[i] ); } else { ConstructDefaultElement( &m_a[m_count-1] ); } m_a[i] = x; // uses T::operator=() to copy x to array } } template void ON_ClassArray::Remove( ) { Remove(m_count-1); } template void ON_ClassArray::Remove( int i ) { if ( i >= 0 && i < m_count ) { DestroyElement( m_a[i] ); // This call to memset is ok even when T has a vtable // because in-place construction is used later. memset( (void*)(&m_a[i]), 0, sizeof(T) ); Move( i, i+1, m_count-1-i ); // This call to memset is ok even when T has a vtable // because in-place construction is used later. memset( (void*)(&m_a[m_count-1]), 0, sizeof(T) ); ConstructDefaultElement(&m_a[m_count-1]); m_count--; } } template void ON_ClassArray::Empty() { int i; for ( i = m_count-1; i >= 0; i-- ) { DestroyElement( m_a[i] ); // This call to memset is ok even when T has a vtable // because in-place construction is used later. memset( (void*)(&m_a[i]), 0, sizeof(T) ); ConstructDefaultElement( &m_a[i] ); } m_count = 0; } template void ON_ClassArray::Reverse() { // NOTE: // If anything in "T" depends on the value of this's address, // then don't call Reverse(). char t[sizeof(T)]; int i = 0; int j = m_count-1; for ( /*empty*/; i < j; i++, j-- ) { memcpy( (void*)(t), (void*)(&m_a[i]), sizeof(T) ); memcpy( (void*)(&m_a[i]), (void*)(&m_a[j]), sizeof(T) ); memcpy( (void*)(&m_a[j]), (void*)(t), sizeof(T) ); } } template void ON_ClassArray::Swap( int i, int j ) { if ( i != j && i >= 0 && j >= 0 && i < m_count && j < m_count ) { char t[sizeof(T)]; memcpy( (void*)(t), (void*)(&m_a[i]), sizeof(T) ); memcpy( (void*)(&m_a[i]), (void*)(&m_a[j]), sizeof(T) ); memcpy( (void*)(&m_a[j]), (void*)(t), sizeof(T) ); } } template int ON_ClassArray::Search( const T* key, int (*compar)(const T*,const T*) ) const { for ( int i = 0; i < m_count; i++ ) { if (!compar(key,m_a+i)) return i; } return -1; } template int ON_ClassArray::BinarySearch( const T* key, int (*compar)(const T*,const T*) ) const { const T* found = (key&&m_a&&m_count>0) ? (const T*)bsearch( key, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ) : nullptr; return (nullptr != found && found >= m_a) ? ((int)(found - m_a)) : -1; } template int ON_ClassArray::BinarySearch( const T* key, int (*compar)(const T*,const T*), int count ) const { if ( count > m_count ) count = m_count; if ( count <= 0 ) return -1; const T* found = (key&&m_a&&m_count>0) ? (const T*)bsearch( key, m_a, count, sizeof(T), (int(*)(const void*,const void*))compar ) : nullptr; return (nullptr != found && found >= m_a) ? ((int)(found - m_a)) : -1; } template int ON_ClassArray::InsertInSortedList(const T& e, int (*compar)(const T*, const T*)) { const int count = m_count; if (count < 0) return -1; if (0 == count) { Insert(0, e); return 0; } const unsigned ucount = ((unsigned)count); unsigned i0 = 0; unsigned i1 = ucount; while (i0 < i1) { const unsigned i = (i0 + i1) / 2; const int c = compar(&e, m_a + i); if (c < 0) { i1 = i; } else if (c > 0) { i0 = i + 1; } else { i1 = i; while (i1 + 1 < ucount && 0 == compar(&e, m_a + (i1 + 1))) ++i1; i0 = i1; } } if (i0 <= ucount) { Insert(i0, e); return ((int)i0); } return -1; } template int ON_ClassArray::InsertInSortedList(const T& e, int (*compar)(const T*, const T*), int count) { if (count > m_count) count = m_count; if (count < 0) return -1; if (0 == count) { Insert(0, e); return 0; } const unsigned ucount = ((unsigned)count); unsigned i0 = 0; unsigned i1 = ucount; while (i0 < i1) { const unsigned i = (i0 + i1) / 2; const int c = compar(&e, m_a + i); if (c < 0) { i1 = i; } else if (c > 0) { i0 = i + 1; } else { i1 = i; while (i1 + 1 < ucount && 0 == compar(&e, m_a + (i1 + 1))) ++i1; i0 = i1; } } if (i0 <= ucount) { Insert(i0, e); return ((int)i0); } return -1; } template bool ON_ClassArray::HeapSort( int (*compar)(const T*,const T*) ) { bool rc = false; if ( m_a && m_count > 0 && compar ) { if ( m_count > 1 ) ON_hsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); rc = true; } return rc; } template bool ON_ClassArray::QuickSort( int (*compar)(const T*,const T*) ) { bool rc = false; if ( m_a && m_count > 0 && compar ) { if ( m_count > 1 ) ON_qsort( m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); rc = true; } return rc; } template bool ON_ObjectArray::HeapSort( int (*compar)(const T*,const T*) ) { bool rc = false; // The "this->" in this->m_count and this->m_a // are needed for gcc 4 to compile. if ( this->m_a && this->m_count > 0 && compar ) { if ( this->m_count > 1 ) { ON_hsort( this->m_a, this->m_count, sizeof(T), (int(*)(const void*,const void*))compar ); // The MemoryRelocate step is required to synch userdata back pointers // so the user data destructor will work correctly. int i; for ( i = 0; i < this->m_count; i++ ) { this->m_a[i].MemoryRelocate(); } } rc = true; } return rc; } template bool ON_ObjectArray::QuickSort( int (*compar)(const T*,const T*) ) { bool rc = false; // The "this->" in this->m_count and this->m_a // are needed for gcc 4 to compile. if ( this->m_a && this->m_count > 0 && compar ) { if ( this->m_count > 1 ) { ON_qsort( this->m_a, this->m_count, sizeof(T), (int(*)(const void*,const void*))compar ); // The MemoryRelocate step is required to synch userdata back pointers // so the user data destructor will work correctly. int i; for ( i = 0; i < this->m_count; i++ ) { this->m_a[i].MemoryRelocate(); } } rc = true; } return rc; } template bool ON_ClassArray::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*) ) const { bool rc = false; if ( m_a && m_count > 0 && compar && index ) { if ( m_count > 1 ) ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*))compar ); else if ( m_count == 1 ) index[0] = 0; rc = true; } return rc; } template bool ON_ClassArray::Sort( ON::sort_algorithm sa, int* index, int (*compar)(const T*,const T*,void*),void* p ) const { bool rc = false; if ( m_a && m_count > 0 && compar && index ) { if ( m_count > 1 ) ON_Sort(sa, index, m_a, m_count, sizeof(T), (int(*)(const void*,const void*,void*))compar, p ); else if ( m_count == 1 ) index[0] = 0; rc = true; } return rc; } template bool ON_ClassArray::Permute( const int* index ) { bool rc = false; if ( m_a && m_count > 0 && index ) { int i; T* buffer = (T*)onmalloc(m_count*sizeof(buffer[0])); memcpy( (void*)(buffer), (void*)(m_a), m_count*sizeof(T) ); for (i = 0; i < m_count; i++ ) memcpy( (void*)(m_a+i), (void*)(buffer+index[i]), sizeof(T) ); // must use memcopy and not operator= onfree(buffer); rc = true; } return rc; } template void ON_ClassArray::Zero() { int i; if ( m_a && m_capacity > 0 ) { for ( i = m_capacity-1; i >= 0; i-- ) { DestroyElement(m_a[i]); // This call to memset is ok even when T has a vtable // because in-place construction is used later. memset( (void*)(&m_a[i]), 0, sizeof(T) ); ConstructDefaultElement(&m_a[i]); } } } // memory managment //////////////////////////////////////////////////// template T* ON_ClassArray::Reserve( size_t newcap ) { if( (size_t)m_capacity < newcap ) SetCapacity( newcap ); return m_a; } template void ON_ClassArray::Shrink() { SetCapacity( m_count ); } template void ON_ClassArray::Destroy() { SetCapacity( 0 ); } // low level memory managment ////////////////////////////////////////// template void ON_ClassArray::SetCount( int count ) { if ( count >= 0 && count <= m_capacity ) m_count = count; } template T* ON_ClassArray::SetCapacity( size_t new_capacity ) { if (0 == m_capacity) { // Allow "expert" users of ON_SimpleArray<>.SetArray(*,*,0) to clean up after themselves // and deals with the case when the forget to clean up after themselves. m_a = nullptr; m_count = 0; } // uses "placement" for class construction/destruction int i; int capacity = (new_capacity > 0 && new_capacity < ON_UNSET_UINT_INDEX) ? (int)new_capacity : 0; if ( capacity <= 0 ) { if ( m_a ) { for ( i = m_capacity-1; i >= 0; i-- ) { DestroyElement(m_a[i]); } Realloc(m_a,0); m_a = 0; } m_count = 0; m_capacity = 0; } else if ( m_capacity < capacity ) { // growing m_a = Realloc( m_a, capacity ); // initialize new elements with default constructor if ( 0 != m_a ) { // even when m_a is an array of classes with vtable pointers, // this call to memset(..., 0, ...) is what I want to do // because in-place construction will be used when needed // on this memory. memset( (void*)(m_a + m_capacity), 0, (capacity-m_capacity)*sizeof(T) ); for ( i = m_capacity; i < capacity; i++ ) { ConstructDefaultElement(&m_a[i]); } m_capacity = capacity; } else { // memory allocation failed m_capacity = 0; m_count = 0; } } else if ( m_capacity > capacity ) { // shrinking for ( i = m_capacity-1; i >= capacity; i-- ) { DestroyElement(m_a[i]); } if ( m_count > capacity ) m_count = capacity; m_capacity = capacity; m_a = Realloc( m_a, capacity ); if ( 0 == m_a ) { // memory allocation failed m_capacity = 0; m_count = 0; } } return m_a; } ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// template< class T> int ON_CompareIncreasing( const T* a, const T* b) { if( *a < *b ) return -1; if( *b < *a ) return 1; return 0; } template< class T> int ON_CompareDecreasing( const T* a, const T* b) { if( *b < *a ) return -1; if( *a < *b ) return 1; return 0; } #pragma ON_PRAGMA_WARNING_POP #endif