blob: 91cf53878989e4e8c64f8cde51e370064c72c00d [file] [log] [blame]
/*
* Copyright (c) 2011, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.indexer.pagedstorage.filesystem;
import com.google.dart.indexer.pagedstorage.util.MathUtils;
import java.io.EOFException;
import java.io.IOException;
/**
* This class is an abstraction of an in-memory random access file. Data compression using the LZF
* algorithm is supported as well.
*/
public class InMemoryFileObject implements FileObject {
private static final int BLOCK_SIZE_SHIFT = 16;
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private String name;
private long length;
private long pos;
private byte[][] data;
private long lastModified;
InMemoryFileObject(String name) {
this.name = name;
data = new byte[0][];
touch();
}
@Override
public void close() {
pos = 0;
}
@Override
public long getFilePointer() {
return pos;
}
public long getLastModified() {
return lastModified;
}
@Override
public String getName() {
return name;
}
@Override
public long length() {
return length;
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
readWrite(b, off, len, false);
}
@Override
public void seek(long pos) {
this.pos = (int) pos;
}
@Override
public void setFileLength(long newLength) {
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);
byte[] d = data[lastPage];
for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
}
} else {
changeLength(newLength);
}
}
public void setName(String name) {
this.name = name;
}
@Override
public void sync() {
// nothing to do
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
touch();
readWrite(b, off, len, true);
}
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] = new byte[BLOCK_SIZE];
}
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);
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);
}
off += l;
pos += l;
len -= l;
}
}
private void touch() {
lastModified = System.currentTimeMillis();
}
}