#include "ccommon.h" #include "genconst.h" #include "ccustio.h" #include "cbpltree.h" #include "bplpriv.h" #include "btrread.h" #include "btrbase.h" #include "checks.h" #include void Move(byte ,int ,int ,int ,char *,char *); /* @(SHF) Funzioni di base per il B+Tree @($) ZPage B+TREE @(ID) Azzera una pagina del B+TREE. @(FD) */ void ZPage(ActPage) Page *ActPage; /* pagina attuale */ { ActPage->PA.PageHeader.TPage = EmptyPage; ActPage->PA.PageHeader.PathNumber = 0; ActPage->PA.PageHeader.Flags = 0; ActPage->PA.PageHeader.Father = NoLink; ActPage->PA.PageHeader.Prev = NoLink; ActPage->PA.PageHeader.Next = NoLink; ActPage->PA.PageHeader.NKey = 0; memset(ActPage->PA.AreaForKey, ' ', AreaLen); } /* @($) GetHeader B+TREE @(ID) Legge l'header di un indice. Restituisce il codice di errore. @(FD) @(ISV) junk = variabile spazzatura per la chiamata della funzione "sleep". w = descrittore della finestra. @(FSV) */ int GetHeader(FilCheck, HPage, LockMode, Err) FilCtrl *FilCheck; /* identificatore indice */ Page *HPage; /* pagina dell' indice */ int LockMode; /* lock sull'header */ int *Err; /* codice di errore */ { do { CRead(&FilCheck->Fil, (char *) HPage, 1L, LockMode); if (TESTLOCK(FilCheck->Fil.IOR)) message_box("Sono in attesa dell' indice"); } while TESTLOCK(FilCheck->Fil.IOR); return((*Err = FilCheck->Fil.IOR)); } /* @($) PutHeader B+TREE @(ID) Scrive l'header di un indice. Restituisce il codice di errore. @(FD) */ int PutHeader(FilCheck, HPage, LockMode, Err) FilCtrl *FilCheck; /* identificatore indice */ Page *HPage; /* pagina in cui scrivere l'header */ int LockMode; /* lock sull'header */ int *Err; /* errore */ { CWrite(&FilCheck->Fil, (char *) HPage, 1L, LockMode); return((*Err = FilCheck->Fil.IOR)); } /* @($) GetAPage B+TREE @(ID) Legge una pagina generica dell'indice. Restituisce il codice di errore. @(FD) */ int GetAPage(FilCheck, ActPage, Pg, Err) FilCtrl *FilCheck; /* identificatore indice */ Page *ActPage; /* pagina letta */ RecNoType Pg; /* numero di pagina */ int *Err; /* codice di errore */ { CRead(&FilCheck->Fil, (char *) ActPage, Pg + FilCheck->FHead.CABlock + 1, NoLock); return((*Err = FilCheck->Fil.IOR)); } /* @($) PutAPage B+TREE @(ID) Scrive una pagina generica dell'indice. Restituisce il codice di errore. @(FD) */ int PutAPage(FilCheck, ActPage, Pg, Err) FilCtrl *FilCheck; /* identificatore indice */ Page *ActPage; /* pagina attiva */ RecNoType Pg; /* numero della pagina */ int *Err; /* codice errore */ { CWrite(&FilCheck->Fil, (char *) ActPage, Pg + FilCheck->FHead.CABlock + 1, NoLock); return((*Err = FilCheck->Fil.IOR)); } /* @($) GetAKey B+TREE @(ID) Estrae una chiave da una pagina. @(FD) @(ISV) BgnItem = numero di byte precedenti alla chiave dall'inizio della pagina. @(FSV) */ void GetAKey(ActPage, KeyLen, Pstion, KKey, Index) Page ActPage; /* pagina attiva */ int KeyLen; /* lunghezza della chiave */ int Pstion; /* numero chiave nella pagina */ TKey KKey; /* Valore chiave (in output) */ RecNoType *Index; /* indice associato a questa chiave */ { int BgnItem; BgnItem = (Pstion - 1) * (KeyLen + IndLen); strcpy(KKey, (char *)(ActPage.PA.AreaForKey + BgnItem)); *Index = *((RecNoType *)(ActPage.PA.AreaForKey + BgnItem + KeyLen)); } /* @($) PutAKey B+TREE @(ID) Mette una chiave in una pagina. @(FD) @(ISV) BgnItem = numero di byte precedenti alla chiave dall'inizio della pagina. @(FSV) */ void PutAKey(KKey, Index, Pstion, KeyLen, ActPage) TKey KKey; /* valore della chiave */ RecNoType Index; /* indice */ int Pstion; /* posizione nella quale scrivere la chiave */ int KeyLen; /* lunghezza della chiave */ Page *ActPage;/* pagina attiva */ { register int BgnItem; BgnItem = (Pstion - 1) * (KeyLen + IndLen); strcpy((char *)(ActPage->PA.AreaForKey + BgnItem), KKey); *((RecNoType *)(ActPage->PA.AreaForKey + BgnItem + KeyLen)) = Index; } /* @($) Repos B+TREE @(ID) Riposizione all'ultima chiave letta dall'ultima operazione. @(FD) @(ISV) WKey, WKey1 = variabili di lavoro contenenti chiavi. WPos = variabile di lavoro contenente il numero di chiave nella pagina. WPag = variabile di lavoro contenente il numero di pagina attuale. WInd,WInd1 = variabili di lavoro contenenti indici associati alle chavi Wkey. @(FSV) @(IN) Serve solo in multiutenza (XENIX). @(FN) */ void Repos(FilCheck, BError) FilCtrl *FilCheck; /* identificatore indice */ int *BError; /* codice errore (in output) */ { TKey WKey, WKey1; int WPos = FilCheck->Pos, WError; RecNoType WPag = FilCheck->CurPag, WInd, WInd1; if (GetAPage(FilCheck, &PathPage, WPag, BError) != NoErr) return; if (PathPage.PA.PageHeader.NKey < WPos) WPos = PathPage.PA.PageHeader.NKey ; GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, WPos, WKey, &WInd); if ((strcmp(WKey, FilCheck->Key) == 0) && (WInd == FilCheck->Ind)) return ; strcpy(WKey1, FilCheck->Key) ; WInd = FilCheck->Ind ; BTrRead(FilCheck, FilCheck->Key, WKey, &WInd1, BError) ; if (*BError == BTrEmptyTree) return ; if (FilCheck->Base[FilCheck->PN].DupKeys == TRUE) { while ((strcmp(WKey, WKey1) == 0) && (WInd1 < WInd)) { if (WPos < PathPage.PA.PageHeader.NKey) WPos++; else if (PathPage.PA.PageHeader.Next == NoLink) break; else { WPag = PathPage.PA.PageHeader.Next; if (GetAPage(FilCheck, &PathPage, WPag, BError) != NoErr) return; WPos = 1; } GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, WPos, WKey, &WInd1); } } if (GetAPage(FilCheck, &PathPage, WPag, &WError) != NoErr) return; if ((strcmp(WKey, WKey1) == 0) && (WInd == WInd1)) { FilCheck->CurPag = WPag; FilCheck->Pos = WPos; } else { if (*BError != BTrEOF) { FilCheck->CurPag = WPag; FilCheck->Pos = WPos; strcpy(FilCheck->Key, WKey) ; FilCheck->Ind = WInd1; *BError = BTrKeyNotFound; } } } /* @($) BTrNext B+TREE @(ID) Restituisce la chiave successiva all'ultima letta. @(FD) @(ISV) WKey = variabile di lavoro contenente la chiave. - Versione DOS e XENIX @(FSV) */ void BTrNext(FilCheck, KKey, IndSr, BError) FilCtrl *FilCheck; /* identificatore indice */ TKey KKey; /* valore della chiave */ RecNoType *IndSr; /* indice associato */ int *BError; /* codice errore */ { TKey WKey; if (FilCheck->CurPag == NoLink) { MaxKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key); BTrRead(FilCheck, FilCheck->Key, WKey, IndSr, BError) ; *BError = BTrEOF; } else { #ifdef DOS if (adelete) { adelete = FALSE; #endif Repos(FilCheck, BError) ; if (*BError != NoErr) { if (*BError == BTrKeyNotFound) { strcpy(KKey,FilCheck->Key); *IndSr = FilCheck->Ind ; *BError = NoErr ; } return; } #ifdef DOS } else if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return; #endif if (FilCheck->Pos < PathPage.PA.PageHeader.NKey) { FilCheck->Pos++; GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr); } else { if (PathPage.PA.PageHeader.Next == NoLink) { GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr); /*MaxKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key);*/ *BError = BTrEOF; } else { FilCheck->CurPag = PathPage.PA.PageHeader.Next; if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return; FilCheck->Pos = 1; GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr); } } strcpy(KKey,FilCheck->Key); FilCheck->Ind = *IndSr ; } } /* @($) BTrPrev B+TREE @(ID) Restituisce la chiave successiva all'ultima. @(FD) @(ISV) WKey = variabile di lavoro contenente una chiave. - Versione DOS e XENIX @(FSV) */ void BTrPrev(FilCheck, KKey, IndSr, BError) FilCtrl *FilCheck; /* identificatore indice */ TKey KKey; /* valore della chiave (in output) */ RecNoType *IndSr; /* indice associato */ int *BError; /* codice di errore */ { TKey WKey; if (FilCheck->CurPag == NoLink) { MinKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key); BTrRead(FilCheck, FilCheck->Key, WKey, IndSr, BError) ; *BError = BTrBOF; } else { #ifdef DOS if (adelete) { adelete = FALSE; #endif Repos(FilCheck, BError) ; if ((*BError != NoErr) && (*BError != BTrKeyNotFound)) { if (*BError == BTrEOF) { strcpy(KKey,FilCheck->Key); *IndSr = FilCheck->Ind ; *BError = NoErr ; } return; } #ifdef DOS } else if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return; #endif *BError = NoErr; if (FilCheck->Pos > 1) { FilCheck->Pos--; GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr); } else { if (PathPage.PA.PageHeader.Prev == NoLink) { GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr); /*MinKey(FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Key);*/ *BError = BTrBOF; } else { FilCheck->CurPag = PathPage.PA.PageHeader.Prev; if (GetAPage(FilCheck, &PathPage, FilCheck->CurPag, BError) != NoErr) return; FilCheck->Pos = PathPage.PA.PageHeader.NKey; GetAKey(PathPage, FilCheck->Base[FilCheck->PN].KeyLen, FilCheck->Pos, FilCheck->Key, IndSr); } } strcpy(KKey, FilCheck->Key); FilCheck->Ind = *IndSr ; } } /* @($) Move B+TREE @(ID) Sposta a destra od a sinistra un numero di byte "NByt" da una sorgente "Buff1" ad una destinazione "Buff2". @(FD) @(ISV) w = area di transito. @(FSV) */ void Move(RghOrLft, NByt, BytBeg, BytDest, Buff1, Buff2) byte RghOrLft; /* destra o sinistra */ int NByt; /* numero di byte da spostare */ int BytBeg; /* num. byte inizio sorgente */ int BytDest; /* num. byte inizio destinazione */ AreaKey Buff1; /* sorgente */ AreaKey Buff2; /* destinazione */ { AreaKey w; memcpy(w, &Buff1[BytBeg], NByt); memcpy(&Buff2[BytDest], w, NByt); } /* @($) ShiftPage B+TREE @(ID) Esegue uno spostamento di un certo numero di posizioni (coppie chiave-puntatore) all'interno della pagina. @(FD) @(ISV) Len = lunghezza di una coppia chiave-puntatore. Beg = numero di byte dall'inizio della pagina alla chiave "StartK" (esclusa). Dest = byte al quale si deve postare la coppia chiave-puntatore. @(FSV) */ void ShiftPage(RghOrLft, StartK, NPos, MxKey, KeyLen, Page1) byte RghOrLft; /* destra o sinistra */ int StartK; /* chiave di partenza */ int NPos; /* numero di posizioni da saltare */ int MxKey; /* numero max chiavi in una pagina */ int KeyLen; /* lunghezza di una chiave */ Page *Page1; /* pagina attiva */ { register int Len = KeyLen + IndLen; register int Beg = (StartK - 1) * Len; register int Dest; if (RghOrLft == RightDir) { Dest = Beg + NPos * Len; if (Page1->PA.PageHeader.NKey + NPos <= MxKey) { Move(RightDir, (Page1->PA.PageHeader.NKey - StartK + 1) * Len, Beg, Dest, Page1->PA.AreaForKey, Page1->PA.AreaForKey); Page1->PA.PageHeader.NKey += NPos; } } else { Dest = Beg - NPos * Len; if (StartK - NPos > 0) { if (StartK <= Page1->PA.PageHeader.NKey) Move(LeftDir, (Page1->PA.PageHeader.NKey - StartK + 1) * Len, Beg, Dest, Page1->PA.AreaForKey, Page1->PA.AreaForKey); Page1->PA.PageHeader.NKey -= NPos; memset(&Page1->PA.AreaForKey[Page1->PA.PageHeader.NKey * Len], ' ', NPos * Len); } } } /* @($) ShiftOut B+TREE @(ID) Sposta un certo numero di chiavi da una pagina. @(FD) @(ISV) Len = lunghezza coppia chiave-puntatore. @(FSV) */ void ShiftOut(FromK, Numk, ToK, KeyLen, Page1, Page2) int FromK; /* prima chiave da spostare */ int Numk; /* numero di chiavi da spostare */ int ToK; /* posizione nella pagina destinazione */ int KeyLen;/* lunghezza della chiave */ Page *Page1;/* puntatore alla pagina destinazione */ Page *Page2;/* puntatore alla pagina sorgente */ { register int Len = KeyLen + IndLen; if (Page1->PA.PageHeader.TPage == Page2->PA.PageHeader.TPage) Move(LeftDir, Numk * Len, (FromK - 1) * Len, (ToK - 1) * Len, Page2->PA.AreaForKey, Page1->PA.AreaForKey); } /* @($) FindPos B+TREE @(ID) Cerca una chiave all'interno di una pagina. Restituisce la chiave piu' simile. @(FD) @(ISV) low = numero della chiave. j = incremento in byte di coppia chiave puntatore. @(FSV) */ int FindPos(ActPage, KKey, KeyLen, KeyOut, IndOut) Page *ActPage; /* pagina attiva */ TKey KKey; /* valore chiave (input) */ int KeyLen; /* lunghezza della chiave */ TKey KeyOut; /* valore chiave (output) */ RecNoType *IndOut; /* indice associato */ { register int low = 1, j = 0; while (strcmp((char *)(ActPage->PA.AreaForKey + j), KKey) < 0) if (low < ActPage->PA.PageHeader.NKey) { low++; j += (KeyLen + IndLen); } else break; strcpy(KeyOut, (char *)(ActPage->PA.AreaForKey + j)); *IndOut = *((RecNoType *)(ActPage->PA.AreaForKey + j + KeyLen)); return(low); } /* @($) PerformOp B+TREE @(ID) Effettua operazioni di Underflow e Overflow sull'indice. @(FD) @(ISV) Pst = variabile di lavoro contenente numeri chiave. W = contatore. OldKey = variabile di lavoro contenente il valore di chiavi. @(FSV) */ void PerformOp(RecOp,ActPage,Pg,OvUnFlow,Temp,MxKey,MnKey,KeyLen,BError) KOp RecOp; /* struttura contenente le operazioni da effettuare */ Page *ActPage; /* pagina attiva */ RecNoType Pg; /* numero pagina */ BOOLEAN *OvUnFlow;/* underflow opp. overflow */ KeyInd *Temp; /* di lavoro, contiene una coppia chiave-puntatore */ int MxKey; /* numero max chiave */ int MnKey; /* numero min chiave */ int KeyLen; /* lunghezza della chiave */ int *BError; /* codice errore */ { int Pst, W, i; TKey OldKey; for (W = 0; W <= 1; W++) { if (RecOp[W].FlagOp) { RecOp[W].FlagOp = FALSE; if (RecOp[W].Which == Inser) { Pst = 0; while (Pst < ActPage->PA.PageHeader.NKey ) { GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex); if (IntIndex == RecOp[W].where) break; } if (ActPage->PA.PageHeader.NKey == MxKey) { GetAKey(*ActPage, KeyLen, MxKey, IntKey, &IntIndex); if (strcmp(IntKey, RecOp[W].KeyAndInd.KKey) < 0) { strcpy(Temp->KKey, RecOp[W].KeyAndInd.KKey); Temp->Ind = RecOp[W].KeyAndInd.Ind; } else { strcpy(Temp->KKey, IntKey); Temp->Ind = IntIndex; ActPage->PA.PageHeader.NKey--; ShiftPage(RightDir, Pst, OnePos, MxKey, KeyLen, ActPage); PutAKey(RecOp[W].KeyAndInd.KKey, RecOp[W].KeyAndInd.Ind, Pst, KeyLen, ActPage); } *OvUnFlow = TRUE; } else { *OvUnFlow = FALSE; if (strcmp(IntKey, RecOp[W].KeyAndInd.KKey) < 0) { ActPage->PA.PageHeader.NKey++; PutAKey(RecOp[W].KeyAndInd.KKey, RecOp[W].KeyAndInd.Ind, ActPage->PA.PageHeader.NKey, KeyLen, ActPage); RecOp[W].FlagOp = TRUE; RecOp[W].Which = Change; RecOp[W].KeyAndInd.Ind = Pg; } else { ShiftPage(RightDir, Pst, OnePos, MxKey, KeyLen, ActPage); PutAKey(RecOp[W].KeyAndInd.KKey, RecOp[W].KeyAndInd.Ind, Pst, KeyLen, ActPage); } } } else if (RecOp[W].Which == Change) { Pst = 0; GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex); while ((IntIndex != RecOp[W].KeyAndInd.Ind) && (Pst < ActPage->PA.PageHeader.NKey)) GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex); if (IntIndex == RecOp[W].KeyAndInd.Ind) PutAKey(RecOp[W].KeyAndInd.KKey, IntIndex, Pst, KeyLen, ActPage); *OvUnFlow = FALSE; if ((IntIndex == RecOp[W].KeyAndInd.Ind) && (Pst == ActPage->PA.PageHeader.NKey)) { RecOp[W].FlagOp = TRUE; RecOp[W].Which = Change; RecOp[W].KeyAndInd.Ind = Pg; } } else if (RecOp[W].Which == Delet) { Pst = 0; GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex); while ((IntIndex != RecOp[W].KeyAndInd.Ind) && (Pst < ActPage->PA.PageHeader.NKey)) GetAKey(*ActPage, KeyLen, ++Pst, IntKey, &IntIndex); if (IntIndex == RecOp[W].KeyAndInd.Ind) { *OvUnFlow = (ActPage->PA.PageHeader.NKey == MnKey); ShiftPage(LeftDir, ++Pst, OnePos, MxKey, KeyLen, ActPage); } } if ((!OvUnFlow) && (Pst >= ActPage->PA.PageHeader.NKey)) { RecOp[W].FlagOp = TRUE; RecOp[W].Which = Change; GetAKey(*ActPage, KeyLen, ActPage->PA.PageHeader.NKey, IntKey, &IntIndex); strcpy(RecOp[W].KeyAndInd.KKey, IntKey); RecOp[W].KeyAndInd.Ind = Pg; } } } } /* @($) ZrecOp B+TREE @(ID) Azzera la struttura delle operazioni da effettuare. @(FD) @(ISV) i = contatore. @(FSV) */ void ZRecOp(R) KOp R; /* struttura operazioni da azzerare */ { int i; for (i = 0; i <= 1; i++) { R[i].FlagOp = FALSE; R[i].Which = Nothing; R[i].Posit = NoLink; R[i].where = -1; MinKey(MaxArray, R[i].KeyAndInd.KKey); R[i].KeyAndInd.Ind = NoLink; } }