123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- /* ---------------------------------------------------------------------------
- Nullsoft Database Engine
- --------------------
- codename: Near Death Experience
- --------------------------------------------------------------------------- */
- /* ---------------------------------------------------------------------------
- Raw Index Class
- --------------------------------------------------------------------------- */
- #include "nde.h"
- #include <stdio.h>
- 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;i<NUM_SPECIAL_RECORDS;i++)
- /*secindex->index->*/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;i<NUM_SPECIAL_RECORDS;i++)
- {
- SecIndex->index->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;i<NEntries;i++)
- {
- coopSave[i] = GetCooperative(i);
- SetCooperative(i, i);
- }
- if (SecIndex->index->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;i<NEntries;i++)
- {
- Record *rec = s->GetRecord(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<NEntries;i++) {
- if (IndexTable[i*2+1] <= 0) return 1;
- }
- return 0;
- }
- //---------------------------------------------------------------------------
- void Index::UpdateMe(Index *Me, int NewIdx, int OldIdx)
- {
- int j=NewIdx > OldIdx ? -1 : 1;
- for (int i=min(NewIdx, OldIdx);i<=max(NewIdx, OldIdx)&&i<NEntries;i++)
- {
- if (i == NewIdx) continue;
- int v = GetCooperative(i);
- IndexField *f = SecIndex;
- while (f->index->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;
- }
|