package com.revolsys.collection.bplus;
import java.util.ArrayList;
import java.util.List;
import com.revolsys.io.page.MethodPageValueManager;
import com.revolsys.io.page.Page;
import com.revolsys.io.page.PageManager;
import com.revolsys.io.page.PageValueManager;
public class BPlusTreePageValueManager<T> implements PageValueManager<T> {
public static <T> PageValueManager<T> newPageValueManager(final PageManager pageManager,
final PageValueManager<T> valueSerializer) {
return new BPlusTreePageValueManager<>(pageManager, valueSerializer);
}
private final PageManager pageManager;
private final PageValueManager<T> valueSerializer;
public BPlusTreePageValueManager(final PageManager pageManager,
final PageValueManager<T> valueSerializer) {
this.pageManager = pageManager;
this.valueSerializer = valueSerializer;
}
@Override
public void disposeBytes(final byte[] bytes) {
final int pageIndex = MethodPageValueManager.getIntValue(bytes);
Page dataPage = this.pageManager.getPage(pageIndex);
dataPage.setOffset(0);
byte pageType = dataPage.readByte();
while (pageType == BPlusTreeMap.EXTENDED) {
BPlusTreeMap.skipHeader(dataPage);
final int nextPageIndex = dataPage.readInt();
this.pageManager.removePage(dataPage);
dataPage = this.pageManager.getPage(nextPageIndex);
dataPage.setOffset(0);
pageType = dataPage.readByte();
}
if (pageType == BPlusTreeMap.DATA) {
this.pageManager.removePage(dataPage);
this.pageManager.releasePage(dataPage);
} else {
throw new IllegalArgumentException(
"Expecting a data page " + BPlusTreeMap.DATA + " not " + pageType);
}
}
@Override
public byte[] getBytes(final Page page) {
return page.readBytes(4);
}
@Override
public byte[] getBytes(final T value) {
final byte[] valueBytes = this.valueSerializer.getBytes(value);
int offset = 0;
final int pageSize = this.pageManager.getPageSize();
Page page = this.pageManager.newPage();
try {
final int pageIndex = page.getIndex();
while (valueBytes.length + 3 > offset + pageSize) {
final Page nextPage = this.pageManager.newPage();
BPlusTreeMap.writePageHeader(page, BPlusTreeMap.EXTENDED);
page.writeInt(nextPage.getIndex());
page.writeBytes(valueBytes, offset, pageSize - 7);
BPlusTreeMap.setNumBytes(page);
this.pageManager.releasePage(page);
page = nextPage;
offset += pageSize - 7;
}
BPlusTreeMap.writePageHeader(page, BPlusTreeMap.DATA);
page.writeBytes(valueBytes, offset, valueBytes.length - offset);
BPlusTreeMap.setNumBytes(page);
return PageValueManager.INT.getBytes(pageIndex);
} finally {
this.pageManager.releasePage(page);
}
}
@Override
public <V extends T> V getValue(final byte[] indexBytes) {
final int pageIndex = MethodPageValueManager.getIntValue(indexBytes);
Page dataPage = this.pageManager.getPage(pageIndex);
try {
dataPage.setOffset(0);
byte pageType = dataPage.readByte();
final List<byte[]> pageBytes = new ArrayList<>();
int size = 0;
while (pageType == BPlusTreeMap.EXTENDED) {
final int numBytes = dataPage.readShort() - 7;
final int nextPageIndex = dataPage.readInt();
final byte[] bytes = dataPage.readBytes(numBytes);
pageBytes.add(bytes);
size += bytes.length;
this.pageManager.releasePage(dataPage);
dataPage = this.pageManager.getPage(nextPageIndex);
dataPage.setOffset(0);
pageType = dataPage.readByte();
}
if (pageType == BPlusTreeMap.DATA) {
final int numBytes = dataPage.readShort() - 3;
final byte[] bytes = dataPage.readBytes(numBytes);
pageBytes.add(bytes);
size += bytes.length;
} else {
throw new IllegalArgumentException(
"Expecting a data page " + BPlusTreeMap.DATA + " not " + pageType);
}
final byte[] valueBytes = new byte[size];
int offset = 0;
for (final byte[] bytes : pageBytes) {
System.arraycopy(bytes, 0, valueBytes, offset, bytes.length);
offset += bytes.length;
}
return this.valueSerializer.getValue(valueBytes);
} finally {
this.pageManager.releasePage(dataPage);
}
}
//
// public <V extends T> V removeFromPage(Page page) {
// final int pageIndex = page.readInt();
// Page dataPage = pageManager.getPage(pageIndex);
// dataPage.setOffset(0);
// byte pageType = dataPage.readByte();
// List<byte[]> pageBytes = new ArrayList<byte[]>();
// int size = 0;
// while (pageType == BPlusTreeMap.EXTENDED) {
// int numBytes = dataPage.readShort() - 7;
// int nextPageIndex = dataPage.readInt();
// byte[] bytes = dataPage.readBytes(numBytes);
// pageBytes.add(bytes);
// size += bytes.length;
// pageManager.removePage(dataPage);
// dataPage = pageManager.getPage(nextPageIndex);
// dataPage.setOffset(0);
// pageType = dataPage.readByte();
// }
// if (pageType == BPlusTreeMap.DATA) {
// final int numBytes = dataPage.readShort() - 3;
// final byte[] bytes = dataPage.readBytes(numBytes);
// pageBytes.add(bytes);
// size += bytes.length;
// pageManager.removePage(dataPage);
// } else {
// throw new IllegalArgumentException("Expecting a data page "
// + BPlusTreeMap.DATA + " not " + pageType);
// }
// byte[] valueBytes = new byte[size];
// int offset = 0;
// for (byte[] bytes : pageBytes) {
// System.arraycopy(bytes, 0, valueBytes, offset, bytes.length);
// offset += bytes.length;
// }
// return valueSerializer.readFromByteArray(valueBytes);
// }
@Override
public <V extends T> V readFromPage(final Page page) {
final byte[] indexBytes = getBytes(page);
return getValue(indexBytes);
}
}