aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu/gcj/runtime/PersistentByteMap.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/gcj/runtime/PersistentByteMap.java')
-rw-r--r--libjava/gnu/gcj/runtime/PersistentByteMap.java187
1 files changed, 161 insertions, 26 deletions
diff --git a/libjava/gnu/gcj/runtime/PersistentByteMap.java b/libjava/gnu/gcj/runtime/PersistentByteMap.java
index 230d7858576..a20f5b8ab2b 100644
--- a/libjava/gnu/gcj/runtime/PersistentByteMap.java
+++ b/libjava/gnu/gcj/runtime/PersistentByteMap.java
@@ -39,10 +39,6 @@ USAGE:
BUGS/FEATURES:
remove() isn't written yet.
- we can't change the capacity of a PersistentByteMap.
-
- 0x12345678 is a bad choice for the magic number.
-
capacity is fixed once the map has been created.
We use linear probing to resolve collisions. It might be
@@ -51,11 +47,7 @@ BUGS/FEATURES:
table is half full there are only on average 1.5 probes for a
successful search and 2.5 probes for an unsuccessful one.
- We don't use unique strings. This wastes space.
-
- capacity should probably be prime, but we don't check that.
-
- we don't do any locking at all: adding to a PersistentByteMap
+ We don't do any locking at all: adding to a PersistentByteMap
at runtime is possible, but it requires filesystem locks
around get(), put(), and remove().
*/
@@ -67,6 +59,7 @@ import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.security.MessageDigest;
+import java.math.BigInteger;
public class PersistentByteMap
{
@@ -94,12 +87,18 @@ public class PersistentByteMap
private long length; // the length of the underlying file
+ private final File name; // The name of the underlying file
+
static private final int UNUSED_ENTRY = -1;
static public final int KEYS = 0;
static public final int VALUES = 1;
static public final int ENTRIES = 2;
+ private HashMap values; // A map of strings in the string table.
+
+ FileChannel fc; // The underlying file channel.
+
static final public class AccessMode
{
private final FileChannel.MapMode mapMode;
@@ -108,10 +107,12 @@ public class PersistentByteMap
{
READ_ONLY = new AccessMode(FileChannel.MapMode.READ_ONLY);
READ_WRITE = new AccessMode(FileChannel.MapMode.READ_WRITE);
+ PRIVATE = new AccessMode(FileChannel.MapMode.PRIVATE);
}
public static final AccessMode READ_ONLY;
public static final AccessMode READ_WRITE;
+ public static final AccessMode PRIVATE;
private AccessMode(FileChannel.MapMode mode)
{
@@ -119,8 +120,9 @@ public class PersistentByteMap
}
}
- private PersistentByteMap()
+ private PersistentByteMap(File name)
{
+ this.name = name;
}
public PersistentByteMap(String filename, AccessMode mode)
@@ -132,7 +134,7 @@ public class PersistentByteMap
public PersistentByteMap(File f, AccessMode mode)
throws IOException
{
- FileChannel fc;
+ name = f;
if (mode == AccessMode.READ_ONLY)
{
@@ -149,7 +151,7 @@ public class PersistentByteMap
buf = fc.map(mode.mapMode, 0, length);
int magic = getWord (MAGIC);
- if (magic != 0x12345678)
+ if (magic != 0x67636a64) /* "gcjd" */
throw new IllegalArgumentException(f.getName());
table_base = getWord (TABLE_BASE);
@@ -167,8 +169,27 @@ public class PersistentByteMap
{
f.createNewFile();
RandomAccessFile raf = new RandomAccessFile(f, "rw");
-
- this.capacity = capacity;
+
+ {
+ // The user has explicitly provided a size for the table.
+ // We're going to make that size prime. This isn't
+ // strictly necessary but it can't hurt.
+ //
+ // We expand the size by 3/2 because the hash table is
+ // intolerably slow when more than 2/3 full.
+
+ BigInteger size = new BigInteger(Integer.toString(capacity * 3/2));
+ BigInteger two = BigInteger.ONE.add(BigInteger.ONE);
+
+ if (size.getLowestSetBit() != 0) // A hard way to say isEven()
+ size = size.add(BigInteger.ONE);
+
+ while (! size.isProbablePrime(10))
+ size = size.add(two);
+
+ this.capacity = capacity = size.intValue();
+ }
+
table_base = 64;
string_base = table_base + capacity * TABLE_ENTRY_SIZE;
string_size = 0;
@@ -183,13 +204,13 @@ public class PersistentByteMap
for (long i = 0; i < totalFileSize; i+= 4096)
raf.write(_4k);
- FileChannel fc = raf.getChannel();
+ fc = raf.getChannel();
buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());
for (int i = 0; i < capacity; i++)
putKeyPos(UNUSED_ENTRY, i);
- putWord(0x12345678, MAGIC);
+ putWord(0x67636a64, MAGIC);
putWord(0x01, VERSION);
putWord(capacity, CAPACITY);
putWord(table_base, TABLE_BASE);
@@ -197,15 +218,17 @@ public class PersistentByteMap
putWord(file_size, FILE_SIZE);
putWord(elements, ELEMENTS);
buf.force();
+
+ length = fc.size();
+ string_size = 0;
}
- static public PersistentByteMap emptyPersistentByteMap(String filename,
- int capacity, int strtabSize)
+ static public PersistentByteMap
+ emptyPersistentByteMap(File name, int capacity, int strtabSize)
throws IOException
{
- File f = new File(filename);
- PersistentByteMap m = new PersistentByteMap();
- m.init(m, f, capacity, strtabSize);
+ PersistentByteMap m = new PersistentByteMap(name);
+ m.init(m, name, capacity, strtabSize);
return m;
}
@@ -313,9 +336,7 @@ public class PersistentByteMap
{
int hashIndex = hash(digest);
- // With the the table 2/3 full there will be on average 2 probes
- // for a successful search and 5 probes for an unsuccessful one.
- if (elements >= capacity * 2/3)
+ if (elements >= capacity())
throw new IllegalAccessException("Table Full: " + elements);
do
@@ -336,7 +357,7 @@ public class PersistentByteMap
int newValue = addBytes((byte[])value);
putValuePos(newValue, hashIndex);
return;
- }
+ }
hashIndex++;
hashIndex %= capacity;
@@ -347,6 +368,33 @@ public class PersistentByteMap
private int addBytes (byte[] data)
throws IllegalAccessException
{
+ if (data.length > 16)
+ {
+ // Keep track of long strings in the hope that we will be able
+ // to re-use them.
+ if (values == null)
+ {
+ values = new HashMap();
+
+ for (int i = 0; i < capacity; i++)
+ if (getKeyPos(i) != UNUSED_ENTRY)
+ {
+ int pos = getValuePos(i);
+ ByteWrapper bytes = new ByteWrapper(getBytes(pos));
+ values.put(bytes, new Integer(pos));
+ }
+ }
+
+ {
+ Object result = values.get(new ByteWrapper(data));
+ if (result != null)
+ {
+ // We already have this value in the string table
+ return ((Integer)result).intValue();
+ }
+ }
+ }
+
if (data.length + INT_SIZE >= this.length)
throw new IllegalAccessException("String table Full");
@@ -363,6 +411,9 @@ public class PersistentByteMap
file_size = extent;
putWord (string_size, STRING_SIZE);
putWord (file_size, FILE_SIZE);
+
+ if (data.length > 16)
+ values.put(new ByteWrapper(data), new Integer(top - string_base));
return top - string_base;
}
@@ -377,11 +428,68 @@ public class PersistentByteMap
return elements;
}
+ public int stringTableSize()
+ {
+ return string_size;
+ }
+
public int capacity()
{
- return capacity;
+ // With the the table 2/3 full there will be on average 2 probes
+ // for a successful search and 5 probes for an unsuccessful one.
+ return capacity * 2/3;
}
+ public void force()
+ {
+ buf.force();
+ }
+
+ public File getFile()
+ {
+ return name;
+ }
+
+ // Close the map. Once this has been done, the map can no longer be
+ // used.
+ public void close()
+ {
+ force();
+ fc.close();
+ }
+
+ public void
+ putAll(PersistentByteMap t)
+ throws IllegalAccessException
+ {
+ // We can use a fast copy if the size of a map has not changed.
+ if (this.elements == 0 && t.capacity == this.capacity
+ && t.length == this.length)
+ {
+ this.buf.position(0);
+ t.buf.position(0);
+ this.buf.put(t.buf);
+ this.table_base = t.table_base;
+ this.string_base = t.string_base;
+ this.string_size = t.string_size;
+ this.file_size = t.file_size;
+ this.elements = t.elements;
+ if (t.values != null)
+ this.values = (HashMap)t.values.clone();
+ return;
+ }
+
+ // Otherwise do it the hard way.
+ Iterator iterator = t.iterator(PersistentByteMap.ENTRIES);
+ while (iterator.hasNext())
+ {
+ PersistentByteMap.MapEntry entry
+ = (PersistentByteMap.MapEntry)iterator.next();
+ this.put((byte[])entry.getKey(), (byte[])entry.getValue());
+ }
+ }
+
+
private final class HashIterator implements Iterator
{
/** Current index in the physical hash table. */
@@ -481,4 +589,31 @@ public class PersistentByteMap
return bucket;
}
}
+
+ // A wrapper class for a byte array that allows collections to be
+ // made.
+ private final class ByteWrapper
+ {
+ final byte[] bytes;
+ final int hash;
+
+ public ByteWrapper (byte[] bytes)
+ {
+ int sum = 0;
+ this.bytes = bytes;
+ for (int i = 0; i < bytes.length; i++)
+ sum += bytes[i];
+ hash = sum;
+ }
+
+ public int hashCode()
+ {
+ return hash;
+ }
+
+ public boolean equals(Object obj)
+ {
+ return Arrays.equals(bytes, ((ByteWrapper)obj).bytes);
+ }
+ }
}