/* * Copyright 2011 Paula Gearon. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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 org.mulgara.util.io; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Map; import java.util.Set; import static org.mulgara.util.io.Bytes.LONG_SIZE; /** * A file-based hash map mapping long to long */ public class LLHashMap implements Map<Long,Long> { /** The internal map that maps ByteBuffer to ByteBuffer */ private final FileHashMap map; /** * Creates a hash map for Long to Long. * @param f The file to put the table into. * @throws IOException If there is an error accessing the file. */ public LLHashMap(File f) throws IOException { map = new FileHashMap(f, LONG_SIZE, LONG_SIZE); } /** * Creates a hash map for Long to Long. * @param f The file to put the table into. * @param loadFactor The fraction of the table above with the table will get expanded. * @param initialSize The initial number of slots available to put data into before being rehashed. * @throws IOException If there is an error accessing the file. */ public LLHashMap(File f, float loadFactor, long initialSize) throws IOException { map = new FileHashMap(f, LONG_SIZE, LONG_SIZE, loadFactor, initialSize); } /** * @see java.io.Closeable#close() */ public void close() throws IOException { map.close(); } /** * Closes the object and removes it from the filesystem. * @throws IOException If there is an error closing or removing the files. */ public void closeAndDelete() throws IOException { map.closeAndDelete(); } @Override public void clear() { map.clear(); } @Override public boolean containsKey(Object key) { return map.containsKey(toBytes(key)); } public boolean containsKey(long key) { return map.containsKey(toBytes(key)); } @Override public boolean containsValue(Object value) { return map.containsValue(toBytes(value)); } public boolean containsValue(long value) { return map.containsValue(toBytes(value)); } @Override public Set<Map.Entry<Long, Long>> entrySet() { return new ArrayBufferSetWrapper<Map.Entry<Long,Long>,Map.Entry<ByteBuffer,ByteBuffer>>(map.entrySet(), new SetDataConverter<Map.Entry<Long,Long>, Map.Entry<ByteBuffer,ByteBuffer>>() { public Map.Entry<ByteBuffer,ByteBuffer> toSetData(Map.Entry<Long,Long> d) { return new BBKeyValue(toBytes(d.getKey()), toBytes(d.getValue())); } public Map.Entry<Long,Long> fromSetData(Map.Entry<ByteBuffer,ByteBuffer> b) { return new KeyValue<Long,Long>(fromBytes(b.getKey()), fromBytes(b.getValue())); } }); } @Override public Long get(Object key) { ByteBuffer b = map.get(toBytes(key)); return b == null ? null : fromBytes(b); } public long get(long key) { return fromBytes(map.get(toBytes(key))); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public Set<Long> keySet() { return new ArrayBufferSetWrapper<Long,ByteBuffer>(map.keySet(), new SetDataConverter<Long, ByteBuffer>() { public ByteBuffer toSetData(Long d) { return toBytes(d); } public Long fromSetData(ByteBuffer b) { return fromBytes(b); } }); } @Override public Long put(Long key, Long value) { ByteBuffer[] pair = toBytesPair(key, value); return fromBytes(map.put(pair[0], pair[1])); } public long put(long key, long value) { ByteBuffer[] pair = toBytesPair(key, value); return fromBytes(map.put(pair[0], pair[1])); } @Override public void putAll(Map<? extends Long, ? extends Long> m) { for (java.util.Map.Entry<? extends Long, ? extends Long> e: m.entrySet()) { put(e.getKey(), e.getValue()); } } @Override public Long remove(Object key) { return fromBytes(map.remove(toBytes(key))); } public long remove(long key) { return fromBytes(map.remove(toBytes(key))); } @Override public int size() { return map.size(); } @Override public Collection<Long> values() { return new ArrayBufferSetWrapper<Long,ByteBuffer>((Set<ByteBuffer>)map.values(), new SetDataConverter<Long, ByteBuffer>() { public ByteBuffer toSetData(Long d) { return toBytes(d); } public Long fromSetData(ByteBuffer b) { return fromBytes(b); } }); } /** * Convenience method to avoid casting Object that will then be sent * for auto unboxing. * @param l A Long value as an Object. * @return A new {@link ByteBuffer} containing the number. Allocated according to the type * configured with {@link IOUtil#MEM_TYPE_PROP} */ private static final ByteBuffer toBytes(Object l) { ByteBuffer b = IOUtil.allocate(LONG_SIZE); b.asLongBuffer().put(0, ((Long)l).longValue()); return b; } /** * Converts a pair of long numbers into a pair of ByteBuffers. * @param first The first number to convert. * @param second The second number to convert. * @return An array containing the new {@link ByteBuffer}s containing the numbers. * Allocated according to the type configured with {@link IOUtil#MEM_TYPE_PROP} */ private static final ByteBuffer[] toBytesPair(long first, long second) { ByteBuffer[] pair = new ByteBuffer[2]; ByteBuffer b = toBytes(first, second); b.position(LONG_SIZE); pair[1] = b.slice(); b.flip(); pair[0] = b.slice(); return pair; } /** * Converts a pair of long numbers into a ByteBuffer. * @param first The first number to convert. * @param second The second number to convert. * @return A {@link ByteBuffer}s containing the numbers. * Allocated according to the type configured with {@link IOUtil#MEM_TYPE_PROP} */ private static final ByteBuffer toBytes(long first, long second) { ByteBuffer b = IOUtil.allocate(LONG_SIZE << 1); b.asLongBuffer().put(0, first).put(1, second); return b; } /** * Converts a ByteBuffer into a long value. * @param bb The buffer to convert. * @return The long value from the buffer. */ private static final long fromBytes(ByteBuffer bb) { return bb == null ? -1L : bb.asLongBuffer().get(0); } /** A {@link Map.Entry} implementation for use with {@link FileHashMap#entrySet()}. */ public static class KeyValue<K,V> implements Map.Entry<K,V> { private final K key; protected V value; public KeyValue(K k, V v) { key = k; value = v; } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V v) { V old = value; value = v; return old; } } /** A {@link Map.Entry} implementation for use with {@link FileHashMap#entrySet()}. */ public static class BBKeyValue extends KeyValue<ByteBuffer,ByteBuffer> { public BBKeyValue(ByteBuffer k, ByteBuffer v) { super(k, v); } public ByteBuffer setValue(ByteBuffer v) { int size = value == null ? v.capacity() : value.capacity(); byte[] old = new byte[size]; value.position(0); value.limit(size); value.get(old); value.position(0); v.position(0); v.limit(size); value.put(v); return ByteBuffer.wrap(old); } } // debug byte[] dump() throws IOException { return map.dump(); } }