org.glowroot.shaded.h2.store.PageStreamTrunk Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.glowroot.shaded.h2.store;
import org.glowroot.shaded.h2.api.ErrorCode;
import org.glowroot.shaded.h2.engine.Session;
import org.glowroot.shaded.h2.message.DbException;
/**
* A trunk page of a stream. It contains the page numbers of the stream, and the
* page number of the next trunk. The format is:
*
* - page type: byte (0)
* - checksum: short (1-2)
* - previous trunk page, or 0 if none: int (3-6)
* - log key: int (7-10)
* - next trunk page: int (11-14)
* - number of pages: short (15-16)
* - page ids (17-)
*
*/
public class PageStreamTrunk extends Page {
private static final int DATA_START = 17;
/**
* The previous stream trunk.
*/
int parent;
/**
* The next stream trunk.
*/
int nextTrunk;
private final PageStore store;
private int logKey;
private int[] pageIds;
private int pageCount;
private Data data;
private PageStreamTrunk(PageStore store, int parent, int pageId, int next,
int logKey, int[] pageIds) {
setPos(pageId);
this.parent = parent;
this.store = store;
this.nextTrunk = next;
this.logKey = logKey;
this.pageCount = pageIds.length;
this.pageIds = pageIds;
}
private PageStreamTrunk(PageStore store, Data data, int pageId) {
setPos(pageId);
this.data = data;
this.store = store;
}
/**
* Read a stream trunk page.
*
* @param store the page store
* @param data the data
* @param pageId the page id
* @return the page
*/
static PageStreamTrunk read(PageStore store, Data data, int pageId) {
PageStreamTrunk p = new PageStreamTrunk(store, data, pageId);
p.read();
return p;
}
/**
* Create a new stream trunk page.
*
* @param store the page store
* @param parent the parent page
* @param pageId the page id
* @param next the next trunk page
* @param logKey the log key
* @param pageIds the stream data page ids
* @return the page
*/
static PageStreamTrunk create(PageStore store, int parent, int pageId,
int next, int logKey, int[] pageIds) {
return new PageStreamTrunk(store, parent, pageId, next, logKey, pageIds);
}
/**
* Read the page from the disk.
*/
private void read() {
data.reset();
data.readByte();
data.readShortInt();
parent = data.readInt();
logKey = data.readInt();
nextTrunk = data.readInt();
pageCount = data.readShortInt();
pageIds = new int[pageCount];
for (int i = 0; i < pageCount; i++) {
pageIds[i] = data.readInt();
}
}
/**
* Get the data page id at the given position.
*
* @param index the index (0, 1, ...)
* @return the value, or -1 if the index is too large
*/
int getPageData(int index) {
if (index >= pageIds.length) {
return -1;
}
return pageIds[index];
}
@Override
public void write() {
data = store.createData();
data.writeByte((byte) Page.TYPE_STREAM_TRUNK);
data.writeShortInt(0);
data.writeInt(parent);
data.writeInt(logKey);
data.writeInt(nextTrunk);
data.writeShortInt(pageCount);
for (int i = 0; i < pageCount; i++) {
data.writeInt(pageIds[i]);
}
store.writePage(getPos(), data);
}
/**
* Get the number of pages that can be addressed in a stream trunk page.
*
* @param pageSize the page size
* @return the number of pages
*/
static int getPagesAddressed(int pageSize) {
return (pageSize - DATA_START) / 4;
}
/**
* Check if the given data page is in this trunk page.
*
* @param dataPageId the page id
* @return true if it is
*/
boolean contains(int dataPageId) {
for (int i = 0; i < pageCount; i++) {
if (pageIds[i] == dataPageId) {
return true;
}
}
return false;
}
/**
* Free this page and all data pages. Pages after the last used data page
* (if within this list) are empty and therefore not just freed, but marked
* as not used.
*
* @param lastUsedPage the last used data page
* @return the number of pages freed
*/
int free(int lastUsedPage) {
store.free(getPos(), false);
int freed = 1;
boolean notUsed = false;
for (int i = 0; i < pageCount; i++) {
int page = pageIds[i];
if (notUsed) {
store.freeUnused(page);
} else {
store.free(page, false);
}
freed++;
if (page == lastUsedPage) {
notUsed = true;
}
}
return freed;
}
/**
* Get the estimated memory size.
*
* @return number of double words (4 bytes)
*/
@Override
public int getMemory() {
return store.getPageSize() >> 2;
}
@Override
public void moveTo(Session session, int newPos) {
// not required
}
int getLogKey() {
return logKey;
}
public int getNextTrunk() {
return nextTrunk;
}
/**
* An iterator over page stream trunk pages.
*/
static class Iterator {
private final PageStore store;
private int first;
private int next;
private int previous;
private boolean canDelete;
private int current;
Iterator(PageStore store, int first) {
this.store = store;
this.next = first;
}
int getCurrentPageId() {
return current;
}
/**
* Get the next trunk page or null if no next trunk page.
*
* @return the next trunk page or null
*/
PageStreamTrunk next() {
canDelete = false;
if (first == 0) {
first = next;
} else if (first == next) {
return null;
}
if (next == 0 || next >= store.getPageCount()) {
return null;
}
Page p;
current = next;
try {
p = store.getPage(next);
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.FILE_CORRUPTED_1) {
// wrong checksum means end of stream
return null;
}
throw e;
}
if (p == null || p instanceof PageStreamTrunk ||
p instanceof PageStreamData) {
canDelete = true;
}
if (!(p instanceof PageStreamTrunk)) {
return null;
}
PageStreamTrunk t = (PageStreamTrunk) p;
if (previous > 0 && t.parent != previous) {
return null;
}
previous = next;
next = t.nextTrunk;
return t;
}
/**
* Check if the current page can be deleted. It can if it's empty, a
* stream trunk, or a stream data page.
*
* @return true if it can be deleted
*/
boolean canDelete() {
return canDelete;
}
}
@Override
public boolean canRemove() {
return true;
}
@Override
public String toString() {
return "page[" + getPos() + "] stream trunk key:" + logKey +
" next:" + nextTrunk;
}
@Override
public boolean canMove() {
return false;
}
}