提交 78a4f621 authored 作者: Thomas Mueller's avatar Thomas Mueller

Build target for the MVStore; remove DataType.getMaxLength; reduce cross-dependencies

上级 a0281dad
...@@ -412,6 +412,14 @@ The MVStore is included in the latest H2 jar file. ...@@ -412,6 +412,14 @@ The MVStore is included in the latest H2 jar file.
</p><p> </p><p>
There are no special requirements to use it. There are no special requirements to use it.
The MVStore should run on any JVM as well as on Android. The MVStore should run on any JVM as well as on Android.
</p><p>
To build just the MVStore (without the database engine), run:
</p>
<pre>
./build.sh jarMVStore
</pre>
<p>
This will create the file <code>bin/h2mvstore-${version}.jar</code> (about 130 KB).
</p> </p>
<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html> <!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
...@@ -9,7 +9,7 @@ package org.h2.compress; ...@@ -9,7 +9,7 @@ package org.h2.compress;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.Utils; import org.h2.mvstore.DataUtils;
/** /**
* An input stream to read from an LZF stream. * An input stream to read from an LZF stream.
...@@ -32,7 +32,7 @@ public class LZFInputStream extends InputStream { ...@@ -32,7 +32,7 @@ public class LZFInputStream extends InputStream {
} }
private static byte[] ensureSize(byte[] buff, int len) { private static byte[] ensureSize(byte[] buff, int len) {
return buff == null || buff.length < len ? Utils.newBytes(len) : buff; return buff == null || buff.length < len ? DataUtils.newBytes(len) : buff;
} }
private void fillBuffer() throws IOException { private void fillBuffer() throws IOException {
......
...@@ -44,7 +44,7 @@ public class ConditionInConstantSet extends Condition { ...@@ -44,7 +44,7 @@ public class ConditionInConstantSet extends Condition {
this.valueList = valueList; this.valueList = valueList;
this.valueSet = new HashSet<Value>(valueList.size()); this.valueSet = new HashSet<Value>(valueList.size());
for (Expression expression : valueList) { for (Expression expression : valueList) {
this.valueSet.add(expression.getValue(session)); valueSet.add(expression.getValue(session));
} }
} }
...@@ -53,6 +53,7 @@ public class ConditionInConstantSet extends Condition { ...@@ -53,6 +53,7 @@ public class ConditionInConstantSet extends Condition {
if (leftVal == ValueNull.INSTANCE) { if (leftVal == ValueNull.INSTANCE) {
return leftVal; return leftVal;
} }
int todoFix;
Value firstRightValue = null; Value firstRightValue = null;
for (Value v : valueSet) { for (Value v : valueSet) {
if (v != ValueNull.INSTANCE) { if (v != ValueNull.INSTANCE) {
......
...@@ -30,6 +30,7 @@ import org.h2.engine.Database; ...@@ -30,6 +30,7 @@ import org.h2.engine.Database;
import org.h2.engine.Mode; import org.h2.engine.Mode;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.schema.Sequence; import org.h2.schema.Sequence;
import org.h2.security.BlockCipher; import org.h2.security.BlockCipher;
...@@ -1334,7 +1335,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1334,7 +1335,7 @@ public class Function extends Expression implements FunctionCall {
private static byte[] getPaddedArrayCopy(byte[] data, int blockSize) { private static byte[] getPaddedArrayCopy(byte[] data, int blockSize) {
int size = MathUtils.roundUpInt(data.length, blockSize); int size = MathUtils.roundUpInt(data.length, blockSize);
byte[] newData = Utils.newBytes(size); byte[] newData = DataUtils.newBytes(size);
System.arraycopy(data, 0, newData, 0, data.length); System.arraycopy(data, 0, newData, 0, data.length);
return newData; return newData;
} }
......
...@@ -40,7 +40,6 @@ import org.h2.result.ResultInterface; ...@@ -40,7 +40,6 @@ import org.h2.result.ResultInterface;
import org.h2.result.UpdatableRow; import org.h2.result.UpdatableRow;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.CompareMode; import org.h2.value.CompareMode;
...@@ -721,7 +720,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet { ...@@ -721,7 +720,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet {
throw DbException.getInvalidValueException("scale", scale); throw DbException.getInvalidValueException("scale", scale);
} }
BigDecimal bd = get(columnLabel).getBigDecimal(); BigDecimal bd = get(columnLabel).getBigDecimal();
return bd == null ? null : MathUtils.setScale(bd, scale); return bd == null ? null : ValueDecimal.setScale(bd, scale);
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
} }
...@@ -747,7 +746,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet { ...@@ -747,7 +746,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet {
throw DbException.getInvalidValueException("scale", scale); throw DbException.getInvalidValueException("scale", scale);
} }
BigDecimal bd = get(columnIndex).getBigDecimal(); BigDecimal bd = get(columnIndex).getBigDecimal();
return bd == null ? null : MathUtils.setScale(bd, scale); return bd == null ? null : ValueDecimal.setScale(bd, scale);
} catch (Exception e) { } catch (Exception e) {
throw logAndConvert(e); throw logAndConvert(e);
} }
......
...@@ -13,12 +13,10 @@ import java.nio.ByteBuffer; ...@@ -13,12 +13,10 @@ import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils;
/** /**
* Utility methods * Utility methods
...@@ -72,6 +70,11 @@ public class DataUtils { ...@@ -72,6 +70,11 @@ public class DataUtils {
*/ */
public static final int PAGE_MEMORY_CHILD = 16; public static final int PAGE_MEMORY_CHILD = 16;
/**
* An 0-size byte array.
*/
private static final byte[] EMPTY_BYTES = {};
/** /**
* Get the length of the variable size int. * Get the length of the variable size int.
* *
...@@ -199,8 +202,10 @@ public class DataUtils { ...@@ -199,8 +202,10 @@ public class DataUtils {
* @param buff the target buffer * @param buff the target buffer
* @param s the string * @param s the string
* @param len the number of characters * @param len the number of characters
* @return the byte buffer
*/ */
public static void writeStringData(ByteBuffer buff, String s, int len) { public static ByteBuffer writeStringData(ByteBuffer buff, String s, int len) {
buff = DataUtils.ensureCapacity(buff, 3 * len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int c = s.charAt(i); int c = s.charAt(i);
if (c < 0x80) { if (c < 0x80) {
...@@ -214,6 +219,7 @@ public class DataUtils { ...@@ -214,6 +219,7 @@ public class DataUtils {
buff.put((byte) (c & 0x3f)); buff.put((byte) (c & 0x3f));
} }
} }
return buff;
} }
/** /**
...@@ -584,7 +590,8 @@ public class DataUtils { ...@@ -584,7 +590,8 @@ public class DataUtils {
public static IllegalArgumentException newIllegalArgumentException( public static IllegalArgumentException newIllegalArgumentException(
String message, Object... arguments) { String message, Object... arguments) {
return initCause(new IllegalArgumentException( return initCause(new IllegalArgumentException(
formatMessage(message, arguments)), arguments); MessageFormat.format(message, arguments) + getVersion()),
arguments);
} }
/** /**
...@@ -608,7 +615,8 @@ public class DataUtils { ...@@ -608,7 +615,8 @@ public class DataUtils {
public static IllegalStateException newIllegalStateException( public static IllegalStateException newIllegalStateException(
String message, Object... arguments) { String message, Object... arguments) {
return initCause(new IllegalStateException( return initCause(new IllegalStateException(
formatMessage(message, arguments)), arguments); MessageFormat.format(message, arguments) + getVersion()),
arguments);
} }
private static <T extends Exception> T initCause(T e, Object... arguments) { private static <T extends Exception> T initCause(T e, Object... arguments) {
...@@ -622,39 +630,111 @@ public class DataUtils { ...@@ -622,39 +630,111 @@ public class DataUtils {
return e; return e;
} }
private static String formatMessage(String pattern, Object... arguments) {
for (int i = 0, size = arguments.length; i < size; i++) {
Object o = arguments[i];
if (o instanceof String) {
arguments[i] = StringUtils.quoteIdentifier(o.toString());
}
}
return MessageFormat.format(pattern, arguments) + getVersion();
}
private static String getVersion() { private static String getVersion() {
return " [" + Constants.VERSION_MAJOR + "." + return " [" + Constants.VERSION_MAJOR + "." +
Constants.VERSION_MINOR + "." + Constants.BUILD_ID + "]"; Constants.VERSION_MINOR + "." + Constants.BUILD_ID + "]";
} }
/** /**
* Convert a char array to a byte array. The char array is cleared after * Convert the text to UTF-8 format. For the Unicode characters
* use. * 0xd800-0xdfff only one byte is returned.
*
* @param s the text
* @return the UTF-8 representation
*/
public static byte[] utf8Encode(String s) {
try {
return s.getBytes(Constants.UTF8);
} catch (Exception e) {
// UnsupportedEncodingException
throw newIllegalArgumentException("UTF-8 not supported", e);
}
}
/**
* Convert a UTF-8 representation of a text to the text.
*
* @param utf8 the UTF-8 representation
* @return the text
*/
public static String utf8Decode(byte[] utf8) {
try {
return new String(utf8, Constants.UTF8);
} catch (Exception e) {
// UnsupportedEncodingException
throw newIllegalArgumentException("UTF-8 not supported", e);
}
}
/**
* Convert a UTF-8 representation of a text to the text using the given
* offset and length.
*
* @param bytes the UTF-8 representation
* @param offset the offset in the bytes array
* @param length the number of bytes
* @return the text
*/
public static String utf8Decode(byte[] bytes, int offset, int length) {
try {
return new String(bytes, offset, length, Constants.UTF8);
} catch (Exception e) {
// UnsupportedEncodingException
throw newIllegalArgumentException("UTF-8 not supported", e);
}
}
/**
* 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 passwordChars the password characters * @param len the number of bytes requested
* @return the byte array * @return the byte array
* @throws OutOfMemoryError
*/ */
static byte[] getPasswordBytes(char[] passwordChars) { public static byte[] newBytes(int len) {
// using UTF-16 if (len == 0) {
int len = passwordChars.length; return EMPTY_BYTES;
byte[] password = new byte[len * 2]; }
for (int i = 0; i < len; i++) { try {
char c = passwordChars[i]; return new byte[len];
password[i + i] = (byte) (c >>> 8); } catch (OutOfMemoryError e) {
password[i + i + 1] = (byte) c; Error e2 = new OutOfMemoryError("Requested memory: " + len);
e2.initCause(e);
throw e2;
} }
Arrays.fill(passwordChars, (char) 0); }
return password;
/**
* Ensure the byte buffer has the given capacity, plus 1 KB. If not, a new,
* larger byte buffer is created and the data is copied.
*
* @param buff the byte buffer
* @param len the minimum remaining capacity
* @return the byte buffer (possibly a new one)
*/
public static ByteBuffer ensureCapacity(ByteBuffer buff, int len) {
len += 1024;
if (buff.remaining() > len) {
return buff;
}
return grow(buff, len);
}
private static ByteBuffer grow(ByteBuffer buff, int len) {
len = buff.remaining() + len;
int capacity = buff.capacity();
// grow at most 1 MB at a time
len = Math.max(len, Math.min(capacity + 1024 * 1024, capacity * 2));
ByteBuffer temp = ByteBuffer.allocate(len);
buff.flip();
temp.put(buff);
return temp;
} }
} }
...@@ -25,10 +25,9 @@ import org.h2.mvstore.cache.FilePathCache; ...@@ -25,10 +25,9 @@ import org.h2.mvstore.cache.FilePathCache;
import org.h2.mvstore.type.StringDataType; import org.h2.mvstore.type.StringDataType;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathCrypt; import org.h2.store.fs.FilePathCrypt;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FilePathNio;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils;
/* /*
...@@ -43,7 +42,6 @@ H:3,... ...@@ -43,7 +42,6 @@ H:3,...
TODO: TODO:
- separate jar file
- automated 'kill process' and 'power failure' test - automated 'kill process' and 'power failure' test
- mvcc with multiple transactions - mvcc with multiple transactions
- update checkstyle - update checkstyle
...@@ -53,7 +51,6 @@ TODO: ...@@ -53,7 +51,6 @@ TODO:
- possibly split chunk data into immutable and mutable - possibly split chunk data into immutable and mutable
- compact: avoid processing pages using a counting bloom filter - compact: avoid processing pages using a counting bloom filter
- defragment (re-creating maps, specially those with small pages) - defragment (re-creating maps, specially those with small pages)
- remove DataType.getMaxLength (use ByteArrayOutputStream or getMemory)
- chunk header: store changed chunk data as row; maybe after the root - chunk header: store changed chunk data as row; maybe after the root
- chunk checksum (header, last page, 2 bytes per page?) - chunk checksum (header, last page, 2 bytes per page?)
- is there a better name for the file header, - is there a better name for the file header,
...@@ -84,7 +81,7 @@ TODO: ...@@ -84,7 +81,7 @@ TODO:
- MVStoreTool.shrink to shrink a store (create, copy, rename, delete) - MVStoreTool.shrink to shrink a store (create, copy, rename, delete)
-- and for MVStore on Windows, auto-detect renamed file -- and for MVStore on Windows, auto-detect renamed file
- ensure data is overwritten eventually if the system doesn't have a timer - ensure data is overwritten eventually if the system doesn't have a timer
- SSD-friendly write (always in blocks of 128 or 256 KB?) - SSD-friendly write (always in blocks of 4 MB / 1 second?)
- close the file on out of memory or disk write error (out of disk space or so) - close the file on out of memory or disk write error (out of disk space or so)
- implement a sharded map (in one store, multiple stores) - implement a sharded map (in one store, multiple stores)
-- to support concurrent updates and writes, and very large maps -- to support concurrent updates and writes, and very large maps
...@@ -178,7 +175,13 @@ public class MVStore { ...@@ -178,7 +175,13 @@ public class MVStore {
private boolean closed; private boolean closed;
MVStore(HashMap<String, Object> config) { MVStore(HashMap<String, Object> config) {
this.fileName = (String) config.get("fileName"); String f = (String) config.get("fileName");
if (f != null && !f.startsWith("nio:")) {
// nio is used by default
FilePathNio.class.getName();
f = "nio:" + f;
}
this.fileName = f;
this.readOnly = "r".equals(config.get("openMode")); this.readOnly = "r".equals(config.get("openMode"));
this.compress = "1".equals(config.get("compress")); this.compress = "1".equals(config.get("compress"));
if (fileName != null) { if (fileName != null) {
...@@ -194,7 +197,8 @@ public class MVStore { ...@@ -194,7 +197,8 @@ public class MVStore {
} }
/** /**
* Open a store in exclusive mode. * Open a store in exclusive mode. For a file-based store, the parent
* directory must already exist.
* *
* @param fileName the file name (null for in-memory) * @param fileName the file name (null for in-memory)
* @return the store * @return the store
...@@ -370,7 +374,10 @@ public class MVStore { ...@@ -370,7 +374,10 @@ public class MVStore {
if (fileName == null) { if (fileName == null) {
return; return;
} }
FileUtils.createDirectories(FileUtils.getParent(fileName)); FilePath parent = FilePath.get(fileName).getParent();
if (!parent.exists()) {
throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent);
}
try { try {
if (readOnly) { if (readOnly) {
openFile(); openFile();
...@@ -402,7 +409,7 @@ public class MVStore { ...@@ -402,7 +409,7 @@ public class MVStore {
} }
file = f.open(readOnly ? "r" : "rw"); file = f.open(readOnly ? "r" : "rw");
if (filePassword != null) { if (filePassword != null) {
byte[] password = DataUtils.getPasswordBytes(filePassword); byte[] password = FilePathCrypt.getPasswordBytes(filePassword);
file = new FilePathCrypt.FileCrypt(fileName, password, file); file = new FilePathCrypt.FileCrypt(fileName, password, file);
} }
file = FilePathCache.wrap(file); file = FilePathCache.wrap(file);
...@@ -496,7 +503,7 @@ public class MVStore { ...@@ -496,7 +503,7 @@ public class MVStore {
fileReadCount++; fileReadCount++;
DataUtils.readFully(file, 0, buff); DataUtils.readFully(file, 0, buff);
for (int i = 0; i < 3 * BLOCK_SIZE; i += BLOCK_SIZE) { for (int i = 0; i < 3 * BLOCK_SIZE; i += BLOCK_SIZE) {
String s = StringUtils.utf8Decode(buff.array(), i, BLOCK_SIZE) String s = DataUtils.utf8Decode(buff.array(), i, BLOCK_SIZE)
.trim(); .trim();
HashMap<String, String> m = DataUtils.parseMap(s); HashMap<String, String> m = DataUtils.parseMap(s);
String f = m.remove("fletcher"); String f = m.remove("fletcher");
...@@ -510,7 +517,7 @@ public class MVStore { ...@@ -510,7 +517,7 @@ public class MVStore {
check = -1; check = -1;
} }
s = s.substring(0, s.lastIndexOf("fletcher") - 1) + " "; s = s.substring(0, s.lastIndexOf("fletcher") - 1) + " ";
byte[] bytes = StringUtils.utf8Encode(s); byte[] bytes = DataUtils.utf8Encode(s);
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2); int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
if (check != checksum) { if (check != checksum) {
continue; continue;
...@@ -535,10 +542,10 @@ public class MVStore { ...@@ -535,10 +542,10 @@ public class MVStore {
fileHeader.put("rootChunk", "" + rootChunkStart); fileHeader.put("rootChunk", "" + rootChunkStart);
fileHeader.put("version", "" + currentVersion); fileHeader.put("version", "" + currentVersion);
DataUtils.appendMap(buff, fileHeader); DataUtils.appendMap(buff, fileHeader);
byte[] bytes = StringUtils.utf8Encode(buff.toString() + " "); byte[] bytes = DataUtils.utf8Encode(buff.toString() + " ");
int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2); int checksum = DataUtils.getFletcher32(bytes, bytes.length / 2 * 2);
DataUtils.appendMap(buff, "fletcher", Integer.toHexString(checksum)); DataUtils.appendMap(buff, "fletcher", Integer.toHexString(checksum));
bytes = StringUtils.utf8Encode(buff.toString()); bytes = DataUtils.utf8Encode(buff.toString());
DataUtils.checkArgument(bytes.length <= BLOCK_SIZE, DataUtils.checkArgument(bytes.length <= BLOCK_SIZE,
"File header too large: {}", buff); "File header too large: {}", buff);
return bytes; return bytes;
...@@ -655,7 +662,6 @@ public class MVStore { ...@@ -655,7 +662,6 @@ public class MVStore {
chunks.put(c.id, c); chunks.put(c.id, c);
meta.put("chunk." + c.id, c.asString()); meta.put("chunk." + c.id, c.asString());
int maxLength = 1 + 4 + 4 + 8;
for (MVMap<?, ?> m : mapsChanged.values()) { for (MVMap<?, ?> m : mapsChanged.values()) {
if (m == meta || !m.hasUnsavedChanges()) { if (m == meta || !m.hasUnsavedChanges()) {
continue; continue;
...@@ -664,7 +670,6 @@ public class MVStore { ...@@ -664,7 +670,6 @@ public class MVStore {
if (p.getTotalCount() == 0) { if (p.getTotalCount() == 0) {
meta.put("root." + m.getId(), "0"); meta.put("root." + m.getId(), "0");
} else { } else {
maxLength += p.getMaxLengthTempRecursive();
meta.put("root." + m.getId(), String.valueOf(Long.MAX_VALUE)); meta.put("root." + m.getId(), String.valueOf(Long.MAX_VALUE));
} }
} }
...@@ -681,17 +686,12 @@ public class MVStore { ...@@ -681,17 +686,12 @@ public class MVStore {
applyFreedChunks(); applyFreedChunks();
} }
} while (freedChunks.size() > 0); } while (freedChunks.size() > 0);
maxLength += meta.getRoot().getMaxLengthTempRecursive();
ByteBuffer buff; ByteBuffer buff;
if (maxLength > 16 * 1024 * 1024) { if (writeBuffer != null) {
buff = ByteBuffer.allocate(maxLength); buff = writeBuffer;
buff.clear();
} else { } else {
if (writeBuffer != null && writeBuffer.capacity() >= maxLength) { buff = ByteBuffer.allocate(1024 * 1024);
buff = writeBuffer;
buff.clear();
} else {
writeBuffer = buff = ByteBuffer.allocate(maxLength + 128 * 1024);
}
} }
// need to patch the header later // need to patch the header later
c.writeHeader(buff); c.writeHeader(buff);
...@@ -703,7 +703,8 @@ public class MVStore { ...@@ -703,7 +703,8 @@ public class MVStore {
} }
Page p = m.openVersion(storeVersion).getRoot(); Page p = m.openVersion(storeVersion).getRoot();
if (p.getTotalCount() > 0) { if (p.getTotalCount() > 0) {
long root = p.writeUnsavedRecursive(c, buff); buff = p.writeUnsavedRecursive(c, buff);
long root = p.getPos();
meta.put("root." + m.getId(), "" + root); meta.put("root." + m.getId(), "" + root);
} }
} }
...@@ -718,7 +719,7 @@ public class MVStore { ...@@ -718,7 +719,7 @@ public class MVStore {
// this will modify maxLengthLive, but // this will modify maxLengthLive, but
// the correct value is written in the chunk header // the correct value is written in the chunk header
meta.getRoot().writeUnsavedRecursive(c, buff); buff = meta.getRoot().writeUnsavedRecursive(c, buff);
int chunkLength = buff.position(); int chunkLength = buff.position();
...@@ -755,6 +756,9 @@ public class MVStore { ...@@ -755,6 +756,9 @@ public class MVStore {
fileWriteCount++; fileWriteCount++;
DataUtils.writeFully(file, filePos, buff); DataUtils.writeFully(file, filePos, buff);
fileSize = Math.max(fileSize, filePos + buff.position()); fileSize = Math.max(fileSize, filePos + buff.position());
if (buff.capacity() <= 4 * 1024 * 1024) {
writeBuffer = buff;
}
// overwrite the header if required // overwrite the header if required
if (!atEnd) { if (!atEnd) {
...@@ -1440,7 +1444,7 @@ public class MVStore { ...@@ -1440,7 +1444,7 @@ public class MVStore {
/** /**
* Use the following file name. If the file does not exist, it is * Use the following file name. If the file does not exist, it is
* automatically created. * automatically created. The parent directory already must exist.
* *
* @param fileName the file name * @param fileName the file name
* @return this * @return this
......
...@@ -12,7 +12,6 @@ import java.io.Writer; ...@@ -12,7 +12,6 @@ import java.io.Writer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
/** /**
* Utility methods used in combination with the MVStore. * Utility methods used in combination with the MVStore.
...@@ -47,7 +46,7 @@ public class MVStoreTool { ...@@ -47,7 +46,7 @@ public class MVStoreTool {
*/ */
public static void dump(String fileName, Writer writer) { public static void dump(String fileName, Writer writer) {
PrintWriter pw = new PrintWriter(writer, true); PrintWriter pw = new PrintWriter(writer, true);
if (!FileUtils.exists(fileName)) { if (!FilePath.get(fileName).exists()) {
pw.println("File not found: " + fileName); pw.println("File not found: " + fileName);
return; return;
} }
......
...@@ -11,7 +11,6 @@ import java.nio.channels.FileChannel; ...@@ -11,7 +11,6 @@ import java.nio.channels.FileChannel;
import java.util.Arrays; import java.util.Arrays;
import org.h2.compress.Compressor; import org.h2.compress.Compressor;
import org.h2.mvstore.type.DataType; import org.h2.mvstore.type.DataType;
import org.h2.util.Utils;
/** /**
* A page (a node or a leaf). * A page (a node or a leaf).
...@@ -720,7 +719,7 @@ public class Page { ...@@ -720,7 +719,7 @@ public class Page {
Compressor compressor = map.getStore().getCompressor(); Compressor compressor = map.getStore().getCompressor();
int lenAdd = DataUtils.readVarInt(buff); int lenAdd = DataUtils.readVarInt(buff);
int compLen = pageLength + start - buff.position(); int compLen = pageLength + start - buff.position();
byte[] comp = Utils.newBytes(compLen); byte[] comp = DataUtils.newBytes(compLen);
buff.get(comp); buff.get(comp);
int l = compLen + lenAdd; int l = compLen + lenAdd;
buff = ByteBuffer.allocate(l); buff = ByteBuffer.allocate(l);
...@@ -762,8 +761,9 @@ public class Page { ...@@ -762,8 +761,9 @@ public class Page {
* *
* @param chunk the chunk * @param chunk the chunk
* @param buff the target buffer * @param buff the target buffer
* @return the target buffer
*/ */
private void write(Chunk chunk, ByteBuffer buff) { private ByteBuffer write(Chunk chunk, ByteBuffer buff) {
int start = buff.position(); int start = buff.position();
buff.putInt(0); buff.putInt(0);
buff.putShort((byte) 0); buff.putShort((byte) 0);
...@@ -776,7 +776,7 @@ public class Page { ...@@ -776,7 +776,7 @@ public class Page {
int compressStart = buff.position(); int compressStart = buff.position();
DataType keyType = map.getKeyType(); DataType keyType = map.getKeyType();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
keyType.write(buff, keys[i]); buff = keyType.write(buff, keys[i]);
} }
if (type == DataUtils.PAGE_TYPE_NODE) { if (type == DataUtils.PAGE_TYPE_NODE) {
for (int i = 0; i <= len; i++) { for (int i = 0; i <= len; i++) {
...@@ -788,7 +788,7 @@ public class Page { ...@@ -788,7 +788,7 @@ public class Page {
} else { } else {
DataType valueType = map.getValueType(); DataType valueType = map.getValueType();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
valueType.write(buff, values[i]); buff = valueType.write(buff, values[i]);
} }
} }
if (map.getStore().getCompress()) { if (map.getStore().getCompress()) {
...@@ -818,45 +818,7 @@ public class Page { ...@@ -818,45 +818,7 @@ public class Page {
chunk.maxLength += max; chunk.maxLength += max;
chunk.maxLengthLive += max; chunk.maxLengthLive += max;
chunk.pageCount++; chunk.pageCount++;
} return buff;
/**
* Get the maximum length in bytes to store temporary records, recursively.
*
* @return the maximum length
*/
int getMaxLengthTempRecursive() {
int maxLength = getMaxLength();
if (!isLeaf()) {
for (int i = 0; i <= keyCount; i++) {
Page p = childrenPages[i];
if (p != null) {
maxLength += p.getMaxLengthTempRecursive();
}
}
}
return maxLength;
}
int getMaxLength() {
// length, check, map id, key length, type
int maxLength = 4 + 2 + DataUtils.MAX_VAR_INT_LEN
+ DataUtils.MAX_VAR_INT_LEN + 1;
int len = keyCount;
DataType keyType = map.getKeyType();
for (int i = 0; i < len; i++) {
maxLength += keyType.getMaxLength(keys[i]);
}
if (isLeaf()) {
DataType valueType = map.getValueType();
for (int i = 0; i < len; i++) {
maxLength += valueType.getMaxLength(values[i]);
}
} else {
maxLength += 8 * len;
maxLength += DataUtils.MAX_VAR_LONG_LEN * len;
}
return maxLength;
} }
/** /**
...@@ -865,21 +827,21 @@ public class Page { ...@@ -865,21 +827,21 @@ public class Page {
* *
* @param chunk the chunk * @param chunk the chunk
* @param buff the target buffer * @param buff the target buffer
* @return the page id * @return the target buffer
*/ */
long writeUnsavedRecursive(Chunk chunk, ByteBuffer buff) { ByteBuffer writeUnsavedRecursive(Chunk chunk, ByteBuffer buff) {
if (!isLeaf()) { if (!isLeaf()) {
int len = children.length; int len = children.length;
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Page p = childrenPages[i]; Page p = childrenPages[i];
if (p != null) { if (p != null) {
children[i] = p.writeUnsavedRecursive(chunk, buff); buff = p.writeUnsavedRecursive(chunk, buff);
children[i] = p.getPos();
childrenPages[i] = null; childrenPages[i] = null;
} }
} }
} }
write(chunk, buff); return write(chunk, buff);
return pos;
} }
long getVersion() { long getVersion() {
......
...@@ -11,10 +11,10 @@ import java.io.ByteArrayOutputStream; ...@@ -11,10 +11,10 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.h2.util.IOUtils; import java.util.concurrent.atomic.AtomicReference;
import org.h2.util.StringUtils;
/** /**
* A facility to store streams in a map. Streams are split into blocks, which * A facility to store streams in a map. Streams are split into blocks, which
...@@ -41,6 +41,7 @@ public class StreamStore { ...@@ -41,6 +41,7 @@ public class StreamStore {
private int minBlockSize = 256; private int minBlockSize = 256;
private int maxBlockSize = 256 * 1024; private int maxBlockSize = 256 * 1024;
private final AtomicLong nextKey = new AtomicLong(); private final AtomicLong nextKey = new AtomicLong();
private final AtomicReference<byte[]> nextBuffer = new AtomicReference<byte[]>();
/** /**
* Create a stream store instance. * Create a stream store instance.
...@@ -128,25 +129,46 @@ public class StreamStore { ...@@ -128,25 +129,46 @@ public class StreamStore {
} }
} }
} }
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] readBuffer = nextBuffer.getAndSet(null);
int len = (int) IOUtils.copy(in, buffer, maxBlockSize); if (readBuffer == null) {
readBuffer = new byte[maxBlockSize];
}
byte[] buff = read(in, readBuffer);
if (buff != readBuffer) {
// re-use the buffer if the result was shorter
nextBuffer.set(readBuffer);
}
int len = buff.length;
if (len == 0) { if (len == 0) {
return true; return true;
} }
boolean eof = len < maxBlockSize; boolean eof = len < maxBlockSize;
byte[] data = buffer.toByteArray();
if (len < minBlockSize) { if (len < minBlockSize) {
id.write(0); id.write(0);
DataUtils.writeVarInt(id, len); DataUtils.writeVarInt(id, len);
id.write(data); id.write(buff);
} else { } else {
id.write(1); id.write(1);
DataUtils.writeVarInt(id, len); DataUtils.writeVarInt(id, len);
DataUtils.writeVarLong(id, writeBlock(data)); DataUtils.writeVarLong(id, writeBlock(buff));
} }
return eof; return eof;
} }
private static byte[] read(InputStream in, byte[] target) throws IOException {
int copied = 0;
int remaining = target.length;
while (remaining > 0) {
int len = in.read(target, copied, remaining);
if (len < 0) {
return Arrays.copyOf(target, copied);
}
copied += len;
remaining -= len;
}
return target;
}
private ByteArrayOutputStream putIndirectId(ByteArrayOutputStream id) throws IOException { private ByteArrayOutputStream putIndirectId(ByteArrayOutputStream id) throws IOException {
byte[] data = id.toByteArray(); byte[] data = id.toByteArray();
id = new ByteArrayOutputStream(); id = new ByteArrayOutputStream();
...@@ -222,7 +244,7 @@ public class StreamStore { ...@@ -222,7 +244,7 @@ public class StreamStore {
break; break;
default: default:
throw DataUtils.newIllegalArgumentException( throw DataUtils.newIllegalArgumentException(
"Unsupported id {0}", StringUtils.convertBytesToHex(id)); "Unsupported id {0}", Arrays.toString(id));
} }
} }
} }
...@@ -254,7 +276,7 @@ public class StreamStore { ...@@ -254,7 +276,7 @@ public class StreamStore {
break; break;
default: default:
throw DataUtils.newIllegalArgumentException( throw DataUtils.newIllegalArgumentException(
"Unsupported id {0}", StringUtils.convertBytesToHex(id)); "Unsupported id {0}", Arrays.toString(id));
} }
} }
return length; return length;
...@@ -418,7 +440,7 @@ public class StreamStore { ...@@ -418,7 +440,7 @@ public class StreamStore {
} }
default: default:
throw DataUtils.newIllegalArgumentException( throw DataUtils.newIllegalArgumentException(
"Unsupported id {0}", StringUtils.convertBytesToHex(idBuffer.array())); "Unsupported id {0}", Arrays.toString(idBuffer.array()));
} }
} }
return null; return null;
......
...@@ -26,7 +26,6 @@ import org.h2.store.DataHandler; ...@@ -26,7 +26,6 @@ import org.h2.store.DataHandler;
import org.h2.store.LobStorage; import org.h2.store.LobStorage;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.Utils;
import org.h2.value.CompareMode; import org.h2.value.CompareMode;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueArray; import org.h2.value.ValueArray;
...@@ -136,20 +135,6 @@ public class ValueArrayDataType implements DataType { ...@@ -136,20 +135,6 @@ public class ValueArrayDataType implements DataType {
return a.compareTypeSave(b, compareMode); return a.compareTypeSave(b, compareMode);
} }
public int getMaxLength(Object obj) {
Value[] x = (Value[]) obj;
int len = x.length;
int result = DataUtils.MAX_VAR_INT_LEN;
for (int i = 0; i < len; i++) {
result += getMaxLength(x[i]);
}
return result;
}
private int getMaxLength(Value v) {
return Data.getValueLen(v, handler);
}
public int getMemory(Object obj) { public int getMemory(Object obj) {
Value[] x = (Value[]) obj; Value[] x = (Value[]) obj;
int len = x.length; int len = x.length;
...@@ -173,20 +158,22 @@ public class ValueArrayDataType implements DataType { ...@@ -173,20 +158,22 @@ public class ValueArrayDataType implements DataType {
return x; return x;
} }
public void write(ByteBuffer buff, Object obj) { public ByteBuffer write(ByteBuffer buff, Object obj) {
Value[] x = (Value[]) obj; Value[] x = (Value[]) obj;
int len = x.length; int len = x.length;
DataUtils.writeVarInt(buff, len); DataUtils.writeVarInt(buff, len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
writeValue(buff, x[i]); buff = DataUtils.ensureCapacity(buff, 0);
buff = writeValue(buff, x[i]);
} }
return buff;
} }
private void writeValue(ByteBuffer buff, Value v) { private ByteBuffer writeValue(ByteBuffer buff, Value v) {
int start = buff.position(); int start = buff.position();
if (v == ValueNull.INSTANCE) { if (v == ValueNull.INSTANCE) {
buff.put((byte) 0); buff.put((byte) 0);
return; return buff;
} }
int type = v.getType(); int type = v.getType();
switch (type) { switch (type) {
...@@ -251,6 +238,7 @@ public class ValueArrayDataType implements DataType { ...@@ -251,6 +238,7 @@ public class ValueArrayDataType implements DataType {
writeVarInt(buff, scale); writeVarInt(buff, scale);
byte[] bytes = b.toByteArray(); byte[] bytes = b.toByteArray();
writeVarInt(buff, bytes.length); writeVarInt(buff, bytes.length);
buff = DataUtils.ensureCapacity(buff, bytes.length);
buff.put(bytes, 0, bytes.length); buff.put(bytes, 0, bytes.length);
} }
} }
...@@ -305,6 +293,7 @@ public class ValueArrayDataType implements DataType { ...@@ -305,6 +293,7 @@ public class ValueArrayDataType implements DataType {
buff.put((byte) type); buff.put((byte) type);
byte[] b = v.getBytesNoCopy(); byte[] b = v.getBytesNoCopy();
writeVarInt(buff, b.length); writeVarInt(buff, b.length);
buff = DataUtils.ensureCapacity(buff, b.length);
buff.put(b, 0, b.length); buff.put(b, 0, b.length);
break; break;
} }
...@@ -317,6 +306,7 @@ public class ValueArrayDataType implements DataType { ...@@ -317,6 +306,7 @@ public class ValueArrayDataType implements DataType {
} else { } else {
buff.put((byte) type); buff.put((byte) type);
writeVarInt(buff, b.length); writeVarInt(buff, b.length);
buff = DataUtils.ensureCapacity(buff, b.length);
buff.put(b, 0, b.length); buff.put(b, 0, b.length);
} }
break; break;
...@@ -333,17 +323,17 @@ public class ValueArrayDataType implements DataType { ...@@ -333,17 +323,17 @@ public class ValueArrayDataType implements DataType {
int len = s.length(); int len = s.length();
if (len < 32) { if (len < 32) {
buff.put((byte) (STRING_0_31 + len)); buff.put((byte) (STRING_0_31 + len));
writeStringWithoutLength(buff, s, len); buff = writeStringWithoutLength(buff, s, len);
} else { } else {
buff.put((byte) type); buff.put((byte) type);
writeString(buff, s); buff = writeString(buff, s);
} }
break; break;
} }
case Value.STRING_IGNORECASE: case Value.STRING_IGNORECASE:
case Value.STRING_FIXED: case Value.STRING_FIXED:
buff.put((byte) type); buff.put((byte) type);
writeString(buff, v.getString()); buff = writeString(buff, v.getString());
break; break;
case Value.DOUBLE: { case Value.DOUBLE: {
double x = v.getDouble(); double x = v.getDouble();
...@@ -393,10 +383,11 @@ public class ValueArrayDataType implements DataType { ...@@ -393,10 +383,11 @@ public class ValueArrayDataType implements DataType {
writeVarLong(buff, lob.getPrecision()); writeVarLong(buff, lob.getPrecision());
buff.put((byte) (lob.useCompression() ? 1 : 0)); buff.put((byte) (lob.useCompression() ? 1 : 0));
if (t == -2) { if (t == -2) {
writeString(buff, lob.getFileName()); buff = writeString(buff, lob.getFileName());
} }
} else { } else {
writeVarInt(buff, small.length); writeVarInt(buff, small.length);
buff = DataUtils.ensureCapacity(buff, small.length);
buff.put(small, 0, small.length); buff.put(small, 0, small.length);
} }
} else { } else {
...@@ -409,6 +400,7 @@ public class ValueArrayDataType implements DataType { ...@@ -409,6 +400,7 @@ public class ValueArrayDataType implements DataType {
writeVarLong(buff, lob.getPrecision()); writeVarLong(buff, lob.getPrecision());
} else { } else {
writeVarInt(buff, small.length); writeVarInt(buff, small.length);
buff = DataUtils.ensureCapacity(buff, small.length);
buff.put(small, 0, small.length); buff.put(small, 0, small.length);
} }
} }
...@@ -419,7 +411,8 @@ public class ValueArrayDataType implements DataType { ...@@ -419,7 +411,8 @@ public class ValueArrayDataType implements DataType {
Value[] list = ((ValueArray) v).getList(); Value[] list = ((ValueArray) v).getList();
writeVarInt(buff, list.length); writeVarInt(buff, list.length);
for (Value x : list) { for (Value x : list) {
writeValue(buff, x); buff = DataUtils.ensureCapacity(buff, 0);
buff = writeValue(buff, x);
} }
break; break;
} }
...@@ -432,7 +425,8 @@ public class ValueArrayDataType implements DataType { ...@@ -432,7 +425,8 @@ public class ValueArrayDataType implements DataType {
int columnCount = meta.getColumnCount(); int columnCount = meta.getColumnCount();
writeVarInt(buff, columnCount); writeVarInt(buff, columnCount);
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
writeString(buff, meta.getColumnName(i + 1)); buff = DataUtils.ensureCapacity(buff, 0);
buff = writeString(buff, meta.getColumnName(i + 1));
writeVarInt(buff, meta.getColumnType(i + 1)); writeVarInt(buff, meta.getColumnType(i + 1));
writeVarInt(buff, meta.getPrecision(i + 1)); writeVarInt(buff, meta.getPrecision(i + 1));
writeVarInt(buff, meta.getScale(i + 1)); writeVarInt(buff, meta.getScale(i + 1));
...@@ -442,7 +436,7 @@ public class ValueArrayDataType implements DataType { ...@@ -442,7 +436,7 @@ public class ValueArrayDataType implements DataType {
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
int t = org.h2.value.DataType.convertSQLTypeToValueType(meta.getColumnType(i + 1)); int t = org.h2.value.DataType.convertSQLTypeToValueType(meta.getColumnType(i + 1));
Value val = org.h2.value.DataType.readValue(null, rs, i + 1, t); Value val = org.h2.value.DataType.readValue(null, rs, i + 1, t);
writeValue(buff, val); buff = writeValue(buff, val);
} }
} }
buff.put((byte) 0); buff.put((byte) 0);
...@@ -461,6 +455,7 @@ public class ValueArrayDataType implements DataType { ...@@ -461,6 +455,7 @@ public class ValueArrayDataType implements DataType {
.throwInternalError("value size error: got " + (buff.position() - start) + " expected " + Data.getValueLen(v, handler)); .throwInternalError("value size error: got " + (buff.position() - start) + " expected " + Data.getValueLen(v, handler));
} }
} }
return buff;
} }
private static void writeVarInt(ByteBuffer buff, int x) { private static void writeVarInt(ByteBuffer buff, int x) {
...@@ -479,13 +474,14 @@ public class ValueArrayDataType implements DataType { ...@@ -479,13 +474,14 @@ public class ValueArrayDataType implements DataType {
buff.put((byte) x); buff.put((byte) x);
} }
private static void writeString(ByteBuffer buff, String s) { private static ByteBuffer writeString(ByteBuffer buff, String s) {
int len = s.length(); int len = s.length();
writeVarInt(buff, len); writeVarInt(buff, len);
writeStringWithoutLength(buff, s, len); return writeStringWithoutLength(buff, s, len);
} }
private static void writeStringWithoutLength(ByteBuffer buff, String s, int len) { private static ByteBuffer writeStringWithoutLength(ByteBuffer buff, String s, int len) {
buff = DataUtils.ensureCapacity(buff, 3 * len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int c = s.charAt(i); int c = s.charAt(i);
if (c < 0x80) { if (c < 0x80) {
...@@ -499,6 +495,7 @@ public class ValueArrayDataType implements DataType { ...@@ -499,6 +495,7 @@ public class ValueArrayDataType implements DataType {
buff.put((byte) (c & 0x3f)); buff.put((byte) (c & 0x3f));
} }
} }
return buff;
} }
/** /**
...@@ -540,7 +537,7 @@ public class ValueArrayDataType implements DataType { ...@@ -540,7 +537,7 @@ public class ValueArrayDataType implements DataType {
case Value.DECIMAL: { case Value.DECIMAL: {
int scale = readVarInt(buff); int scale = readVarInt(buff);
int len = readVarInt(buff); int len = readVarInt(buff);
byte[] buff2 = Utils.newBytes(len); byte[] buff2 = DataUtils.newBytes(len);
buff.get(buff2, 0, len); buff.get(buff2, 0, len);
BigInteger b = new BigInteger(buff2); BigInteger b = new BigInteger(buff2);
return ValueDecimal.get(new BigDecimal(b, scale)); return ValueDecimal.get(new BigDecimal(b, scale));
...@@ -571,13 +568,13 @@ public class ValueArrayDataType implements DataType { ...@@ -571,13 +568,13 @@ public class ValueArrayDataType implements DataType {
} }
case Value.BYTES: { case Value.BYTES: {
int len = readVarInt(buff); int len = readVarInt(buff);
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
buff.get(b, 0, len); buff.get(b, 0, len);
return ValueBytes.getNoCopy(b); return ValueBytes.getNoCopy(b);
} }
case Value.JAVA_OBJECT: { case Value.JAVA_OBJECT: {
int len = readVarInt(buff); int len = readVarInt(buff);
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
buff.get(b, 0, len); buff.get(b, 0, len);
return ValueJavaObject.getNoCopy(null, b); return ValueJavaObject.getNoCopy(null, b);
} }
...@@ -605,7 +602,7 @@ public class ValueArrayDataType implements DataType { ...@@ -605,7 +602,7 @@ public class ValueArrayDataType implements DataType {
case Value.CLOB: { case Value.CLOB: {
int smallLen = readVarInt(buff); int smallLen = readVarInt(buff);
if (smallLen >= 0) { if (smallLen >= 0) {
byte[] small = Utils.newBytes(smallLen); byte[] small = DataUtils.newBytes(smallLen);
buff.get(small, 0, smallLen); buff.get(small, 0, smallLen);
return LobStorage.createSmallLob(type, small); return LobStorage.createSmallLob(type, small);
} else if (smallLen == -3) { } else if (smallLen == -3) {
...@@ -666,7 +663,7 @@ public class ValueArrayDataType implements DataType { ...@@ -666,7 +663,7 @@ public class ValueArrayDataType implements DataType {
return ValueLong.get(type - LONG_0_7); return ValueLong.get(type - LONG_0_7);
} else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) { } else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) {
int len = type - BYTES_0_31; int len = type - BYTES_0_31;
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
buff.get(b, 0, len); buff.get(b, 0, len);
return ValueBytes.getNoCopy(b); return ValueBytes.getNoCopy(b);
} else if (type >= STRING_0_31 && type < STRING_0_31 + 32) { } else if (type >= STRING_0_31 && type < STRING_0_31 + 32) {
......
...@@ -47,18 +47,13 @@ public class SpatialDataType implements DataType { ...@@ -47,18 +47,13 @@ public class SpatialDataType implements DataType {
return la == lb; return la == lb;
} }
@Override
public int getMaxLength(Object obj) {
return 1 + dimensions * 8 + DataUtils.MAX_VAR_LONG_LEN;
}
@Override @Override
public int getMemory(Object obj) { public int getMemory(Object obj) {
return 40 + dimensions * 4; return 40 + dimensions * 4;
} }
@Override @Override
public void write(ByteBuffer buff, Object obj) { public ByteBuffer write(ByteBuffer buff, Object obj) {
SpatialKey k = (SpatialKey) obj; SpatialKey k = (SpatialKey) obj;
int flags = 0; int flags = 0;
for (int i = 0; i < dimensions; i++) { for (int i = 0; i < dimensions; i++) {
...@@ -74,6 +69,7 @@ public class SpatialDataType implements DataType { ...@@ -74,6 +69,7 @@ public class SpatialDataType implements DataType {
} }
} }
DataUtils.writeVarLong(buff, k.getId()); DataUtils.writeVarLong(buff, k.getId());
return buff;
} }
@Override @Override
......
...@@ -22,15 +22,6 @@ public interface DataType { ...@@ -22,15 +22,6 @@ public interface DataType {
*/ */
int compare(Object a, Object b); int compare(Object a, Object b);
/**
* Get the maximum length in bytes used to store an object. In many cases,
* this method can be faster than calculating the exact length.
*
* @param obj the object
* @return the maximum length
*/
int getMaxLength(Object obj);
/** /**
* Estimate the used memory in bytes. * Estimate the used memory in bytes.
* *
...@@ -44,8 +35,9 @@ public interface DataType { ...@@ -44,8 +35,9 @@ public interface DataType {
* *
* @param buff the target buffer * @param buff the target buffer
* @param obj the value * @param obj the value
* @return the byte buffer
*/ */
void write(ByteBuffer buff, Object obj); ByteBuffer write(ByteBuffer buff, Object obj);
/** /**
* Read an object. * Read an object.
......
...@@ -20,10 +20,6 @@ public class StringDataType implements DataType { ...@@ -20,10 +20,6 @@ public class StringDataType implements DataType {
return a.toString().compareTo(b.toString()); return a.toString().compareTo(b.toString());
} }
public int getMaxLength(Object obj) {
return DataUtils.MAX_VAR_INT_LEN + 3 * obj.toString().length();
}
public int getMemory(Object obj) { public int getMemory(Object obj) {
return 24 + 2 * obj.toString().length(); return 24 + 2 * obj.toString().length();
} }
...@@ -33,11 +29,11 @@ public class StringDataType implements DataType { ...@@ -33,11 +29,11 @@ public class StringDataType implements DataType {
return DataUtils.readString(buff, len); return DataUtils.readString(buff, len);
} }
public void write(ByteBuffer buff, Object obj) { public ByteBuffer write(ByteBuffer buff, Object obj) {
String s = obj.toString(); String s = obj.toString();
int len = s.length(); int len = s.length();
DataUtils.writeVarInt(buff, len); DataUtils.writeVarInt(buff, len);
DataUtils.writeStringData(buff, s, len); return DataUtils.writeStringData(buff, s, len);
} }
} }
......
...@@ -35,6 +35,7 @@ import org.h2.jdbc.JdbcConnection; ...@@ -35,6 +35,7 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcPreparedStatement; import org.h2.jdbc.JdbcPreparedStatement;
import org.h2.jdbc.JdbcStatement; import org.h2.jdbc.JdbcStatement;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.ScriptReader; import org.h2.util.ScriptReader;
...@@ -132,7 +133,7 @@ public class PgServerThread implements Runnable { ...@@ -132,7 +133,7 @@ public class PgServerThread implements Runnable {
} }
int len = dataInRaw.readInt(); int len = dataInRaw.readInt();
len -= 4; len -= 4;
byte[] data = Utils.newBytes(len); byte[] data = DataUtils.newBytes(len);
dataInRaw.readFully(data, 0, len); dataInRaw.readFully(data, 0, len);
dataIn = new DataInputStream(new ByteArrayInputStream(data, 0, len)); dataIn = new DataInputStream(new ByteArrayInputStream(data, 0, len));
switchBlock: switch (x) { switchBlock: switch (x) {
...@@ -246,7 +247,7 @@ public class PgServerThread implements Runnable { ...@@ -246,7 +247,7 @@ public class PgServerThread implements Runnable {
int paramCount = readShort(); int paramCount = readShort();
for (int i = 0; i < paramCount; i++) { for (int i = 0; i < paramCount; i++) {
int paramLen = readInt(); int paramLen = readInt();
byte[] d2 = Utils.newBytes(paramLen); byte[] d2 = DataUtils.newBytes(paramLen);
readFully(d2); readFully(d2);
try { try {
setParameter(prep.prep, i, d2, formatCodes); setParameter(prep.prep, i, d2, formatCodes);
......
...@@ -23,10 +23,10 @@ import java.util.StringTokenizer; ...@@ -23,10 +23,10 @@ import java.util.StringTokenizer;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.mvstore.DataUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.NetUtils; import org.h2.util.NetUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils;
/** /**
* For each connection to a session, an object of this class is created. * For each connection to a session, an object of this class is created.
...@@ -320,7 +320,7 @@ class WebThread extends WebApp implements Runnable { ...@@ -320,7 +320,7 @@ class WebThread extends WebApp implements Runnable {
if (multipart) { if (multipart) {
uploadMultipart(input, len); uploadMultipart(input, len);
} else if (session != null && len > 0) { } else if (session != null && len > 0) {
byte[] bytes = Utils.newBytes(len); byte[] bytes = DataUtils.newBytes(len);
for (int pos = 0; pos < len;) { for (int pos = 0; pos < len;) {
pos += input.read(bytes, pos, len - pos); pos += input.read(bytes, pos, len - pos);
} }
...@@ -360,7 +360,7 @@ class WebThread extends WebApp implements Runnable { ...@@ -360,7 +360,7 @@ class WebThread extends WebApp implements Runnable {
RandomAccessFile f = new RandomAccessFile(file, "rw"); RandomAccessFile f = new RandomAccessFile(file, "rw");
int testSize = (int) Math.min(f.length(), Constants.IO_BUFFER_SIZE); int testSize = (int) Math.min(f.length(), Constants.IO_BUFFER_SIZE);
f.seek(f.length() - testSize); f.seek(f.length() - testSize);
byte[] bytes = Utils.newBytes(Constants.IO_BUFFER_SIZE); byte[] bytes = DataUtils.newBytes(Constants.IO_BUFFER_SIZE);
f.readFully(bytes, 0, testSize); f.readFully(bytes, 0, testSize);
String s = new String(bytes, "ASCII"); String s = new String(bytes, "ASCII");
int x = s.lastIndexOf(boundary); int x = s.lastIndexOf(boundary);
......
...@@ -24,10 +24,10 @@ import org.h2.constant.ErrorCode; ...@@ -24,10 +24,10 @@ import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.Utils;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueArray; import org.h2.value.ValueArray;
...@@ -720,7 +720,7 @@ public class Data { ...@@ -720,7 +720,7 @@ public class Data {
case Value.DECIMAL: { case Value.DECIMAL: {
int scale = readVarInt(); int scale = readVarInt();
int len = readVarInt(); int len = readVarInt();
byte[] buff = Utils.newBytes(len); byte[] buff = DataUtils.newBytes(len);
read(buff, 0, len); read(buff, 0, len);
BigInteger b = new BigInteger(buff); BigInteger b = new BigInteger(buff);
return ValueDecimal.get(new BigDecimal(b, scale)); return ValueDecimal.get(new BigDecimal(b, scale));
...@@ -751,13 +751,13 @@ public class Data { ...@@ -751,13 +751,13 @@ public class Data {
} }
case Value.BYTES: { case Value.BYTES: {
int len = readVarInt(); int len = readVarInt();
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
read(b, 0, len); read(b, 0, len);
return ValueBytes.getNoCopy(b); return ValueBytes.getNoCopy(b);
} }
case Value.JAVA_OBJECT: { case Value.JAVA_OBJECT: {
int len = readVarInt(); int len = readVarInt();
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
read(b, 0, len); read(b, 0, len);
return ValueJavaObject.getNoCopy(null, b); return ValueJavaObject.getNoCopy(null, b);
} }
...@@ -785,7 +785,7 @@ public class Data { ...@@ -785,7 +785,7 @@ public class Data {
case Value.CLOB: { case Value.CLOB: {
int smallLen = readVarInt(); int smallLen = readVarInt();
if (smallLen >= 0) { if (smallLen >= 0) {
byte[] small = Utils.newBytes(smallLen); byte[] small = DataUtils.newBytes(smallLen);
read(small, 0, smallLen); read(small, 0, smallLen);
return LobStorage.createSmallLob(type, small); return LobStorage.createSmallLob(type, small);
} else if (smallLen == -3) { } else if (smallLen == -3) {
...@@ -846,7 +846,7 @@ public class Data { ...@@ -846,7 +846,7 @@ public class Data {
return ValueLong.get(type - LONG_0_7); return ValueLong.get(type - LONG_0_7);
} else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) { } else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) {
int len = type - BYTES_0_31; int len = type - BYTES_0_31;
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
read(b, 0, len); read(b, 0, len);
return ValueBytes.getNoCopy(b); return ValueBytes.getNoCopy(b);
} else if (type >= STRING_0_31 && type < STRING_0_31 + 32) { } else if (type >= STRING_0_31 && type < STRING_0_31 + 32) {
...@@ -1260,7 +1260,7 @@ public class Data { ...@@ -1260,7 +1260,7 @@ public class Data {
} }
private void expand(int plus) { private void expand(int plus) {
byte[] d = Utils.newBytes((data.length + plus) * 2); byte[] d = DataUtils.newBytes((data.length + plus) * 2);
// must copy everything, because pos could be 0 and data may be // must copy everything, because pos could be 0 and data may be
// still required // still required
System.arraycopy(data, 0, d, 0, data.length); System.arraycopy(data, 0, d, 0, data.length);
......
...@@ -10,8 +10,8 @@ import java.io.IOException; ...@@ -10,8 +10,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.tools.CompressTool; import org.h2.tools.CompressTool;
import org.h2.util.Utils;
/** /**
* An input stream that is backed by a file store. * An input stream that is backed by a file store.
...@@ -114,7 +114,7 @@ public class FileStoreInputStream extends InputStream { ...@@ -114,7 +114,7 @@ public class FileStoreInputStream extends InputStream {
page.readInt(); page.readInt();
if (compress != null) { if (compress != null) {
int uncompressed = page.readInt(); int uncompressed = page.readInt();
byte[] buff = Utils.newBytes(remainingInBuffer); byte[] buff = DataUtils.newBytes(remainingInBuffer);
page.read(buff, 0, remainingInBuffer); page.read(buff, 0, remainingInBuffer);
page.reset(); page.reset();
page.checkCapacity(uncompressed); page.checkCapacity(uncompressed);
......
...@@ -100,7 +100,7 @@ public class RecoverTester implements Recorder { ...@@ -100,7 +100,7 @@ public class RecoverTester implements Recorder {
private synchronized void testDatabase(String fileName, PrintWriter out) { private synchronized void testDatabase(String fileName, PrintWriter out) {
out.println("+ write #" + writeCount + " verify #" + verifyCount); out.println("+ write #" + writeCount + " verify #" + verifyCount);
try { try {
FileUtils.copy(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE); IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE);
verifyCount++; verifyCount++;
// avoid using the Engine class to avoid deadlocks // avoid using the Engine class to avoid deadlocks
Properties p = new Properties(); Properties p = new Properties();
...@@ -145,7 +145,7 @@ public class RecoverTester implements Recorder { ...@@ -145,7 +145,7 @@ public class RecoverTester implements Recorder {
} }
testDatabase += "X"; testDatabase += "X";
try { try {
FileUtils.copy(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE); IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE);
// avoid using the Engine class to avoid deadlocks // avoid using the Engine class to avoid deadlocks
Properties p = new Properties(); Properties p = new Properties();
ConnectionInfo ci = new ConnectionInfo("jdbc:h2:" + testDatabase + ";FILE_LOCK=NO", p); ConnectionInfo ci = new ConnectionInfo("jdbc:h2:" + testDatabase + ";FILE_LOCK=NO", p);
......
...@@ -15,7 +15,6 @@ import java.util.List; ...@@ -15,7 +15,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils;
/** /**
* A path to a file. It similar to the Java 7 <code>java.nio.file.Path</code>, * A path to a file. It similar to the Java 7 <code>java.nio.file.Path</code>,
...@@ -25,7 +24,7 @@ import org.h2.util.StringUtils; ...@@ -25,7 +24,7 @@ import org.h2.util.StringUtils;
*/ */
public abstract class FilePath { public abstract class FilePath {
private static final FilePath DEFAULT = new FilePathDisk(); private static FilePath defaultProvider;
private static Map<String, FilePath> providers; private static Map<String, FilePath> providers;
...@@ -51,25 +50,26 @@ public abstract class FilePath { ...@@ -51,25 +50,26 @@ public abstract class FilePath {
public static FilePath get(String path) { public static FilePath get(String path) {
path = path.replace('\\', '/'); path = path.replace('\\', '/');
int index = path.indexOf(':'); int index = path.indexOf(':');
registerDefaultProviders();
if (index < 2) { if (index < 2) {
// use the default provider if no prefix or // use the default provider if no prefix or
// only a single character (drive name) // only a single character (drive name)
return DEFAULT.getPath(path); return defaultProvider.getPath(path);
} }
String scheme = path.substring(0, index); String scheme = path.substring(0, index);
registerDefaultProviders();
FilePath p = providers.get(scheme); FilePath p = providers.get(scheme);
if (p == null) { if (p == null) {
// provider not found - use the default // provider not found - use the default
p = DEFAULT; p = defaultProvider;
} }
return p.getPath(path); return p.getPath(path);
} }
private static void registerDefaultProviders() { private static void registerDefaultProviders() {
if (providers == null) { if (providers == null || defaultProvider == null) {
Map<String, FilePath> map = Collections.synchronizedMap(New.<String, FilePath>hashMap()); Map<String, FilePath> map = Collections.synchronizedMap(New.<String, FilePath>hashMap());
for (String c : new String[] { for (String c : new String[] {
"org.h2.store.fs.FilePathDisk",
"org.h2.store.fs.FilePathMem", "org.h2.store.fs.FilePathMem",
"org.h2.store.fs.FilePathMemLZF", "org.h2.store.fs.FilePathMemLZF",
"org.h2.store.fs.FilePathSplit", "org.h2.store.fs.FilePathSplit",
...@@ -80,6 +80,9 @@ public abstract class FilePath { ...@@ -80,6 +80,9 @@ public abstract class FilePath {
try { try {
FilePath p = (FilePath) Class.forName(c).newInstance(); FilePath p = (FilePath) Class.forName(c).newInstance();
map.put(p.getScheme(), p); map.put(p.getScheme(), p);
if (defaultProvider == null) {
defaultProvider = p;
}
} catch (Exception e) { } catch (Exception e) {
// ignore - the files may be excluded in purpose // ignore - the files may be excluded in purpose
} }
...@@ -267,9 +270,7 @@ public abstract class FilePath { ...@@ -267,9 +270,7 @@ public abstract class FilePath {
*/ */
protected static synchronized String getNextTempFileNamePart(boolean newRandom) { protected static synchronized String getNextTempFileNamePart(boolean newRandom) {
if (newRandom || tempRandom == null) { if (newRandom || tempRandom == null) {
byte[] prefix = new byte[8]; tempRandom = MathUtils.randomInt(Integer.MAX_VALUE) + ".";
MathUtils.randomBytes(prefix);
tempRandom = StringUtils.convertBytesToHex(prefix) + ".";
} }
return tempRandom + tempSequence++; return tempRandom + tempSequence++;
} }
......
...@@ -19,7 +19,6 @@ import org.h2.security.AES; ...@@ -19,7 +19,6 @@ import org.h2.security.AES;
import org.h2.security.BlockCipher; import org.h2.security.BlockCipher;
import org.h2.security.SHA256; import org.h2.security.SHA256;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/** /**
* An encrypted file. * An encrypted file.
...@@ -38,7 +37,7 @@ public class FilePathCrypt extends FilePathWrapper { ...@@ -38,7 +37,7 @@ public class FilePathCrypt extends FilePathWrapper {
public FileChannel open(String mode) throws IOException { public FileChannel open(String mode) throws IOException {
String[] parsed = parse(name); String[] parsed = parse(name);
FileChannel file = FileUtils.open(parsed[1], mode); FileChannel file = FileUtils.open(parsed[1], mode);
byte[] passwordBytes = StringUtils.convertHexToBytes(parsed[0]); byte[] passwordBytes = DataUtils.utf8Encode(parsed[0]);
return new FileCrypt(name, passwordBytes, file); return new FileCrypt(name, passwordBytes, file);
} }
...@@ -93,6 +92,26 @@ public class FilePathCrypt extends FilePathWrapper { ...@@ -93,6 +92,26 @@ public class FilePathCrypt extends FilePathWrapper {
return new String[] { password, fileName }; return new String[] { password, fileName };
} }
/**
* Convert a char array to a byte array. The char array is cleared after
* use.
*
* @param passwordChars the password characters
* @return the byte array
*/
public static byte[] getPasswordBytes(char[] passwordChars) {
// using UTF-16
int len = passwordChars.length;
byte[] password = new byte[len * 2];
for (int i = 0; i < len; i++) {
char c = passwordChars[i];
password[i + i] = (byte) (c >>> 8);
password[i + i + 1] = (byte) c;
}
Arrays.fill(passwordChars, (char) 0);
return password;
}
/** /**
* An encrypted file with a read cache. * An encrypted file with a read cache.
*/ */
......
...@@ -11,7 +11,6 @@ import java.io.InputStream; ...@@ -11,7 +11,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.List; import java.util.List;
import org.h2.message.DbException;
/** /**
* The base class for wrapping / delegating file systems such as * The base class for wrapping / delegating file systems such as
...@@ -46,7 +45,7 @@ public abstract class FilePathWrapper extends FilePath { ...@@ -46,7 +45,7 @@ public abstract class FilePathWrapper extends FilePath {
p.base = base; p.base = base;
return p; return p;
} catch (Exception e) { } catch (Exception e) {
throw DbException.convert(e); throw new IllegalArgumentException("Path: " + path, e);
} }
} }
......
...@@ -13,9 +13,6 @@ import java.io.OutputStream; ...@@ -13,9 +13,6 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.List; import java.util.List;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.IOUtils;
import org.h2.util.New; import org.h2.util.New;
/** /**
...@@ -290,9 +287,8 @@ public class FileUtils { ...@@ -290,9 +287,8 @@ public class FileUtils {
if (dir != null) { if (dir != null) {
if (exists(dir)) { if (exists(dir)) {
if (!isDirectory(dir)) { if (!isDirectory(dir)) {
DbException.get(ErrorCode.FILE_CREATION_FAILED_1, // this will fail
"Could not create directory, " + createDirectory(dir);
"because a file with the same name already exists: " + dir);
} }
} else { } else {
String parent = getParent(dir); String parent = getParent(dir);
...@@ -302,18 +298,6 @@ public class FileUtils { ...@@ -302,18 +298,6 @@ public class FileUtils {
} }
} }
/**
* Copy a file from one directory to another, or to another file.
*
* @param original the original file name
* @param copy the file name of the copy
*/
public static void copy(String original, String copy) throws IOException {
InputStream in = newInputStream(original);
OutputStream out = newOutputStream(copy, false);
IOUtils.copyAndClose(in, out);
}
/** /**
* Try to delete a file (ignore errors). * Try to delete a file (ignore errors).
* *
......
...@@ -26,7 +26,7 @@ import org.h2.compress.LZFOutputStream; ...@@ -26,7 +26,7 @@ import org.h2.compress.LZFOutputStream;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.Utils; import org.h2.mvstore.DataUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -43,10 +43,10 @@ public class CompressTool { ...@@ -43,10 +43,10 @@ public class CompressTool {
private byte[] getBuffer(int min) { private byte[] getBuffer(int min) {
if (min > MAX_BUFFER_SIZE) { if (min > MAX_BUFFER_SIZE) {
return Utils.newBytes(min); return DataUtils.newBytes(min);
} }
if (cachedBuffer == null || cachedBuffer.length < min) { if (cachedBuffer == null || cachedBuffer.length < min) {
cachedBuffer = Utils.newBytes(min); cachedBuffer = DataUtils.newBytes(min);
} }
return cachedBuffer; return cachedBuffer;
} }
...@@ -78,7 +78,7 @@ public class CompressTool { ...@@ -78,7 +78,7 @@ public class CompressTool {
Compressor compress = getCompressor(algorithm); Compressor compress = getCompressor(algorithm);
byte[] buff = getBuffer((len < 100 ? len + 100 : len) * 2); byte[] buff = getBuffer((len < 100 ? len + 100 : len) * 2);
int newLen = compress(in, in.length, compress, buff); int newLen = compress(in, in.length, compress, buff);
byte[] out = Utils.newBytes(newLen); byte[] out = DataUtils.newBytes(newLen);
System.arraycopy(buff, 0, out, 0, newLen); System.arraycopy(buff, 0, out, 0, newLen);
return out; return out;
} }
...@@ -108,7 +108,7 @@ public class CompressTool { ...@@ -108,7 +108,7 @@ public class CompressTool {
try { try {
int len = readVariableInt(in, 1); int len = readVariableInt(in, 1);
int start = 1 + getVariableIntLength(len); int start = 1 + getVariableIntLength(len);
byte[] buff = Utils.newBytes(len); byte[] buff = DataUtils.newBytes(len);
compress.expand(in, start, in.length - start, buff, 0, len); compress.expand(in, start, in.length - start, buff, 0, len);
return buff; return buff;
} catch (Exception e) { } catch (Exception e) {
......
...@@ -24,6 +24,7 @@ import java.io.Writer; ...@@ -24,6 +24,7 @@ import java.io.Writer;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
/** /**
* This utility class contains input/output functions. * This utility class contains input/output functions.
...@@ -477,4 +478,16 @@ public class IOUtils { ...@@ -477,4 +478,16 @@ public class IOUtils {
return new ByteArrayInputStream(StringUtils.utf8Encode(s)); return new ByteArrayInputStream(StringUtils.utf8Encode(s));
} }
/**
* Copy a file from one directory to another, or to another file.
*
* @param original the original file name
* @param copy the file name of the copy
*/
public static void copyFiles(String original, String copy) throws IOException {
InputStream in = FileUtils.newInputStream(original);
OutputStream out = FileUtils.newOutputStream(copy, false);
copyAndClose(in, out);
}
} }
...@@ -10,10 +10,8 @@ import java.io.ByteArrayOutputStream; ...@@ -10,10 +10,8 @@ import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Random; import java.util.Random;
import org.h2.message.DbException;
/** /**
* This is a utility class with mathematical helper functions. * This is a utility class with mathematical helper functions.
...@@ -32,14 +30,37 @@ public class MathUtils { ...@@ -32,14 +30,37 @@ public class MathUtils {
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
private MathUtils() {
// utility class
}
/** /**
* The maximum scale of a BigDecimal value. * Round the value up to the next block size. The block size must be a power
* of two. As an example, using the block size of 8, the following rounding
* operations are done: 0 stays 0; values 1..8 results in 8, 9..16 results
* in 16, and so on.
*
* @param x the value to be rounded
* @param blockSizePowerOf2 the block size
* @return the rounded value
*/ */
private static final int BIG_DECIMAL_SCALE_MAX = 100000; public static int roundUpInt(int x, int blockSizePowerOf2) {
return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2);
}
private MathUtils() { /**
// utility class * Round the value up to the next block size. The block size must be a power
* of two. As an example, using the block size of 8, the following rounding
* operations are done: 0 stays 0; values 1..8 results in 8, 9..16 results
* in 16, and so on.
*
* @param x the value to be rounded
* @param blockSizePowerOf2 the block size
* @return the rounded value
*/
public static long roundUpLong(long x, long blockSizePowerOf2) {
return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2);
} }
private static synchronized SecureRandom getSecureRandom() { private static synchronized SecureRandom getSecureRandom() {
...@@ -198,34 +219,6 @@ public class MathUtils { ...@@ -198,34 +219,6 @@ public class MathUtils {
} }
} }
/**
* Round the value up to the next block size. The block size must be a power
* of two. As an example, using the block size of 8, the following rounding
* operations are done: 0 stays 0; values 1..8 results in 8, 9..16 results
* in 16, and so on.
*
* @param x the value to be rounded
* @param blockSizePowerOf2 the block size
* @return the rounded value
*/
public static int roundUpInt(int x, int blockSizePowerOf2) {
return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2);
}
/**
* Round the value up to the next block size. The block size must be a power
* of two. As an example, using the block size of 8, the following rounding
* operations are done: 0 stays 0; values 1..8 results in 8, 9..16 results
* in 16, and so on.
*
* @param x the value to be rounded
* @param blockSizePowerOf2 the block size
* @return the rounded value
*/
public static long roundUpLong(long x, long blockSizePowerOf2) {
return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2);
}
/** /**
* Get the value that is equal or higher than this value, and that is a * Get the value that is equal or higher than this value, and that is a
* power of two. * power of two.
...@@ -241,20 +234,6 @@ public class MathUtils { ...@@ -241,20 +234,6 @@ public class MathUtils {
return (int) i; return (int) i;
} }
/**
* Set the scale of a BigDecimal value.
*
* @param bd the BigDecimal value
* @param scale the new scale
* @return the scaled value
*/
public static BigDecimal setScale(BigDecimal bd, int scale) {
if (scale > BIG_DECIMAL_SCALE_MAX || scale < -BIG_DECIMAL_SCALE_MAX) {
throw DbException.getInvalidValueException("scale", scale);
}
return bd.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
/** /**
* Convert a long value to an int value. Values larger than the biggest int * Convert a long value to an int value. Values larger than the biggest int
* value is converted to the biggest int value, and values smaller than the * value is converted to the biggest int value, and values smaller than the
......
...@@ -407,32 +407,6 @@ public class Utils { ...@@ -407,32 +407,6 @@ 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) {
if (len == 0) {
return EMPTY_BYTES;
}
try {
return new byte[len];
} catch (OutOfMemoryError e) {
Error e2 = new OutOfMemoryError("Requested memory: " + len);
e2.initCause(e);
throw e2;
}
}
/** /**
* Create an int array with the given size. * Create an int array with the given size.
* *
......
...@@ -26,6 +26,7 @@ import org.h2.engine.Constants; ...@@ -26,6 +26,7 @@ import org.h2.engine.Constants;
import org.h2.engine.SessionInterface; import org.h2.engine.SessionInterface;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.mvstore.DataUtils;
import org.h2.security.SHA256; import org.h2.security.SHA256;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.store.DataReader; import org.h2.store.DataReader;
...@@ -288,7 +289,7 @@ public class Transfer { ...@@ -288,7 +289,7 @@ public class Transfer {
if (len == -1) { if (len == -1) {
return null; return null;
} }
byte[] b = Utils.newBytes(len); byte[] b = DataUtils.newBytes(len);
in.readFully(b); in.readFully(b);
return b; return b;
} }
......
...@@ -45,6 +45,11 @@ public class ValueDecimal extends Value { ...@@ -45,6 +45,11 @@ public class ValueDecimal extends Value {
private static final int DIVIDE_SCALE_ADD = 25; private static final int DIVIDE_SCALE_ADD = 25;
/**
* The maximum scale of a BigDecimal value.
*/
private static final int BIG_DECIMAL_SCALE_MAX = 100000;
private final BigDecimal value; private final BigDecimal value;
private String valueString; private String valueString;
private int precision; private int precision;
...@@ -183,7 +188,7 @@ public class ValueDecimal extends Value { ...@@ -183,7 +188,7 @@ public class ValueDecimal extends Value {
return this; return this;
} }
} }
BigDecimal bd = MathUtils.setScale(value, targetScale); BigDecimal bd = ValueDecimal.setScale(value, targetScale);
return ValueDecimal.get(bd); return ValueDecimal.get(bd);
} }
...@@ -229,4 +234,18 @@ public class ValueDecimal extends Value { ...@@ -229,4 +234,18 @@ public class ValueDecimal extends Value {
return value.precision() + 120; return value.precision() + 120;
} }
/**
* Set the scale of a BigDecimal value.
*
* @param bd the BigDecimal value
* @param scale the new scale
* @return the scaled value
*/
public static BigDecimal setScale(BigDecimal bd, int scale) {
if (scale > BIG_DECIMAL_SCALE_MAX || scale < -BIG_DECIMAL_SCALE_MAX) {
throw DbException.getInvalidValueException("scale", scale);
}
return bd.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
} }
...@@ -17,6 +17,7 @@ import java.sql.SQLException; ...@@ -17,6 +17,7 @@ import java.sql.SQLException;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
import org.h2.store.FileStore; import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream; import org.h2.store.FileStoreInputStream;
...@@ -360,11 +361,11 @@ public class ValueLob extends Value { ...@@ -360,11 +361,11 @@ public class ValueLob extends Value {
buff = IOUtils.readBytesAndClose(in, -1); buff = IOUtils.readBytesAndClose(in, -1);
len = buff.length; len = buff.length;
} else { } else {
buff = Utils.newBytes(len); buff = DataUtils.newBytes(len);
len = IOUtils.readFully(in, buff, 0, len); len = IOUtils.readFully(in, buff, 0, len);
} }
if (len <= handler.getMaxLengthInplaceLob()) { if (len <= handler.getMaxLengthInplaceLob()) {
byte[] small = Utils.newBytes(len); byte[] small = DataUtils.newBytes(len);
System.arraycopy(buff, 0, small, 0, len); System.arraycopy(buff, 0, small, 0, len);
return ValueLob.createSmallLob(Value.BLOB, small); return ValueLob.createSmallLob(Value.BLOB, small);
} }
...@@ -696,7 +697,7 @@ public class ValueLob extends Value { ...@@ -696,7 +697,7 @@ public class ValueLob extends Value {
int len = getBufferSize(h, compress, Long.MAX_VALUE); int len = getBufferSize(h, compress, Long.MAX_VALUE);
int tabId = tableId; int tabId = tableId;
if (type == Value.BLOB) { if (type == Value.BLOB) {
createFromStream(Utils.newBytes(len), 0, getInputStream(), Long.MAX_VALUE, h); createFromStream(DataUtils.newBytes(len), 0, getInputStream(), Long.MAX_VALUE, h);
} else { } else {
createFromReader(new char[len], 0, getReader(), Long.MAX_VALUE, h); createFromReader(new char[len], 0, getReader(), Long.MAX_VALUE, h);
} }
...@@ -757,7 +758,7 @@ public class ValueLob extends Value { ...@@ -757,7 +758,7 @@ public class ValueLob extends Value {
private static void copyFileTo(DataHandler h, String sourceFileName, String targetFileName) { private static void copyFileTo(DataHandler h, String sourceFileName, String targetFileName) {
synchronized (h.getLobSyncObject()) { synchronized (h.getLobSyncObject()) {
try { try {
FileUtils.copy(sourceFileName, targetFileName); IOUtils.copyFiles(sourceFileName, targetFileName);
} catch (IOException e) { } catch (IOException e) {
throw DbException.convertIOException(e, null); throw DbException.convertIOException(e, null);
} }
......
...@@ -16,6 +16,7 @@ import java.sql.SQLException; ...@@ -16,6 +16,7 @@ import java.sql.SQLException;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
import org.h2.store.FileStore; import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream; import org.h2.store.FileStoreInputStream;
...@@ -438,11 +439,11 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo ...@@ -438,11 +439,11 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
buff = IOUtils.readBytesAndClose(in, -1); buff = IOUtils.readBytesAndClose(in, -1);
len = buff.length; len = buff.length;
} else { } else {
buff = Utils.newBytes(len); buff = DataUtils.newBytes(len);
len = IOUtils.readFully(in, buff, 0, len); len = IOUtils.readFully(in, buff, 0, len);
} }
if (len <= handler.getMaxLengthInplaceLob()) { if (len <= handler.getMaxLengthInplaceLob()) {
byte[] small = Utils.newBytes(len); byte[] small = DataUtils.newBytes(len);
System.arraycopy(buff, 0, small, 0, len); System.arraycopy(buff, 0, small, 0, len);
return ValueLobDb.createSmallLob(Value.BLOB, small, small.length); return ValueLobDb.createSmallLob(Value.BLOB, small, small.length);
} }
......
...@@ -211,7 +211,7 @@ public class ValueTimestamp extends Value { ...@@ -211,7 +211,7 @@ public class ValueTimestamp extends Value {
long n = nanos; long n = nanos;
BigDecimal bd = BigDecimal.valueOf(n); BigDecimal bd = BigDecimal.valueOf(n);
bd = bd.movePointLeft(9); bd = bd.movePointLeft(9);
bd = MathUtils.setScale(bd, targetScale); bd = ValueDecimal.setScale(bd, targetScale);
bd = bd.movePointRight(9); bd = bd.movePointRight(9);
long n2 = bd.longValue(); long n2 = bd.longValue();
if (n2 == n) { if (n2 == n) {
......
...@@ -46,16 +46,6 @@ public class RowDataType implements DataType { ...@@ -46,16 +46,6 @@ public class RowDataType implements DataType {
return 0; return 0;
} }
public int getMaxLength(Object obj) {
Object[] x = (Object[]) obj;
int len = x.length;
int result = DataUtils.MAX_VAR_INT_LEN;
for (int i = 0; i < len; i++) {
result += types[i].getMaxLength(x[i]);
}
return result;
}
public int getMemory(Object obj) { public int getMemory(Object obj) {
Object[] x = (Object[]) obj; Object[] x = (Object[]) obj;
int len = x.length; int len = x.length;
...@@ -75,13 +65,15 @@ public class RowDataType implements DataType { ...@@ -75,13 +65,15 @@ public class RowDataType implements DataType {
return x; return x;
} }
public void write(ByteBuffer buff, Object obj) { public ByteBuffer write(ByteBuffer buff, Object obj) {
Object[] x = (Object[]) obj; Object[] x = (Object[]) obj;
int len = x.length; int len = x.length;
DataUtils.writeVarInt(buff, len); DataUtils.writeVarInt(buff, len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
types[i].write(buff, x[i]); buff = DataUtils.ensureCapacity(buff, 0);
buff = types[i].write(buff, x[i]);
} }
return buff;
} }
} }
...@@ -37,6 +37,8 @@ public class TestConcurrent extends TestMVStore { ...@@ -37,6 +37,8 @@ public class TestConcurrent extends TestMVStore {
public void test() throws Exception { public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir());
testConcurrentOnlineBackup(); testConcurrentOnlineBackup();
testConcurrentMap(); testConcurrentMap();
testConcurrentIterate(); testConcurrentIterate();
......
...@@ -43,6 +43,9 @@ public class TestMVRTree extends TestMVStore { ...@@ -43,6 +43,9 @@ public class TestMVRTree extends TestMVStore {
} }
public void test() { public void test() {
FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir());
testExample(); testExample();
testMany(); testMany();
testSimple(); testSimple();
......
...@@ -40,6 +40,8 @@ public class TestMVStore extends TestBase { ...@@ -40,6 +40,8 @@ public class TestMVStore extends TestBase {
public void test() throws Exception { public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir());
testEncryptedFile(); testEncryptedFile();
testFileFormatChange(); testFileFormatChange();
testRecreateMap(); testRecreateMap();
......
...@@ -127,14 +127,14 @@ public class TestObjectDataType extends TestBase { ...@@ -127,14 +127,14 @@ public class TestObjectDataType extends TestBase {
assertTrue(ot.getMemory(x) >= 0); assertTrue(ot.getMemory(x) >= 0);
ot.getMemory(last); ot.getMemory(last);
assertTrue(ot.getMaxLength(x) >= 1); assertTrue(ot.getMemory(x) >= 0);
ot.getMemory(last); ot.getMemory(last);
assertEquals(0, ot.compare(x, x)); assertEquals(0, ot.compare(x, x));
ByteBuffer buff = ByteBuffer.allocate(ot.getMaxLength(x) + 1); ByteBuffer buff = ByteBuffer.allocate(1024);
ot.getMemory(last); ot.getMemory(last);
ot.write(buff, x); buff = ot.write(buff, x);
buff.put((byte) 123); buff.put((byte) 123);
buff.flip(); buff.flip();
......
...@@ -40,6 +40,9 @@ public class TestStreamStore extends TestBase { ...@@ -40,6 +40,9 @@ public class TestStreamStore extends TestBase {
@Override @Override
public void test() throws IOException { public void test() throws IOException {
FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir());
testVeryLarge(); testVeryLarge();
testDetectIllegalId(); testDetectIllegalId();
testTreeStructure(); testTreeStructure();
......
...@@ -29,6 +29,7 @@ public class TestClearReferences extends TestBase { ...@@ -29,6 +29,7 @@ public class TestClearReferences extends TestBase {
"org.h2.engine.SessionRemote.sessionFactory", "org.h2.engine.SessionRemote.sessionFactory",
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem", "org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.store.RecoverTester.instance", "org.h2.store.RecoverTester.instance",
"org.h2.store.fs.FilePath.defaultProvider",
"org.h2.store.fs.FilePath.providers", "org.h2.store.fs.FilePath.providers",
"org.h2.store.fs.FilePath.tempRandom", "org.h2.store.fs.FilePath.tempRandom",
"org.h2.tools.CompressTool.cachedBuffer", "org.h2.tools.CompressTool.cachedBuffer",
......
...@@ -31,6 +31,7 @@ import org.h2.test.utils.AssertThrows; ...@@ -31,6 +31,7 @@ import org.h2.test.utils.AssertThrows;
import org.h2.test.utils.FilePathDebug; import org.h2.test.utils.FilePathDebug;
import org.h2.tools.Backup; import org.h2.tools.Backup;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.util.IOUtils;
/** /**
* Tests various file system. * Tests various file system.
...@@ -425,7 +426,7 @@ public class TestFileSystem extends TestBase { ...@@ -425,7 +426,7 @@ public class TestFileSystem extends TestBase {
List<String> list = FileUtils.newDirectoryStream(fsBase); List<String> list = FileUtils.newDirectoryStream(fsBase);
assertEquals(1, list.size()); assertEquals(1, list.size());
assertTrue(list.get(0).endsWith("test")); assertTrue(list.get(0).endsWith("test"));
FileUtils.copy(fsBase + "/test", fsBase + "/test3"); IOUtils.copyFiles(fsBase + "/test", fsBase + "/test3");
FileUtils.moveTo(fsBase + "/test3", fsBase + "/test2"); FileUtils.moveTo(fsBase + "/test3", fsBase + "/test2");
FileUtils.moveTo(fsBase + "/test2", fsBase + "/test2"); FileUtils.moveTo(fsBase + "/test2", fsBase + "/test2");
assertTrue(!FileUtils.exists(fsBase + "/test3")); assertTrue(!FileUtils.exists(fsBase + "/test3"));
......
...@@ -20,6 +20,7 @@ import org.h2.store.fs.FileUtils; ...@@ -20,6 +20,7 @@ import org.h2.store.fs.FileUtils;
import org.h2.store.fs.Recorder; import org.h2.store.fs.Recorder;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Recover; import org.h2.tools.Recover;
import org.h2.util.IOUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.Profiler; import org.h2.util.Profiler;
import org.h2.util.Utils; import org.h2.util.Utils;
...@@ -95,7 +96,7 @@ public class TestReopen extends TestBase implements Recorder { ...@@ -95,7 +96,7 @@ public class TestReopen extends TestBase implements Recorder {
System.out.println("+ write #" + writeCount + " verify #" + verifyCount); System.out.println("+ write #" + writeCount + " verify #" + verifyCount);
try { try {
FileUtils.copy(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE); IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE);
verifyCount++; verifyCount++;
// avoid using the Engine class to avoid deadlocks // avoid using the Engine class to avoid deadlocks
Properties p = new Properties(); Properties p = new Properties();
...@@ -142,7 +143,7 @@ public class TestReopen extends TestBase implements Recorder { ...@@ -142,7 +143,7 @@ public class TestReopen extends TestBase implements Recorder {
} }
testDatabase += "X"; testDatabase += "X";
try { try {
FileUtils.copy(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE); IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE);
// avoid using the Engine class to avoid deadlocks // avoid using the Engine class to avoid deadlocks
Properties p = new Properties(); Properties p = new Properties();
ConnectionInfo ci = new ConnectionInfo("jdbc:h2:" + testDatabase + ";FILE_LOCK=NO", p); ConnectionInfo ci = new ConnectionInfo("jdbc:h2:" + testDatabase + ";FILE_LOCK=NO", p);
......
...@@ -200,7 +200,7 @@ public class FileShell extends Tool { ...@@ -200,7 +200,7 @@ public class FileShell extends Tool {
String source = getFile(list[i++]); String source = getFile(list[i++]);
String target = getFile(list[i++]); String target = getFile(list[i++]);
end(list, i); end(list, i);
FileUtils.copy(source, target); IOUtils.copyFiles(source, target);
} else if ("head".equals(c)) { } else if ("head".equals(c)) {
String file = getFile(list[i++]); String file = getFile(list[i++]);
end(list, i); end(list, i);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论