All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.map.VanillaChronicleMap Maven / Gradle / Ivy

/*
 * Copyright 2014 Higher Frequency Trading
 *
 * http://www.higherfrequencytrading.com
 *
 * 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 net.openhft.chronicle.map;

import net.openhft.chronicle.hash.KeyContext;
import net.openhft.chronicle.hash.impl.VanillaChronicleHash;
import net.openhft.chronicle.hash.serialization.BytesReader;
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop;
import net.openhft.chronicle.hash.serialization.internal.MetaProvider;
import net.openhft.chronicle.hash.serialization.internal.SerializationBuilder;
import net.openhft.chronicle.map.impl.*;
import net.openhft.chronicle.map.impl.ret.InstanceReturnValue;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.model.DataValueClasses;
import net.openhft.lang.threadlocal.Provider;
import net.openhft.lang.threadlocal.ThreadLocalCopies;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Set;
import java.util.function.BiFunction;

import static net.openhft.chronicle.map.ChronicleMapBuilder.greatestCommonDivisor;

public class VanillaChronicleMap,
        V, VI, MVI extends MetaBytesInterop, R>
        extends VanillaChronicleHash,
        ExternalMapQueryContext>
        implements AbstractChronicleMap  {

    private static final Logger LOG = LoggerFactory.getLogger(VanillaChronicleMap.class);

    private static final long serialVersionUID = 3L;

    /////////////////////////////////////////////////
    // Value Data model
    final Class vClass;
    final Class nativeValueClass;
    public final SizeMarshaller valueSizeMarshaller;
    public final BytesReader originalValueReader;
    public final VI originalValueInterop;
    public final MVI originalMetaValueInterop;
    public final MetaProvider metaValueInteropProvider;

    final DefaultValueProvider defaultValueProvider;

    public final boolean constantlySizedEntry;

    public transient Provider> valueReaderProvider;
    public transient Provider valueInteropProvider;

    /////////////////////////////////////////////////
    // Event listener and meta data
    public final int metaDataBytes;

    /////////////////////////////////////////////////
    // Behavior
    final boolean putReturnsNull;
    final boolean removeReturnsNull;

    /////////////////////////////////////////////////
    // Memory management and dependent fields
    public final Alignment alignment;
    public final int worstAlignment;
    public final boolean couldNotDetermineAlignmentBeforeAllocation;

    transient Set> entrySet;
    
    // TODO initialize
    public transient MapEntryOperations entryOperations;
    public transient MapMethods methods;
    
    transient ThreadLocal queryCxt;
    transient ThreadLocal iterCxt;
    

    public VanillaChronicleMap(ChronicleMapBuilder builder, boolean replicated)
            throws IOException {
        super(builder, replicated);
        SerializationBuilder valueBuilder = builder.valueBuilder;
        vClass = valueBuilder.eClass;
        if (vClass.getName().endsWith("$$Native")) {
            nativeValueClass = vClass;
        } else if (vClass.isInterface()) {
            Class nativeValueClass = null;
            try {
                nativeValueClass = DataValueClasses.directClassFor(vClass);
            } catch (Exception e) {
                // fall through
            }
            this.nativeValueClass = nativeValueClass;
        } else {
            nativeValueClass = null;
        }
        valueSizeMarshaller = valueBuilder.sizeMarshaller();
        originalValueReader = valueBuilder.reader();
        originalValueInterop = (VI) valueBuilder.interop();
        originalMetaValueInterop = (MVI) valueBuilder.metaInterop();
        metaValueInteropProvider = (MetaProvider) valueBuilder.metaInteropProvider();

        defaultValueProvider = builder.defaultValueProvider();

        constantlySizedEntry = builder.constantlySizedEntries();

        // Event listener and meta data
        metaDataBytes = builder.metaDataBytes();

        // Behavior
        putReturnsNull = builder.putReturnsNull();
        removeReturnsNull = builder.removeReturnsNull();

        // Concurrency (number of segments), memory management and dependent fields
        alignment = builder.valueAlignment();
        worstAlignment = builder.worstAlignment(replicated);
        int alignment = this.alignment.alignment();
        couldNotDetermineAlignmentBeforeAllocation =
                greatestCommonDivisor((int) chunkSize, alignment) != alignment;

        initTransientsFromBuilder(builder);
        initTransients();
    }
    
    void initTransientsFromBuilder(ChronicleMapBuilder builder) {
        this.entryOperations = (MapEntryOperations) builder.entryOperations;
        this.methods = (MapMethods) builder.methods;
    }

    @Override
    public void initTransients() {
        super.initTransients();
        ownInitTransients();
    }

    private void ownInitTransients() {
        valueReaderProvider = Provider.of((Class) originalValueReader.getClass());
        valueInteropProvider = Provider.of((Class) originalValueInterop.getClass());

        if (defaultValueProvider instanceof ConstantValueProvider) {
            ConstantValueProvider constantValueProvider =
                    (ConstantValueProvider) defaultValueProvider;
            if (constantValueProvider.wasDeserialized()) {
                ThreadLocalCopies copies = valueReaderProvider.getCopies(null);
                BytesReader valueReader = valueReaderProvider.get(copies, originalValueReader);
                constantValueProvider.initTransients(valueReader);
            }
        }
        initQueryContext();
        initIterationContext();
    }

     void initQueryContext() {
        queryCxt = new ThreadLocal>() {
            @Override
            protected CompiledMapQueryContext initialValue() {
                return new CompiledMapQueryContext<>(VanillaChronicleMap.this);
            }
        };
    }
    
    void initIterationContext() {
        iterCxt = new ThreadLocal>() {
            @Override
            protected CompiledMapIterationContext initialValue() {
                return new CompiledMapIterationContext<>(VanillaChronicleMap.this);
            }
        };
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        ownInitTransients();
    }

    public final V checkValue(Object value) {
        if (!vClass.isInstance(value)) {
            throw new ClassCastException("Value must be a " + vClass.getName() +
                    " but was a " + value.getClass());
        }
        return (V) value;
    }

    @Override
    public MapKeyContext context(K key) {
        return queryContext(key).deprecatedMapKeyContext();
    }

    @Override
    public int actualSegments() {
        return actualSegments;
    }

    public V defaultValue(KeyContext keyContext) {
        if (defaultValueProvider != null)
            return defaultValueProvider.get(keyContext);
        throw new IllegalStateException("To call acquire*() methods, " +
                "you should specify either default value or default value provider " +
                "during map building");
    }

    @Override
    public V newValueInstance() {
        if (vClass == CharSequence.class || vClass == StringBuilder.class)
            return (V) new StringBuilder();
        return newInstance(vClass, false);
    }

    @Override
    public K newKeyInstance() {
        return newInstance(kClass, true);
    }

    @Override
    public Class keyClass() {
        return kClass;
    }

    @Override
    public Class valueClass() {
        return vClass;
    }

    static  T newInstance(Class aClass, boolean isKey) {
        try {
            // TODO optimize -- save heap class / direct class in transient fields and call
            // newInstance() on them directly
            return isKey ? DataValueClasses.newInstance(aClass) :
                    DataValueClasses.newDirectInstance(aClass);

        } catch (Exception e) {
            if (e.getCause() instanceof IllegalStateException)
                throw e;

            if (aClass.isInterface())
                throw new IllegalStateException("It not possible to create a instance from " +
                        "interface=" + aClass.getSimpleName() + " we recommend you create an " +
                        "instance in the usual way.", e);

            try {
                return (T) aClass.newInstance();
            } catch (Exception e1) {
                throw new IllegalStateException("It has not been possible to create a instance " +
                        "of class=" + aClass.getSimpleName() +
                        ", Note : its more efficient if your chronicle map is configured with " +
                        "interface key " +
                        "and value types rather than classes, as this method is able to use " +
                        "interfaces to generate off heap proxies that point straight at your data. " +
                        "In this case you have used a class and chronicle is unable to create an " +
                        "instance of this class has it does not have a default constructor. " +
                        "If your class is mutable, we " +
                        "recommend you create and instance of your class=" + aClass.getSimpleName() +
                        " in the usual way, rather than using this method.", e);
            }
        }
    }


    @NotNull
    @Override
    public final MapKeyContext acquireContext(K key, V usingValue) {
        QueryContextInterface q = queryContext(key);
        // TODO optimize to update lock in certain cases
        try {
            q.writeLock().lock();
            acquireUsingBody(q, usingValue);
            return q.deprecatedMapAcquireContext();
        } catch (Throwable e) {
            try {
                q.close();
            } catch (Throwable suppressed) {
                e.addSuppressed(suppressed);
            }
            throw e;
        }
    }
    
    private static  void checkAcquiredUsing(V acquired, V using) {
        if (acquired != using) {
            throw new IllegalArgumentException("acquire*() MUST reuse the given " +
                    "value. Given value " + using + " cannot be reused to read " + acquired);
        }
        
    }

    @NotNull
    @Override
    public final Set> entrySet() {
        return (entrySet != null) ? entrySet : (entrySet = newEntrySet());
    }

    @Override
    public int hashCode() {
        return mapHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return mapEquals(obj);
    }

    @Override
    public String toString() {
        return mapToString();
    }
    
    @Override
    public void clear() {
        forEachEntry(KeyContext::remove);
    }

    public final long readValueSize(Bytes entry) {
        long valueSize = valueSizeMarshaller.readSize(entry);
        alignment.alignPositionAddr(entry);
        return valueSize;
    }
    
    private CompiledMapQueryContext q() {
        return (CompiledMapQueryContext) queryCxt.get();
    }

    public QueryContextInterface mapContext() {
        CompiledMapQueryContext q = q().getContext();
        q.initUsed(true);
        return q;
    }
    
    private CompiledMapIterationContext i() {
        return (CompiledMapIterationContext) iterCxt.get();
    }

    @Override
    public IterationContextInterface iterationContext() {
        CompiledMapIterationContext c = i().getContext();
        c.initUsed(true);
        return c;
    }

    @Override
    @NotNull
    public QueryContextInterface queryContext(Object key) {
        checkKey(key);
        QueryContextInterface q = mapContext();
        q.inputKeyInstanceValue().initKey((K) key);
        q.initInputKey(q.inputKeyInstanceValue());
        return q;
    }

    @Override
    public V get(Object key) {
        try (QueryContextInterface q = queryContext(key)) {
            methods.get(q, q.defaultReturnValue());
            return q.defaultReturnValue().returnValue();
        }
    }

    @Override
    public V getUsing(K key, V usingValue) {
        try (QueryContextInterface q = queryContext(key)) {
            q.usingReturnValue().initUsingReturnValue(usingValue);
            methods.get(q, q.usingReturnValue());
            return q.usingReturnValue().returnValue();
        }
    }

    @Override
    public V acquireUsing(@NotNull K key, V usingValue) {
        try (QueryContextInterface q = queryContext(key)) {
            V returnValue = acquireUsingBody(q, usingValue);
            return returnValue;
        }
    }

    private V acquireUsingBody(QueryContextInterface q, V usingValue) {
        q.usingReturnValue().initUsingReturnValue(usingValue);
        methods.acquireUsing(q, q.usingReturnValue());
        V returnValue = q.usingReturnValue().returnValue();
        checkAcquiredUsing(returnValue, usingValue);
        return returnValue;
    }

    @Override
    public V putIfAbsent(K key, V value) {
        checkValue(value);
        try (QueryContextInterface q = queryContext(key)) {
            q.inputValueInstanceValue().initValue(value);
            methods.putIfAbsent(q, q.inputValueInstanceValue(), q.defaultReturnValue());
            return q.defaultReturnValue().returnValue();
        }
    }

    @Override
    public boolean remove(Object key, Object value) {
        if (value == null)
            return false; // ConcurrentHashMap compatibility
        V v = checkValue(value);
        try (QueryContextInterface q = queryContext(key)) {
            q.inputValueInstanceValue().initValue(v);
            return methods.remove(q, q.inputValueInstanceValue());
        }
    }

    @Override
    public boolean replace(K key, V oldValue, V newValue) {
        checkValue(oldValue);
        checkValue(newValue);
        try (QueryContextInterface q = queryContext(key)) {
            q.inputValueInstanceValue().initValue(oldValue);
            return methods.replace(q, q.inputValueInstanceValue(), q.wrapValueAsValue(newValue));
        }
    }

    @Override
    public V replace(K key, V value) {
        checkValue(value);
        try (QueryContextInterface q = queryContext(key)) {
            q.inputValueInstanceValue().initValue(value);
            methods.replace(q, q.inputValueInstanceValue(), q.defaultReturnValue());
            return q.defaultReturnValue().returnValue();
        }
    }

    @Override
    public boolean containsKey(Object key) {
        try (QueryContextInterface q = queryContext(key)) {
            return methods.containsKey(q);
        }
    }

    @Override
    public V put(K key, V value) {
        checkValue(value);
        try (QueryContextInterface q = queryContext(key)) {
            q.inputValueInstanceValue().initValue(value);
            InstanceReturnValue returnValue =
                    putReturnsNull ? NullReturnValue.get() : q.defaultReturnValue();
            methods.put(q, q.inputValueInstanceValue(), returnValue);
            return returnValue.returnValue();
        }
    }

    @Override
    public V remove(Object key) {
        try (QueryContextInterface q = queryContext(key)) {
            InstanceReturnValue returnValue =
                    removeReturnsNull ? NullReturnValue.get() : q.defaultReturnValue();
            methods.remove(q, returnValue);
            return returnValue.returnValue();
        }
    }

    @Override
    public V merge(K key, V value,
                   BiFunction remappingFunction) {
        try (QueryContextInterface q = queryContext(key)) {
            q.inputValueInstanceValue().initValue(value);
            methods.merge(q, q.inputValueInstanceValue(), remappingFunction,
                    q.defaultReturnValue());
            return q.defaultReturnValue().returnValue();
        }
    }

    @Override
    public V compute(K key, BiFunction remappingFunction) {
        try (QueryContextInterface q = queryContext(key)) {
            methods.compute(q, remappingFunction, q.defaultReturnValue());
            return q.defaultReturnValue().returnValue();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy