/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.store;

import org.h2.api.ErrorCode;
import org.h2.engine.Session;
import org.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-)
/** * 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: * <ul> * <li>page type: byte (0)</li> * <li>checksum: short (1-2)</li> * <li>previous trunk page, or 0 if none: int (3-6)</li> * <li>log key: int (7-10)</li> * <li>next trunk page: int (11-14)</li> * <li>number of pages: short (15-16)</li> * <li>page ids (17-)</li> * </ul> */
public class PageStreamTrunk extends Page { private static final int DATA_START = 17;
The previous stream trunk.
/** * The previous stream trunk. */
int parent;
The next stream trunk.
/** * 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.
Params:
  • store – the page store
  • data – the data
  • pageId – the page id
Returns:the page
/** * 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.
Params:
  • store – the page store
  • parent – the parent page
  • pageId – the page id
  • next – the next trunk page
  • logKey – the log key
  • pageIds – the stream data page ids
Returns:the page
/** * 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.
/** * 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.
Params:
  • index – the index (0, 1, ...)
Returns:the value, or -1 if the index is too large
/** * 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.
Params:
  • pageSize – the page size
Returns:the number of pages
/** * 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.
Params:
  • dataPageId – the page id
Returns:true if it is
/** * 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.
Params:
  • lastUsedPage – the last used data page
Returns:the number of pages freed
/** * 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.
Returns:number of double words (4 bytes)
/** * 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.
/** * 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.
Returns:the next trunk page or null
/** * 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.
Returns:true if it can be deleted
/** * 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; } }