00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "codemodel.h"
00020
00021 #include <QHash>
00022 #include <QVector>
00023
00024 #include "appendedlist.h"
00025 #include "repositories/itemrepository.h"
00026 #include "identifier.h"
00027 #include "indexedstring.h"
00028 #include <util/embeddedfreetree.h>
00029 #include "referencecounting.h"
00030
00031 #define ifDebug(x)
00032
00033 namespace KDevelop {
00034
00035 class CodeModelItemHandler {
00036 public:
00037 static int leftChild(const CodeModelItem& m_data) {
00038 return (int)m_data.referenceCount;
00039 }
00040 static void setLeftChild(CodeModelItem& m_data, int child) {
00041 m_data.referenceCount = (uint)child;
00042 }
00043 static int rightChild(const CodeModelItem& m_data) {
00044 return (int)m_data.uKind;
00045 }
00046 static void setRightChild(CodeModelItem& m_data, int child) {
00047 m_data.uKind = (uint)child;
00048 }
00049
00050 static void copyTo(const CodeModelItem& m_data, CodeModelItem& data) {
00051 data = m_data;
00052 }
00053
00054 static void createFreeItem(CodeModelItem& data) {
00055 data = CodeModelItem();
00056 data.referenceCount = (uint)-1;
00057 data.uKind = (uint)-1;
00058 }
00059
00060 static bool isFree(const CodeModelItem& m_data) {
00061 return !m_data.id.isValid();
00062 }
00063
00064 static const CodeModelItem& data(const CodeModelItem& m_data) {
00065 return m_data;
00066 }
00067
00068 static bool equals(const CodeModelItem& m_data, const CodeModelItem& rhs) {
00069 return m_data.id == rhs.id;
00070 }
00071 };
00072
00073
00074 DEFINE_LIST_MEMBER_HASH(CodeModelRepositoryItem, items, CodeModelItem)
00075
00076 class CodeModelRepositoryItem {
00077 public:
00078 CodeModelRepositoryItem() : centralFreeItem(-1) {
00079 initializeAppendedLists();
00080 }
00081 CodeModelRepositoryItem(const CodeModelRepositoryItem& rhs, bool dynamic = true) : file(rhs.file), centralFreeItem(rhs.centralFreeItem) {
00082 initializeAppendedLists(dynamic);
00083 copyListsFrom(rhs);
00084 }
00085
00086 ~CodeModelRepositoryItem() {
00087 freeAppendedLists();
00088 }
00089
00090 unsigned int hash() const {
00091
00092
00093 return file.index();
00094 }
00095
00096 size_t itemSize() const {
00097 return dynamicSize();
00098 }
00099
00100 uint classSize() const {
00101 return sizeof(CodeModelRepositoryItem);
00102 }
00103
00104 IndexedString file;
00105 int centralFreeItem;
00106
00107 START_APPENDED_LISTS(CodeModelRepositoryItem);
00108 APPENDED_LIST_FIRST(CodeModelRepositoryItem, CodeModelItem, items);
00109 END_APPENDED_LISTS(CodeModelRepositoryItem, items);
00110 };
00111
00112 class CodeModelRequestItem {
00113 public:
00114
00115 CodeModelRequestItem(const CodeModelRepositoryItem& item) : m_item(item) {
00116 }
00117 enum {
00118 AverageSize = 30
00119 };
00120
00121 unsigned int hash() const {
00122 return m_item.hash();
00123 }
00124
00125 size_t itemSize() const {
00126 return m_item.itemSize();
00127 }
00128
00129 void createItem(CodeModelRepositoryItem* item) const {
00130 Q_ASSERT(shouldDoDUChainReferenceCounting(item));
00131 Q_ASSERT(shouldDoDUChainReferenceCounting(((char*)item) + (itemSize()-1)));
00132 new (item) CodeModelRepositoryItem(m_item, false);
00133 }
00134
00135 static void destroy(CodeModelRepositoryItem* item, KDevelop::AbstractItemRepository&) {
00136 Q_ASSERT(shouldDoDUChainReferenceCounting(item));
00137
00138 item->~CodeModelRepositoryItem();
00139 }
00140
00141 static bool persistent(const CodeModelRepositoryItem* item) {
00142 Q_UNUSED(item);
00143 return true;
00144 }
00145
00146 bool equals(const CodeModelRepositoryItem* item) const {
00147 return m_item.file == item->file;
00148 }
00149
00150 const CodeModelRepositoryItem& m_item;
00151 };
00152
00153
00154 class CodeModelPrivate {
00155 public:
00156
00157 CodeModelPrivate() : m_repository("Code Model") {
00158 }
00159
00160 ItemRepository<CodeModelRepositoryItem, CodeModelRequestItem> m_repository;
00161 };
00162
00163 CodeModel::CodeModel() : d(new CodeModelPrivate())
00164 {
00165 }
00166
00167 CodeModel::~CodeModel()
00168 {
00169 delete d;
00170 }
00171
00172 void CodeModel::addItem(const IndexedString& file, const IndexedQualifiedIdentifier& id, CodeModelItem::Kind kind)
00173 {
00174 ifDebug( kDebug() << "addItem" << file.str() << id.identifier().toString() << id.index; )
00175
00176 if(!id.isValid())
00177 return;
00178 CodeModelRepositoryItem item;
00179 item.file = file;
00180 CodeModelRequestItem request(item);
00181
00182 uint index = d->m_repository.findIndex(item);
00183
00184 CodeModelItem newItem;
00185 newItem.id = id;
00186 newItem.kind = kind;
00187 newItem.referenceCount = 1;
00188
00189 #ifdef TEST_REFERENCE_COUNTING_2
00190 {
00191 uint count = 0;
00192 const CodeModelItem* i;
00193 this->items(file, count, i);
00194 for(int a = 0; a < count; ++a)
00195 Q_ASSERT(i[a].id.hasReferenceCount());
00196 }
00197 #endif
00198
00199 if(index) {
00200 const CodeModelRepositoryItem* oldItem = d->m_repository.itemFromIndex(index);
00201 EmbeddedTreeAlgorithms<CodeModelItem, CodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), oldItem->centralFreeItem);
00202
00203 int listIndex = alg.indexOf(newItem);
00204
00205 QMutexLocker lock(d->m_repository.mutex());
00206
00207 DynamicItem<CodeModelRepositoryItem, true> editableItem = d->m_repository.dynamicItemFromIndex(index);
00208 CodeModelItem* items = const_cast<CodeModelItem*>(editableItem->items());
00209
00210 if(listIndex != -1) {
00211
00212 ++items[listIndex].referenceCount;
00213 items[listIndex].kind = kind;
00214 return;
00215 }else{
00216
00217 EmbeddedTreeAddItem<CodeModelItem, CodeModelItemHandler> add(items, editableItem->itemsSize(), editableItem->centralFreeItem, newItem);
00218
00219 if(add.newItemCount() != editableItem->itemsSize()) {
00220
00221
00222 item.itemsList().resize(add.newItemCount());
00223 add.transferData(item.itemsList().data(), item.itemsList().size(), &item.centralFreeItem);
00224
00225 d->m_repository.deleteItem(index);
00226 }else{
00227
00228 #ifdef TEST_REFERENCE_COUNTING_2
00229 {
00230 uint count = 0;
00231 const CodeModelItem* i;
00232 this->items(file, count, i);
00233 for(int a = 0; a < count; ++a)
00234 Q_ASSERT(i[a].id.hasReferenceCount());
00235 }
00236 #endif
00237 return;
00238 }
00239 }
00240 }else{
00241
00242 item.itemsList().append(newItem);
00243 }
00244
00245 #ifdef TEST_REFERENCE_COUNTING_2
00246 {
00247 uint count = 0;
00248 const CodeModelItem* i;
00249 this->items(file, count, i);
00250 for(int a = 0; a < count; ++a)
00251 Q_ASSERT(i[a].id.hasReferenceCount());
00252 }
00253 #endif
00254
00255 Q_ASSERT(!d->m_repository.findIndex(request));
00256
00257
00258 volatile uint newIndex = d->m_repository.index(request);
00259 Q_UNUSED(newIndex);
00260 ifDebug( kDebug() << "new index" << newIndex; )
00261
00262 #ifdef TEST_REFERENCE_COUNTING_2
00263 {
00264 uint count = 0;
00265 const CodeModelItem* i;
00266 this->items(file, count, i);
00267 for(int a = 0; a < count; ++a)
00268 Q_ASSERT(i[a].id.hasReferenceCount());
00269 }
00270 #endif
00271
00272 Q_ASSERT(d->m_repository.findIndex(request));
00273 }
00274
00275 void CodeModel::updateItem(const IndexedString& file, const IndexedQualifiedIdentifier& id, CodeModelItem::Kind kind)
00276 {
00277 ifDebug( kDebug() << file.str() << id.identifier().toString() << kind; )
00278
00279 if(!id.isValid())
00280 return;
00281
00282 #ifdef TEST_REFERENCE_COUNTING_2
00283 {
00284 uint count = 0;
00285 const CodeModelItem* i;
00286 this->items(file, count, i);
00287 for(int a = 0; a < count; ++a)
00288 Q_ASSERT(i[a].id.hasReferenceCount());
00289 }
00290 #endif
00291
00292 CodeModelRepositoryItem item;
00293 item.file = file;
00294 CodeModelRequestItem request(item);
00295
00296 CodeModelItem newItem;
00297 newItem.id = id;
00298 newItem.kind = kind;
00299 newItem.referenceCount = 1;
00300
00301 uint index = d->m_repository.findIndex(item);
00302
00303 if(index) {
00304
00305 QMutexLocker lock(d->m_repository.mutex());
00306 DynamicItem<CodeModelRepositoryItem, true> oldItem = d->m_repository.dynamicItemFromIndex(index);
00307
00308 EmbeddedTreeAlgorithms<CodeModelItem, CodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), oldItem->centralFreeItem);
00309 int listIndex = alg.indexOf(newItem);
00310 Q_ASSERT(listIndex != -1);
00311
00312 CodeModelItem* items = const_cast<CodeModelItem*>(oldItem->items());
00313
00314 Q_ASSERT(items[listIndex].id == id);
00315 items[listIndex].kind = kind;
00316
00317 #ifdef TEST_REFERENCE_COUNTING_2
00318 {
00319 uint count = 0;
00320 const CodeModelItem* i;
00321 this->items(file, count, i);
00322 for(int a = 0; a < count; ++a)
00323 Q_ASSERT(i[a].id.hasReferenceCount());
00324 }
00325 #endif
00326
00327 return;
00328 }
00329
00330 Q_ASSERT(0);
00331 }
00332
00333 void CodeModel::removeItem(const IndexedString& file, const IndexedQualifiedIdentifier& id)
00334
00335 {
00336 if(!id.isValid())
00337 return;
00338 #ifdef TEST_REFERENCE_COUNTING_2
00339 {
00340 uint count = 0;
00341 const CodeModelItem* i;
00342 this->items(file, count, i);
00343 for(int a = 0; a < count; ++a)
00344 Q_ASSERT(i[a].id.hasReferenceCount());
00345 }
00346 #endif
00347 ifDebug( kDebug() << "removeItem" << file.str() << id.identifier().toString(); )
00348 CodeModelRepositoryItem item;
00349 item.file = file;
00350 CodeModelRequestItem request(item);
00351
00352 uint index = d->m_repository.findIndex(item);
00353
00354 if(index) {
00355
00356 CodeModelItem searchItem;
00357 searchItem.id = id;
00358
00359 QMutexLocker lock(d->m_repository.mutex());
00360 DynamicItem<CodeModelRepositoryItem, true> oldItem = d->m_repository.dynamicItemFromIndex(index);
00361
00362 EmbeddedTreeAlgorithms<CodeModelItem, CodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), oldItem->centralFreeItem);
00363
00364 int listIndex = alg.indexOf(searchItem);
00365 if(listIndex == -1)
00366 return;
00367
00368 CodeModelItem* items = const_cast<CodeModelItem*>(oldItem->items());
00369
00370 --items[listIndex].referenceCount;
00371
00372 if(oldItem->items()[listIndex].referenceCount)
00373 return;
00374
00375
00376
00377 EmbeddedTreeRemoveItem<CodeModelItem, CodeModelItemHandler> remove(items, oldItem->itemsSize(), oldItem->centralFreeItem, searchItem);
00378
00379 uint newItemCount = remove.newItemCount();
00380 if(newItemCount != oldItem->itemsSize()) {
00381 if(newItemCount == 0) {
00382
00383 d->m_repository.deleteItem(index);
00384 #ifdef TEST_REFERENCE_COUNTING_2
00385 {
00386 uint count = 0;
00387 const CodeModelItem* i;
00388 this->items(file, count, i);
00389 for(int a = 0; a < count; ++a)
00390 Q_ASSERT(i[a].id.hasReferenceCount());
00391 }
00392 #endif
00393
00394 return;
00395 }else{
00396
00397 item.itemsList().resize(newItemCount);
00398 remove.transferData(item.itemsList().data(), item.itemsSize(), &item.centralFreeItem);
00399
00400
00401 d->m_repository.deleteItem(index);
00402
00403 d->m_repository.index(request);
00404 #ifdef TEST_REFERENCE_COUNTING_2
00405 {
00406 uint count = 0;
00407 const CodeModelItem* i;
00408 this->items(file, count, i);
00409 for(int a = 0; a < count; ++a)
00410 Q_ASSERT(i[a].id.hasReferenceCount());
00411 }
00412 #endif
00413 return;
00414 }
00415 }
00416 }
00417 #ifdef TEST_REFERENCE_COUNTING_2
00418 {
00419 uint count = 0;
00420 const CodeModelItem* i;
00421 this->items(file, count, i);
00422 for(int a = 0; a < count; ++a)
00423 Q_ASSERT(i[a].id.hasReferenceCount());
00424 }
00425 #endif
00426 }
00427
00428 void CodeModel::items(const IndexedString& file, uint& count, const CodeModelItem*& items) const
00429 {
00430 ifDebug( kDebug() << "items" << file.str(); )
00431
00432 CodeModelRepositoryItem item;
00433 item.file = file;
00434 CodeModelRequestItem request(item);
00435
00436 uint index = d->m_repository.findIndex(item);
00437
00438 if(index) {
00439 const CodeModelRepositoryItem* repositoryItem = d->m_repository.itemFromIndex(index);
00440 ifDebug( kDebug() << "found index" << index << repositoryItem->itemsSize(); )
00441 count = repositoryItem->itemsSize();
00442 items = repositoryItem->items();
00443 }else{
00444 ifDebug( kDebug() << "found no index"; )
00445 count = 0;
00446 items = 0;
00447 }
00448 }
00449
00450 CodeModel& CodeModel::self() {
00451 static CodeModel ret;
00452 return ret;
00453 }
00454
00455 }
00456