/** * Creates a new UnsafeMetricDefinition * * @param globalId The global id of the metric * @param timestamp The creation timestamp of this metric, or -1L if the metric is new, in which * case the currnt time will be substituted * @param name The metric name * @param opaqueKey The opaque key */ public UnsafeMetricDefinition(long globalId, long timestamp, String name, byte[] opaqueKey) { byte[] nameBytes = getBytes(name); byte[] opaqueBytes = getBytes(opaqueKey); int size = BASE_SIZE + nameBytes.length + opaqueBytes.length; address[0] = UnsafeAdapter.allocateAlignedMemory(size); UnsafeAdapter.registerForDeAlloc(this); UnsafeAdapter.putByte(address[0], DELETE_FLAG); UnsafeAdapter.putInt(address[0] + SIZE, size); UnsafeAdapter.putLong(address[0] + ID, globalId); UnsafeAdapter.putLong( address[0] + TIMESTAMP, timestamp != -1L ? timestamp : System.currentTimeMillis()); UnsafeAdapter.putInt(address[0] + NAME_SIZE, nameBytes.length); UnsafeAdapter.putInt(address[0] + OPAQUE_SIZE, opaqueBytes.length); if (nameBytes.length > 0) { UnsafeAdapter.copyMemory( nameBytes, UnsafeAdapter.BYTE_ARRAY_OFFSET, null, address[0] + NAME_BYTES, nameBytes.length); } if (opaqueBytes.length > 0) { UnsafeAdapter.copyMemory( opaqueBytes, UnsafeAdapter.BYTE_ARRAY_OFFSET, null, address[0] + NAME_BYTES + nameBytes.length, opaqueBytes.length); } }
/** * Returns the bytes for the name * * @return the bytes for the name */ public byte[] getNameBytes() { if (address[0] == -1L) return null; int size = getNameSize(); if (size == 0) return EMPTY_BYTE_ARR; byte[] bytes = new byte[size]; UnsafeAdapter.copyMemory( null, address[0] + NAME_BYTES, bytes, UnsafeAdapter.BYTE_ARRAY_OFFSET, size); return bytes; }
/** * {@inheritDoc} * * @see org.helios.rindle.metric.IMetricDefinition#getOpaqueKey() */ @Override public byte[] getOpaqueKey() { int size = getOpaqueSize(); if (size == 0) return null; byte[] bytes = new byte[size]; UnsafeAdapter.copyMemory( null, address[0] + NAME_BYTES + getNameSize(), bytes, UnsafeAdapter.BYTE_ARRAY_OFFSET, size); return bytes; }
/** * {@inheritDoc} * * @see org.helios.rindle.metric.IMetricDefinition#getCreatedTimestamp() */ @Override public long getCreatedTimestamp() { if (address[0] == -1L) return IMetricDefinition.NO_ENTRY_VALUE; return UnsafeAdapter.getLong(address[0] + TIMESTAMP); }
/** * Updates the byte size of this metric * * @return the new byte size of this metric */ protected int updateByteSize() { int size = BASE_SIZE + getNameSize() + getOpaqueSize(); UnsafeAdapter.putInt(address[0] + SIZE, size); return size; }
/** * {@inheritDoc} * * @see org.helios.rindle.metric.IMetricDefinition#getId() */ @Override public long getId() { if (address[0] == -1L) return IMetricDefinition.NO_ENTRY_VALUE; return UnsafeAdapter.getLong(address[0] + ID); }
/** * Returns the byte size of this metric * * @return the byte size of this metric */ protected int getByteSize() { if (address[0] == -1L) return -1; return UnsafeAdapter.getInt(address[0] + SIZE); }
/** Touches the metric created timestamp */ protected void touch() { UnsafeAdapter.putLong(address[0] + TIMESTAMP, System.currentTimeMillis()); }
/** * Updates the id of this metric * * @param id the new id */ protected void setId(long id) { UnsafeAdapter.putLong(address[0] + ID, id); }
/** * Returns the size of the opaque key * * @return the size of the opaque key */ protected int getOpaqueSize() { if (address[0] == -1L) return -1; return UnsafeAdapter.getInt(address[0] + OPAQUE_SIZE); }
/** * Title: CharBufferStringKeyCache * * <p>Description: An {@link IStringKeyCache} implementation using {@link CharBuffer}s * * <p>Company: Helios Development Group LLC * * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.rindle.store.CharBufferStringKeyCache</code> */ public class CharBufferStringKeyCache implements IStringKeyCache { /** The spin lock */ protected final SpinLock lock = UnsafeAdapter.allocateSpinLock(); /** The cache of Chronicle entry ids keyed by the long hash code of the name */ private final TObjectLongHashMap<CharBuffer> cache; private final boolean offHeap; /** * Creates a new CharBufferStringKeyCache * * @param initialCapacity used to find a prime capacity for the table. * @param loadFactor used to calculate the threshold over which rehashing takes place. * @param offHeap If true, buffer is placed off-heap, on-heap otherwise */ public CharBufferStringKeyCache(int initialCapacity, float loadFactor, boolean offHeap) { cache = new TObjectLongHashMap<CharBuffer>(initialCapacity, loadFactor, NO_ENTRY_VALUE); this.offHeap = offHeap; } /** * Creates a new on-heap CharBufferStringKeyCache with the default load factor * * @param initialCapacity used to find a prime capacity for the table. */ public CharBufferStringKeyCache(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR, false); } /** * Wraps the passed key in a CharBuffer * * @param key The key to wrap * @return The CharBuffer wrapped key */ protected CharBuffer wrap(CharSequence key) { CharBuffer cb = null; if (!offHeap) { cb = ((CharBuffer) CharBuffer.allocate(key.length()).append(key).flip()).asReadOnlyBuffer(); } else { cb = ((CharBuffer) ByteBuffer.allocateDirect(key.length() * 2).asCharBuffer().append(key).flip()) .asReadOnlyBuffer(); } return cb; } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#size() */ @Override public int size() { try { lock.xlock(); return cache.size(); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#containsKey(java.lang.CharSequence) */ @Override public boolean containsKey(CharSequence key) { if (key == null) return false; try { lock.xlock(); if (cache.isEmpty()) return false; return cache.containsKey(wrap(key)); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#clear() */ @Override public void clear() { try { lock.xlock(); cache.clear(); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IKeyCache#purge() */ @Override public void purge() { try { lock.xlock(); cache.clear(); cache.trimToSize(); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#get(java.lang.CharSequence) */ @Override public long get(CharSequence key) { if (key == null) return NO_ENTRY_VALUE; try { lock.xlock(); if (cache.isEmpty()) return NO_ENTRY_VALUE; return cache.get(wrap(key)); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#put(java.lang.CharSequence, long) */ @Override public long put(CharSequence key, long value) { if (key == null) throw new IllegalArgumentException("The passed key was null"); try { lock.xlock(); return cache.put(wrap(key), value); } finally { lock.xunlock(); } } /** * Unguarded direct put for bulk puts * * @param key The key * @param value The long value * @return the previous value associated with they key or {@link #NO_ENTRY_VALUE} if there was no * mapping for the key. */ protected long _put(CharSequence key, long value) { return cache.put(wrap(key), value); } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#putIfAbsent(java.lang.CharSequence, * long) */ @Override public long putIfAbsent(CharSequence key, long value) { if (key == null) throw new IllegalArgumentException("The passed key was null"); try { lock.xlock(); return cache.putIfAbsent(wrap(key), value); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#remove(java.lang.CharSequence) */ @Override public long remove(CharSequence key) { if (key == null) throw new IllegalArgumentException("The passed key was null"); try { lock.xlock(); return cache.remove(wrap(key)); } finally { lock.xunlock(); } } /** * {@inheritDoc} * * @see org.helios.rindle.store.chronicle.IStringKeyCache#putAll(java.util.Map) */ @Override public void putAll(Map<? extends CharSequence, ? extends Long> map) { if (map == null) throw new IllegalArgumentException("The passed map was null"); if (map.isEmpty()) return; try { lock.xlock(); for (Map.Entry<? extends CharSequence, ? extends Long> entry : map.entrySet()) { _put(entry.getKey(), entry.getValue().longValue()); } } finally { lock.xunlock(); } } /** * Adjusts the primitive value mapped to the key if the key is present in the map. * * @param key The stringy key * @param value The value * @return true if a mapping was found and modified. * @see gnu.trove.map.hash.TObjectLongHashMap#adjustValue(java.lang.Object, long) */ public boolean adjustValue(CharSequence key, long value) { if (key == null) throw new IllegalArgumentException("The passed key was null"); try { lock.xlock(); return cache.adjustValue(wrap(key), value); } finally { lock.xunlock(); } } /** * Compresses the cache to the minimum prime size * * @see gnu.trove.impl.hash.THash#trimToSize() */ public final void trimToSize() { try { lock.xlock(); cache.trimToSize(); } finally { lock.xunlock(); } } /** * The auto-compaction factor controls whether and when a table performs a compaction * automatically after a certain number of remove operations. If the value is non-zero, the number * of removes that need to occur for auto-compaction is the size of table at the time of the * previous compaction (or the initial capacity) multiplied by this factor. * * <p>Setting this value to zero will disable auto-compaction. * * @param factor a <tt>float</tt> that indicates the auto-compaction factor * @see gnu.trove.impl.hash.THash#setAutoCompactionFactor(float) */ public void setAutoCompactionFactor(float factor) { try { lock.xlock(); cache.setAutoCompactionFactor(factor); } finally { lock.xunlock(); } } /** * Returns the cache's auto compaction factor * * @return a <<tt>float</tt> that represents the auto-compaction factor. * @see gnu.trove.impl.hash.THash#getAutoCompactionFactor() */ public float getAutoCompactionFactor() { try { lock.xlock(); return cache.getAutoCompactionFactor(); } finally { lock.xunlock(); } } /** * Temporarily disables auto-compaction. MUST be followed by calling {@link * #reenableAutoCompaction}. * * @see gnu.trove.impl.hash.THash#tempDisableAutoCompaction() */ public void tempDisableAutoCompaction() { try { lock.xlock(); cache.tempDisableAutoCompaction(); } finally { lock.xunlock(); } } /** * Re-enable auto-compaction after it was disabled via {@link #tempDisableAutoCompaction()}. * * @param check_for_compaction True if compaction should be performed if needed before returning. * If false, no compaction will be performed. * @see gnu.trove.impl.hash.THash#reenableAutoCompaction(boolean) */ public void reenableAutoCompaction(boolean check_for_compaction) { try { lock.xlock(); cache.reenableAutoCompaction(check_for_compaction); } finally { lock.xunlock(); } } }