提交 36c69f54 authored 作者: Thomas Mueller's avatar Thomas Mueller

File system: new method FileSystem.setReadOnly.

上级 b193172c
......@@ -74,4 +74,5 @@ public interface FileObject {
* @return the name
*/
String getName();
}
......@@ -9,7 +9,6 @@ package org.h2.store.fs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.h2.constant.SysProperties;
import org.h2.util.IOUtils;
......
......@@ -6,235 +6,43 @@
*/
package org.h2.store.fs;
import java.io.EOFException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.h2.compress.CompressLZF;
import org.h2.util.MathUtils;
/**
* This class is an abstraction of an in-memory random access file.
* Data compression using the LZF algorithm is supported as well.
* This class represents an in-memory file.
*/
public class FileObjectMemory implements FileObject {
private static final int CACHE_SIZE = 8;
private static final int BLOCK_SIZE_SHIFT = 10;
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private static final CompressLZF LZF = new CompressLZF();
private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
private static final byte[] COMPRESSED_EMPTY_BLOCK;
//## Java 1.4 begin ##
private static final Cache<CompressItem, CompressItem> COMPRESS_LATER = new Cache<CompressItem, CompressItem>(CACHE_SIZE);
//## Java 1.4 end ##
private String name;
private final boolean compress;
private long length;
private final FileObjectMemoryData data;
private long pos;
private byte[][] data;
private long lastModified;
static {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
COMPRESSED_EMPTY_BLOCK = new byte[len];
System.arraycopy(BUFFER, 0, COMPRESSED_EMPTY_BLOCK, 0, len);
}
/**
* This small cache compresses the data if an element leaves the cache.
*/
//## Java 1.4 begin ##
static class Cache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
private int size;
Cache(int size) {
super(size, (float) 0.75, true);
this.size = size;
}
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
if (size() < size) {
return false;
}
CompressItem c = (CompressItem) eldest.getKey();
compress(c.data, c.page);
return true;
}
}
/**
* Represents a compressed item.
*/
static class CompressItem {
/**
* The file data.
*/
byte[][] data;
/**
* The page to compress.
*/
int page;
public int hashCode() {
return page;
}
public boolean equals(Object o) {
if (o instanceof CompressItem) {
CompressItem c = (CompressItem) o;
return c.data == data && c.page == page;
}
return false;
}
}
//## Java 1.4 end ##
FileObjectMemory(String name, boolean compress) {
this.name = name;
this.compress = compress;
data = new byte[0][];
touch();
}
private static void compressLater(byte[][] data, int page) {
//## Java 1.4 begin ##
CompressItem c = new CompressItem();
c.data = data;
c.page = page;
synchronized (LZF) {
COMPRESS_LATER.put(c, c);
}
//## Java 1.4 end ##
}
private static void expand(byte[][] data, int page) {
byte[] d = data[page];
if (d.length == BLOCK_SIZE) {
return;
}
byte[] out = new byte[BLOCK_SIZE];
if (d != COMPRESSED_EMPTY_BLOCK) {
synchronized (LZF) {
LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE);
}
}
data[page] = out;
}
/**
* Compress the data in a byte array.
*
* @param data the page array
* @param page which page to compress
*/
static void compress(byte[][] data, int page) {
byte[] d = data[page];
synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
if (len <= BLOCK_SIZE) {
d = new byte[len];
System.arraycopy(BUFFER, 0, d, 0, len);
data[page] = d;
}
}
}
private void touch() {
lastModified = System.currentTimeMillis();
FileObjectMemory(FileObjectMemoryData data) {
this.data = data;
}
public long length() {
return length;
return data.length();
}
public void setFileLength(long newLength) {
touch();
if (newLength < length) {
public void setFileLength(long newLength) throws IOException {
data.touch();
if (newLength < length()) {
pos = Math.min(pos, newLength);
changeLength(newLength);
long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
if (end != newLength) {
int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
expand(data, lastPage);
byte[] d = data[lastPage];
for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
if (compress) {
compressLater(data, lastPage);
}
}
} else {
changeLength(newLength);
}
data.setFileLength(newLength);
}
public void seek(long newPos) {
this.pos = (int) newPos;
}
private void changeLength(long len) {
length = len;
len = MathUtils.roundUpLong(len, BLOCK_SIZE);
int blocks = (int) (len >>> BLOCK_SIZE_SHIFT);
if (blocks != data.length) {
byte[][] n = new byte[blocks][];
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
for (int i = data.length; i < blocks; i++) {
n[i] = COMPRESSED_EMPTY_BLOCK;
}
data = n;
}
}
private void readWrite(byte[] b, int off, int len, boolean write) throws IOException {
long end = pos + len;
if (end > length) {
if (write) {
changeLength(end);
} else {
if (len == 0) {
return;
}
throw new EOFException("File: " + name);
}
}
while (len > 0) {
int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, page);
byte[] block = data[page];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) {
System.arraycopy(b, off, block, blockOffset, l);
} else {
System.arraycopy(block, blockOffset, b, off, l);
}
if (compress) {
compressLater(data, page);
}
off += l;
pos += l;
len -= l;
}
}
public void write(byte[] b, int off, int len) throws IOException {
touch();
readWrite(b, off, len, true);
data.touch();
pos = data.readWrite(pos, b, off, len, true);
}
public void readFully(byte[] b, int off, int len) throws IOException {
readWrite(b, off, len, false);
pos = data.readWrite(pos, b, off, len, false);
}
public long getFilePointer() {
......@@ -246,18 +54,27 @@ public class FileObjectMemory implements FileObject {
}
public void sync() {
// nothing to do
data.sync();
}
public void setName(String name) {
this.name = name;
data.setName(name);
}
public String getName() {
return name;
return data.getName();
}
public long getLastModified() {
return lastModified;
return data.getLastModified();
}
boolean canWrite() {
return data.canWrite();
}
boolean setReadOnly() {
return data.setReadOnly();
}
}
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store.fs;
import java.io.EOFException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.h2.compress.CompressLZF;
import org.h2.util.MathUtils;
/**
* This class contains the data of an in-memory random access file.
* Data compression using the LZF algorithm is supported as well.
*/
public class FileObjectMemoryData {
private static final int CACHE_SIZE = 8;
private static final int BLOCK_SIZE_SHIFT = 10;
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private static final CompressLZF LZF = new CompressLZF();
private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
private static final byte[] COMPRESSED_EMPTY_BLOCK;
//## Java 1.4 begin ##
private static final Cache<CompressItem, CompressItem> COMPRESS_LATER = new Cache<CompressItem, CompressItem>(CACHE_SIZE);
//## Java 1.4 end ##
private String name;
private final boolean compress;
private long length;
private byte[][] data;
private long lastModified;
private boolean isReadOnly;
static {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
COMPRESSED_EMPTY_BLOCK = new byte[len];
System.arraycopy(BUFFER, 0, COMPRESSED_EMPTY_BLOCK, 0, len);
}
/**
* This small cache compresses the data if an element leaves the cache.
*/
//## Java 1.4 begin ##
static class Cache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
private int size;
Cache(int size) {
super(size, (float) 0.75, true);
this.size = size;
}
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
if (size() < size) {
return false;
}
CompressItem c = (CompressItem) eldest.getKey();
compress(c.data, c.page);
return true;
}
}
/**
* Represents a compressed item.
*/
static class CompressItem {
/**
* The file data.
*/
byte[][] data;
/**
* The page to compress.
*/
int page;
public int hashCode() {
return page;
}
public boolean equals(Object o) {
if (o instanceof CompressItem) {
CompressItem c = (CompressItem) o;
return c.data == data && c.page == page;
}
return false;
}
}
//## Java 1.4 end ##
FileObjectMemoryData(String name, boolean compress) {
this.name = name;
this.compress = compress;
data = new byte[0][];
lastModified = System.currentTimeMillis();
}
private static void compressLater(byte[][] data, int page) {
//## Java 1.4 begin ##
CompressItem c = new CompressItem();
c.data = data;
c.page = page;
synchronized (LZF) {
COMPRESS_LATER.put(c, c);
}
//## Java 1.4 end ##
}
private static void expand(byte[][] data, int page) {
byte[] d = data[page];
if (d.length == BLOCK_SIZE) {
return;
}
byte[] out = new byte[BLOCK_SIZE];
if (d != COMPRESSED_EMPTY_BLOCK) {
synchronized (LZF) {
LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE);
}
}
data[page] = out;
}
/**
* Compress the data in a byte array.
*
* @param data the page array
* @param page which page to compress
*/
static void compress(byte[][] data, int page) {
byte[] d = data[page];
synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
if (len <= BLOCK_SIZE) {
d = new byte[len];
System.arraycopy(BUFFER, 0, d, 0, len);
data[page] = d;
}
}
}
void touch() throws IOException {
if (isReadOnly) {
throw new IOException("Read only");
}
lastModified = System.currentTimeMillis();
}
long length() {
return length;
}
void setFileLength(long newLength) {
if (newLength < length) {
changeLength(newLength);
long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
if (end != newLength) {
int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
expand(data, lastPage);
byte[] d = data[lastPage];
for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
if (compress) {
compressLater(data, lastPage);
}
}
} else {
changeLength(newLength);
}
}
private void changeLength(long len) {
length = len;
len = MathUtils.roundUpLong(len, BLOCK_SIZE);
int blocks = (int) (len >>> BLOCK_SIZE_SHIFT);
if (blocks != data.length) {
byte[][] n = new byte[blocks][];
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
for (int i = data.length; i < blocks; i++) {
n[i] = COMPRESSED_EMPTY_BLOCK;
}
data = n;
}
}
long readWrite(long pos, byte[] b, int off, int len, boolean write) throws IOException {
long end = pos + len;
if (end > length) {
if (write) {
changeLength(end);
} else {
if (len == 0) {
return pos;
}
throw new EOFException("File: " + name);
}
}
while (len > 0) {
int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, page);
byte[] block = data[page];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) {
System.arraycopy(b, off, block, blockOffset, l);
} else {
System.arraycopy(block, blockOffset, b, off, l);
}
if (compress) {
compressLater(data, page);
}
off += l;
pos += l;
len -= l;
}
return pos;
}
public void sync() {
// nothing to do
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public long getLastModified() {
return lastModified;
}
public boolean canWrite() {
return !isReadOnly;
}
public boolean setReadOnly() {
isReadOnly = true;
return true;
}
}
......@@ -290,6 +290,13 @@ public abstract class FileSystem {
*/
public abstract InputStream openFileInputStream(String fileName) throws IOException;
/**
* Disable the ability to write.
*
* @return true if the call was successful
*/
public abstract boolean setReadOnly(String fileName);
/**
* Get the next temporary file name part (the part in the middle).
*
......
......@@ -247,6 +247,12 @@ public class FileSystemDisk extends FileSystem {
return f.exists() && !canWriteInternal(f);
}
public boolean setReadOnly(String fileName) {
fileName = translateFileName(fileName);
File f = new File(fileName);
return f.setReadOnly();
}
public String normalize(String fileName) {
fileName = translateFileName(fileName);
File f = new File(fileName);
......
......@@ -33,7 +33,7 @@ public class FileSystemMemory extends FileSystem {
public static final String PREFIX_LZF = "memLZF:";
private static final FileSystemMemory INSTANCE = new FileSystemMemory();
private static final TreeMap<String, FileObjectMemory> MEMORY_FILES = new TreeMap<String, FileObjectMemory>();
private static final TreeMap<String, FileObjectMemoryData> MEMORY_FILES = new TreeMap<String, FileObjectMemoryData>();
private FileSystemMemory() {
// don't allow construction
......@@ -51,7 +51,7 @@ public class FileSystemMemory extends FileSystem {
oldName = normalize(oldName);
newName = normalize(newName);
synchronized (MEMORY_FILES) {
FileObjectMemory f = getMemoryFile(oldName);
FileObjectMemoryData f = getMemoryFile(oldName);
f.setName(newName);
MEMORY_FILES.remove(oldName);
MEMORY_FILES.put(newName, f);
......@@ -119,7 +119,11 @@ public class FileSystemMemory extends FileSystem {
}
public boolean isReadOnly(String fileName) {
return false;
return !getMemoryFile(fileName).canWrite();
}
public boolean setReadOnly(String fileName) {
return getMemoryFile(fileName).setReadOnly();
}
public String normalize(String fileName) {
......@@ -191,33 +195,32 @@ public class FileSystemMemory extends FileSystem {
public OutputStream openFileOutputStream(String fileName, boolean append) {
try {
FileObjectMemory obj = getMemoryFile(fileName);
obj.seek(0);
return new FileObjectOutputStream(obj, append);
FileObjectMemoryData obj = getMemoryFile(fileName);
FileObjectMemory m = new FileObjectMemory(obj);
return new FileObjectOutputStream(m, append);
} catch (IOException e) {
throw DbException.convertIOException(e, fileName);
}
}
public InputStream openFileInputStream(String fileName) {
FileObjectMemory obj = getMemoryFile(fileName);
obj.seek(0);
return new FileObjectInputStream(obj);
FileObjectMemoryData obj = getMemoryFile(fileName);
FileObjectMemory m = new FileObjectMemory(obj);
return new FileObjectInputStream(m);
}
public FileObject openFileObject(String fileName, String mode) {
FileObjectMemory obj = getMemoryFile(fileName);
obj.seek(0);
return obj;
FileObjectMemoryData obj = getMemoryFile(fileName);
return new FileObjectMemory(obj);
}
private FileObjectMemory getMemoryFile(String fileName) {
private FileObjectMemoryData getMemoryFile(String fileName) {
fileName = normalize(fileName);
synchronized (MEMORY_FILES) {
FileObjectMemory m = MEMORY_FILES.get(fileName);
FileObjectMemoryData m = MEMORY_FILES.get(fileName);
if (m == null) {
boolean compress = fileName.startsWith(PREFIX_LZF);
m = new FileObjectMemory(fileName, compress);
m = new FileObjectMemoryData(fileName, compress);
MEMORY_FILES.put(fileName, m);
}
return m;
......
......@@ -36,6 +36,20 @@ public class FileSystemSplit extends FileSystem {
return getFileSystem(fileName).canWrite(fileName);
}
public boolean setReadOnly(String fileName) {
fileName = translateFileName(fileName);
boolean result = false;
for (int i = 0;; i++) {
String f = getFileName(fileName, i);
if (getFileSystem(f).exists(f)) {
result = getFileSystem(f).setReadOnly(f);
} else {
break;
}
}
return result;
}
public void copy(String original, String copy) {
original = translateFileName(original);
copy = translateFileName(copy);
......
......@@ -140,6 +140,10 @@ public class FileSystemZip extends FileSystem {
return true;
}
public boolean setReadOnly(String fileName) {
return true;
}
public long length(String fileName) {
try {
ZipFile file = openZipFile(fileName);
......
......@@ -320,6 +320,10 @@ public class FileSystemDatabase extends FileSystem {
return false;
}
public boolean setReadOnly(String fileName) {
return false;
}
public synchronized long length(String fileName) {
try {
long id = getId(fileName, false);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论