/* * Copyright 2015 Terracotta, Inc., a Software AG company. * * 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.terracotta.offheapstore.storage; import org.terracotta.offheapstore.util.Factory; /** * A {@code StorageEngine} composed of two independent engines, one for the * keys, one for the values. * * @param <K> key type handled by this engine * @param <V> value type handled by this engine * * @author Chris Dennis */ public class SplitStorageEngine<K, V> implements StorageEngine<K, V> { public static <K, V> Factory<SplitStorageEngine<K, V>> createFactory(final Factory<? extends HalfStorageEngine<K>> keyFactory, final Factory<? extends HalfStorageEngine<V>> valueFactory) { return new Factory<SplitStorageEngine<K, V>>() { @Override public SplitStorageEngine<K, V> newInstance() { return new SplitStorageEngine<K, V>(keyFactory.newInstance(), valueFactory.newInstance()); } }; } protected final HalfStorageEngine<? super K> keyStorageEngine; protected final HalfStorageEngine<? super V> valueStorageEngine; /** * Creates a composite storage engine, with independent key and value engines. * * @param keyStorageEngine storage engine for the keys * @param valueStorageEngine storage engine for the values */ public SplitStorageEngine(HalfStorageEngine<? super K> keyStorageEngine, HalfStorageEngine<? super V> valueStorageEngine) { this.keyStorageEngine = keyStorageEngine; this.valueStorageEngine = valueStorageEngine; } @Override public Long writeMapping(K key, V value, int hash, int metadata) { Integer keyEncoding = keyStorageEngine.write(key, hash); if (keyEncoding == null) { return null; } Integer valueEncoding = valueStorageEngine.write(value, hash); if (valueEncoding == null) { keyStorageEngine.free(keyEncoding); return null; } return encoding(keyEncoding, valueEncoding); } @Override public void attachedMapping(long encoding, int hash, int metadata) { //no-op } @Override public void freeMapping(long encoding, int hash, boolean removal) { keyStorageEngine.free(keyEncoding(encoding)); valueStorageEngine.free(valueEncoding(encoding)); } @SuppressWarnings("unchecked") @Override public V readValue(long encoding) { return (V) valueStorageEngine.read(valueEncoding(encoding)); } @Override public boolean equalsValue(Object value, long encoding) { return valueStorageEngine.equals(value, valueEncoding(encoding)); } @SuppressWarnings("unchecked") @Override public K readKey(long encoding, int hashCode) { return (K) keyStorageEngine.read(keyEncoding(encoding)); } @Override public boolean equalsKey(Object key, long encoding) { return keyStorageEngine.equals(key, keyEncoding(encoding)); } @Override public void clear() { keyStorageEngine.clear(); valueStorageEngine.clear(); } @Override public long getAllocatedMemory() { return keyStorageEngine.getAllocatedMemory() + valueStorageEngine.getAllocatedMemory(); } @Override public long getOccupiedMemory() { return keyStorageEngine.getOccupiedMemory() + valueStorageEngine.getOccupiedMemory(); } @Override public long getVitalMemory() { return keyStorageEngine.getVitalMemory() + valueStorageEngine.getVitalMemory(); } @Override public long getDataSize() { return keyStorageEngine.getDataSize() + valueStorageEngine.getDataSize(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("SplitStorageEngine:\n"); sb.append("Keys:\n"); sb.append(keyStorageEngine).append('\n'); sb.append("Values:\n"); sb.append(valueStorageEngine); return sb.toString(); } @Override public void invalidateCache() { keyStorageEngine.invalidateCache(); valueStorageEngine.invalidateCache(); } @Override public void bind(Owner owner) { keyStorageEngine.bind(owner, ~0L << Integer.SIZE); valueStorageEngine.bind(owner, ~0L >>> Integer.SIZE); } @Override public void destroy() { keyStorageEngine.destroy(); valueStorageEngine.destroy(); } @Override public boolean shrink() { return keyStorageEngine.shrink() || valueStorageEngine.shrink(); } public static int valueEncoding(long encoding) { return (int) encoding; } public static int keyEncoding(long encoding) { return (int) (encoding >>> Integer.SIZE); } public static long encoding(int keyEncoding, int valueEncoding) { return (((long) keyEncoding) << Integer.SIZE) | (valueEncoding & 0xffffffffL); } }