/* * Copyright (C) 2012, 2016 higherfrequencytrading.com * Copyright (C) 2016 Roman Leventov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.chronicle.hash.serialization; import net.openhft.chronicle.bytes.Bytes; import net.openhft.chronicle.wire.WireIn; import net.openhft.chronicle.wire.WireOut; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; import java.util.Map; import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded; /** * Marshaller of {@code Map<K, V>}. Uses {@link HashMap} (hence default key objects' equality and * {@code hashCode()} as the map implementation to deserialize into. * * <p>This marshaller supports multimap emulation on top of Chronicle Map, that is possible but * inefficient. See <a href="https://github.com/OpenHFT/Chronicle-Map#chronicle-map-is-not">the * README section</a>. * * <p>Look for pre-defined key and value marshallers in {@link * net.openhft.chronicle.hash.serialization.impl} package. This package is not included into * Javadocs, but present in Chronicle Map distribution. If there are no existing marshallers for * your {@code Map} key or value types, define {@link BytesReader} and {@link BytesWriter} yourself. * * @param <K> the key type of serialized Maps * @param <V> the value type of serialized Maps * @see ListMarshaller * @see SetMarshaller */ public final class MapMarshaller<K, V> implements BytesReader<Map<K, V>>, BytesWriter<Map<K, V>>, StatefulCopyable<MapMarshaller<K, V>> { // Config fields private BytesReader<K> keyReader; private BytesWriter<? super K> keyWriter; private BytesReader<V> valueReader; private BytesWriter<? super V> valueWriter; // Cache fields private transient Deque<K> orderedKeys; private transient Deque<V> orderedValues; /** * Constructs a {@code MapMarshaller} with the given map keys' and values' serializers. * * @param keyReader map keys' reader * @param keyWriter map keys' writer * @param valueReader map values' reader * @param valueWriter map values' writer */ public MapMarshaller( BytesReader<K> keyReader, BytesWriter<? super K> keyWriter, BytesReader<V> valueReader, BytesWriter<? super V> valueWriter) { this.keyReader = keyReader; this.keyWriter = keyWriter; this.valueReader = valueReader; this.valueWriter = valueWriter; initTransients(); } private void initTransients() { orderedKeys = new ArrayDeque<>(); orderedValues = new ArrayDeque<>(); } @NotNull @Override public Map<K, V> read(Bytes in, @Nullable Map<K, V> using) { int size = in.readInt(); if (using == null) { using = new HashMap<>(((int) (size / 0.75))); for (int i = 0; i < size; i++) { using.put(keyReader.read(in, null), valueReader.read(in, null)); } } else { using.forEach((k, v) -> { orderedKeys.add(k); orderedValues.add(v); }); using.clear(); for (int i = 0; i < size; i++) { using.put(keyReader.read(in, orderedKeys.pollFirst()), valueReader.read(in, orderedValues.pollFirst())); } orderedKeys.clear(); // for GC, avoid zombie object links orderedValues.clear(); } return using; } @Override public void write(Bytes out, @NotNull Map<K, V> toWrite) { out.writeInt(toWrite.size()); toWrite.forEach((k, v) -> { keyWriter.write(out, k); valueWriter.write(out, v); }); } @Override public MapMarshaller<K, V> copy() { return new MapMarshaller<>(copyIfNeeded(keyReader), copyIfNeeded(keyWriter), copyIfNeeded(valueReader), copyIfNeeded(valueWriter)); } @Override public void readMarshallable(@NotNull WireIn wireIn) { keyReader = wireIn.read(() -> "keyReader").typedMarshallable(); keyWriter = wireIn.read(() -> "keyWriter").typedMarshallable(); valueReader = wireIn.read(() -> "valueReader").typedMarshallable(); valueWriter = wireIn.read(() -> "valueWriter").typedMarshallable(); initTransients(); } @Override public void writeMarshallable(@NotNull WireOut wireOut) { wireOut.write(() -> "keyReader").typedMarshallable(keyReader); wireOut.write(() -> "keyWriter").typedMarshallable(keyWriter); wireOut.write(() -> "valueReader").typedMarshallable(valueReader); wireOut.write(() -> "valueWriter").typedMarshallable(valueWriter); } }