Simbody
3.4 (development)
|
00001 #ifndef SimTK_SimTKCOMMON_STABLEARRAY_H_ 00002 #define SimTK_SimTKCOMMON_STABLEARRAY_H_ 00003 00004 /* -------------------------------------------------------------------------- * 00005 * Simbody(tm): SimTKcommon * 00006 * -------------------------------------------------------------------------- * 00007 * This is part of the SimTK biosimulation toolkit originating from * 00008 * Simbios, the NIH National Center for Physics-Based Simulation of * 00009 * Biological Structures at Stanford, funded under the NIH Roadmap for * 00010 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. * 00011 * * 00012 * Portions copyright (c) 2005-12 Stanford University and the Authors. * 00013 * Authors: Michael Sherman * 00014 * Contributors: * 00015 * * 00016 * Licensed under the Apache License, Version 2.0 (the "License"); you may * 00017 * not use this file except in compliance with the License. You may obtain a * 00018 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * 00019 * * 00020 * Unless required by applicable law or agreed to in writing, software * 00021 * distributed under the License is distributed on an "AS IS" BASIS, * 00022 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 00023 * See the License for the specific language governing permissions and * 00024 * limitations under the License. * 00025 * -------------------------------------------------------------------------- */ 00026 00027 #include "SimTKcommon/internal/common.h" 00028 #include "SimTKcommon/internal/Array.h" 00029 00030 #include <cstddef> 00031 #include <cassert> 00032 00033 namespace SimTK { 00034 00054 template <class T> class StableArray { 00055 public: 00056 StableArray() : nOccupiedSlots(0) { } 00057 00058 // Allocate and fill every slot with the same value. 00059 explicit StableArray(size_t z, const T& ival=T()) : stuff(z), nOccupiedSlots(z) { 00060 for (size_t i=0; i<z; ++i) stuff[i] = new T(ival); 00061 } 00062 00063 // Copy constructor must preserve slot numbers. 00064 StableArray(const StableArray& s) : stuff(s.size(),0), nOccupiedSlots(0) { 00065 for (size_t i=0; i<s.size(); ++i) 00066 if (!s.empty(i)) initializeEmptyElement(i, s[i]); 00067 assert(nItems() == s.nItems()); 00068 } 00069 00070 // Assignment must preserve slot numbers. 00071 StableArray& operator=(const StableArray& s) { 00072 clear(); 00073 stuff.resize(s.size(),0); 00074 for (size_t i=0; i<s.size(); ++i) 00075 if (!s.empty(i)) initializeEmptyElement(i, s[i]); 00076 assert(nItems() == s.nItems()); 00077 return *this; 00078 } 00079 00080 ~StableArray() { clear(); } 00081 00082 bool empty() const { return stuff.size()==0; } 00083 bool empty(size_t i) const { 00084 assert(i < stuff.size()); 00085 return stuff[i] == 0; 00086 } 00087 size_t size() const {return stuff.size();} 00088 size_t nItems() const {return nOccupiedSlots;} 00089 00090 // This routine preserves as many items as possible and fills 00091 // in all empty slots with default values. The array will 00092 // thus have exactly newz slots with nItems==newz. 00093 // I'm not sure this is useful for anything. 00094 void resize(size_t newz, const T& ival=T()) { 00095 const size_t oldz = stuff.size(); 00096 // If we're throwing anything away, destruct it. 00097 for (size_t i=newz; i < oldz; ++i) 00098 eraseElementIfNecessary(i); 00099 stuff.resize(newz,0); 00100 // Fill in all empty slots with default values. 00101 for (size_t i=0; i < newz; ++i) 00102 initializeElementIfNecessary(i,ival); 00103 assert(nItems() == newz); 00104 } 00105 00106 void clear() { 00107 for (size_t i=0; i < stuff.size(); ++i) 00108 eraseElementIfNecessary(i); 00109 stuff.resize(0); 00110 assert(nItems()==0); 00111 } 00112 00113 // You can push a new item onto the end, or put one in an 00114 // empty slot. 00115 void push_back(const T& t) { 00116 stuff.push_back(new T(t)); 00117 ++nOccupiedSlots; 00118 } 00119 00120 // Remove the last element and shrink the list by 1. If the second-to-the-last 00121 // was empty we'll reduce the length more, so that pop_back() guarantees either 00122 // that the the last element (back()) is not empty, or the entire list is empty. 00123 // Don't call this on an empty array. 00124 void pop_back() { 00125 assert(!empty() && stuff.back()); 00126 eraseOccupiedElement(stuff.size()-1); // last element *better* be occupied! 00127 00128 // Skip over any exposed empties. No need to adjust count. 00129 do { stuff.pop_back(); } while (!stuff.empty() && !stuff.back()); 00130 } 00131 00132 void insertAt(size_t i, const T& t) { 00133 assert(i <= stuff.size()); 00134 if (i == size()) push_back(t); 00135 else initializeEmptyElement(i,t); 00136 } 00137 00138 size_t findFreeSlot() const { 00139 if (nItems() == size()) 00140 return size(); // no room at the inn! 00141 for (size_t i=0; i<size(); ++i) 00142 if (empty(i)) return i; 00143 assert(false); // where's the empty slot??? 00144 return size_t(-1); 00145 } 00146 00147 // Look for the first occupied slot at or after i. Returns 00148 // size() (that is, one past the end) if none found. Use like this: 00149 // for (size_t i=findNextItem(0); i < size(); i=findNextItem(i+1)) 00150 // do something with item[i] here 00151 size_t findNextItem(size_t i) { 00152 assert(i < stuff.size()); 00153 for (; i < stuff.size() && !stuff[i]; ++i); 00154 return i; 00155 } 00156 00157 // Insert the item in the first available slot, or grow the array 00158 // by one and stick it on the end if there are no free slots. The 00159 // slot in which it was placed is returned. 00160 size_t insert(const T& t) { 00161 const size_t free = findFreeSlot(); 00162 insertAt(free, t); 00163 return free; 00164 } 00165 00166 00167 // Erasing an element slot if it isn't already empty. If we erase the 00168 // last element we don't have to leave a hole, and in fact we might 00169 // expose a hole in the second-to-the-last element too. In that 00170 // case we erase by resizing away trailing detritus, otherwise we'll 00171 // make a hole. 00172 void erase(size_t i) { 00173 if (i == stuff.size()-1) pop_back(); 00174 else eraseElementIfNecessary(i); 00175 } 00176 00177 // This returns the first *occupied* element and blows up if there isn't one. 00178 const T& front() const { 00179 const size_t firstItem = findNextItem(0); 00180 assert(firstItem < stuff.size()); 00181 return *stuff[firstItem]; 00182 } 00183 T& front() { 00184 const size_t firstItem = findNextItem(0); 00185 assert(firstItem < stuff.size()); 00186 return *stuff[firstItem]; 00187 } 00188 00189 // We don't need to search for back() because the rules here ensure that the 00190 // last element is not empty. 00191 const T& back() const {assert(!empty() && stuff.back()); return *stuff.back();} 00192 T& back() {assert(!empty() && stuff.back()); return *stuff.back();} 00193 00194 const T& operator[](size_t i) const { 00195 assert(i < stuff.size() && stuff[i]); 00196 return *stuff[i]; 00197 } 00198 T& operator[](size_t i) { 00199 assert(i < stuff.size() && stuff[i]); 00200 return *stuff[i]; 00201 } 00202 00203 private: 00204 size_t nOccupiedSlots; // not counting empty slots 00205 Array_<T*,size_t> stuff; 00206 00207 // Note that this can leave empty slots at the end of the list which 00208 // is not a legitimate condition for the StableArray. 00209 00210 void eraseOccupiedElement(size_t i) { 00211 assert(i < stuff.size() && stuff[i]); 00212 delete stuff[i]; stuff[i]=0; --nOccupiedSlots; 00213 } 00214 00215 void initializeEmptyElement(size_t i, const T& t) { 00216 assert(i < stuff.size() && !stuff[i]); 00217 stuff[i] = new T(t); ++nOccupiedSlots; 00218 } 00219 00220 void eraseElementIfNecessary(size_t i) { 00221 assert(i < stuff.size()); 00222 if (stuff[i]) eraseOccupiedElement(i); 00223 } 00224 00225 void initializeElementIfNecessary(size_t i, const T& t) { 00226 assert(i < stuff.size()); 00227 if (!stuff[i]) initializeEmptyElement(i,t); 00228 } 00229 00230 }; 00231 00232 } // namespace SimTK 00233 00234 #endif // SimTK_SimTKCOMMON_STABLEARRAY_H_