/** * Creates a new root block for the B-tree. The new root will have two children: the old root, and * the specified block. Since the root must always be in block 0 of the file, the contents of the * old root will get transferred to a new block. * * @param e the directory entry to be added as a child of the new root */ public void makeNewRoot(DirEntry e) { Constant firstval = contents.getDataVal(0); int level = contents.getFlag(); Block newblk = contents.split(0, level); // ie, transfer all the records DirEntry oldroot = new DirEntry(firstval, newblk.number()); insertEntry(oldroot); insertEntry(e); contents.setFlag(level + 1); }
/** * Returns the block number of the B-tree leaf block that contains the specified search key. * * @param searchkey the search key value * @return the block number of the leaf block containing that search key */ public int search(Constant searchkey) { Block childblk = findChildBlock(searchkey); while (contents.getFlag() > 0) { contents.close(); contents = new BTreePage(childblk, ti, tx); childblk = findChildBlock(searchkey); } return childblk.number(); }
private long save() { int currentUnsavedMemory = unsavedMemory; long version = ++currentVersion; long time = getTimeSinceCreation(); lastCommitTime = time; WriteBuffer buff = getWriteBuffer(); BTreeChunk c = new BTreeChunk(++lastChunkId); chunks.put(c.id, c); c.time = time; c.version = version; c.pagePositions = new ArrayList<Long>(); BTreePage p = map.root; if (p.getTotalCount() > 0) { p.writeUnsavedRecursive(c, buff); c.rootPagePos = p.getPos(); p.writeEnd(); } c.pagePositionsOffset = buff.position(); for (long pos : c.pagePositions) buff.putLong(pos); int chunkBodyLength = buff.position(); chunkBodyLength = MathUtils.roundUpInt(chunkBodyLength, BLOCK_SIZE); buff.limit(chunkBodyLength); buff.position(0); c.blockCount = chunkBodyLength / BLOCK_SIZE + CHUNK_HEADER_BLOCKS; // include chunk header(2 blocks). c.fileStorage = getFileStorage(c.id); // chunk header writeChunkHeader(c); // chunk body write(c.fileStorage, CHUNK_HEADER_SIZE, buff.getBuffer()); c.fileStorage.sync(); for (BTreeChunk chunk : chunks.values()) { if (chunk.changed) { writeChunkHeader(chunk); chunk.fileStorage.sync(); chunk.changed = false; } } releaseWriteBuffer(buff); // some pages might have been changed in the meantime (in the newest version) unsavedMemory = Math.max(0, unsavedMemory - currentUnsavedMemory); lastStoredVersion = version - 1; return version; }
/** * Inserts a new directory entry into the B-tree block. If the block is at level 0, then the entry * is inserted there. Otherwise, the entry is inserted into the appropriate child node, and the * return value is examined. A non-null return value indicates that the child node split, and so * the returned entry is inserted into this block. If this block splits, then the method similarly * returns the entry information of the new block to its caller; otherwise, the method returns * null. * * @param e the directory entry to be inserted * @return the directory entry of the newly-split block, if one exists; otherwise, null */ public DirEntry insert(DirEntry e) { if (contents.getFlag() == 0) return insertEntry(e); Block childblk = findChildBlock(e.dataVal()); BTreeDir child = new BTreeDir(childblk, ti, tx); DirEntry myentry = child.insert(e); child.close(); return (myentry != null) ? insertEntry(myentry) : null; }
/** * Read a page. * * @param pos the page position * @return the page */ BTreePage readPage(long pos) { if (pos == 0) { throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, "Position 0"); } BTreePage p = cache == null ? null : cache.get(pos); if (p == null) { BTreeChunk c = getChunk(pos); long filePos = CHUNK_HEADER_SIZE + DataUtils.getPageOffset(pos); if (filePos < 0) { throw DataUtils.newIllegalStateException( DataUtils.ERROR_FILE_CORRUPT, "Negative position {0}", filePos); } long maxPos = c.blockCount * BLOCK_SIZE; p = BTreePage.read(c.fileStorage, pos, map, filePos, maxPos); cachePage(pos, p, p.getMemory()); } return p; }
private DirEntry insertEntry(DirEntry e) { int newslot = 1 + contents.findSlotBefore(e.dataVal()); contents.insertDir(newslot, e.dataVal(), e.blockNumber()); if (!contents.isFull()) return null; // else page is full, so split it int level = contents.getFlag(); int splitpos = contents.getNumRecs() / 2; Constant splitval = contents.getDataVal(splitpos); Block newblk = contents.split(splitpos, level); return new DirEntry(splitval, newblk.number()); }
/** * Compact the storage by moving all live pages to new chunks. * * @return if anything was written */ public synchronized boolean compactRewriteFully() { checkOpen(); if (lastChunk == null) { // nothing to do return false; } BTreeCursor<?, ?> cursor = (BTreeCursor<?, ?>) map.cursor(null); BTreePage lastPage = null; while (cursor.hasNext()) { cursor.next(); BTreePage p = cursor.getPage(); if (p == lastPage) { continue; } Object k = p.getKey(0); Object v = p.getValue(0); map.put(k, v); lastPage = p; } commitAndSave(); // TODO 删除之前的所有chunk return true; }
/** Closes the directory page. */ public void close() { contents.close(); }
private Block findChildBlock(Constant searchkey) { int slot = contents.findSlotBefore(searchkey); if (contents.getDataVal(slot + 1).equals(searchkey)) slot++; int blknum = contents.getChildNum(slot); return new Block(filename, blknum); }