/* --------------------------------------------------------------------------- Nullsoft Database Engine -------------------- codename: Near Death Experience --------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- Raw Index Class --------------------------------------------------------------------------- */ #include "nde.h" #include const char *iSign="NDEINDEX"; //--------------------------------------------------------------------------- Index::Index(VFILE *H, unsigned char id, int pos, int type, bool newindex, int nentries, Table *parentTable) { Handle = H; IndexTable = NULL; MaxSize=0; locateUpToDate = false; Id = id; Position = pos; // DataType = type; SecIndex = NULL; InChain=false; InInsert=false; InChainIdx = -1; pTable = parentTable; TableHandle = pTable->Handle; SetGlobalLocateUpToDate(false); if (!newindex) { Vfseek(Handle, (int)strlen(__INDEX_SIGNATURE__), SEEK_SET); Vfread(&NEntries, sizeof(NEntries), Handle); } else NEntries = nentries; LoadIndex(newindex); } //--------------------------------------------------------------------------- Index::~Index() { if (IndexTable) free(IndexTable); } //--------------------------------------------------------------------------- int Index::Insert(int N) { return Insert(NULL, N, false); } //--------------------------------------------------------------------------- int Index::Insert(Index *parindex, int N, bool localonly) { if (InChain) return -1; Index *p = parindex; IndexField *f = 0; locateUpToDate = false; SetGlobalLocateUpToDate(false); InInsert=true; if (N > NEntries) N = NEntries; if ((NEntries+1) > (int)MaxSize) { MaxSize *=2;//+= BLOCK_SIZE; int *newBlock = (int *)calloc(MaxSize, sizeof(int)*2); memcpy(newBlock, IndexTable, NEntries*sizeof(int)*2); free(IndexTable); IndexTable = newBlock; } if (N < NEntries && Id == PRIMARY_INDEX) { memmove(IndexTable+(N+1)*2, IndexTable+(N*2), (NEntries-N)*sizeof(int)*2); NEntries++; } else { N=NEntries; NEntries++; } Update(N, 0, NULL, localonly); // Should be always safe to cat the new record since if we are primary index, // then secondary is sorted, so value will be moved at update // if we are a secondary index, then an insertion will insert at the end of the primary index anyway if (!localonly && SecIndex) { InChain=true; int pp = SecIndex->index->Insert(this, N, false); InChain=false; IndexTable[N*2+1] = pp == -1 ? N : pp; if (N < NEntries-1 && Id == PRIMARY_INDEX) { int pidx = -1; if (!parindex) { int v = pp; f = SecIndex; if (f) { while (f->index->SecIndex->index != this) { v = f->index->GetCooperative(v); f = f->index->SecIndex; } p = f->index; pidx = v; } } if (p && pidx != -1) { p->SetCooperative(pidx, N); UpdateMe(p, N, NEntries); } } } InInsert=false; return N; } //--------------------------------------------------------------------------- void Index::LoadIndex(bool newindex) { if (IndexTable) free(IndexTable); MaxSize = ((NEntries) / BLOCK_SIZE + 1) * BLOCK_SIZE; IndexTable = (int *)calloc(MaxSize, sizeof(int)*2); if (!newindex) { Vfseek(Handle, (int)strlen(__INDEX_SIGNATURE__)+4+((NEntries*4*2)+4)*(Position+1), SEEK_SET); int v = 0; Vfread(&v, sizeof(v), Handle); Id = (unsigned char)v; Vfread(IndexTable, NEntries*2*sizeof(int), Handle); } } //--------------------------------------------------------------------------- int Index::Update(int Idx, int Pos, RecordBase *record, bool localonly) { return Update(NULL, -1, Idx, Pos, record, false, localonly); } //--------------------------------------------------------------------------- int Index::Update(Index *parindex, int paridx, int Idx, int Pos, RecordBase *record, bool forceLast, bool localonly) { if (InChain) return InChainIdx; int NewIdx=Idx; int oldSecPtr = IndexTable[Idx*2+1]; if (!forceLast && Id == PRIMARY_INDEX || record == NULL || Idx < NUM_SPECIAL_RECORDS) { if (Idx < NEntries && Idx >= 0) { IndexTable[Idx*2] = Pos; if (!localonly && SecIndex && SecIndex->index != this && !InInsert) { InChain=true; InChainIdx = Idx; SecIndex->index->Update(this, Idx, IndexTable[Idx*2+1], Pos, record, forceLast, false); InChainIdx = -1; InChain=false; } } else { #ifdef WIN32 MessageBox(NULL, L"Updating outside range", L"Oops", 16); #else printf("NDE Error: updating outside range!\n"); #endif } } else { if (forceLast) NewIdx = NEntries; else { if (Pos != Get(Idx) || Id != PRIMARY_INDEX) { int state = 0; NewIdx = FindSortedPlace(record->GetField(Id), Idx, &state, QFIND_ENTIRE_SCOPE); } } if (NewIdx <= NEntries && NewIdx >= NUM_SPECIAL_RECORDS) { if (NewIdx != Idx) { Index *p = parindex; IndexField *f = 0; int pidx = paridx; NewIdx = MoveIndex(Idx, NewIdx); if (SecIndex->index != this) { if (!parindex) { int v = GetCooperative(NewIdx); f = SecIndex; if (f) { while (f->index->SecIndex->index != this) { v = f->index->GetCooperative(v); f = f->index->SecIndex; } p = f->index; pidx = v; } } if (p) { p->SetCooperative(pidx, NewIdx); UpdateMe(p, NewIdx, Idx); } } } IndexTable[NewIdx*2] = Pos; if (!localonly && SecIndex && SecIndex->index != this && !InInsert) // Actually, we should never be InInsert and here, but lets cover our ass { InChain=true; InChainIdx = oldSecPtr; SecIndex->index->Update(this, NewIdx, oldSecPtr, Pos, record, forceLast, false); InChainIdx = -1; InChain=false; } } else { #ifdef WIN32 MessageBox(NULL, L"QSort failed and tried to update index outside range", L"Oops", 16); #else printf("NDE Error: qsort failed and tried to update index outside range!\n"); #endif } } locateUpToDate = false; SetGlobalLocateUpToDate(false); pTable->IndexModified(); return NewIdx; } //--------------------------------------------------------------------------- int Index::Get(int Idx) { if (Idx < NEntries) return IndexTable[Idx*2]; else { #ifdef WIN32 MessageBox(NULL, L"Requested index outside range", L"Oops", 16); #else printf("NDE Error: requested index outside range!\n"); #endif return -1; } } //--------------------------------------------------------------------------- void Index::Set(int Idx, int P) { if (Idx < NEntries) IndexTable[Idx*2]=P; else { #ifdef WIN32 MessageBox(NULL, L"Updating index outside range", L"Oops", 16); #else printf("NDE Error: updating index outside range!\n"); #endif } } //--------------------------------------------------------------------------- void Index::WriteIndex(void) { if (Id == PRIMARY_INDEX) { Vfseek(Handle, (int)strlen(__INDEX_SIGNATURE__), SEEK_SET); Vfwrite(&NEntries, sizeof(NEntries), Handle); } Vfseek(Handle, (int)strlen(__INDEX_SIGNATURE__)+4+((NEntries*4*2)+4)*(Position+1), SEEK_SET); int v=(int)Id; Vfwrite(&v, sizeof(v), Handle); Vfwrite(IndexTable, NEntries*2*sizeof(int), Handle); } //--------------------------------------------------------------------------- int Index::MoveIndex(int idx, int newidx) { if (idx == newidx) return newidx; int oldPos=IndexTable[idx*2], oldPtr=IndexTable[idx*2+1]; if (NEntries > idx+1) memmove(IndexTable+idx*2, IndexTable+(idx+1)*2, (NEntries-idx)*sizeof(int)*2); if (newidx > idx) newidx--; if (NEntries > newidx) memmove(IndexTable+(newidx+1)*2, IndexTable+newidx*2, (NEntries-newidx-1)*sizeof(int)*2); IndexTable[newidx*2] = oldPos; IndexTable[newidx*2+1] = oldPtr; return newidx; } //--------------------------------------------------------------------------- void Index::Colaborate(IndexField *secindex) { SecIndex = secindex; for (int i=0;iindex->*/SetCooperative(i, i); } //--------------------------------------------------------------------------- void Index::Propagate(void) { int i; if (!SecIndex || SecIndex->ID == PRIMARY_INDEX) return; SecIndex->index->NEntries=NUM_SPECIAL_RECORDS; for (i=0;iindex->Set(i, Get(i)); SecIndex->index->SetCooperative(i, GetCooperative(i)); } Scanner *s = pTable->NewScanner(); if (!s) { #ifdef WIN32 MessageBox(NULL, L"Failed to create a scanner in reindex", L"Oops", 16); #else printf("NDE Error: failed to create a scanner in reindex!\n"); #endif return; } int *coopSave = (int *)calloc(NEntries, sizeof(int)); if (!coopSave) { #ifdef WIN32 MessageBox(NULL, L"Alloc failed in reindex", L"Oops", 16); #else printf("NDE Error: alloc failed in reindex!\n"); #endif return; } for (i=0;iindex->SecIndex->index->Id != PRIMARY_INDEX) { #ifdef WIN32 MessageBox(NULL, L"Propagating existing index", L"Oops", 16); #else printf("NDE Error: propagating existing index!\n"); #endif free(coopSave); return; } s->SetWorkingIndexById(-1); for (i=NUM_SPECIAL_RECORDS;iGetRecord(coopSave[i]); if (rec) { SecIndex->index->NEntries++; // SecIndex->index->Insert(NULL, i, true); SecIndex->index->SetCooperative(i, coopSave[i]); SecIndex->index->Set(i, Get(i)); SecIndex->index->Update(i, SecIndex->index->Get(i), rec, true); /* SecIndex->index->SetCooperative(i, q);*/ rec->Release(); } else { #ifdef WIN32 MessageBox(NULL, L"Unable to read record in reindex", L"Oops", 16); #else printf("NDE Error: unable to read record in reindex!\n"); #endif } } free(coopSave); if (NEntries != SecIndex->index->NEntries) { #ifdef WIN32 MessageBox(NULL, L"Secindex->NEntries desynchronized in reindex", L"Oops", 16); #else printf("NDE Error: Secindex->NEntries desynchronized in reindex!\n"); #endif } pTable->DeleteScanner(s); } //--------------------------------------------------------------------------- void Index::SetCooperative(int Idx, int secpos) { if (Idx < NEntries && Idx >= 0) IndexTable[Idx*2+1] = secpos; else { #ifdef WIN32 MessageBox(NULL, L"Cooperative update outside range", L"Oops", 16); #else printf("NDE Error: cooperative update outside range!\n"); #endif } } //--------------------------------------------------------------------------- int Index::GetCooperative(int Idx) { if (Idx < NEntries && Idx >= 0) return IndexTable[Idx*2+1]; else { #ifdef WIN32 MessageBox(NULL, L"Cooperative request outside range", L"Oops", 16); #else printf("NDE Error: cooperative request outside range!\n"); #endif } return -1; } //--------------------------------------------------------------------------- int Index::NeedFix() { for (int i=NUM_SPECIAL_RECORDS;i OldIdx ? -1 : 1; for (int i=min(NewIdx, OldIdx);i<=max(NewIdx, OldIdx)&&iindex->SecIndex->index != this) { v = f->index->GetCooperative(v); f = f->index->SecIndex; } Me->SetCooperative(v, Me->GetCooperative(v)+j); } } //--------------------------------------------------------------------------- Field *Index::QuickFindField(unsigned char Idx, int Pos) { int ThisPos = Pos; while (ThisPos) { Vfseek(TableHandle, ThisPos, SEEK_SET); Field entry(ThisPos); uint32_t next_pos; entry.ReadField(pTable, ThisPos, true, &next_pos); if (entry.GetFieldId() == Idx) { return entry.ReadField(pTable, ThisPos); } ThisPos = next_pos; } return NULL; } //--------------------------------------------------------------------------- // Dynamic qsort (i definitly rule) int Index::FindSortedPlace(Field *thisField, int curIdx, int *laststate, int start) { return FindSortedPlaceEx(thisField, curIdx, laststate, start, COMPARE_MODE_EXACT); } //--------------------------------------------------------------------------- // and here again ugly switch int Index::FindSortedPlaceEx(Field *thisField, int curIdx, int *laststate, int start, int comp_mode) { int top=start != QFIND_ENTIRE_SCOPE ? start : NUM_SPECIAL_RECORDS; int bottom=NEntries-1; int compEntry = 0; int cePos = 0; Field *compField=NULL; int i = 0; Field *cfV=NULL; if (NEntries <= NUM_SPECIAL_RECORDS) return NUM_SPECIAL_RECORDS; switch(comp_mode) { case COMPARE_MODE_EXACT: while (bottom-top >= 1) { compEntry=(bottom-top)/2+top; if (compEntry == curIdx) compEntry++; if (compEntry == bottom) break; cePos = Get(compEntry); if (!cePos) bottom = compEntry; else { compField = QuickFindField(Id, Get(compEntry)); cfV = compField; if (!thisField) { if (!compField) i = 0; else i = 1; } else i = thisField->Compare(cfV); switch (i) { case 1: top = compEntry+1; break; case -1: bottom = compEntry-1; break; case 0: *laststate=0; delete compField; return compEntry+1; } delete compField; } } compEntry=(bottom-top)/2+top; if (compEntry == curIdx) return curIdx; compField = QuickFindField(Id, Get(compEntry)); cfV = compField; if (thisField) *laststate = thisField->Compare(cfV); else { if (!compField) *laststate = 0; else *laststate = 1; } break; case COMPARE_MODE_CONTAINS: while (bottom-top >= 1) { compEntry=(bottom-top)/2+top; if (compEntry == curIdx) compEntry++; if (compEntry == bottom) break; cePos = Get(compEntry); if (!cePos) bottom = compEntry; else { compField = QuickFindField(Id, Get(compEntry)); cfV = compField; if (!thisField) { if (!compField) i = 0; else i = 1; } else i = thisField->Contains(cfV); switch (i) { case 1: top = compEntry+1; break; case -1: bottom = compEntry-1; break; case 0: *laststate=0; delete compField; return compEntry+1; } if (compField) { delete compField; } } } compEntry=(bottom-top)/2+top; if (compEntry == curIdx) return curIdx; compField = QuickFindField(Id, Get(compEntry)); cfV = compField; if (thisField) *laststate = thisField->Contains(cfV); else { if (!compField) *laststate = 0; else *laststate = 1; } break; case COMPARE_MODE_STARTS: while (bottom-top >= 1) { compEntry=(bottom-top)/2+top; if (compEntry == curIdx) compEntry++; if (compEntry == bottom) break; cePos = Get(compEntry); if (!cePos) bottom = compEntry; else { compField = QuickFindField(Id, Get(compEntry)); cfV = compField; if (!thisField) { if (!compField) i = 0; else i = 1; } else i = thisField->Starts(cfV); switch (i) { case 1: top = compEntry+1; break; case -1: bottom = compEntry-1; break; case 0: *laststate=0; delete compField; return compEntry+1; } if (compField) { delete compField; } } } compEntry=(bottom-top)/2+top; if (compEntry == curIdx) return curIdx; compField = QuickFindField(Id, Get(compEntry)); cfV = compField; if (thisField) *laststate = thisField->Starts(cfV); else { if (!compField) *laststate = 0; else *laststate = 1; } break; } switch (*laststate) { case -1: delete compField; return compEntry; case 1: case 0: delete compField; return /*compEntry==NEntries-1 ? compEntry : */compEntry+1; } return NUM_SPECIAL_RECORDS; // we're not supposed to be here :/ } int Index::QuickFind(int Id, Field *field, int start) { return QuickFindEx(Id, field, start, COMPARE_MODE_EXACT); } //--------------------------------------------------------------------------- int Index::QuickFindEx(int Id, Field *field, int start, int comp_mode) { int laststate = 0; Field *compField=NULL, *cfV=NULL; int i = FindSortedPlaceEx(field, Id, &laststate, start, comp_mode)-1; // -1 because we don't insert but just search if (laststate != 0) return FIELD_NOT_FOUND; if (i < start) return FIELD_NOT_FOUND; switch(comp_mode) { case COMPARE_MODE_CONTAINS: while (--i>=NUM_SPECIAL_RECORDS) { compField = QuickFindField(Id, Get(i)); cfV = compField; if (field->Contains(cfV) != 0) { if (compField) { delete compField; } return i+1; } if (compField) { delete compField; } } break; case COMPARE_MODE_EXACT: while (--i>=NUM_SPECIAL_RECORDS) { compField = QuickFindField(Id, Get(i)); cfV = compField; if (field->Compare(cfV) != 0) { if (compField) { delete compField; } return i+1; } if (compField) { delete compField; } } break; case COMPARE_MODE_STARTS: while (--i>=NUM_SPECIAL_RECORDS) { compField = QuickFindField(Id, Get(i)); cfV = compField; if (field->Starts(cfV) != 0) { delete compField; return i+1; } delete compField; } break; } return i+1; } //--------------------------------------------------------------------------- int Index::TranslateIndex(int Pos, Index *index) { int v = GetCooperative(Pos); IndexField *f = SecIndex; while (f->index != this && f->index != index) { v = f->index->GetCooperative(v); f = f->index->SecIndex; } return v; } //--------------------------------------------------------------------------- void Index::Delete(int Idx, int Pos, Record *record) { Update(NULL, -1, Idx, Pos, record, true, false); Shrink(); } //--------------------------------------------------------------------------- void Index::Shrink(void) { if (InChain) return; if (SecIndex && SecIndex->index != this) // Actually, we should never be InInsert and here, but lets cover our ass { InChain=true; SecIndex->index->Shrink(); InChain=false; } NEntries--; } //--------------------------------------------------------------------------- void Index::SetGlobalLocateUpToDate(bool isUptodate) { if (!pTable) return; pTable->SetGlobalLocateUpToDate(isUptodate); } unsigned char Index::GetId() { return Id; }