/* * BBoom.java - Joris van Rantwijk * in026 Inleiding databeheer * * B+ boom. */ import java.io.RandomAccessFile; import java.io.IOException; /* * Implementatie van een B+ boom volgens variant-4 uit het dictaat. * * De interne knopen worden opgeslagen in een indexfile. Elke knoop * komt in een aparte pagina in de file. De eerste pagina van de indexfile * heeft een afwijkend formaat: hierin staat eerst de hoogte van de boom, * en daarna het paginanummer van de wortel van de boom. * Een interne knoop kan maximaal 2*k opvolgers hebben. Voor elke opvolger * worden twee getallen opgeslagen: de grootste sleutelwaarde van de * opvolger en het paginanummer van de opvolger. Als de verwijzing naar * een opvolger niet in gebruik is (de knoop bevat vrije ruimte) dan staan * beide getallen op -1. * De verwijzingen naar opvolgers zijn gesorteerd op oplopende * sleutelwaarde; de ongebruikte verwijzingen komen als laatste. * Of de opvolger een interne of een externe knoop is, kan worden bepaald * aan de hand van de boomhoogte. * * De externe knopen (bladeren) worden opgeslagen in de recordfile. * Elke knoop komt in een aparte pagina in de file. Elke pagina * bevat maximaal bucketsize records. Een vrije positie binnen de pagina * wordt aangegeven met een sleutelwaarde van -1. * De records in een pagina zijn gesorteerd op oplopende sleutelwaarde; * de vrije posities komen als laatste. * In elke pagina staat achter de records ook een getal dat verwijst naar * de volgende pagina. We doen daar in dit voorbeeld niets mee, maar het kan * worden gebruikt voor het sequentieel doorlopen van het bestand. * * Bij het verwijderen van records moeten pagina's soms samengevoegd worden. * Hierdoor kunnen vrije pagina's ontstaan. Dit kan zowel in de index als * in de recordfile gebeuren. In beide files houden we een gelinkte lijst * van vrije pagina's bij, door aan het begin van elke vrije pagina een * verwijzing naar de volgende vrije pagina op te nemen. In een aparte file * bewaren we dan een verwijzing naar de eerste vrije pagina van elke file. */ //=========================================================================== // Class BBoom // Implementatie van B+ boom (variant-4). //=========================================================================== public class BBoom extends BestandsOrganisatie { // De gebruikte files. private RandomAccessFile ifile; private RandomAccessFile rfile; private RandomAccessFile vfile; // De omvang van een pagina in bytes. private final static int PAGESIZE = 1024; // Het maximale aantal records per pagina. private int bucketsize; private int k = PAGESIZE / 16; // Orde van de boom. private int h; // De boomhoogte private int root; // Het paginanummer van de wortel private int ifree; // Eerste vrije indexpagina private int rfree; // Eerste vrije recordpagina // Construeer een BBoom object. // Gebruik de gegeven filenaam, en het gegeven recordtype. //----------------------------------------------------------------------- public BBoom(String filenaam, Class recordType) throws IOException { int i; this.recordType = recordType; this.recordLen = Record.newRecord(recordType).getRecordlen(); // Bereken de bucketsize (het aantal records dat in een pagina past). bucketsize = (PAGESIZE - 4) / recordLen; // Open de benodigde files. Als de files nog niet bestaan, // worden er automatisch lege files aangemaakt. ifile = new RandomAccessFile(filenaam + "i", "rw"); rfile = new RandomAccessFile(filenaam + "r", "rw"); vfile = new RandomAccessFile(filenaam + "v", "rw"); // Controleer de lengte van de indexfile. if (ifile.length() == 0) { // Het bestand bevat nog geen gegevens. Initialiseer het bestand // door het aanmaken van een index bestaande uit alleen een // wortelknoop, en een lege recordpagina. h = 1; root = 1; ifree = -1; rfree = -1; // Schrijf eerste pagina van indexfile. ifile.seek(0); ifile.writeInt(h); ifile.writeInt(root); diskaccess++; // Schrijf wortelpagina. ifile.seek(PAGESIZE); ifile.writeInt(0); ifile.writeInt(0); for (i = 1; i < 2*k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; // Schrijf lege recordpagina. Record r = Record.newRecord(recordType); rfile.seek(0); for (i = 0; i < bucketsize; i++) r.write(rfile); rfile.writeInt(-1); diskaccess++; } else { // Lees alvast de boomhoogte en wortellocatie. ifile.seek(0); h = ifile.readInt(); root = ifile.readInt(); // Lees de verwijzingen naar ketens van vrije pagina's. vfile.seek(0); ifree = vfile.readInt(); rfree = vfile.readInt(); } } // Sluit het bestand. //----------------------------------------------------------------------- public void close() throws IOException { // Schrijf de (mogelijk gewijzigde) verwijzingen naar vrije pagina's. vfile.seek(0); vfile.writeInt(ifree); vfile.writeInt(rfree); // Sluit alle files. ifile.close(); rfile.close(); vfile.close(); } // Alloceer een pagina in de indexfile. // Hergebruik een vrije pagina als dat mogelijk is, voeg anders een // pagina toe aan het eind van de file. //----------------------------------------------------------------------- private int allocPageI() throws IOException { int p; if (ifree != -1) { p = ifree; ifile.seek(ifree * PAGESIZE); ifree = ifile.readInt(); } else { p = (int) ((ifile.length() + PAGESIZE - 1) / PAGESIZE); } return (p); } // Alloceer een pagina in de recordfile. // Hergebruik een vrije pagina als dat mogelijk is, voeg anders een // pagina toe aan het eind van de file. //----------------------------------------------------------------------- private int allocPageR() throws IOException { int p; if (rfree != -1) { p = rfree; rfile.seek(rfree * PAGESIZE); rfree = rfile.readInt(); } else { p = (int) ((rfile.length() + PAGESIZE - 1) / PAGESIZE); } return (p); } // Geef een pagina uit de indexfile vrij door hem toe te voegen // aan de lijst van vrije pagina's. //----------------------------------------------------------------------- private void freePageI(int p) throws IOException { ifile.seek(p * PAGESIZE); ifile.writeInt(ifree); ifree = p; } // Geef een pagina uit de recordfile vrij door hem toe te voegen // aan de lijst van vrije pagina's. //----------------------------------------------------------------------- private void freePageR(int p) throws IOException { rfile.seek(p * PAGESIZE); rfile.writeInt(rfree); rfree = p; } // Zoek het record met sleutelwaarde <key>. // Geef de positie van het record in de rfile terug. // Als het record niet wordt gevonden, geef -1. //----------------------------------------------------------------------- private int findRecord(int key) throws IOException { Record r = Record.newRecord(recordType); int i, j, d, p, a, b; // Begin met zoeken bij de wortel van de boom. p = root; j = -1; // Loop van boven naar beneden door alle niveau's van de boom. for (d = 0; d < h; d++) { ifile.seek(p * PAGESIZE); diskaccess++; // Doorloop de interne knoop met paginanummer p. // Zoek de eerste opvolger waarvan de max.sleutelw >= key. for (i = 0; i < 2*k; i++) { // Lees max.sleutelw en verwijzing. a = ifile.readInt(); b = ifile.readInt(); // Bewaar laatste geldige verwijzing. if (b != -1) j = b; // Kijk of dit de opvolger is die we zoeken. if (a >= key) break; } // j bevat nu het paginanummer van de gekozen opvolger. // Bekijk deze opvolger in de volgende ronde. p = j; } // p bevat nu het paginanummer van de recordpagina waarin // het record zich moet bevinden. rfile.seek(p * PAGESIZE); diskaccess++; for (i = 0; i < bucketsize; i++) { r.read(rfile); if (r.key == key) { // Hoera! We hebben het record gevonden. // Geef de positie terug. return (p * PAGESIZE + i * recordLen); } } // We hebben het record niet gevonden. return (-1); } // Voeg het record <r> toe aan een pagina in de recordfile. // In p[0] staat het nummer van de pagina, in v[0] staat de hoogste // sleutelwaarde van de pagina. // Als de pagina vol is, moet hij eerst gesplitst worden. In dat geval, // geven we in v[0] de hoogste sleutelwaarde van de eerste pagina, in // p[0] het nummer van de 1e pagina, in v[1] de hoogste sleutelwaarde // van de tweede pagina en in p[1] het nummer van de tweede pagina. // Als er geen splitsing nodig was geven we in v[0] de hoogste sleutel // van de pagina, p[0] blijft ongewijzigd, v[1] = -1 en p[1] = -1. //----------------------------------------------------------------------- private void insertExternal(Record r, int p[], int[] v) throws IOException { Record[] pag = new Record[bucketsize + 1]; Record t = Record.newRecord(recordType); int i, next; // Lees de pagina in het geheugen. rfile.seek(p[0] * PAGESIZE); for (i = 0; i < bucketsize; i++) { pag[i] = Record.newRecord(recordType); pag[i].read(rfile); } next = rfile.readInt(); diskaccess++; // Schuif de records op om plaats te maken voor het nieuwe record. for (i = bucketsize; i > 0 && (pag[i-1].key == -1 || pag[i-1].key > r.key); i--) pag[i] = pag[i-1]; // Voeg het nieuwe record tussen. pag[i] = r; // Pas eventueel de maximale sleutelwaarde aan. if (r.key > v[0]) v[0] = r.key; // Bepaal of een splitsing nodig is. if (pag[bucketsize].key != -1) { // De pagina is te vol :-( splits hem in twee pagina's. // In de oude pagina komt de eerste helft van de records, // in de nieuwe pagina de tweede helft van de records. p[1] = allocPageR(); v[1] = v[0]; v[0] = pag[bucketsize / 2].key; // Schrijf de eerste helft van de records. rfile.seek(p[0] * PAGESIZE); for (i = 0; i <= bucketsize / 2; i++) pag[i].write(rfile); // Vul aan met ongeldige records. for (i = bucketsize / 2 + 1; i < bucketsize; i++) t.write(rfile); // Schrijf verwijzing naar de nieuwe pagina. rfile.write(p[1]); diskaccess++; // Schrijf de tweede helft van de records. rfile.seek(p[1] * PAGESIZE); for (i = bucketsize / 2 + 1; i <= bucketsize; i++) pag[i].write(rfile); // Vul aan met ongeldige records. for (i = 0; i < bucketsize / 2; i++) t.write(rfile); // Schrijf verwijzing naar de volgende pagina. rfile.writeInt(next); diskaccess++; } else { // Er is geen splitsing nodig. p[1] = -1; v[1] = -1; // Schrijf de pagina terug naar de record file. rfile.seek(p[0] * PAGESIZE); for (i = 0; i < bucketsize; i++) pag[i].write(rfile); // Schrijf verwijzing naar de volgende pagina. rfile.writeInt(next); diskaccess++; } } // Voeg het record r toe aan de subboom waarvan p[0] de wortel is. // De grootste sleutelwaarde in de knoop p[0] is v[0]. We bevinden // ons op diepte d in de boom. // Misschien moet de knoop gesplitst worden. In dat geval geven we // in v[0] de hoogste sleutelwaarde van de 1e deelknoop, in p[0] het // pagina nummer van de 1e knoop, in v[1] de hoogste sleutelwaarde van // de 2e knoop, in p[1] het paginanummer van de 2e knoop. // Als er geen splitsing nodig was geven we in v[0] de hoogste sleutel // van de knoop, p[0] is ongewijzigd en v[1] = -1 en p[1] = -1. //----------------------------------------------------------------------- private void insertInternal(Record r, int[] p, int[] v, int d) throws IOException { boolean change = false; int[] opvv = new int[2*k + 1]; int[] opvp = new int[2*k + 1]; int[] newp = new int[2]; int[] newv = new int[2]; int i, j; // Lees de knoop in het geheugen. ifile.seek(p[0] * PAGESIZE); for (i = 0; i < 2*k; i++) { opvv[i] = ifile.readInt(); opvp[i] = ifile.readInt(); } diskaccess++; opvv[2*k] = -1; opvp[2*k] = -1; // Bepaal in welke opvolger het record moet worden toegevoegd. j = -1; for (i = 0; i < 2*k; i++) { // Bewaar de laatste geldige verwijzing. if (opvv[i] != -1) j = i; // Is dit de juiste opvolger ? if (opvv[i] >= r.key) break; } // j bevat nu het nummer van de gekozen opvolger. newp[0] = opvp[j]; newv[0] = opvv[j]; // Voeg het record toe in de gekozen opvolger. // Als we onder in de boom zijn, is de opvolger een recordpagina. if (d == h) insertExternal(r, newp, newv); else insertInternal(r, newp, newv, d + 1); // Pas eventueel de maximale sleutelwaarde aan. if (r.key > v[0]) v[0] = r.key; // Ga na of de opvolgerknoop gesplitst is. if (newp[1] != -1) { // De opvolger is gesplitst, de nieuwe opvolgerknoop moet // worden toegevoegd in deze knoop. // Schuif de opvolgers op in de array. for (i = 2*k - 1; i > j; i--) { opvp[i+1] = opvp[i]; opvv[i+1] = opvv[i]; } // Wijzig de waarden van de gesplitste opvolger, voeg de // nieuwe opvolger tussen. opvp[j] = newp[0]; opvv[j] = newv[0]; opvp[j+1] = newp[1]; opvv[j+1] = newv[1]; // De inhoud van deze pagina is gewijzigd. change = true; } else { // Er was geen splitsing in de opvolger. if (newp[0] != opvp[j] || newv[0] != opvv[j]) { // De gegevens van de opvolger zijn gewijzigd. // Registreer de veranderingen in deze knoop. opvp[j] = newp[0]; opvv[j] = newv[0]; change = true; } } // Bepaal of een splitsing nodig is. if (opvp[2*k] != -1) { // De knoop is te vol. Splits hem in twee knopen. // In de oude knoop komt de eerste helft van de opvolgers, // de andere helft komt in een nieuwe knoop. p[1] = allocPageI(); v[1] = v[0]; v[0] = opvv[k]; // Schrijf de eerste helft van de opvolgers. ifile.seek(p[0] * PAGESIZE); for (i = 0; i <= k; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } // Vul aan met lege verwijzingen. for (i = k+1; i < 2*k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; // Schrijf de tweede helft van de opvolgers. ifile.seek(p[1] * PAGESIZE); for (i = k + 1; i <= 2*k; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } // Vul aan met lege verwijzingen. for (i = 0; i < k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; } else { // Er is geen splitsing nodig. p[1] = -1; v[1] = -1; // Schrijf de knoop alleen als er veranderingen waren. if (change) { ifile.seek(p[0] * PAGESIZE); for (i = 0; i < 2*k; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } diskaccess++; } } } // Probeer de twee record pagina's p[0] en p[1] samen te voegen. // Als dit niet mogelijk is, zorg er dan in elk geval voor dat beide // pagina's ongeveer evenveel records bevatten. v[0] en v[1] bevatten de // hoogste sleutelwaardes, deze moeten eventueel gewijzigd worden. // Na samenvoegen geven we p[1] = -1 en v[1] = -1 terug. //----------------------------------------------------------------------- private void joinExternal(int[] p, int[] v) throws IOException { Record[] rec = new Record[2*bucketsize]; Record t = Record.newRecord(recordType); int next1, next2, nrec; int i; // Lees de records uit beide pagina's en stop ze bij elkaar. // Tel het aantal records. nrec = 0; rfile.seek(p[0] * PAGESIZE); for (i = 0; i < bucketsize; i++) { Record r = Record.newRecord(recordType); r.read(rfile); if (r.key != -1) { rec[nrec] = r; nrec++; } } next1 = rfile.readInt(); diskaccess++; rfile.seek(p[1] * PAGESIZE); for (i = 0; i < bucketsize; i++) { Record r = Record.newRecord(recordType); r.read(rfile); if (r.key != -1) { rec[nrec] = r; nrec++; } } next2 = rfile.readInt(); diskaccess++; // Bepaal of alle records in 1 pagina passen. if (nrec <= bucketsize) { // Stop alle records in 1 pagina. rfile.seek(p[0] * PAGESIZE); for (i = 0; i < nrec; i++) rec[i].write(rfile); for (i = nrec; i < bucketsize; i++) t.write(rfile); rfile.writeInt(next2); // Geef de andere pagina vrij. freePageR(p[1]); // Update de administratie van de aanroepende functie. v[0] = rec[nrec-1].key; p[1] = -1; v[1] = -1; } else { // Verdeel de records netjes over 2 pagina's. // In de 1e pagina komen nrec/2 records. rfile.seek(p[0] * PAGESIZE); for (i = 0; i < nrec/2; i++) rec[i].write(rfile); for (i = nrec/2; i < bucketsize; i++) t.write(rfile); rfile.writeInt(next1); diskaccess++; // In de 2e pagina knoop komt de rest. rfile.seek(p[1] * PAGESIZE); for (i = nrec/2; i < nrec; i++) rec[i].write(rfile); for (i = nrec - nrec/2; i < bucketsize; i++) t.write(rfile); rfile.writeInt(next2); diskaccess++; // Update de administratie van de aanroepende functie. v[0] = rec[nrec/2-1].key; v[1] = rec[nrec-1].key; } } // Probeer de twee interne knopen p[0] en p[1] samen te voegen. // Als dit niet mogelijk is, zorg er dan in elk geval voor dat beide // knopen ongeveer evenveel opvolgers hebben. v[0] en v[1] bevatten de // hoogste sleutelwaardes, deze moeten eventueel gewijzigd worden. // Na samenvoegen geven we p[1] = -1 en v[1] = -1 terug. //----------------------------------------------------------------------- private void joinInternal(int[] p, int[] v) throws IOException { int[] opvv = new int[2*2*k]; int[] opvp = new int[2*2*k]; int nopv; int i; // Lees de opvolgers van beide knopen en stop ze bij elkaar. // Tel het totaal aantal opvolgers. nopv = 0; ifile.seek(p[0] * PAGESIZE); for (i = 0; i < 2*k; i++) { int a = ifile.readInt(); int b = ifile.readInt(); if (a != -1) { opvv[nopv] = a; opvp[nopv] = b; nopv++; } } diskaccess++; ifile.seek(p[1] * PAGESIZE); for (i = 0; i < 2*k; i++) { int a = ifile.readInt(); int b = ifile.readInt(); if (a != -1) { opvv[nopv] = a; opvp[nopv] = b; nopv++; } } diskaccess++; // Bepaal of de opvolgers in 1 knoop passen. if (nopv <= 2*k) { // Stop alle opvolgers in 1 knoop. ifile.seek(p[0] * PAGESIZE); for (i = 0; i < nopv; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } for (i = nopv; i < 2*k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; // Geef de andere knoop vrij. freePageI(p[1]); // Update de administratie van de aanroepende functie. v[0] = opvv[nopv-1]; p[1] = -1; v[1] = -1; } else { // Verdeel de opvolgers netjes over twee knopen. // In de eerste knoop komen nopv/2 opvolgers. ifile.seek(p[0] * PAGESIZE); for (i = 0; i < nopv/2; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } for (i = nopv/2; i < 2*k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; // In de tweede knoop komt de rest. ifile.seek(p[1] * PAGESIZE); for (i = nopv/2; i < nopv; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } for (i = nopv - nopv/2; i < 2*k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; // Update de administratie van de aanroepende functie. v[0] = opvv[nopv/2-1]; v[1] = opvv[nopv-1]; } } // Verwijder het record met sleute key uit een pagina in de recordfile. // Geef true als de pagina na de verwijdering minder dan halfvol is. // Geef een exceptie als het record niet in de opgegeven pagina zit. //----------------------------------------------------------------------- private boolean removeExternal(int key, int p) throws Exception, IOException { Record[] pag = new Record[bucketsize]; Record t = Record.newRecord(recordType); int i, next; // Lees de pagina in het geheugen. rfile.seek(p * PAGESIZE); for (i = 0; i < bucketsize; i++) { pag[i] = Record.newRecord(recordType); pag[i].read(rfile); } next = rfile.readInt(); diskaccess++; // Zoek het record dat we moeten verwijderen. for (i = 0; i < bucketsize; i++) if (pag[i].key == key) break; if (i == bucketsize) { // Het record bevindt zich niet in de pagina. throw new Exception("Record niet gevonden"); } // Verwijder het record, en schuif de volgende records een plaats op. while (i+1 < bucketsize) { pag[i] = pag[i+1]; i++; } pag[bucketsize-1] = t; // Schrijf de pagina terug naar de record file. rfile.seek(p * PAGESIZE); for (i = 0; i < bucketsize; i++) pag[i].write(rfile); // Schrijf verwijzing naar de volgende pagina. rfile.writeInt(next); diskaccess++; // Geef aan de aanroeper door of deze pagina nu voor minder dan de // helft vol is. return (pag[(bucketsize-1)/2].key == -1); } // Verwijder het record met sleutel key uit de subboom waarvan p de // wortel is. We bevinden ons op diepte d in de boom. // Geef een exceptie als het record niet gevonden wordt. //----------------------------------------------------------------------- private boolean removeInternal(int key, int p, int d) throws Exception, IOException { int[] opvv = new int[2*k]; int[] opvp = new int[2*k]; int[] newp = new int[2]; int[] newv = new int[2]; boolean needjoin; int i, j; // Lees de knoop in het geheugen. ifile.seek(p * PAGESIZE); for (i = 0; i < 2*k; i++) { opvv[i] = ifile.readInt(); opvp[i] = ifile.readInt(); } diskaccess++; // Bepaal in welke subboom het record zich moet bevinden. j = -1; for (i = 0; i < 2*k; i++) { // Bewaar de laatste geldige verwijzing. if (opvv[i] != -1) j = i; // Is dit de juiste opvolger ? if (opvv[i] >= key) break; } // j bevat nu het nummer van de gekozen opvolger. // Als we onder in de boom zijn, is de opvolger een externe knoop. if (d == h) needjoin = removeExternal(key, opvp[j]); else needjoin = removeInternal(key, opvp[j], d + 1); // Als de opvolger nog minstens halfvol is, hoeven we verder // niets te veranderen. if (!needjoin) return false; // De opvolger is voor minder dan de helft vol. Om de minimale // vullingsgraad te herstellen, combineren we hem met een andere // opvolger. if (j > 0) { // Combineer de opvolger met zijn linker buurman. newp[0] = opvp[j-1]; newv[0] = opvv[j-1]; newp[1] = opvp[j]; newv[1] = opvv[j]; if (d == h) joinExternal(newp, newv); else joinInternal(newp, newv); opvp[j-1] = newp[0]; opvv[j-1] = newv[0]; opvp[j] = newp[1]; opvv[j] = newv[1]; // Als de twee opvolgers samengevoegd zijn, schuiven we de // volgende opvolgers een plaats op. if (opvp[j] == -1) { for (i = j; i+1 < 2*k; i++) { opvp[i] = opvp[i+1]; opvv[i] = opvv[i+1]; } opvp[2*k-1] = -1; opvv[2*k-1] = -1; } } else if (opvp[j+1] != -1) { // Combineer de opvolger met zijn rechter buurman. newp[0] = opvp[j]; newv[0] = opvv[j]; newp[1] = opvp[j+1]; newv[1] = opvv[j+1]; if (d == h) joinExternal(newp, newv); else joinInternal(newp, newv); opvp[j] = newp[0]; opvv[j] = newv[0]; opvp[j+1] = newp[1]; opvv[j+1] = newv[1]; // Als de twee opvolgers samengevoegd zijn, schuiven we de // volgende opvolgers een plaats op. if (opvp[j+1] == -1) { for (i = j+1; i+1 < 2*k; i++) { opvp[i] = opvp[i+1]; opvv[i] = opvv[i+1]; } opvp[2*k-1] = -1; opvv[2*k-1] = -1; } } // Schrijf de knoop terug naar de index file. ifile.seek(p * PAGESIZE); for (i = 0; i < 2*k; i++) { ifile.writeInt(opvv[i]); ifile.writeInt(opvp[i]); } diskaccess++; // Geef aan de aanroeper door of deze knoop nu voor minder dan de // helft vol is. return (opvp[k-1] == -1); } // Voeg het record r toe. //----------------------------------------------------------------------- public void insert(Record r) throws Exception, IOException { int[] newp = new int[2]; int[] newv = new int[2]; int i; // Controleer of het record nog niet bestaat. if ( findRecord(r.key) != -1 ) { // Het record bestaat al, geef een exceptie. throw new Exception("Record bestaat al"); } // Voeg het record toe aan de boom. newp[0] = root; newv[0] = 0; // Deze waarde is niet van belang. insertInternal(r, newp, newv, 1); // Ga na of de wortel gesplitst is. if (newp[1] != -1) { // De wortel van de boom is gesplitst. // Maak een nieuwe wortelknoop. // Alloceer een nieuwe wortelpagina, // verhoog de diepte van de boom. root = allocPageI(); h++; // Schrijf naar de nieuwe wortelpagina verwijzingen naar de // twee knopen die uit de oude wortel zijn ontstaan. ifile.seek(root * PAGESIZE); ifile.writeInt(newv[0]); ifile.writeInt(newp[0]); ifile.writeInt(newv[1]); ifile.writeInt(newp[1]); // Vul aan met lege verwijzingen. for (i = 2; i < 2*k; i++) { ifile.writeInt(-1); ifile.writeInt(-1); } diskaccess++; // Update eerste pagina van indexfile. ifile.seek(0); ifile.writeInt(h); ifile.writeInt(root); diskaccess++; } } // Vervang een bestaand record door r. //----------------------------------------------------------------------- public void update(Record r) throws Exception, IOException { int p; // Bepaal de positie van het record. p = findRecord(r.key); if (p == -1) { // Het record bestaat niet, geef een exceptie. throw new Exception("Record niet gevonden"); } // Overschrijf het record met <r>. rfile.seek(p); r.write(rfile); diskaccess++; } // Zoek het record met de sleutel key. //----------------------------------------------------------------------- public Record retrieve(int key) throws IOException { Record r = Record.newRecord(recordType); int p; // Bepaal de positie van het record. p = findRecord(key); if (p == -1) { // Het record bestaat niet, geef null terug. return null; } // Lees het record. rfile.seek(p); r.read(rfile); diskaccess++; // Geef het record terug. return r; } // Verwijder het record met de sleutel key. //----------------------------------------------------------------------- public void delete(int key) throws Exception, IOException { int[] opvp = new int[2*k]; int[] opvv = new int[2*k]; boolean b; int i; // Verwijder het record uit de boom. b = removeInternal(key, root, 1); if ( b && h > 1 ) { // Als de wortel slechts 1 interne knoop als opvolger heeft, // kan deze opvolger de nieuwe wortel worden. // Lees de wortelpagina. ifile.seek(root * PAGESIZE); for (i = 0; i < 2*k; i++) { opvv[i] = ifile.readInt(); opvp[i] = ifile.readInt(); } diskaccess++; if (opvp[1] == -1) { // De wortel heeft slechts 1 opvolger. // Verwijder de oude wortelpagina. freePageI(root); // De opvolger van de oude wortel wordt de nieuwe wortel. // De hoogte van de boom vermindert daardoor. root = opvp[0]; h--; // Update eerste pagina van indexfile. ifile.seek(0); ifile.writeInt(h); ifile.writeInt(root); diskaccess++; } } } } //=========================================================================== // End //===========================================================================