/* * Copyright 2012 Future Systems * * 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.krakenapps.eventstorage.engine; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class LongSet extends AbstractSet<Long> { private static final int DEFAULT_CACHE_SIZE = 50000; private File file; private RandomAccessFile raf; private Set<Long> cache; private int cacheSize; private Object cacheLock = new Object(); private Map<Short, Long> fps = new HashMap<Short, Long>(); private BufferedRandomAccessFile block1; private Object block1lock = new Object(); private long nextFp1; private BufferedRandomAccessFile block2; private Object block2lock = new Object(); private long nextFp2; private int size; public LongSet() { this(DEFAULT_CACHE_SIZE); } public LongSet(int cacheSize) { this.cache = new HashSet<Long>(cacheSize); this.cacheSize = cacheSize; } @Override public boolean add(Long e) { if (contains(e)) return false; synchronized (cacheLock) { if (!cache.add(e)) return false; } if (cache.size() >= cacheSize) { try { flush(); } catch (IOException e1) { return false; } } return true; } @Override public boolean contains(Object o) { try { if (!(o instanceof Long)) return false; if (cache.contains(o)) return true; Long e = (Long) o; short key1 = (short) ((e >> 32) & 0xFFFF); int key2 = (int) ((e >> 16) & 0xFFFF); int key3 = (int) (e & 0xFFFF); Long fp1 = fps.get(key1); if (fp1 == null || raf == null) return false; long fp2 = 0L; synchronized (block1lock) { block1.seek(fp1 + key2 * 8); fp2 = block1.readLong(); } if (fp2 == 0L) return false; synchronized (block2lock) { block2.seek(fp2 + key3 / 8); return (block2.readByte() & (1 << (key3 % 8))) != 0; } } catch (IOException e) { return false; } } private synchronized void flush() throws IOException { if (raf == null) { File dir = new File(System.getProperty("kraken.data.dir"), "kraken-eventstorage/data/"); if (!dir.exists()) dir.mkdirs(); file = new File(dir, "longset" + System.currentTimeMillis() + ".tmp"); raf = new RandomAccessFile(file, "rw"); raf.setLength(0); block1 = new BufferedRandomAccessFile(raf, 512 * 1024); nextFp1 = 0L; block2 = new BufferedRandomAccessFile(raf, 8 * 1024); nextFp2 = 524288L; } List<Long> sorted = null; synchronized (cacheLock) { sorted = new ArrayList<Long>(cache); cache.clear(); } Collections.sort(sorted); for (Long e : sorted) { short key1 = (short) ((e >>> 32) & 0xFFFF); int key2 = (int) (((e >>> 16) & 0xFFFF) << 3); int key3 = (int) (e & 0xFFFF); Long fp1 = fps.get(key1); if (fp1 == null) { fps.put(key1, nextFp1); fp1 = nextFp1; nextFp1 += 524288L; if (nextFp1 <= nextFp2) nextFp1 = (nextFp2 / 524288L + 1L) * 524288L; } fp1 += key2; long fp2 = 0L; synchronized (block1lock) { block1.seek(fp1); fp2 = block1.readLong(); if (fp2 == 0) { fp2 = nextFp2; block1.seek(fp1); block1.writeLong(fp2); nextFp2 += 8192L; if (nextFp2 % 524288 == 0 && nextFp2 <= nextFp1) nextFp2 = nextFp1 + 524288L; } } fp2 += key3 / 8; synchronized (block2lock) { block2.seek(fp2); byte b = (byte) ((block2.readByte() & 0xFF) | (1 << (key3 % 8))); block2.seek(fp2); block2.write(b); } } } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public Iterator<Long> iterator() { // TODO Auto-generated method stub return null; } @Override public int size() { return size; } public void close() { if (raf != null) { try { raf.close(); } catch (IOException e) { } file.delete(); } } }