提交 b9db1cc2 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: unified exception handling; the version is included in the messages.…

MVStore: unified exception handling; the version is included in the messages. Old data is now retained for 45 seconds by default.
上级 9455decf
......@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>MVStore: compress is now disabled by default, and can be enabled on request.
<ul><li>MVStore: unified exception handling; the version is included in the messages.
</li><li>MVStore: old data is now retained for 45 seconds by default.
</ul><li>MVStore: compress is now disabled by default, and can be enabled on request.
</li></ul>
<h2>Version 1.3.170 (2012-11-30)</h2>
......
......@@ -138,7 +138,7 @@ encrypted using AES-128 and XTEA encryption algorithms
<h3>Other Features and Tools</h3>
<ul>
<li>Small footprint (smaller than 1 MB), low memory requirements
<li>Small footprint (smaller than 1.5 MB), low memory requirements
</li><li>Multiple index types (b-tree, tree, hash)
</li><li>Support for multi-dimensional indexes
</li><li>CSV (comma separated values) file support
......@@ -350,9 +350,9 @@ H2 1.3,
<td class="compareY">Yes</td>
</tr><tr>
<td>Footprint (jar/dll size)</td>
<td>~1 MB *5</td>
<td>~2 MB</td>
<td>~1 MB</td>
<td>~1.5 MB *5</td>
<td>~3 MB</td>
<td>~1.5 MB</td>
<td>~4 MB</td>
<td>~6 MB</td>
</tr>
......
......@@ -27,7 +27,7 @@ Welcome to H2, the Java SQL database. The main features of H2 are:
<li>Very fast, open source, JDBC API
</li><li>Embedded and server modes; in-memory databases
</li><li>Browser based Console application
</li><li>Small footprint: around 1 MB jar file size
</li><li>Small footprint: around 1.5 MB jar file size
</li></ul>
<table style="border: 0px; width: 470px;">
......
......@@ -327,6 +327,22 @@ with a fluent API to simplify building a store instance.
There is a tool (<code>MVStoreTool</code>) to dump the contents of a file.
</p>
<h3>Exception Handling</h3>
<p>
This tool does not throw checked exceptions.
Instead, unchecked exceptions are thrown if needed.
The error message always contains the version of the tool.
The following exceptions can occur:
</p>
<ul><li><code>IllegalStateException</code> if a map was already closed,
in IO exception occurred, for example if the file was locked, is already closed,
could not be opened or closed, if reading or writing failed,
if the file is corrupt, or if there is an internal error in the tool.
</li><li><code>IllegalArgumentException</code> if a method was called with an illegal argument.
</li><li><code>UnsupportedOperationException</code> if a method was called that is not supported,
for example trying to modify a read-only map or view.
</ul>
<h2 id="differences">Similar Projects and Differences to Other Storage Engines</h2>
<p>
Unlike similar storage engines like LevelDB and Kyoto Cabinet, the MVStore is written in Java
......
......@@ -53,7 +53,7 @@ public class ChangeCursor<K, V> implements Iterator<K> {
}
public void remove() {
throw new UnsupportedOperationException();
throw DataUtils.unsupportedOperationException("Removing is not supported");
}
private void fetchNext() {
......
......@@ -89,7 +89,7 @@ public class Chunk {
*/
static Chunk fromHeader(ByteBuffer buff, long start) {
if (buff.get() != 'c') {
throw new RuntimeException("File corrupt");
throw DataUtils.illegalStateException("File corrupt reading chunk at position " + start);
}
int length = buff.getInt();
int chunkId = buff.getInt();
......
......@@ -67,7 +67,7 @@ public class Cursor<K> implements Iterator<K> {
}
public void remove() {
throw new UnsupportedOperationException();
throw DataUtils.unsupportedOperationException("Removing is not supported");
}
/**
......
......@@ -14,6 +14,7 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import org.h2.engine.Constants;
import org.h2.util.New;
/**
......@@ -304,7 +305,8 @@ public class DataUtils {
* @param pos the absolute position within the file
* @param dst the byte buffer
*/
public static void readFully(FileChannel file, long pos, ByteBuffer dst) throws IOException {
public static void readFully(FileChannel file, long pos, ByteBuffer dst) {
try {
do {
int len = file.read(dst, pos);
if (len < 0) {
......@@ -313,6 +315,9 @@ public class DataUtils {
pos += len;
} while (dst.remaining() > 0);
dst.rewind();
} catch (IOException e) {
throw illegalStateException("Reading from " + file + " failed; length " + dst.remaining() + " at " + pos, e);
}
}
/**
......@@ -322,12 +327,16 @@ public class DataUtils {
* @param pos the absolute position within the file
* @param src the source buffer
*/
public static void writeFully(FileChannel file, long pos, ByteBuffer src) throws IOException {
public static void writeFully(FileChannel file, long pos, ByteBuffer src) {
try {
int off = 0;
do {
int len = file.write(src, pos + off);
off += len;
} while (src.remaining() > 0);
} catch (IOException e) {
throw illegalStateException("Writing to " + file + " failed; length " + src.remaining() + " at " + pos, e);
}
}
......@@ -544,4 +553,25 @@ public class DataUtils {
return (s2 << 16) | s1;
}
public static IllegalStateException illegalStateException(String message) {
return new IllegalStateException(message + version());
}
public static IllegalStateException illegalStateException(String message, Exception e) {
return new IllegalStateException(message + version(), e);
}
public static IllegalArgumentException illegalArgumentException(String message) {
return new IllegalArgumentException(message + version());
}
public static UnsupportedOperationException unsupportedOperationException(String message) {
return new UnsupportedOperationException(message + version());
}
private static String version() {
return " [" + Constants.VERSION_MAJOR + "." +
Constants.VERSION_MINOR + "." + Constants.BUILD_ID + "]";
}
}
......@@ -839,7 +839,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/
protected void checkOpen() {
if (closed) {
throw new IllegalStateException("This map is closed");
throw DataUtils.illegalStateException("This map is closed");
}
}
......@@ -851,7 +851,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
protected void checkWrite() {
if (readOnly) {
checkOpen();
throw new IllegalStateException("This map is read-only");
throw DataUtils.unsupportedOperationException("This map is read-only");
}
}
......@@ -893,10 +893,11 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/
public MVMap<K, V> openVersion(long version) {
if (readOnly) {
throw new IllegalArgumentException("This map is read-only - need to call the method on the writable map");
throw DataUtils.unsupportedOperationException(
"This map is read-only - need to call the method on the writable map");
}
if (version < createVersion) {
throw new IllegalArgumentException("Unknown version");
throw DataUtils.illegalArgumentException("Unknown version");
}
Page newest = null;
// need to copy because it can change
......
......@@ -28,6 +28,7 @@ import org.h2.mvstore.type.StringDataType;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
/*
......@@ -40,8 +41,6 @@ header:
H:3,...
TODO:
- allocate memory with Utils.newBytes and so on
- unified exception handling
- concurrent map; avoid locking during IO (pre-load pages)
- maybe split database into multiple files, to speed up compact
- automated 'kill process' and 'power failure' test
......@@ -90,6 +89,8 @@ TODO:
- auto-save if there are too many changes (required for StreamStore)
- StreamStore optimization: avoid copying bytes
- unlimited transaction size
- MVStoreTool.shrink to shrink a store (create, copy, rename, delete)
-- and for MVStore on Windows, auto-detect renamed file
*/
......@@ -320,7 +321,7 @@ public class MVStore {
private MVMap<String, String> getMetaMap(long version) {
Chunk c = getChunkForVersion(version);
if (c == null) {
throw new IllegalArgumentException("Unknown version: " + version);
throw DataUtils.illegalArgumentException("Unknown version: " + version);
}
c = readChunkHeader(c.start);
MVMap<String, String> oldMeta = meta.openReadOnly();
......@@ -353,10 +354,6 @@ public class MVStore {
if (clazz == String.class) {
return StringDataType.INSTANCE;
}
if (dataTypeFactory == null) {
throw new RuntimeException("No data type factory set " +
"and don't know how to serialize " + clazz);
}
String s = dataTypeFactory.getDataType(clazz);
return dataTypeFactory.buildDataType(s);
}
......@@ -390,12 +387,12 @@ public class MVStore {
if (readOnly) {
fileLock = file.tryLock(0, Long.MAX_VALUE, true);
if (fileLock == null) {
throw new RuntimeException("The file is locked: " + fileName);
throw new IOException("The file is locked: " + fileName);
}
} else {
fileLock = file.tryLock();
if (fileLock == null) {
throw new RuntimeException("The file is locked: " + fileName);
throw new IOException("The file is locked: " + fileName);
}
}
fileSize = file.size();
......@@ -414,8 +411,12 @@ public class MVStore {
}
}
} catch (Exception e) {
try {
close();
throw convert(e);
} catch (Exception e2) {
// ignore
}
throw DataUtils.illegalStateException("Could not open " + fileName, e);
}
}
......@@ -445,12 +446,11 @@ public class MVStore {
}
private void readFileHeader() {
try {
byte[] headers = new byte[2 * BLOCK_SIZE];
fileReadCount++;
DataUtils.readFully(file, 0, ByteBuffer.wrap(headers));
for (int i = 0; i <= BLOCK_SIZE; i += BLOCK_SIZE) {
String s = new String(headers, i, BLOCK_SIZE, "UTF-8").trim();
String s = StringUtils.utf8Decode(headers, i, BLOCK_SIZE).trim();
fileHeader = DataUtils.parseMap(s);
rootChunkStart = Long.parseLong(fileHeader.get("rootChunk"));
creationTime = Long.parseLong(fileHeader.get("creationTime"));
......@@ -458,32 +458,28 @@ public class MVStore {
lastMapId = Integer.parseInt(fileHeader.get("lastMapId"));
int check = (int) Long.parseLong(fileHeader.get("fletcher"), 16);
s = s.substring(0, s.lastIndexOf("fletcher") - 1) + " ";
byte[] bytes = s.getBytes("UTF-8");
byte[] bytes = StringUtils.utf8Encode(s);
int checksum = DataUtils.getFletcher32(bytes,
bytes.length / 2 * 2);
if (check == checksum) {
return;
}
}
throw new RuntimeException("File header is corrupt");
} catch (Exception e) {
throw convert(e);
}
throw DataUtils.illegalStateException("File header is corrupt");
}
private void writeFileHeader() {
try {
StringBuilder buff = new StringBuilder();
fileHeader.put("lastMapId", "" + lastMapId);
fileHeader.put("rootChunk", "" + rootChunkStart);
fileHeader.put("version", "" + currentVersion);
DataUtils.appendMap(buff, fileHeader);
byte[] bytes = (buff.toString() + " ").getBytes("UTF-8");
byte[] bytes = StringUtils.utf8Encode(buff.toString() + " ");
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
DataUtils.appendMap(buff, "fletcher", Integer.toHexString(checksum));
bytes = buff.toString().getBytes("UTF-8");
bytes = StringUtils.utf8Encode(buff.toString());
if (bytes.length > BLOCK_SIZE) {
throw new IllegalArgumentException("File header too large: " + buff);
throw DataUtils.illegalArgumentException("File header too large: " + buff);
}
ByteBuffer header = ByteBuffer.allocate(2 * BLOCK_SIZE);
header.put(bytes);
......@@ -493,13 +489,6 @@ public class MVStore {
fileWriteCount++;
DataUtils.writeFully(file, 0, header);
fileSize = Math.max(fileSize, 2 * BLOCK_SIZE);
} catch (Exception e) {
throw convert(e);
}
}
private static RuntimeException convert(Exception e) {
throw new RuntimeException("Exception: " + e, e);
}
/**
......@@ -524,7 +513,7 @@ public class MVStore {
maps.clear();
mapsChanged.clear();
} catch (Exception e) {
throw convert(e);
throw DataUtils.illegalStateException("Closing failed for file " + fileName, e);
} finally {
file = null;
}
......@@ -645,7 +634,7 @@ public class MVStore {
if (ASSERT) {
if (freedChunks.size() > 0) {
throw new RuntimeException("Temporary freed chunks");
throw DataUtils.illegalStateException("Temporary freed chunks");
}
}
......@@ -672,12 +661,8 @@ public class MVStore {
c.metaRootPos = meta.getRoot().getPos();
c.writeHeader(buff);
buff.rewind();
try {
fileWriteCount++;
DataUtils.writeFully(file, filePos, buff);
} catch (IOException e) {
throw new RuntimeException(e);
}
fileSize = Math.max(fileSize, filePos + buff.position());
rootChunkStart = filePos;
revertTemp();
......@@ -704,7 +689,7 @@ public class MVStore {
Chunk c = chunks.get(f.id);
c.maxLengthLive += f.maxLengthLive;
if (c.maxLengthLive < 0) {
throw new RuntimeException("Corrupt max length");
throw DataUtils.illegalStateException("Corrupt max length: " + c.maxLengthLive);
}
}
}
......@@ -719,7 +704,6 @@ public class MVStore {
*/
private void shrinkFileIfPossible(int minPercent) {
long used = getFileLengthUsed();
try {
if (used >= fileSize) {
return;
}
......@@ -730,11 +714,12 @@ public class MVStore {
if (savedPercent < minPercent) {
return;
}
try {
file.truncate(used);
fileSize = used;
} catch (Exception e) {
throw convert(e);
} catch (IOException e) {
throw DataUtils.illegalStateException("Could not truncate to size " + used, e);
}
fileSize = used;
}
private long getFileLengthUsed() {
......@@ -800,15 +785,11 @@ public class MVStore {
}
private Chunk readChunkHeader(long start) {
try {
fileReadCount++;
ByteBuffer buff = ByteBuffer.allocate(40);
DataUtils.readFully(file, start, buff);
buff.rewind();
return Chunk.fromHeader(buff, start);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
......@@ -898,11 +879,7 @@ public class MVStore {
private void copyLive(Chunk chunk, ArrayList<Chunk> old) {
ByteBuffer buff = ByteBuffer.allocate(chunk.length);
try {
DataUtils.readFully(file, chunk.start, buff);
} catch (IOException e) {
throw new RuntimeException(e);
}
Chunk.fromHeader(buff, chunk.start);
int chunkLength = chunk.length;
// mark a change, even if it doesn't look like there was a change
......@@ -967,7 +944,7 @@ public class MVStore {
if (p == null) {
Chunk c = getChunk(pos);
if (c == null) {
throw new RuntimeException("Chunk " + DataUtils.getPageChunkId(pos) + " not found");
throw DataUtils.illegalStateException("Chunk " + DataUtils.getPageChunkId(pos) + " not found");
}
long filePos = c.start;
filePos += DataUtils.getPageOffset(pos);
......@@ -1193,7 +1170,7 @@ public class MVStore {
*/
public void rollbackTo(long version) {
if (!isKnownVersion(version)) {
throw new IllegalArgumentException("Unknown version: " + version);
throw DataUtils.illegalArgumentException("Unknown version: " + version);
}
// TODO could remove newer temporary pages on rollback
for (MVMap<?, ?> m : mapsChanged.values()) {
......
......@@ -39,7 +39,7 @@ public class MVStoreBuilder {
private MVStoreBuilder set(String key, Object value) {
if (config.containsKey(key)) {
throw new IllegalArgumentException("Parameter " + config.get(key) + " is already set");
throw DataUtils.illegalArgumentException("Parameter " + config.get(key) + " is already set");
}
config.put(key, value);
return this;
......
......@@ -6,12 +6,12 @@
*/
package org.h2.mvstore;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import org.h2.compress.Compressor;
import org.h2.mvstore.type.DataType;
import org.h2.util.Utils;
/**
* A page (a node or a leaf).
......@@ -129,7 +129,6 @@ public class Page {
long pos, long filePos, long fileSize) {
ByteBuffer buff;
int maxLength = DataUtils.getPageMaxLength(pos);
try {
maxLength = (int) Math.min(fileSize - filePos, maxLength);
int length = maxLength;
if (maxLength == Integer.MAX_VALUE) {
......@@ -140,9 +139,6 @@ public class Page {
}
buff = ByteBuffer.allocate(length);
DataUtils.readFully(file, filePos, buff);
} catch (IOException e) {
throw new RuntimeException(e);
}
Page p = new Page(map, 0);
p.pos = pos;
int chunkId = DataUtils.getPageChunkId(pos);
......@@ -409,7 +405,7 @@ public class Page {
}
}
if (check != totalCount) {
throw new AssertionError("Expected: " + check + " got: "
throw DataUtils.illegalStateException("Expected: " + check + " got: "
+ totalCount);
}
}
......@@ -653,20 +649,20 @@ public class Page {
int start = buff.position();
int pageLength = buff.getInt();
if (pageLength > maxLength) {
throw new RuntimeException("Length too large, expected =< "
throw DataUtils.illegalStateException("File corrupted, expected length =< "
+ maxLength + " got " + pageLength);
}
short check = buff.getShort();
int mapId = DataUtils.readVarInt(buff);
if (mapId != map.getId()) {
throw new RuntimeException("Error reading page, expected map "
throw DataUtils.illegalStateException("File corrupted, expected map id "
+ map.getId() + " got " + mapId);
}
int checkTest = DataUtils.getCheckValue(chunkId)
^ DataUtils.getCheckValue(offset)
^ DataUtils.getCheckValue(pageLength);
if (check != (short) checkTest) {
throw new RuntimeException("Error in check value, expected "
throw DataUtils.illegalStateException("File corrupted, expected check value "
+ checkTest + " got " + check);
}
int len = DataUtils.readVarInt(buff);
......@@ -679,7 +675,7 @@ public class Page {
Compressor compressor = map.getStore().getCompressor();
int lenAdd = DataUtils.readVarInt(buff);
int compLen = pageLength + start - buff.position();
byte[] comp = new byte[compLen];
byte[] comp = Utils.newBytes(compLen);
buff.get(comp);
int l = compLen + lenAdd;
buff = ByteBuffer.allocate(l);
......@@ -865,7 +861,7 @@ public class Page {
public int getMemory() {
if (MVStore.ASSERT) {
if (memory != calculateMemory()) {
throw new RuntimeException("Memory calculation error");
throw DataUtils.illegalStateException("Memory calculation error");
}
}
return memory;
......
......@@ -14,6 +14,7 @@ import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.h2.util.IOUtils;
import org.h2.util.StringUtils;
/**
* A facility to store streams in a map. Streams are split into blocks, which
......@@ -207,7 +208,7 @@ public class StreamStore {
map.remove(k2);
break;
default:
throw new IllegalArgumentException("Unsupported id");
throw DataUtils.illegalArgumentException("Unsupported id " + StringUtils.convertBytesToHex(id));
}
}
}
......@@ -238,7 +239,7 @@ public class StreamStore {
DataUtils.readVarLong(idBuffer);
break;
default:
throw new IllegalArgumentException("Unsupported id");
throw DataUtils.illegalArgumentException("Unsupported id " + StringUtils.convertBytesToHex(id));
}
}
return length;
......@@ -401,7 +402,8 @@ public class StreamStore {
return nextBuffer();
}
default:
throw new IllegalArgumentException("Unsupported id");
throw DataUtils.illegalArgumentException("Unsupported id " +
StringUtils.convertBytesToHex(idBuffer.array()));
}
}
return null;
......
......@@ -12,6 +12,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.h2.mvstore.DataUtils;
/**
* A scan resistant cache that uses keys of type long. It is meant to cache
......@@ -65,7 +66,7 @@ public class CacheLongKeyLIRS<V> {
setMaxMemory(maxMemory);
setAverageMemory(averageMemory);
if (Integer.bitCount(segmentCount) != 1) {
throw new IllegalArgumentException("The segment count must be a power of 2, is " + segmentCount);
throw DataUtils.illegalArgumentException("The segment count must be a power of 2, is " + segmentCount);
}
this.segmentCount = segmentCount;
this.segmentMask = segmentCount - 1;
......@@ -221,7 +222,7 @@ public class CacheLongKeyLIRS<V> {
*/
public void setMaxMemory(long maxMemory) {
if (maxMemory <= 0) {
throw new IllegalArgumentException("Max memory must be larger than 0");
throw DataUtils.illegalArgumentException("Max memory must be larger than 0");
}
this.maxMemory = maxMemory;
if (segments != null) {
......@@ -240,7 +241,7 @@ public class CacheLongKeyLIRS<V> {
*/
public void setAverageMemory(int averageMemory) {
if (averageMemory <= 0) {
throw new IllegalArgumentException("Average memory must be larger than 0");
throw DataUtils.illegalArgumentException("Average memory must be larger than 0");
}
this.averageMemory = averageMemory;
if (segments != null) {
......@@ -682,7 +683,7 @@ public class CacheLongKeyLIRS<V> {
*/
synchronized V put(long key, int hash, V value, int memory) {
if (value == null) {
throw new NullPointerException();
throw DataUtils.illegalArgumentException("The value may not be null");
}
V old;
Entry<V> e = find(key, hash);
......@@ -947,7 +948,7 @@ public class CacheLongKeyLIRS<V> {
*/
void setMaxMemory(long maxMemory) {
if (maxMemory <= 0) {
throw new IllegalArgumentException("Max memory must be larger than 0");
throw DataUtils.illegalArgumentException("Max memory must be larger than 0");
}
this.maxMemory = maxMemory;
}
......@@ -960,7 +961,7 @@ public class CacheLongKeyLIRS<V> {
*/
void setAverageMemory(int averageMemory) {
if (averageMemory <= 0) {
throw new IllegalArgumentException("Average memory must be larger than 0");
throw DataUtils.illegalArgumentException("Average memory must be larger than 0");
}
this.averageMemory = averageMemory;
}
......
......@@ -22,7 +22,7 @@ public class SpatialDataType implements DataType {
public SpatialDataType(int dimensions) {
if (dimensions <= 0 || dimensions > 255) {
throw new IllegalArgumentException("Dimensions: " + dimensions);
throw DataUtils.illegalArgumentException("Dimensions: " + dimensions);
}
this.dimensions = dimensions;
}
......
......@@ -143,7 +143,7 @@ public class ObjectDataType implements DataType {
case TYPE_SERIALIZED_OBJECT:
return new SerializedObjectType(this);
}
throw new RuntimeException("Unsupported type: " + typeId);
throw DataUtils.illegalStateException("Unsupported type: " + typeId);
}
@Override
......@@ -196,7 +196,7 @@ public class ObjectDataType implements DataType {
} else if (tag >= TAG_BYTE_ARRAY_0_15 && tag <= TAG_BYTE_ARRAY_0_15 + 15) {
typeId = TYPE_BYTE_ARRAY;
} else {
throw new RuntimeException("Unknown tag: " + tag);
throw DataUtils.illegalStateException("Unknown tag: " + tag);
}
}
}
......@@ -250,7 +250,7 @@ public class ObjectDataType implements DataType {
return TYPE_CHARACTER;
}
if (obj == null) {
throw new NullPointerException();
throw DataUtils.illegalArgumentException("Null is not supported");
}
return TYPE_SERIALIZED_OBJECT;
}
......@@ -377,7 +377,7 @@ public class ObjectDataType implements DataType {
@Override
public final Object read(ByteBuffer buff) {
throw new RuntimeException();
throw DataUtils.illegalStateException("Internal error");
}
/**
......@@ -1232,13 +1232,14 @@ public class ObjectDataType implements DataType {
@Override
public Object read(ByteBuffer buff, int tag) {
int len;
byte[] data;
if (tag == TYPE_BYTE_ARRAY) {
len = DataUtils.readVarInt(buff);
int len = DataUtils.readVarInt(buff);
data = Utils.newBytes(len);
} else {
len = tag - TAG_BYTE_ARRAY_0_15;
int len = tag - TAG_BYTE_ARRAY_0_15;
data = Utils.newBytes(len);
}
byte[] data = new byte[len];
buff.get(data);
return data;
}
......@@ -1497,7 +1498,7 @@ public class ObjectDataType implements DataType {
@Override
public Object read(ByteBuffer buff, int tag) {
int len = DataUtils.readVarInt(buff);
byte[] data = new byte[len];
byte[] data = Utils.newBytes(len);
buff.get(data);
return Utils.deserialize(data);
}
......
......@@ -365,7 +365,7 @@ public class StringUtils {
* @param length the number of bytes
* @return the text
*/
private static String utf8Decode(byte[] bytes, int offset, int length) {
public static String utf8Decode(byte[] bytes, int offset, int length) {
try {
return new String(bytes, offset, length, Constants.UTF8);
} catch (Exception e) {
......
......@@ -382,16 +382,20 @@ public class Utils {
* Create an array of bytes with the given size. If this is not possible
* because not enough memory is available, an OutOfMemoryError with the
* requested size in the message is thrown.
* <p>
* This method should be used if the size of the array is user defined, or
* stored in a file, so wrong size data can be distinguished from regular
* out-of-memory.
*
* @param len the number of bytes requested
* @return the byte array
* @throws OutOfMemoryError
*/
public static byte[] newBytes(int len) {
try {
if (len == 0) {
return EMPTY_BYTES;
}
try {
return new byte[len];
} catch (OutOfMemoryError e) {
Error e2 = new OutOfMemoryError("Requested memory: " + len);
......
......@@ -10,7 +10,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import org.h2.mvstore.cache.CacheLIRS;
import org.h2.dev.cache.CacheLIRS;
import org.h2.test.TestBase;
import org.h2.util.New;
......
......@@ -130,14 +130,14 @@ public class TestMVStore extends TestBase {
MVStore s1 = MVStoreBuilder.fileBased(fileName).open();
s1.close();
fail();
} catch (Exception e) {
} catch (IllegalStateException e) {
// expected
}
try {
MVStore s1 = MVStoreBuilder.fileBased(fileName).readOnly().open();
s1.close();
fail();
} catch (Exception e) {
} catch (IllegalStateException e) {
// expected
}
s.close();
......
......@@ -4,7 +4,7 @@
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.cache;
package org.h2.dev.cache;
import java.util.AbstractMap;
import java.util.ArrayList;
......@@ -622,7 +622,7 @@ public class CacheLIRS<K, V> extends AbstractMap<K, V> implements Map<K, V> {
*/
synchronized V put(K key, int hash, V value, int memory) {
if (value == null) {
throw new NullPointerException();
throw new NullPointerException("The value may not be null");
}
V old;
Entry<K, V> e = find(key, hash);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论