diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java
index 7a12a592d..8dce56d17 100755
--- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java
+++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashBuilder.java
@@ -23,6 +23,7 @@ import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig;
import net.openhft.chronicle.hash.replication.TimeProvider;
import net.openhft.chronicle.hash.serialization.*;
import net.openhft.chronicle.map.ChronicleMap;
+import net.openhft.chronicle.map.ChronicleMapBuilder;
import net.openhft.chronicle.set.ChronicleSet;
import net.openhft.chronicle.set.ChronicleSetBuilder;
import net.openhft.lang.io.Bytes;
@@ -46,7 +47,7 @@ import java.util.concurrent.TimeUnit;
/**
* This interface defines the meaning of configurations, common to {@link
- * net.openhft.chronicle.map.ChronicleMapBuilder} and {@link ChronicleSetBuilder}, i.
+ * ChronicleMapBuilder} and {@link ChronicleSetBuilder}, i.
* e. Chronicle hash container configurations.
*
*
{@code ChronicleHashBuilder} is mutable. Configuration methods mutate the builder and return
@@ -57,7 +58,7 @@ import java.util.concurrent.TimeUnit;
* @param the type of keys in hash containers, created by this builder
* @param the container type, created by this builder, i. e. {@link ChronicleMap} or {@link
* ChronicleSet}
- * @param the concrete builder type, i. e. {@link net.openhft.chronicle.map.ChronicleMapBuilder}
+ * @param the concrete builder type, i. e. {@link ChronicleMapBuilder}
* or {@link ChronicleSetBuilder}
*/
public interface ChronicleHashBuilderIf key is a boxed primitive type or {@link Byteable} subclass, i. e. if key size is known
+ * statically, it is automatically accounted and shouldn't be specified by user.
+ *
* @param keySize number of bytes, taken by serialized form of keys
* @return this builder back
+ * @throws IllegalStateException if key size is known statically and shouldn't be configured
+ * by user
+ * @throws IllegalArgumentException if the given {@code keySize} is non-positive
* @see #constantKeySizeBySample(Object)
* @see #entrySize(int)
*/
@@ -131,7 +138,7 @@ public interface ChronicleHashBuilderThere are three major patterns of this configuration usage: - Key (and value, in
* {@code ChronicleMap} case) sizes are constant. Configure them via {@link
- * #constantKeySizeBySample(Object)} and {@link net.openhft.chronicle.map.ChronicleMapBuilder#constantValueSizeBySample(Object)}
+ * #constantKeySizeBySample(Object)} and {@link ChronicleMapBuilder#constantValueSizeBySample(Object)}
* methods, and you will experience no memory waste at all.
- Key (and/or value size, in
* {@code ChronicleMap} case) varies moderately. Specify them using corresponding methods, or
* specify entry size directly by calling this method, by sizes somewhere between average and
@@ -145,10 +152,11 @@ public interface ChronicleHashBuilderHowever, remember that
- Operations with entries that span several "entry sizes"
* are a bit slower, than entries which take a single "entry size". That is why "chunk" approach
* is not recommended, when key (and/or value size) varies moderately.
- The maximum
- * number of chunks could be taken by an entry is 64. {@link IllegalArgumentException} is thrown
- * on attempt to insert too large entry, compared to the configured or computed entry size.
- * - The number of "entries" {@linkplain #entries(long) in the whole map} and {@linkplain
- * #actualEntriesPerSegment(long) per segment} is actually the number "chunks".
+ * number of chunks could be taken by an entry is {@link #maxEntryOversizeFactor(int)}, of max
+ * 64 chunks. {@link IllegalArgumentException} is thrown on attempt to insert too large entry,
+ * compared to the configured or computed entry size. - The number of "entries"
+ * {@linkplain #entries(long) in the whole map} and {@linkplain #actualEntriesPerSegment(long)
+ * per segment} is actually the number "chunks".
*
* Example: if values in your {@code ChronicleMap} are adjacency lists of some social graph,
* where nodes are represented as {@code long} ids, and adjacency lists are serialized in
@@ -169,9 +177,22 @@ public interface ChronicleHashBuilder - If key (and/or value, in {@code ChronicleMap} case) size varies
* moderately, you should pass to this method the maximum number of entries + 5-25%, depending
* on your data properties and configured {@linkplain #keySize(int) key}/{@linkplain
- * net.openhft.chronicle.map.ChronicleMapBuilder#valueSize(int)
+ * ChronicleMapBuilder#valueSize(int)
* value}/{@linkplain #entrySize(int) entry} sizes.
- If your data size varies in a wide
* range, pass the maximum number of entries multiplied by average data size and divided by the
* configured "entry size" (i. e. chunk size). See an example in the documentation to {@link
@@ -292,7 +313,7 @@ public interface ChronicleHashBuilder keyMarshaller);
+ B keyMarshaller(@NotNull BytesMarshaller super K> keyMarshaller);
/**
* Configures the marshallers, used to serialize/deserialize keys to/from off-heap memory in
@@ -332,11 +353,10 @@ public interface ChronicleHashBuilderDefault key deserialization factory is {@link NewInstanceObjectFactory}, which creates a
* new key instance using {@link Class#newInstance()} default constructor. You could provide an
@@ -348,9 +368,8 @@ public interface ChronicleHashBuilder keyDeserializationFactory);
@@ -401,8 +420,6 @@ public interface ChronicleHashBuilder statelessClient(InetSocketAddress remoteAddress);
-
ChronicleHashInstanceConfig instance();
/**
@@ -414,12 +431,10 @@ public interface ChronicleHashBuilderThis method is a shortcut for {@code instance().create()}.
*
* @return a new off-heap hash container
- * @throws IOException if any IO error relates to off-heap memory allocation, or establishing
- * replication connections, occurs
* @see #createPersistedTo(File)
* @see #instance()
*/
- C create() throws IOException;
+ C create();
/**
* Opens a hash container residing the specified file, or creates a new one if the file not yet
@@ -450,5 +465,7 @@ public interface ChronicleHashBuilder statelessClient(InetSocketAddress remoteAddress);
+
+ C createStatelessClient(InetSocketAddress serverAddress) throws IOException;
}
diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashInstanceConfig.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashInstanceConfig.java
index 21b01661b..8afd3d188 100644
--- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashInstanceConfig.java
+++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashInstanceConfig.java
@@ -53,5 +53,7 @@ public interface ChronicleHashInstanceConfig {
ChronicleHashInstanceConfig persistedTo(File file);
+ ChronicleHashInstanceConfig name(String name);
+
C create() throws IOException;
}
diff --git a/src/main/java/net/openhft/chronicle/hash/StatelessClientConfig.java b/src/main/java/net/openhft/chronicle/hash/StatelessClientConfig.java
index 01836241e..38add9b6a 100644
--- a/src/main/java/net/openhft/chronicle/hash/StatelessClientConfig.java
+++ b/src/main/java/net/openhft/chronicle/hash/StatelessClientConfig.java
@@ -18,8 +18,10 @@ package net.openhft.chronicle.hash;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.set.ChronicleSet;
+import org.jetbrains.annotations.NotNull;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
@@ -41,5 +43,7 @@ import java.util.concurrent.TimeUnit;
public interface StatelessClientConfig {
StatelessClientConfig timeout(long timeout, TimeUnit units);
+ StatelessClientConfig name(String name);
+
C create() throws IOException;
}
diff --git a/src/main/java/net/openhft/chronicle/hash/replication/AbstractReplication.java b/src/main/java/net/openhft/chronicle/hash/replication/AbstractReplication.java
index 881fa91dd..e85fec8ff 100644
--- a/src/main/java/net/openhft/chronicle/hash/replication/AbstractReplication.java
+++ b/src/main/java/net/openhft/chronicle/hash/replication/AbstractReplication.java
@@ -35,6 +35,7 @@ public abstract class AbstractReplication implements Serializable {
private final
@Nullable
RemoteNodeValidator remoteNodeValidator;
+ private final boolean bootstrapOnlyLocalEntries;
// package-private to forbid subclassing from outside of the package
AbstractReplication(byte localIdentifier, Builder builder) {
@@ -42,14 +43,16 @@ public abstract class AbstractReplication implements Serializable {
tcpConfig = builder.tcpConfig;
udpConfig = builder.udpConfig;
remoteNodeValidator = builder.remoteNodeValidator;
+ bootstrapOnlyLocalEntries = builder.bootstrapOnlyLocalEntries;
}
@Override
public String toString() {
- return ", localIdentifier=" + localIdentifier +
+ return "localIdentifier=" + localIdentifier +
", tcpConfig=" + tcpConfig +
", udpConfig=" + udpConfig +
- ", remoteNodeValidator=" + remoteNodeValidator;
+ ", remoteNodeValidator=" + remoteNodeValidator +
+ ", bootstrapOnlyLocalEntries=" + bootstrapOnlyLocalEntries;
}
public byte identifier() {
@@ -71,6 +74,10 @@ public abstract class AbstractReplication implements Serializable {
return remoteNodeValidator;
}
+ public boolean bootstrapOnlyLocalEntries() {
+ return bootstrapOnlyLocalEntries;
+ }
+
/**
* Builder of {@link AbstractReplication} configurations. This class and it's subclasses are mutable,
* configuration methods mutate the builder and return it back for convenient chaining.
@@ -84,6 +91,7 @@ public abstract class AbstractReplication implements Serializable {
private TcpTransportAndNetworkConfig tcpConfig = null;
private UdpTransportConfig udpConfig = null;
private RemoteNodeValidator remoteNodeValidator = null;
+ private boolean bootstrapOnlyLocalEntries = false;
// package-private to forbid subclassing from outside of the package
Builder() {
@@ -115,6 +123,29 @@ public abstract class AbstractReplication implements Serializable {
return (B) this;
}
+
+ /**
+ * Configures if the node, provided with replication, created by this builder, should
+ * replicate only local data, last updated with own identifier, or all data currently
+ * present on the node, when a new node joins the replication grid.
+ *
+ *
Default configuration is {@code false}, potentially swamping new nodes with
+ * duplicates. However, this does guarantee that all the data is replicated over to the new
+ * node and is useful especially in the case that the originating node of some data is not
+ * currently running.
+ *
+ * @param bootstrapOnlyLocalEntries if a node provided with this replication should
+ * replicate only local data to the new nodes joining
+ * the replication network
+ * @return this builder back
+ */
+ @NotNull
+ public B bootstrapOnlyLocalEntries(boolean bootstrapOnlyLocalEntries) {
+ this.bootstrapOnlyLocalEntries = bootstrapOnlyLocalEntries;
+ return (B) this;
+ }
+
+
/**
* Creates a Replication instance with the given node (server) identifier.
*
diff --git a/src/main/java/net/openhft/chronicle/hash/replication/ReplicationHub.java b/src/main/java/net/openhft/chronicle/hash/replication/ReplicationHub.java
index d8a7ac3e3..81d1b1a7d 100644
--- a/src/main/java/net/openhft/chronicle/hash/replication/ReplicationHub.java
+++ b/src/main/java/net/openhft/chronicle/hash/replication/ReplicationHub.java
@@ -73,7 +73,7 @@ public final class ReplicationHub extends AbstractReplication {
@Override
public String toString() {
return "ReplicationHub{" + super.toString() +
- "maxEntrySize=" + maxEntrySize +
+ ", maxEntrySize=" + maxEntrySize +
", channels=" + Arrays.toString(channels) + '}';
}
diff --git a/src/main/java/net/openhft/chronicle/hash/replication/SingleChronicleHashReplication.java b/src/main/java/net/openhft/chronicle/hash/replication/SingleChronicleHashReplication.java
index 6032edfc2..a4d869c2b 100644
--- a/src/main/java/net/openhft/chronicle/hash/replication/SingleChronicleHashReplication.java
+++ b/src/main/java/net/openhft/chronicle/hash/replication/SingleChronicleHashReplication.java
@@ -29,6 +29,11 @@ public final class SingleChronicleHashReplication extends AbstractReplication {
super(localIdentifier, builder);
}
+ @Override
+ public String toString() {
+ return "SingleChronicleHashReplication{" + super.toString() + "}";
+ }
+
public static final class Builder
extends AbstractReplication.Builder {
private Builder() {}
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java b/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java
new file mode 100644
index 000000000..1c4444a24
--- /dev/null
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/DeserializationFactoryConfigurableBytesReader.java
@@ -0,0 +1,26 @@
+/*
+ * 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.hash.serialization;
+
+import net.openhft.lang.io.serialization.ObjectFactory;
+
+public interface DeserializationFactoryConfigurableBytesReader>
+ extends BytesReader {
+
+ R withDeserializationFactory(ObjectFactory deserializationFactory);
+}
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java
index 7e7dd742c..ed8a20cde 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/ByteableMarshaller.java
@@ -18,34 +18,28 @@
package net.openhft.chronicle.hash.serialization.internal;
-import net.openhft.chronicle.hash.serialization.BytesInterop;
-import net.openhft.chronicle.hash.serialization.BytesReader;
-import net.openhft.chronicle.hash.serialization.Hasher;
-import net.openhft.chronicle.hash.serialization.SizeMarshaller;
+import net.openhft.chronicle.hash.serialization.*;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.MultiStoreBytes;
import net.openhft.lang.io.NativeBytes;
import net.openhft.lang.io.serialization.*;
-import net.openhft.lang.io.serialization.impl.AllocateInstanceObjectFactory;
import net.openhft.lang.model.Byteable;
import org.jetbrains.annotations.NotNull;
-public class ByteableMarshaller
- implements BytesInterop, BytesReader, SizeMarshaller {
+public abstract class ByteableMarshaller
+ implements BytesInterop,
+ DeserializationFactoryConfigurableBytesReader>,
+ SizeMarshaller {
private static final long serialVersionUID = 0L;
public static ByteableMarshaller of(@NotNull Class eClass) {
- return new ByteableMarshaller(eClass);
+ return new Default<>(eClass);
}
- public static ByteableMarshaller of(@NotNull Class eClass,
- ObjectFactory factory) {
- if (factory instanceof AllocateInstanceObjectFactory) {
- Class allocatedClass = ((AllocateInstanceObjectFactory) factory).allocatedClass();
- return new Default(allocatedClass);
- } else {
- return new WithCustomFactory(eClass, factory);
- }
+ @Override
+ public ByteableMarshaller withDeserializationFactory(
+ ObjectFactory deserializationFactory) {
+ return new WithCustomFactory<>(tClass, deserializationFactory);
}
@NotNull
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java
index 8fe37b984..db1c88600 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/BytesReaders.java
@@ -41,6 +41,18 @@ public final class BytesReaders {
return new SimpleBytesReader(marshaller);
}
+ /**
+ * Returns {@code BytesMarshaller} {@code m}, if the given reader is the result of {@link
+ * #fromBytesMarshaller(BytesMarshaller) fromBytesMarshaller(m)} call, {@code null} otherwise.
+ *
+ * @param reader reader to extract {@code BytesMarshaller} from
+ * @param type of the objects marshalled
+ * @return {@code BytesMarshaller} from which the specified reader was created, or {@code null}
+ */
+ public static BytesMarshaller getBytesMarshaller(BytesReader reader) {
+ return reader instanceof SimpleBytesReader ? ((SimpleBytesReader) reader).marshaller : null;
+ }
+
private static class SimpleBytesReader implements BytesReader {
private static final long serialVersionUID = 0L;
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java
index 31a897e16..ca3906aec 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CopyingMetaBytesInterop.java
@@ -24,6 +24,7 @@ import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.DirectBytes;
import net.openhft.lang.io.DirectStore;
import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.io.serialization.JDKObjectSerializer;
import net.openhft.lang.io.serialization.JDKZObjectSerializer;
import net.openhft.lang.threadlocal.Provider;
import net.openhft.lang.threadlocal.StatefulCopyable;
@@ -203,7 +204,7 @@ public abstract class CopyingMetaBytesInterop implements MetaBytesInterop<
return buffer = store.bytes();
}
} else {
- buffer = new DirectStore(JDKZObjectSerializer.INSTANCE, maxSize, false).bytes();
+ buffer = new DirectStore(JDKObjectSerializer.INSTANCE, maxSize, false).bytes();
return buffer;
}
}
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java
index 0e71de34d..3cf898023 100644
--- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java
+++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/DataValueBytesMarshallers.java
@@ -18,6 +18,7 @@ package net.openhft.chronicle.hash.serialization.internal;
import net.openhft.chronicle.hash.serialization.BytesReader;
import net.openhft.chronicle.hash.serialization.BytesWriter;
+import net.openhft.chronicle.hash.serialization.DeserializationFactoryConfigurableBytesReader;
import net.openhft.compiler.CompilerUtils;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.ObjectFactory;
@@ -56,18 +57,6 @@ public final class DataValueBytesMarshallers {
}
}
- public static BytesReader acquireBytesReader(Class tClass, ObjectFactory factory) {
- Class readerClass = acquireReaderWithCustomFactory(tClass);
- try {
- Constructor constructor = readerClass.getConstructor(ObjectFactory.class);
- Object reader = constructor.newInstance(factory);
- return (BytesReader) reader;
- } catch (IllegalAccessException | NoSuchMethodException | InstantiationException |
- InvocationTargetException e) {
- throw new AssertionError(e);
- }
- }
-
public static BytesWriter acquireBytesWriter(Class tClass) {
Class readerClass = acquireWriterClass(tClass);
try {
@@ -106,6 +95,7 @@ public final class DataValueBytesMarshallers {
}
}
readersClassMap.put(tClass, readerClass);
+ acquireReaderWithCustomFactory(tClass);
return readerClass;
}
@@ -146,7 +136,7 @@ public final class DataValueBytesMarshallers {
if (dumpCode)
LoggerFactory.getLogger(DataValueGenerator.class).info(actual);
ClassLoader classLoader = tClass.getClassLoader();
- String className = bytesReaderName(tClass, false) + "$WithCustomFactory";
+ String className = withCustomFactoryName(tClass);
try {
c = classLoader.loadClass(className);
} catch (ClassNotFoundException ignored) {
@@ -161,6 +151,10 @@ public final class DataValueBytesMarshallers {
return c;
}
+ private static String withCustomFactoryName(Class tClass) {
+ return bytesReaderName(tClass, false) + "$WithCustomFactory";
+ }
+
private static String generateWithCustomFactoryClass(Class tClass) {
DataValueModel> dvModel = DataValueModels.acquireModel(tClass);
@@ -217,10 +211,14 @@ public final class DataValueBytesMarshallers {
SortedSet imported = newImported();
imported.add(BytesReader.class);
+ imported.add(DeserializationFactoryConfigurableBytesReader.class);
+ imported.add(ObjectFactory.class);
imported.add(Bytes.class);
imported.add(ByteableMarshaller.class);
imported.add(Byteable.class);
imported.add(dvModel.type());
+ imported.add(InvocationTargetException.class);
+ imported.add(Constructor.class);
StringBuilder read = generateReadBody(dvModel, imported);
@@ -231,10 +229,12 @@ public final class DataValueBytesMarshallers {
String bytesReaderName = bytesReaderName(tClass, false);
String simpleReaderName = bytesReaderName(simpleName(tClass));
sb.append("\npublic class ").append(simpleReaderName)
- .append(" implements BytesReader<").append(simpleName).append("> {\n");
+ .append(" implements DeserializationFactoryConfigurableBytesReader<")
+ .append(simpleName).append(", ").append(simpleReaderName).append("> {\n");
declareSerialVersionUID(sb);
declareStaticInstance(sb, tClass);
generatePrivateConstructor(sb, simpleReaderName);
+ generateWithDeserializationFactory(tClass, dvModel, sb, simpleName, simpleReaderName);
generateGetInstance(sb, tClass);
generateDelegatingRead(sb, simpleName);
generateRead(read, sb, simpleName, tClass);
@@ -242,6 +242,31 @@ public final class DataValueBytesMarshallers {
return sb.toString();
}
+ private static void generateWithDeserializationFactory(Class> tClass,
+ DataValueModel> dvModel,
+ StringBuilder sb, String simpleName,
+ String simpleReaderName) {
+ appendOverride(sb);
+ sb.append(" public ").append(simpleReaderName)
+ .append(" withDeserializationFactory(ObjectFactory<").append(simpleName)
+ .append("> factory) {\n");
+ sb.append(" try {\n");
+ sb.append(" ")
+ .append("Class cfc = Class.forName(\"")
+ .append(getPackage(dvModel))
+ .append(".").append(withCustomFactoryName(tClass)).append("\");\n");
+ sb.append(
+ " Constructor constructor = cfc.getConstructor(ObjectFactory.class);\n" +
+ " Object reader = constructor.newInstance(factory);\n" +
+ " return (" + simpleReaderName + ") reader;\n" +
+ " } catch (ClassNotFoundException | IllegalAccessException | " +
+ "NoSuchMethodException | InstantiationException |\n" +
+ " InvocationTargetException e) {\n" +
+ " throw new AssertionError(e);\n" +
+ " }\n");
+ sb.append(" }\n\n");
+ }
+
private static StringBuilder generateReadBody(DataValueModel> dvModel,
SortedSet imported) {
StringBuilder read = new StringBuilder();
@@ -272,7 +297,7 @@ public final class DataValueBytesMarshallers {
.append(bytesReaderName(type, false))
.append(".INSTANCE").append(".read(bytes, ")
.append(computeNonScalarOffset(dvModel, type)).append(", ")
- .append(defaultGetter.getName()).append("(i)));");
+ .append("toReuse.").append(defaultGetter.getName()).append("(i)));\n");
read.append(" }\n");
} else {
read.append(" toReuse.")
@@ -280,18 +305,30 @@ public final class DataValueBytesMarshallers {
.append(bytesReaderName(type, false))
.append(".INSTANCE").append(".read(bytes, ")
.append(computeNonScalarOffset(dvModel, type)).append(", ")
- .append(defaultGetter.getName()).append("()));");
+ .append("toReuse.").append(defaultGetter.getName()).append("()));\n");
}
} else {
if (model.isArray()) {
read.append(" for (int i = 0; i < ")
.append(model.indexSize().value()).append("; i++) {\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ read.append(" long pos = bytes.position();\n");
read.append(" toReuse.").append(defaultSetter.getName())
.append("(i, bytes.read").append(bytesType(type)).append("());\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ read.append(" bytes.position(pos + ")
+ .append(fieldSize(model)).append(");\n");
read.append(" }\n");
} else {
- read.append(" toReuse.").append(defaultSetter.getName())
+ read.append(" {\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ read.append(" long pos = bytes.position();\n");
+ read.append(" toReuse.").append(defaultSetter.getName())
.append("(bytes.read").append(bytesType(type)).append("());\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ read.append(" bytes.position(pos + ")
+ .append(fieldSize(model)).append(");\n");
+ read.append(" }\n");
}
}
}
@@ -390,7 +427,7 @@ public final class DataValueBytesMarshallers {
write.append(" for (int i = 0; i < ")
.append(model.indexSize().value()).append("; i++) {\n");
write.append(" ")
- .append(type.getName()).append(" ").append(" $ = e.")
+ .append(normalize(type)).append(" $ = e.")
.append(defaultGetter.getName()).append("(i);\n");
write.append(" ")
.append("if ($ == null) shouldNotBeNull();\n");
@@ -400,7 +437,7 @@ public final class DataValueBytesMarshallers {
write.append(" }\n");
} else {
write.append(" {")
- .append(type.getName()).append(" ").append(" $ = e.")
+ .append(normalize(type)).append(" $ = e.")
.append(defaultGetter.getName()).append("();\n");
write.append(" ")
.append("if ($ == null) shouldNotBeNull();\n");
@@ -412,12 +449,25 @@ public final class DataValueBytesMarshallers {
if (model.isArray()) {
write.append(" for (int i = 0; i < ")
.append(model.indexSize().value()).append("; i++) {\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ write.append(" long pos = bytes.position();\n");
write.append(" bytes.write").append(bytesType(type))
.append("(e.").append(defaultGetter.getName()).append("(i));\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ write.append(" bytes.position(pos + ")
+ .append(fieldSize(model)).append(");\n");
+
write.append(" }\n");
} else {
- write.append(" bytes.write").append(bytesType(type))
+ write.append(" {\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ write.append(" long pos = bytes.position();\n");
+ write.append(" bytes.write").append(bytesType(type))
.append("(e.").append(defaultGetter.getName()).append("());\n");
+ if (CharSequence.class.isAssignableFrom(type))
+ write.append(" bytes.position(pos + ")
+ .append(fieldSize(model)).append(");\n");
+ write.append(" }\n");
}
}
}
@@ -432,8 +482,7 @@ public final class DataValueBytesMarshallers {
sb.append(" try {\n");
sb.append(" if (toReuse == null)\n");
sb.append(" toReuse = getInstance();\n");
- sb.append(" if (toReuse instanceof Byteable && ")
- .append("((Byteable) toReuse).bytes() == null) {\n");
+ sb.append(" if (toReuse instanceof Byteable) {\n");
sb.append(" " +
"ByteableMarshaller.setBytesAndOffset(((Byteable) toReuse), bytes);\n");
sb.append(" bytes.skip(size);\n");
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueKeyMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueKeyMarshaller.java
deleted file mode 100644
index cf67d2d27..000000000
--- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueKeyMarshaller.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.hash.serialization.internal;
-
-import net.openhft.chronicle.hash.serialization.BytesInterop;
-import net.openhft.chronicle.hash.serialization.BytesReader;
-import net.openhft.chronicle.hash.serialization.Hasher;
-import net.openhft.chronicle.hash.serialization.SizeMarshaller;
-import net.openhft.lang.io.Bytes;
-import net.openhft.lang.model.Byteable;
-import net.openhft.lang.model.DataValueClasses;
-import net.openhft.lang.values.LongValue;
-
-public class LongValueKeyMarshaller
- implements BytesInterop, BytesReader, SizeMarshaller {
- private static final long serialVersionUID = 0L;
-
- public static final LongValueKeyMarshaller INSTANCE = new LongValueKeyMarshaller();
-
- private static final Class heapLongValueClass =
- DataValueClasses.heapClassFor(LongValue.class);
-
- LongValueKeyMarshaller() {}
-
- private Object readResolve() {
- return INSTANCE;
- }
-
- @Override
- public long size(LongValue e) {
- return 8L;
- }
-
- @Override
- public int sizeEncodingSize(long size) {
- return 0;
- }
-
- @Override
- public void writeSize(Bytes bytes, long size) {
- // do nothing
- }
-
- @Override
- public boolean startsWith(Bytes bytes, LongValue e) {
- return e.getValue() == bytes.readLong(0);
- }
-
- @Override
- public long hash(LongValue e) {
- return Hasher.hash(e.getValue());
- }
-
- @Override
- public void write(Bytes bytes, LongValue e) {
- bytes.writeLong(e.getValue());
- }
-
- @Override
- public long readSize(Bytes bytes) {
- return 8L;
- }
-
- @Override
- public LongValue read(Bytes bytes, long size) {
- return read(bytes, size, null);
- }
-
- @Override
- public LongValue read(Bytes bytes, long size, LongValue toReuse) {
- if (toReuse == null ||
- (toReuse instanceof Byteable && ((Byteable) toReuse).bytes() == null)) {
- try {
- toReuse = heapLongValueClass.newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }
- toReuse.setValue(bytes.readLong());
- return toReuse;
- }
-}
diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueValueMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueValueMarshaller.java
deleted file mode 100644
index 3473e584b..000000000
--- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/LongValueValueMarshaller.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.hash.serialization.internal;
-
-import net.openhft.lang.io.Bytes;
-import net.openhft.lang.model.Byteable;
-import net.openhft.lang.model.DataValueClasses;
-import net.openhft.lang.values.LongValue;
-
-public final class LongValueValueMarshaller extends LongValueKeyMarshaller {
- private static final long serialVersionUID = 0L;
-
- public static final LongValueValueMarshaller INSTANCE = new LongValueValueMarshaller();
-
- private static final Class directLongValueClass =
- DataValueClasses.directClassFor(LongValue.class);
-
- private LongValueValueMarshaller() {}
-
- private Object readResolve() {
- return INSTANCE;
- }
-
- @Override
- public LongValue read(Bytes bytes, long size, LongValue toReuse) {
- if (!(toReuse instanceof Byteable)) {
- try {
- toReuse = directLongValueClass.newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- throw new AssertionError(e);
- }
- }
- Byteable value = (Byteable) toReuse;
- ByteableMarshaller.setBytesAndOffset(value, bytes);
- bytes.skip(8L);
- return toReuse;
- }
-}
diff --git a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java
deleted file mode 100755
index bee59229d..000000000
--- a/src/main/java/net/openhft/chronicle/map/AbstractChronicleMapBuilder.java
+++ /dev/null
@@ -1,1017 +0,0 @@
-/*
- * 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.*;
-import net.openhft.chronicle.hash.replication.*;
-import net.openhft.chronicle.hash.serialization.BytesReader;
-import net.openhft.chronicle.hash.serialization.BytesWriter;
-import net.openhft.chronicle.hash.serialization.SizeMarshaller;
-import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop;
-import net.openhft.chronicle.hash.serialization.internal.MetaBytesWriter;
-import net.openhft.chronicle.hash.serialization.internal.MetaProvider;
-import net.openhft.chronicle.set.ChronicleSetBuilder;
-import net.openhft.lang.Maths;
-import net.openhft.lang.io.ByteBufferBytes;
-import net.openhft.lang.io.Bytes;
-import net.openhft.lang.io.BytesStore;
-import net.openhft.lang.io.DirectStore;
-import net.openhft.lang.io.serialization.*;
-import net.openhft.lang.io.serialization.impl.AllocateInstanceObjectFactory;
-import net.openhft.lang.io.serialization.impl.NewInstanceObjectFactory;
-import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
-import net.openhft.lang.model.Byteable;
-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.*;
-import java.lang.reflect.Constructor;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import static net.openhft.chronicle.map.Objects.builderEquals;
-
-/**
- * {@code AbstractChronicleMapBuilder} manages most of {@link ChronicleMap} configurations; has two concrete
- * subclasses: {@link OnHeapUpdatableChronicleMapBuilder} should be used to create maps with ordinary values,
- * {@link ChronicleMapBuilder} -- maps with {@link Byteable} values, which point directly to off-heap memory;
- * could be used as a classic builder and/or factory. This means
- * that in addition to the standard builder usage pattern: {@code
- * ChronicleMap map = ChronicleMapOnHeapUpdatableBuilder
- * .of(Key.class, Value.class)
- * .entries(100500)
- * // ... other configurations
- * .create();}
- * one of concrete {@code AbstractChronicleMapBuilder} subclasses, {@link OnHeapUpdatableChronicleMapBuilder}
- * or {@link ChronicleMapBuilder}, could be prepared and used to create many similar
- * maps: {@code
- * ChronicleMapBuilder builder = ChronicleMapBuilder
- * .of(Key.class, Value.class)
- * .entries(100500);
- *
- * ChronicleMap map1 = builder.create();
- * ChronicleMap map2 = builder.create();}
- * i. e. created {@code ChronicleMap} instances don't depend on the builder.
- *
- * {@code AbstractChronicleMapBuilder} and it's subclasses are mutable, see a note in {@link
- * ChronicleHashBuilder} interface documentation.
Later in this documentation, "ChronicleMap" means
- * "ChronicleMaps, created by {@code AbstractChronicleMapBuilder}", unless specified different, because
- * theoretically someone might provide {@code ChronicleMap} implementations with completely different
- * properties.
{@code ChronicleMap} ("ChronicleMaps, created by {@code AbstractChronicleMapBuilder}")
- * currently doesn't support resizing. That is why you should always configure {@linkplain
- * #entries(long) number of entries} you are going to insert into the created map at most. See {@link
- * #entries(long)} method documentation for more information on this.
{@code ChronicleMap} allocates
- * memory by equally sized chunks. This size is called {@linkplain #entrySize(int) entry size}, you are
- * strongly recommended to configure it to achieve least memory consumption and best speed. See {@link
- * #entrySize(int)} method documentation for more information on this.
- *
- * @param key type of the maps, produced by this builder
- * @param value type of the maps, produced by this builder
- * @see ChronicleMap
- * @see ChronicleSetBuilder
- */
-abstract class AbstractChronicleMapBuilder>
- implements Cloneable, ChronicleHashBuilder, B>, ChronicleMapBuilderI {
-
- static final byte UDP_REPLICATION_MODIFICATION_ITERATOR_ID = (byte)128;
- private static final Bytes EMPTY_BYTES = new ByteBufferBytes(ByteBuffer.allocate(0));
- private static final int DEFAULT_KEY_OR_VALUE_SIZE = 120;
- private static final int MAX_SEGMENTS = (1 << 30);
- private static final int MAX_SEGMENTS_TO_CHAISE_COMPACT_MULTI_MAPS = (1 << 20);
- private static final Logger LOG =
- LoggerFactory.getLogger(AbstractChronicleMapBuilder.class.getName());
-
- SerializationBuilder keyBuilder;
- SerializationBuilder valueBuilder;
-
- private String name;
-
- // used when configuring the number of segments.
- private int minSegments = -1;
- private int actualSegments = -1;
- // used when reading the number of entries per
- private long actualEntriesPerSegment = -1L;
- private int keySize = 0;
- private K sampleKey;
- private int valueSize = 0;
- private V sampleValue;
- private int entrySize = 0;
- private Alignment alignment = null;
- private long entries = 1 << 20; // 1 million by default
- private long lockTimeOut = 20000;
- private TimeUnit lockTimeOutUnit = TimeUnit.MILLISECONDS;
- private int metaDataBytes = 0;
- private ChronicleHashErrorListener errorListener = ChronicleHashErrorListeners.logging();
- private boolean putReturnsNull = false;
- private boolean removeReturnsNull = false;
-
- // replication
- private TimeProvider timeProvider = TimeProvider.SYSTEM;
- private BytesMarshallerFactory bytesMarshallerFactory;
- private ObjectSerializer objectSerializer;
- private MapEventListener eventListener = null;
- private BytesMapEventListener bytesEventListener = null;
- private V defaultValue = null;
- private DefaultValueProvider defaultValueProvider = null;
- private PrepareValueBytes prepareValueBytes = null;
-
- private SingleChronicleHashReplication singleHashReplication = null;
- private InetSocketAddress[] pushToAddresses;
- public boolean bootstrapOnlyLocalEntries = false;
- private boolean disableOversizedEntries = false;
-
- AbstractChronicleMapBuilder(Class keyClass, Class valueClass) {
- keyBuilder = new SerializationBuilder(keyClass, SerializationBuilder.Role.KEY);
- valueBuilder = new SerializationBuilder(valueClass, SerializationBuilder.Role.VALUE);
- }
-
- protected static boolean offHeapReference(Class valueClass) {
- return Byteable.class.isAssignableFrom(valueClass);
- }
-
- private static long roundUpMapHeaderSize(long headerSize) {
- long roundUp = (headerSize + 127L) & ~127L;
- if (roundUp - headerSize < 64)
- roundUp += 128;
- return roundUp;
- }
-
- private static void checkSegments(int segments) {
- if (segments <= 0 || segments > MAX_SEGMENTS)
- throw new IllegalArgumentException("segments should be positive, " +
- segments + " given");
- if (segments > MAX_SEGMENTS)
- throw new IllegalArgumentException("Max segments is " + MAX_SEGMENTS + ", " +
- segments + " given");
- }
-
- private static long divideUpper(long dividend, long divisor) {
- return ((dividend - 1L) / divisor) + 1L;
- }
-
- private static String pretty(int value) {
- return value > 0 ? value + "" : "not configured";
- }
-
- private static String pretty(Object obj) {
- return obj != null ? obj + "" : "not configured";
- }
-
- @Override
- public ChronicleMapBuilderI pushTo(InetSocketAddress... addresses) {
- this.pushToAddresses = addresses;
- return this;
- }
-
- @Override
- public B clone() {
- try {
- @SuppressWarnings("unchecked")
- final B result = (B) super.clone();
- result.keyBuilder = keyBuilder.clone();
- result.valueBuilder = valueBuilder.clone();
- return result;
- } catch (CloneNotSupportedException e) {
- throw new AssertionError(e);
- }
- }
-
- abstract B self();
-
- /**
- * {@inheritDoc}
- *
- * Example: if keys in your map(s) are English words in {@link String} form, keys size 10
- * (a bit more than average English word length) would be a good choice:
{@code
- * ChronicleMap wordFrequencies = ChronicleMapBuilder
- * .of(String.class, LongValue.class)
- * .entries(50000)
- * .keySize(10)
- * // shouldn't specify valueSize(), because it is statically known
- * .create();}
- * (Note that 10 is chosen as key size in bytes despite strings in Java are UTF-16 encoded (and each
- * character takes 2 bytes on-heap), because default off-heap {@link String} encoding is UTF-8 in {@code
- * ChronicleMap}.)
- *
- * @see #constantKeySizeBySample(Object)
- * @see #entrySize(int)
- */
- @Override
- public B keySize(int keySize) {
- if (keySize <= 0)
- throw new IllegalArgumentException("Key size must be positive");
- this.keySize = keySize;
- return self();
- }
-
- /**
- * {@inheritDoc}
- *
- * For example, if your keys are Git commit hashes:
{@code
- * Map gitCommitMessagesByHash =
- * ChronicleMapBuilder.of(byte[].class, String.class)
- * .constantKeySizeBySample(new byte[20])
- * .create();}
- *
- * @see #keySize(int)
- */
- @Override
- public B constantKeySizeBySample(K sampleKey) {
- this.sampleKey = sampleKey;
- return self();
- }
-
- private int keySize() {
- return keyOrValueSize(keySize, keyBuilder);
- }
-
- public B valueSize(int valueSize) {
- if (valueSize <= 0)
- throw new IllegalArgumentException("Value size must be positive");
- this.valueSize = valueSize;
- return self();
- }
-
- public B constantValueSizeBySample(V sampleValue) {
- this.sampleValue = sampleValue;
- return self();
- }
-
- int valueSize() {
- return keyOrValueSize(valueSize, valueBuilder);
- }
-
- private int keyOrValueSize(int configuredSize, SerializationBuilder builder) {
- if (configuredSize > 0)
- return configuredSize;
- // this means size is statically known
- if (builder.sizeMarshaller().sizeEncodingSize(0L) == 0)
- return (int) builder.sizeMarshaller().readSize(EMPTY_BYTES);
- return DEFAULT_KEY_OR_VALUE_SIZE;
- }
-
- /**
- * {@inheritDoc}
- *
- * In fully default case you can expect entry size to be about 256 bytes. But it is strongly
- * recommended always to configure {@linkplain #keySize(int) key size} and {@linkplain #valueSize(int)
- * value size}, if they couldn't be derived statically.
If entry size is not configured explicitly
- * by calling this method, it is computed based on {@linkplain #metaDataBytes(int) meta data bytes}, plus
- * {@linkplain #keySize(int) key size}, plus {@linkplain #valueSize(int) value size}, plus a few bytes
- * required by implementations.
- *
- * @see #entries(long)
- */
- @Override
- public B entrySize(int entrySize) {
- if (entrySize <= 0)
- throw new IllegalArgumentException("Entry Size must be positive");
- this.entrySize = entrySize;
- return self();
- }
-
- int entrySize(boolean replicated) {
- if (entrySize > 0)
- return entryAndValueAlignment().alignSize(entrySize);
- int size = metaDataBytes;
- int keySize = keySize();
- size += keyBuilder.sizeMarshaller().sizeEncodingSize(keySize);
- size += keySize;
- if (replicated)
- size += ReplicatedChronicleMap.ADDITIONAL_ENTRY_BYTES;
- int valueSize = valueSize();
- size += valueBuilder.sizeMarshaller().sizeEncodingSize(valueSize);
- size = entryAndValueAlignment().alignSize(size);
- size += valueSize;
- // Some cache line heuristics
- for (int i = 1; i <= 4; i++) {
- int bound = i * 64;
- // Not more than 5% oversize.
- // DEFAULT_KEY_OR_VALUE_SIZE and this heuristic are specially adjusted to produce
- // entry size 256 -- the default value prior to keySize and valueSize -- when key and
- // value sizes are both not static nor configured, in both vanilla and replicated modes.
- if (size < bound && (bound - size) <= bound / 20) {
- size = bound;
- break;
- }
- }
- return entryAndValueAlignment().alignSize(size);
- }
-
- public B entryAndValueAlignment(Alignment alignment) {
- this.alignment = alignment;
- return self();
- }
-
- Alignment entryAndValueAlignment() {
- if (alignment != null)
- return alignment;
- return offHeapReference(valueBuilder.eClass) ? Alignment.OF_4_BYTES : Alignment.NO_ALIGNMENT;
- }
-
- @Override
- public B entries(long entries) {
- if (entries <= 0L)
- throw new IllegalArgumentException("Entries should be positive, " + entries + " given");
- this.entries = entries;
- return self();
- }
-
- long entries() {
- return entries;
- }
-
- @Override
- public B actualEntriesPerSegment(long actualEntriesPerSegment) {
- if (actualEntriesPerSegment <= 0L)
- throw new IllegalArgumentException("entries per segment should be positive, " +
- actualEntriesPerSegment + " given");
- if (tooManyEntriesPerSegment(actualEntriesPerSegment))
- throw new IllegalArgumentException("max entries per segment is " +
- MultiMapFactory.MAX_CAPACITY + ", " + actualEntriesPerSegment + " given");
- this.actualEntriesPerSegment = actualEntriesPerSegment;
- return self();
- }
-
- private boolean tooManyEntriesPerSegment(long entriesPerSegment) {
- return entriesPerSegment > MultiMapFactory.MAX_CAPACITY;
- }
-
- long actualEntriesPerSegment() {
- if (actualEntriesPerSegment > 0L)
- return actualEntriesPerSegment;
- int actualSegments = actualSegments();
- long actualEntries = totalEntriesIfPoorDistribution(actualSegments);
- long actualEntriesPerSegment = divideUpper(actualEntries, actualSegments);
- if (tooManyEntriesPerSegment(actualEntriesPerSegment))
- throw new IllegalStateException("max entries per segment is " +
- MultiMapFactory.MAX_CAPACITY + " configured entries() and " +
- "actualSegments() so that there should be " + actualEntriesPerSegment +
- " entries per segment");
- return actualEntriesPerSegment;
- }
-
- private long totalEntriesIfPoorDistribution(int segments) {
- if (segments == 1)
- return entries;
- double poorDistEntriesScale = Math.log(segments) * entries;
- return Math.min(entries * segments,
- (long) (entries + poorDistEntriesScale * 0.14 + 32)); // 12% was min for tests
- }
-
- @Override
- public B minSegments(int minSegments) {
- checkSegments(minSegments);
- this.minSegments = minSegments;
- return self();
- }
-
- int minSegments() {
- return Math.max(estimateSegments(), minSegments);
- }
-
- private int estimateSegments() {
- return (int) Math.min(Maths.nextPower2(entries / 32, 1), estimateSegementBasedOnSize());
- }
-
- private int estimateSegementBasedOnSize() {
- // based on entries with multimap of 100 bytes.
- long size = (long) (Math.log10(entrySize + 32) / 2 * entries);
- if (size < 10 * 1000)
- return 512;
- if (size < 100 * 1000)
- return 1024;
- if (size < 1000 * 1000)
- return 2 * 1024;
- if (size < 10 * 1000 * 1000)
- return 4 * 1024;
- if (size < 100 * 1000 * 1000)
- return 8 * 1024;
- return 16 * 1024;
- }
-
- @Override
- public B actualSegments(int actualSegments) {
- checkSegments(actualSegments);
- this.actualSegments = actualSegments;
- return self();
- }
-
- int actualSegments() {
- if (actualSegments > 0)
- return actualSegments;
- long shortMMapSegments = trySegments(MultiMapFactory.I16_MAX_CAPACITY,
- MAX_SEGMENTS_TO_CHAISE_COMPACT_MULTI_MAPS);
- if (shortMMapSegments > 0L)
- return (int) shortMMapSegments;
- long intMMapSegments = trySegments(MultiMapFactory.MAX_CAPACITY, MAX_SEGMENTS);
- if (intMMapSegments > 0L)
- return (int) intMMapSegments;
- throw new IllegalStateException("Max segments is " + MAX_SEGMENTS + ", configured so much" +
- " entries() that builder automatically decided to use " +
- (-intMMapSegments) + " segments");
- }
-
- private long trySegments(long maxSegmentCapacity, int maxSegments) {
- long segments = divideUpper(totalEntriesIfPoorDistribution(minSegments()),
- maxSegmentCapacity);
- segments = Maths.nextPower2(Math.max(segments, minSegments()), 1L);
- return segments <= maxSegments ? segments : -segments;
- }
-
- int segmentHeaderSize() {
- int segments = actualSegments();
- // reduce false sharing unless we have a lot of segments.
- return segments <= 16 * 1024 ? 64 : 32;
- }
-
- MultiMapFactory multiMapFactory() {
- return MultiMapFactory.forCapacity(actualEntriesPerSegment());
- }
-
- public B lockTimeOut(long lockTimeOut, TimeUnit unit) {
- this.lockTimeOut = lockTimeOut;
- lockTimeOutUnit = unit;
- return self();
- }
-
- long lockTimeOut(TimeUnit unit) {
- return unit.convert(lockTimeOut, lockTimeOutUnit);
- }
-
- @Override
- public B errorListener(ChronicleHashErrorListener errorListener) {
- this.errorListener = errorListener;
- return self();
- }
-
- ChronicleHashErrorListener errorListener() {
- return errorListener;
- }
-
- /**
- * Configures if the maps created by this builder should return {@code null} instead of previous mapped
- * values on {@link ChronicleMap#put(Object, Object) ChornicleMap.put(key, value)} calls.
- *
- * {@link Map#put(Object, Object) Map.put()} returns the previous value, functionality which is rarely
- * used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, it has to create a
- * new object and deserialize the data from off-heap memory. It's expensive for something you probably
- * don't use.
By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract
- * and returns the previous mapped value on {@code put()} calls.
- *
- * @param putReturnsNull {@code true} if you want {@link ChronicleMap#put(Object, Object)
- * ChronicleMap.put()} to not return the value that was replaced but instead return
- * {@code null}
- * @return an instance of the map builder back
- * @see #removeReturnsNull(boolean)
- */
- public B putReturnsNull(boolean putReturnsNull) {
- this.putReturnsNull = putReturnsNull;
- return self();
- }
-
- boolean putReturnsNull() {
- return putReturnsNull;
- }
-
- /**
- * Configures if the maps created by this builder should return {@code null} instead of the last mapped
- * value on {@link ChronicleMap#remove(Object) ChronicleMap.remove(key)} calls.
- *
- * {@link Map#remove(Object) Map.remove()} returns the previous value, functionality which is rarely
- * used but fairly cheap for {@link HashMap}. In the case, for an off heap collection, it has to create a
- * new object and deserialize the data from off-heap memory. It's expensive for something you probably
- * don't use.
- *
- * @param removeReturnsNull {@code true} if you want {@link ChronicleMap#remove(Object)
- * ChronicleMap.remove()} to not return the value of the removed entry but
- * instead return {@code null}
- * @return an instance of the map builder back
- * @see #putReturnsNull(boolean)
- */
- public B removeReturnsNull(boolean removeReturnsNull) {
- this.removeReturnsNull = removeReturnsNull;
- return self();
- }
-
- boolean removeReturnsNull() {
- return removeReturnsNull;
- }
-
- @Override
- public B metaDataBytes(int metaDataBytes) {
- if ((metaDataBytes & 0xFF) != metaDataBytes)
- throw new IllegalArgumentException("MetaDataBytes must be [0..255] was " + metaDataBytes);
- this.metaDataBytes = metaDataBytes;
- return self();
- }
-
- int metaDataBytes() {
- return metaDataBytes;
- }
-
- @Override
- public String toString() {
- return "ChronicleMapBuilder{" +
- "name=" + name +
- ", actualSegments=" + pretty(actualSegments) +
- ", minSegments=" + pretty(minSegments) +
- ", actualEntriesPerSegment=" + pretty(actualEntriesPerSegment) +
- ", keySize=" + pretty(keySize) +
- ", sampleKeyForConstantSizeComputation=" + pretty(sampleKey) +
- ", valueSize=" + pretty(valueSize) +
- ", sampleValueForConstantSizeComputation=" + pretty(sampleValue) +
- ", entrySize=" + pretty(entrySize) +
- ", entryAndValueAlignment=" + entryAndValueAlignment() +
- ", entries=" + entries() +
- ", lockTimeOut=" + lockTimeOut + " " + lockTimeOutUnit +
- ", metaDataBytes=" + metaDataBytes() +
- ", errorListener=" + errorListener() +
- ", putReturnsNull=" + putReturnsNull() +
- ", removeReturnsNull=" + removeReturnsNull() +
- ", timeProvider=" + timeProvider() +
- ", bytesMarshallerFactory=" + pretty(bytesMarshallerFactory) +
- ", objectSerializer=" + pretty(objectSerializer) +
- ", keyBuilder=" + keyBuilder +
- ", valueBuilder=" + valueBuilder +
- ", eventListener=" + eventListener +
- ", defaultValue=" + defaultValue +
- ", defaultValueProvider=" + pretty(defaultValueProvider) +
- ", prepareValueBytes=" + pretty(prepareValueBytes) +
- '}';
- }
-
- @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
- @Override
- public boolean equals(Object o) {
- return builderEquals(this, o);
- }
-
- @Override
- public int hashCode() {
- return toString().hashCode();
- }
-
- @Override
- public B timeProvider(TimeProvider timeProvider) {
- this.timeProvider = timeProvider;
- return self();
- }
-
- TimeProvider timeProvider() {
- return timeProvider;
- }
-
- BytesMarshallerFactory bytesMarshallerFactory() {
- return bytesMarshallerFactory == null ?
- bytesMarshallerFactory = new VanillaBytesMarshallerFactory() :
- bytesMarshallerFactory;
- }
-
- @Override
- public B bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory) {
- this.bytesMarshallerFactory = bytesMarshallerFactory;
- return self();
- }
-
- ObjectSerializer acquireObjectSerializer(ObjectSerializer defaultSerializer) {
- return objectSerializer == null ?
- BytesMarshallableSerializer.create(bytesMarshallerFactory(), defaultSerializer) :
- objectSerializer;
- }
-
- @Override
- public B objectSerializer(ObjectSerializer objectSerializer) {
- this.objectSerializer = objectSerializer;
- return self();
- }
-
- /**
- * {@inheritDoc}
- *
- * Example:
- */
- @Override
- public B keyMarshaller(@NotNull BytesMarshaller keyMarshaller) {
- keyBuilder.marshaller(keyMarshaller);
- return self();
- }
-
- @Override
- public B keyMarshallers(@NotNull BytesWriter keyWriter, @NotNull BytesReader keyReader) {
- keyBuilder.writer(keyWriter);
- keyBuilder.reader(keyReader);
- return self();
- }
-
- @Override
- public B keySizeMarshaller(@NotNull SizeMarshaller keySizeMarshaller) {
- keyBuilder.sizeMarshaller(keySizeMarshaller);
- return self();
- }
-
- @Override
- public B keyDeserializationFactory(@NotNull ObjectFactory keyDeserializationFactory) {
- keyBuilder.factory(keyDeserializationFactory);
- return self();
- }
-
- @Override
- public B immutableKeys() {
- keyBuilder.instancesAreMutable(false);
- return self();
- }
-
- @Override
- public StatelessClientConfig> statelessClient(
- InetSocketAddress remoteAddress) {
- return new StatelessMapConfig<>(this.clone(), remoteAddress);
- }
-
- /**
- * Configures factory which is used to create a new value instance, if value class is either {@link
- * Byteable}, {@link BytesMarshallable} or {@link Externalizable} subclass in maps, created by this
- * builder.
- *
- * Default value deserialization factory is {@link NewInstanceObjectFactory}, which creates a new value
- * instance using {@link Class#newInstance()} default constructor. You could provide an {@link
- * AllocateInstanceObjectFactory}, which uses {@code Unsafe.allocateInstance(Class)} (you might want to do
- * this for better performance or if you don't want to initialize fields), or a factory which calls a
- * value class constructor with some arguments, or a factory which internally delegates to instance pool
- * or {@link ThreadLocal}, to reduce allocations.
- *
- * @param valueDeserializationFactory the value factory used to produce instances to deserialize data in
- * @return this builder back
- */
- public B valueDeserializationFactory(@NotNull ObjectFactory valueDeserializationFactory) {
- valueBuilder.factory(valueDeserializationFactory);
- return self();
- }
-
- public B eventListener(MapEventListener eventListener) {
- this.eventListener = eventListener;
- return self();
- }
-
- MapEventListener eventListener() {
- return eventListener;
- }
-
- public B bytesEventListener(BytesMapEventListener eventListener) {
- this.bytesEventListener = eventListener;
- return self();
- }
-
- BytesMapEventListener bytesEventListener() {
- return bytesEventListener;
- }
-
- /**
- * Specifies the value to be put for each key queried in {@link ChronicleMap#acquireUsing acquireUsing()}
- * method, if the key is absent in the map, created by this builder.
- *
- * This configuration overrides any previous {@link #defaultValueProvider(DefaultValueProvider)}
- * configuration to this {@code AbstractChronicleMapBuilder}.
- *
- * @param defaultValue the default value to be put to the map for absent keys during {@code
- * acquireUsing()} calls
- * @return this builder object back
- */
- public B defaultValue(V defaultValue) {
- this.defaultValue = defaultValue;
- this.defaultValueProvider = null;
- if (defaultValue == null)
- defaultValueProvider = NullValueProvider.INSTANCE;
- this.prepareValueBytes = null;
- return self();
- }
-
- /**
- * Specifies the function to obtain a value for the key during {@link ChronicleMap#acquireUsing
- * acquireUsing()} calls, if the key is absent in the map, created by this builder.
- *
- * This configuration overrides any previous {@link #defaultValue(Object)} configuration to this {@code
- * AbstractChronicleMapBuilder}.
- *
- * @param defaultValueProvider the strategy to obtain a default value by the absent key
- * @return this builder object back
- */
- public B defaultValueProvider(
- @NotNull DefaultValueProvider defaultValueProvider) {
- this.defaultValueProvider = defaultValueProvider;
- this.defaultValue = null;
- this.prepareValueBytes = null;
- return self();
- }
-
- public B prepareValueBytesOnAcquire(@NotNull PrepareValueBytes prepareValueBytes) {
- this.prepareValueBytes = prepareValueBytes;
- this.defaultValue = null;
- this.defaultValueProvider = null;
- return self();
- }
-
- PrepareValueBytesAsWriter prepareValueBytesAsWriter() {
- if (prepareValueBytes == null)
- return null;
- return new PrepareValueBytesAsWriter<>(prepareValueBytes, valueSize());
- }
-
- /**
- * Non-public because should be called only after {@link #preMapConstruction(boolean)}
- */
- DefaultValueProvider defaultValueProvider() {
- if (defaultValueProvider != null)
- return defaultValueProvider;
- if (defaultValue == null)
- return null;
- Object originalValueWriter = valueBuilder.interop();
- Provider writerProvider = Provider.of(originalValueWriter.getClass());
- ThreadLocalCopies copies = writerProvider.getCopies(null);
- Object valueWriter = writerProvider.get(copies, originalValueWriter);
- MetaProvider metaWriterProvider = valueBuilder.metaInteropProvider();
- copies = metaWriterProvider.getCopies(copies);
- MetaBytesWriter metaValueWriter = metaWriterProvider.get(copies,
- valueBuilder.metaInterop(), valueWriter, defaultValue);
- return new ConstantValueProvider(defaultValue, metaValueWriter, valueWriter);
- }
-
- @Override
- public B replication(SingleChronicleHashReplication replication) {
- this.singleHashReplication = replication;
- return self();
- }
-
- @Override
- public B replication(byte identifier) {
- return replication(SingleChronicleHashReplication.builder().createWithId(identifier));
- }
-
- @Override
- public B replication(byte identifier, TcpTransportAndNetworkConfig tcpTransportAndNetwork) {
- return replication(SingleChronicleHashReplication.builder()
- .tcpTransportAndNetwork(tcpTransportAndNetwork).createWithId(identifier));
- }
-
- @Override
- public ChronicleHashInstanceConfig> instance() {
- return new InstanceConfig<>(this.clone(), singleHashReplication, null, null);
- }
-
- @Override
- public ChronicleMap createPersistedTo(File file) throws IOException {
- // clone() to make this builder instance thread-safe, because createWithFile() method
- // computes some state based on configurations, but doesn't synchronize on configuration
- // changes.
- return clone().createWithFile(file, singleHashReplication, null);
- }
-
- @Override
- public ChronicleMap create() {
- // clone() to make this builder instance thread-safe, because createWithoutFile() method
- // computes some state based on configurations, but doesn't synchronize on configuration
- // changes.
- return clone().createWithoutFile(singleHashReplication, null);
- }
-
- ChronicleMap create(InstanceConfig ib) throws IOException {
- if (ib.file != null) {
- return createWithFile(ib.file, ib.singleHashReplication, ib.channel);
- } else {
- return createWithoutFile(ib.singleHashReplication, ib.channel);
- }
- }
-
- ChronicleMap createWithFile(File file, SingleChronicleHashReplication singleHashReplication,
- ReplicationChannel channel) throws IOException {
- pushingToMapEventListener();
- for (int i = 0; i < 10; i++) {
- if (file.exists() && file.length() > 0) {
- try (FileInputStream fis = new FileInputStream(file);
- ObjectInputStream ois = new ObjectInputStream(fis)) {
- VanillaChronicleMap map =
- (VanillaChronicleMap) ois.readObject();
- map.headerSize = roundUpMapHeaderSize(fis.getChannel().position());
- map.createMappedStoreAndSegments(file);
- return establishReplication(map, singleHashReplication, channel);
- } catch (ClassNotFoundException e) {
- throw new IOException(e);
- }
- }
- if (file.createNewFile() || file.length() == 0) {
- break;
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- throw new IOException(e);
- }
- }
- // new file
- if (!file.exists())
- throw new FileNotFoundException("Unable to create " + file);
-
- VanillaChronicleMap map = newMap(singleHashReplication, channel);
-
- try (FileOutputStream fos = new FileOutputStream(file);
- ObjectOutputStream oos = new ObjectOutputStream(fos)) {
- oos.writeObject(map);
- oos.flush();
- map.headerSize = roundUpMapHeaderSize(fos.getChannel().position());
- map.createMappedStoreAndSegments(file);
- }
-
- return establishReplication(map, singleHashReplication, channel);
- }
-
- /**
- * @param identifier id
- * @return map
- */
- public ChronicleMap createReplicated(byte identifier) throws IOException {
- pushingToMapEventListener();
- preMapConstruction(true);
- VanillaChronicleMap map =
- new ReplicatedChronicleMap,
- V, Object, MetaBytesInterop>(this, identifier);
- BytesStore bytesStore = new DirectStore(JDKZObjectSerializer.INSTANCE,
- map.sizeInBytes(), true);
- map.createMappedStoreAndSegments(bytesStore);
- return map;
- }
-
- ChronicleMap createWithoutFile(
- SingleChronicleHashReplication singleHashReplication, ReplicationChannel channel) {
- pushingToMapEventListener();
- try {
- VanillaChronicleMap map = newMap(singleHashReplication, channel);
- BytesStore bytesStore = new DirectStore(JDKZObjectSerializer.INSTANCE,
- map.sizeInBytes(), true);
- map.createMappedStoreAndSegments(bytesStore);
- return establishReplication(map, singleHashReplication, channel);
- } catch (IOException e) {
- // file-less version should never trigger an IOException.
- throw new AssertionError(e);
- }
- }
-
- private void pushingToMapEventListener() {
- if (pushToAddresses == null || pushToAddresses.length == 0) {
- return;
- }
- try {
- Class> pmel = Class.forName("com.higherfrequencytrading.chronicle.enterprise.map.PushingMapEventListener");
- Constructor> constructor = pmel.getConstructor(ChronicleMap[].class);
- // create a stateless client for each address
- ChronicleMap[] statelessClients = new ChronicleMap[pushToAddresses.length];
- ChronicleMapBuilderI cmb = clone();
- cmb.pushTo((InetSocketAddress[]) null);
- for (int i = 0; i < pushToAddresses.length; i++) {
- statelessClients[i] = cmb.statelessClient(pushToAddresses[i]).create();
- }
- eventListener = (MapEventListener) constructor.newInstance((Object) statelessClients);
- } catch (ClassNotFoundException e) {
- LoggerFactory.getLogger(getClass().getName()).warn("Chronicle Enterprise not found in the class path");
- } catch (Exception e) {
- LoggerFactory.getLogger(getClass().getName()).error("PushingMapEventListener failed to load", e);
- }
- }
-
- ChronicleMap createStatelessMap(StatelessMapConfig statelessBuilder)
- throws IOException {
- pushingToMapEventListener();
- preMapConstruction(false);
- return new StatelessChronicleMap(
- statelessBuilder,
- this);
- }
-
- private VanillaChronicleMap newMap(
- SingleChronicleHashReplication singleHashReplication, ReplicationChannel channel) throws IOException {
- boolean replicated = singleHashReplication != null || channel != null;
- preMapConstruction(replicated);
- if (replicated) {
- byte identifier;
- if (singleHashReplication != null) {
- identifier = singleHashReplication.identifier();
- } else {
- identifier = channel.hub().identifier();
- }
- return new ReplicatedChronicleMap,
- V, Object, MetaBytesInterop>(this, identifier);
- } else {
- return new VanillaChronicleMap,
- V, Object, MetaBytesInterop>(this);
- }
- }
-
- void preMapConstruction(boolean replicated) {
- keyBuilder.objectSerializer(acquireObjectSerializer(JDKObjectSerializer.INSTANCE));
- valueBuilder.objectSerializer(acquireObjectSerializer(JDKObjectSerializer.INSTANCE));
-
- long maxSize = (long) entrySize(replicated) * Math.min(maxEntryOversizeFactor(), figureBufferAllocationFactor());
- keyBuilder.maxSize(maxSize);
- valueBuilder.maxSize(maxSize);
-
- if (sampleKey != null)
- keyBuilder.constantSizeBySample(sampleKey);
- if (sampleValue != null)
- valueBuilder.constantSizeBySample(sampleValue);
- }
-
- private ChronicleMap establishReplication(
- VanillaChronicleMap map, SingleChronicleHashReplication singleHashReplication,
- ReplicationChannel channel) throws IOException {
- if (map instanceof ReplicatedChronicleMap) {
- if (singleHashReplication != null && channel != null) {
- throw new AssertionError("Only one non-null replication should be passed");
- }
- ReplicatedChronicleMap result = (ReplicatedChronicleMap) map;
- List replicators = new ArrayList<>(2);
- if (singleHashReplication != null) {
- if (singleHashReplication.tcpTransportAndNetwork() != null)
- replicators.add(Replicators.tcp(singleHashReplication));
- if (singleHashReplication.udpTransport() != null)
- replicators.add(Replicators.udp(singleHashReplication.udpTransport()));
- } else {
- ReplicationHub hub = channel.hub();
- int entrySize = entrySize(true);
- if (entrySize > hub.maxEntrySize()) {
- throw new IllegalArgumentException("During ChannelProviderBuilder setup, " +
- "maxEntrySize=" + hub.maxEntrySize() +
- " was specified, but map with " +
- "entrySize=" + entrySize + " is attempted to apply to the replicator");
- }
- ChannelProvider provider = ChannelProvider.getProvider(hub);
- ChannelProvider.ChronicleChannel ch = provider.createChannel(channel.channelId());
- replicators.add(ch);
- }
- for (Replicator replicator : replicators) {
- Closeable token = replicator.applyTo(this, result, result, map);
- if (replicators.size() == 1 && token.getClass() == UdpReplicator.class) {
- LOG.warn(Replicators.ONLY_UDP_WARN_MESSAGE);
- }
- result.addCloseable(token);
- }
- }
- return map;
- }
-
- private int figureBufferAllocationFactor() {
- // if expected map size is about 1000, seems rather wasteful to allocate
- // key and value serialization buffers each x64 of expected entry size..
- return (int) Math.max(4L, entries() >> 10);
- }
-
- @Override
- public ChronicleMapBuilderI name(String name) {
- this.name = name;
- return this;
- }
-
- @Override
- public ChronicleMapBuilderI bootstrapOnlyLocalEntries(boolean bootstapOnlyLocalEntries) {
- this.bootstrapOnlyLocalEntries = bootstapOnlyLocalEntries;
- return this;
- }
-
- public String name() {
- return this.name;
- }
-
- @Override
- public ChronicleMapBuilderI disableOversizedEntries() {
- this.disableOversizedEntries = true;
- return this;
- }
-
- public int maxEntryOversizeFactor() {
- return disableOversizedEntries ? 1 : VanillaChronicleMap.MAX_ENTRY_OVERSIZE_FACTOR;
- }
-}
-
diff --git a/src/main/java/net/openhft/chronicle/map/BytesMapEventListener.java b/src/main/java/net/openhft/chronicle/map/BytesMapEventListener.java
index 2746dd9fd..745221023 100644
--- a/src/main/java/net/openhft/chronicle/map/BytesMapEventListener.java
+++ b/src/main/java/net/openhft/chronicle/map/BytesMapEventListener.java
@@ -33,7 +33,7 @@ import java.io.Serializable;
* by the serialized key itself.
* - From {@code valuePos} offset value area in the given {@code entry}, serialized value size
* (using {@linkplain ChronicleMapBuilder#keySizeMarshaller(SizeMarshaller)}), then (optionally)
- * alignment (see {@link OffHeapUpdatableChronicleMapBuilder#entryAndValueAlignment(Alignment)}
+ * alignment (see {@link ChronicleMapBuilder#entryAndValueAlignment(Alignment)}
* ), followed by the serialized value itself.
*
*/
diff --git a/src/main/java/net/openhft/chronicle/map/ChannelProvider.java b/src/main/java/net/openhft/chronicle/map/ChannelProvider.java
index ea56231c2..bedc095cb 100644
--- a/src/main/java/net/openhft/chronicle/map/ChannelProvider.java
+++ b/src/main/java/net/openhft/chronicle/map/ChannelProvider.java
@@ -506,7 +506,7 @@ final class ChannelProvider implements Closeable {
}
@Override
- protected Closeable applyTo(AbstractChronicleMapBuilder builder,
+ protected Closeable applyTo(ChronicleMapBuilder builder,
Replica map, EntryExternalizable entryExternalizable,
final ChronicleMap chronicleMap) {
add(chronicleChannel, map, entryExternalizable);
diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
index db2acbc35..d2f85c646 100755
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
+++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java
@@ -36,7 +36,7 @@ import java.util.concurrent.ConcurrentMap;
*
* For information on
- how to construct a {@code ChronicleMap}
- {@code
* ChronicleMap} flavors and properties
- available configurations
see {@link
- * AbstractChronicleMapBuilder} documentation.
+ * ChronicleMapBuilder} documentation.
*
* Functionally this interface defines some methods supporting garbage-free off-heap programming:
* {@link #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}.
@@ -98,7 +98,7 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
* no mapping for the key
* @see #get(Object)
* @see #acquireUsing(Object, Object)
- * @see OnHeapUpdatableChronicleMapBuilder#valueMarshaller(BytesMarshaller)
+ * @see ChronicleMapBuilder#valueMarshaller(BytesMarshaller)
*/
V getUsing(K key, V usingValue);
@@ -141,7 +141,7 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
* no mapping for the key
* @see #get(Object)
* @see #getUsing(Object, Object)
- * @see OnHeapUpdatableChronicleMapBuilder#valueMarshaller(BytesMarshaller)
+ * @see ChronicleMapBuilder#valueMarshaller(BytesMarshaller)
*/
@NotNull
ReadContext getUsingLocked(@NotNull K key, @NotNull V usingValue);
@@ -149,8 +149,8 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
/**
* Acquire a value for a key, creating if absent.
*
- * If the specified key is absent in the map, {@linkplain AbstractChronicleMapBuilder#defaultValue(Object)
- * default value} is taken or {@linkplain AbstractChronicleMapBuilder#defaultValueProvider(DefaultValueProvider)
+ *
If the specified key is absent in the map, {@linkplain ChronicleMapBuilder#defaultValue(Object)
+ * default value} is taken or {@linkplain ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider)
* default value provider} is called. Then this object is put to this map for the specified
* key.
*
@@ -169,8 +169,8 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
* }}
*
*
- * Where {@code defaultValue(key)} returns either {@linkplain AbstractChronicleMapBuilder#defaultValue(Object)
- * default value} or {@link AbstractChronicleMapBuilder#defaultValueProvider(DefaultValueProvider)
+ * Where {@code defaultValue(key)} returns either {@linkplain ChronicleMapBuilder#defaultValue(Object)
+ * default value} or {@link ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider)
* defaultValueProvider.}
*
* If the {@code ChronicleMap} is off-heap updatable, i. e. created via {@link
diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
index 5dcee5383..bb0a72da2 100755
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
+++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java
@@ -1,104 +1,423 @@
+/*
+ * 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.ChronicleHashBuilder;
-import net.openhft.chronicle.hash.ChronicleHashErrorListener;
-import net.openhft.chronicle.hash.ChronicleHashInstanceConfig;
-import net.openhft.chronicle.hash.StatelessClientConfig;
-import net.openhft.chronicle.hash.replication.SingleChronicleHashReplication;
-import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig;
-import net.openhft.chronicle.hash.replication.TimeProvider;
+import net.openhft.chronicle.hash.*;
+import net.openhft.chronicle.hash.replication.*;
+import net.openhft.chronicle.hash.serialization.BytesInterop;
import net.openhft.chronicle.hash.serialization.BytesReader;
import net.openhft.chronicle.hash.serialization.BytesWriter;
+import net.openhft.chronicle.hash.serialization.DeserializationFactoryConfigurableBytesReader;
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
-import net.openhft.lang.io.serialization.BytesMarshaller;
-import net.openhft.lang.io.serialization.BytesMarshallerFactory;
-import net.openhft.lang.io.serialization.ObjectFactory;
-import net.openhft.lang.io.serialization.ObjectSerializer;
-import net.openhft.lang.model.Byteable;
-import net.openhft.lang.model.DataValueClasses;
+import net.openhft.chronicle.hash.serialization.SizeMarshallers;
+import net.openhft.chronicle.hash.serialization.internal.MetaBytesInterop;
+import net.openhft.chronicle.hash.serialization.internal.MetaBytesWriter;
+import net.openhft.chronicle.hash.serialization.internal.MetaProvider;
+import net.openhft.chronicle.set.ChronicleSetBuilder;
+import net.openhft.lang.Maths;
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.BytesStore;
+import net.openhft.lang.io.DirectStore;
+import net.openhft.lang.io.serialization.*;
+import net.openhft.lang.io.serialization.impl.AllocateInstanceObjectFactory;
+import net.openhft.lang.io.serialization.impl.NewInstanceObjectFactory;
+import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
+import net.openhft.lang.model.*;
+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.File;
-import java.io.IOException;
+import java.io.*;
+import java.lang.reflect.Constructor;
import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static net.openhft.chronicle.map.Objects.builderEquals;
+import static net.openhft.lang.model.DataValueGenerator.firstPrimitiveFieldType;
/**
- * {@code ChronicleMapBuilder} is intended to be used to create {@link ChronicleMap}s
+ * {@code ChronicleMapBuilder} manages {@link ChronicleMap} configurations; could be used as
+ * a classic builder and/or factory. This means that in addition to the standard builder usage
+ * pattern:
{@code
+ * ChronicleMap map = ChronicleMapOnHeapUpdatableBuilder
+ * .of(Key.class, Value.class)
+ * .entries(100500)
+ * // ... other configurations
+ * .create();}
+ * it could be prepared and used to create many similar maps: {@code
+ * ChronicleMapBuilder builder = ChronicleMapBuilder
+ * .of(Key.class, Value.class)
+ * .entries(100500);
*
- * @param key type of the maps, created by this builder
- * @param value type of the maps, created by this builder
- * @see AbstractChronicleMapBuilder
- * @see net.openhft.chronicle.hash.ChronicleHashBuilder
+ * ChronicleMap map1 = builder.create();
+ * ChronicleMap map2 = builder.create();}
+ * i. e. created {@code ChronicleMap} instances don't depend on the builder.
+ *
+ * {@code ChronicleMapBuilder} is mutable, see a note in {@link ChronicleHashBuilder} interface
+ * documentation.
+ *
+ *
Later in this documentation, "ChronicleMap" means "ChronicleMaps, created by
+ * {@code ChronicleMapBuilder}", unless specified different, because theoretically someone might
+ * provide {@code ChronicleMap} implementations with completely different properties.
+ *
+ *
{@code ChronicleMap} ("ChronicleMaps, created by {@code ChronicleMapBuilder}") currently
+ * doesn't support resizing. That is why you should always configure {@linkplain
+ * #entries(long) number of entries} you are going to insert into the created map at most.
+ * See {@link #entries(long)} method documentation for more information on this.
+ *
+ *
{@code ChronicleMap} allocates memory by equally sized chunks. This size is called {@linkplain
+ * #entrySize(int) entry size}, you are strongly recommended to configure it to achieve least memory
+ * consumption and best speed. See {@link #entrySize(int)} method documentation for more information
+ * on this.
+ *
+ * @param key type of the maps, produced by this builder
+ * @param value type of the maps, produced by this builder
+ * @see ChronicleMap
+ * @see ChronicleSetBuilder
*/
-public final class ChronicleMapBuilder implements ChronicleMapBuilderI, ChronicleHashBuilder, ChronicleMapBuilder> {
+public final class ChronicleMapBuilder implements Cloneable,
+ ChronicleHashBuilder, ChronicleMapBuilder> {
- final ChronicleMapBuilderI delegate;
+ static final byte UDP_REPLICATION_MODIFICATION_ITERATOR_ID = (byte)128;
+ private static final int DEFAULT_KEY_OR_VALUE_SIZE = 120;
+ private static final int MAX_SEGMENTS = (1 << 30);
+ private static final int MAX_SEGMENTS_TO_CHAISE_COMPACT_MULTI_MAPS = (1 << 20);
+ private static final Logger LOG =
+ LoggerFactory.getLogger(ChronicleMapBuilder.class.getName());
+ public static final long DEFAULT_STATELESS_CLIENT_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
+
+ SerializationBuilder keyBuilder;
+ SerializationBuilder valueBuilder;
+
+ private String name;
+
+ // used when configuring the number of segments.
+ private int minSegments = -1;
+ private int actualSegments = -1;
+ // used when reading the number of entries per
+ private long actualEntriesPerSegment = -1L;
+ private int keySize = 0;
+ private K sampleKey;
+ private int valueSize = 0;
+ private V sampleValue;
+ private int entrySize = 0;
+ private int maxEntryOversizeFactor = -1;
+ private Alignment alignment = null;
+ private long entries = 1 << 20; // 1 million by default
+ private long lockTimeOut = 20000;
+ private TimeUnit lockTimeOutUnit = TimeUnit.MILLISECONDS;
+ private int metaDataBytes = 0;
+ private ChronicleHashErrorListener errorListener = ChronicleHashErrorListeners.logging();
+ private boolean putReturnsNull = false;
+ private boolean removeReturnsNull = false;
+
+ // replication
+ private TimeProvider timeProvider = TimeProvider.SYSTEM;
+ private BytesMarshallerFactory bytesMarshallerFactory;
+ private ObjectSerializer objectSerializer;
+ private MapEventListener eventListener = null;
+ private BytesMapEventListener bytesEventListener = null;
+ private V defaultValue = null;
+ private DefaultValueProvider defaultValueProvider = null;
+ private PrepareValueBytes prepareValueBytes = null;
+
+ private SingleChronicleHashReplication singleHashReplication = null;
+ private InetSocketAddress[] pushToAddresses;
+
+ ChronicleMapBuilder(Class keyClass, Class valueClass) {
+ keyBuilder = new SerializationBuilder<>(keyClass, SerializationBuilder.Role.KEY);
+ valueBuilder = new SerializationBuilder<>(valueClass, SerializationBuilder.Role.VALUE);
+ }
/**
* Returns a new {@code ChronicleMapBuilder} instance which is able to {@linkplain #create()
* create} maps with the specified key and value classes.
*
- * {@code ChronicleMapBuilder} analyzes provided key and value classes and automatically
- * chooses the most specific internal builder.
- *
- * @param keyClass class object used to infer key type and discover it's properties via
- * reflection
- * @param valueClass class object used to infer value type and discover it's properties via
- * reflection
+ * @param keyClass class object used to infer key type and discover it's properties
+ * via reflection
+ * @param valueClass class object used to infer value type and discover it's properties
+ * via reflection
* @param key type of the maps, created by the returned builder
* @param value type of the maps, created by the returned builder
* @return a new builder for the given key and value classes
*/
public static ChronicleMapBuilder of(
@NotNull Class keyClass, @NotNull Class valueClass) {
- if (valueClass.isEnum())
- return new ChronicleMapBuilder(OnHeapUpdatableChronicleMapBuilder.of(keyClass, valueClass));
+ return new ChronicleMapBuilder<>(keyClass, valueClass);
+ }
- if (keyClass.isInterface() && !builtInType(keyClass))
- keyClass = DataValueClasses.directClassFor(keyClass);
+ private static long roundUpMapHeaderSize(long headerSize) {
+ long roundUp = (headerSize + 127L) & ~127L;
+ if (roundUp - headerSize < 64)
+ roundUp += 128;
+ return roundUp;
+ }
- if ((valueClass.isInterface() && !builtInType(valueClass))) {
- valueClass = DataValueClasses.directClassFor(valueClass);
- } else if (!offHeapReference(valueClass)) {
- return new ChronicleMapBuilder(OnHeapUpdatableChronicleMapBuilder.of(keyClass, valueClass));
+ private static void checkSegments(int segments) {
+ if (segments <= 0 || segments > MAX_SEGMENTS)
+ throw new IllegalArgumentException("segments should be positive, " +
+ segments + " given");
+ if (segments > MAX_SEGMENTS)
+ throw new IllegalArgumentException("Max segments is " + MAX_SEGMENTS + ", " +
+ segments + " given");
+ }
+
+ private static long divideUpper(long dividend, long divisor) {
+ return ((dividend - 1L) / divisor) + 1L;
+ }
+
+ private static String pretty(int value) {
+ return value > 0 ? value + "" : "not configured";
+ }
+
+ private static String pretty(Object obj) {
+ return obj != null ? obj + "" : "not configured";
+ }
+
+ public ChronicleMapBuilder pushTo(InetSocketAddress... addresses) {
+ this.pushToAddresses = addresses;
+ return this;
+ }
+
+ @Override
+ public ChronicleMapBuilder clone() {
+ try {
+ @SuppressWarnings("unchecked")
+ ChronicleMapBuilder result =
+ (ChronicleMapBuilder) super.clone();
+ result.keyBuilder = keyBuilder.clone();
+ result.valueBuilder = valueBuilder.clone();
+ return result;
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e);
}
-
- ChronicleMapBuilderI builder = new OffHeapUpdatableChronicleMapBuilder(keyClass, valueClass);
- return new ChronicleMapBuilder(builder);
- }
-
-
-
- static boolean builtInType(Class clazz) {
- return clazz.getClassLoader() == Class.class.getClassLoader();
- }
-
- ChronicleMapBuilder(ChronicleMapBuilderI delegate) {
- this.delegate = delegate;
- }
-
- static boolean offHeapReference(Class valueClass) {
- return Byteable.class.isAssignableFrom(valueClass);
}
/**
- * {@inheritDoc} With respect to {@linkplain #entryAndValueAlignment(Alignment) alignment}.
+ * {@inheritDoc}
*
- * Note that the actual entrySize will be aligned to 4 (default {@linkplain
- * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, and
- * entry alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will be 32 (30
- * aligned to 4 bytes).
+ *
Example: if keys in your map(s) are English words in {@link String} form, keys size 10
+ * (a bit more than average English word length) would be a good choice:
{@code
+ * ChronicleMap wordFrequencies = ChronicleMapBuilder
+ * .of(String.class, LongValue.class)
+ * .entries(50000)
+ * .keySize(10)
+ * // shouldn't specify valueSize(), because it is statically known
+ * .create();}
+ * (Note that 10 is chosen as key size in bytes despite strings in Java are UTF-16 encoded (and
+ * each character takes 2 bytes on-heap), because default off-heap {@link String} encoding
+ * is UTF-8 in {@code ChronicleMap}.)
*
- * @see #entryAndValueAlignment(Alignment) // * @see #entries(long)
+ * @throws IllegalStateException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @see #constantKeySizeBySample(Object)
+ * @see #valueSize(int)
+ * @see #entrySize(int)
+ */
+ @Override
+ public ChronicleMapBuilder keySize(int keySize) {
+ checkSizeIsNotStaticallyKnown(keyBuilder);
+ if (keySize <= 0)
+ throw new IllegalArgumentException("Key size must be positive");
+ this.keySize = keySize;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * For example, if your keys are Git commit hashes:
{@code
+ * Map gitCommitMessagesByHash =
+ * ChronicleMapBuilder.of(byte[].class, String.class)
+ * .constantKeySizeBySample(new byte[20])
+ * .immutableKeys()
+ * .create();}
+ *
+ * @see #keySize(int)
+ * @see #constantValueSizeBySample(Object)
+ */
+ @Override
+ public ChronicleMapBuilder constantKeySizeBySample(K sampleKey) {
+ this.sampleKey = sampleKey;
+ return this;
+ }
+
+ private int keySize() {
+ return keyOrValueSize(keySize, keyBuilder);
+ }
+
+ /**
+ * Configures the optimal number of bytes, taken by serialized form of values, put into maps,
+ * created by this builder. If value size is always the same, call {@link
+ * #constantValueSizeBySample(Object)} method instead of this one.
+ *
+ * If values are of boxed primitive type or {@link Byteable} subclass, i. e. if value size
+ * is known statically, it is automatically accounted and shouldn't be specified by user.
+ *
+ *
If value size varies moderately, specify the size higher than average, but lower than the
+ * maximum possible, to minimize average memory overuse. If value size varies in a wide range,
+ * it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it
+ * directly.
+ *
+ * @param valueSize number of bytes, taken by serialized form of values
+ * @return this builder back
+ * @throws IllegalStateException if value size is known statically and shouldn't be configured
+ * by user
+ * @throws IllegalArgumentException if the given {@code valueSize} is non-positive
+ * @see #constantValueSizeBySample(Object)
+ * @see #keySize(int)
+ * @see #entrySize(int)
+ */
+ public ChronicleMapBuilder valueSize(int valueSize) {
+ checkSizeIsNotStaticallyKnown(valueBuilder);
+ if (valueSize <= 0)
+ throw new IllegalArgumentException("Value size must be positive");
+ this.valueSize = valueSize;
+ return this;
+ }
+
+ private static void checkSizeIsNotStaticallyKnown(SerializationBuilder builder) {
+ if (builder.sizeIsStaticallyKnown)
+ throw new IllegalStateException("Size of type " + builder.eClass +
+ " is statically known and shouldn't be specified manually");
+ }
+
+ /**
+ * Configures the constant number of bytes, taken by serialized form of values, put into maps,
+ * created by this builder. This is done by providing the {@code sampleValue}, all values should
+ * take the same number of bytes in serialized form, as this sample object.
+ *
+ * If values are of boxed primitive type or {@link Byteable} subclass, i. e. if value size
+ * is known statically, it is automatically accounted and this method shouldn't be called.
+ *
+ *
If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be
+ * called instead of this one.
+ *
+ * @param sampleValue the sample value
+ * @return this builder back
+ * @see #valueSize(int)
+ * @see #constantKeySizeBySample(Object)
+ */
+ public ChronicleMapBuilder constantValueSizeBySample(V sampleValue) {
+ this.sampleValue = sampleValue;
+ return this;
+ }
+
+ int valueSize() {
+ return keyOrValueSize(valueSize, valueBuilder);
+ }
+
+ private int keyOrValueSize(int configuredSize, SerializationBuilder builder) {
+ if (configuredSize > 0)
+ return configuredSize;
+ if (builder.constantSizeMarshaller())
+ return builder.pseudoReadConstantSize();
+ return DEFAULT_KEY_OR_VALUE_SIZE;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * In fully default case you can expect entry size to be about 256 bytes. But it is strongly
+ * recommended always to configure {@linkplain #keySize(int) key size} and {@linkplain
+ * #valueSize(int) value size}, if they couldn't be derived statically.
+ *
+ *
If entry size is not configured explicitly by calling this method, it is computed based on
+ * {@linkplain #metaDataBytes(int) meta data bytes}, plus {@linkplain #keySize(int) key size},
+ * plus {@linkplain #valueSize(int) value size}, plus a few bytes required by implementations,
+ * with respect to {@linkplain #entryAndValueAlignment(Alignment) alignment}.
+ *
+ *
Note that the actual entrySize will be aligned by the configured {@linkplain
+ * #entryAndValueAlignment(Alignment) entry and value alignment}. I. e. if you set entry size
+ * to 30, and entry alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will
+ * be 32 (30 aligned to 4 bytes).
+ *
+ * @throws IllegalStateException is sizes of both keys and values of maps created by this
+ * builder are statically known, hence entry size shouldn't be configured by user
+ * @see #entryAndValueAlignment(Alignment)
+ * @see #entries(long)
+ * @see #maxEntryOversizeFactor(int)
*/
@Override
public ChronicleMapBuilder entrySize(int entrySize) {
- delegate.entrySize(entrySize);
+ if (keyBuilder.sizeIsStaticallyKnown && valueBuilder.sizeIsStaticallyKnown) {
+ throw new IllegalStateException("Sizes of key type: " + keyBuilder.eClass + " and " +
+ "value type: " + valueBuilder.eClass + " are both statically known, " +
+ "so entry size shouldn't be specified manually");
+ }
+ if (entrySize <= 0)
+ throw new IllegalArgumentException("Entry Size must be positive");
+ this.entrySize = entrySize;
return this;
}
+ int entrySize(boolean replicated) {
+ if (entrySize > 0)
+ return entryAndValueAlignment().alignSize(entrySize);
+ int size = metaDataBytes;
+ int keySize = keySize();
+ size += keyBuilder.sizeMarshaller().sizeEncodingSize(keySize);
+ size += keySize;
+ if (replicated)
+ size += ReplicatedChronicleMap.ADDITIONAL_ENTRY_BYTES;
+ int valueSize = valueSize();
+ size += valueBuilder.sizeMarshaller().sizeEncodingSize(valueSize);
+ size = entryAndValueAlignment().alignSize(size);
+ size += valueSize;
+ // Some cache line heuristics
+ for (int i = 1; i <= 4; i++) {
+ int bound = i * 64;
+ // Not more than 5% oversize.
+ // DEFAULT_KEY_OR_VALUE_SIZE and this heuristic are specially adjusted to produce
+ // entry size 256 -- the default value prior to keySize and valueSize -- when key and
+ // value sizes are both not static nor configured, in both vanilla and replicated modes.
+ if (size < bound && (bound - size) <= bound / 20) {
+ size = bound;
+ break;
+ }
+ }
+ return entryAndValueAlignment().alignSize(size);
+ }
+
+ @Override
+ public ChronicleMapBuilder maxEntryOversizeFactor(int maxEntryOversizeFactor) {
+ if (maxEntryOversizeFactor < 1 || maxEntryOversizeFactor > 64)
+ throw new IllegalArgumentException("maxEntryOversizeFactor should be in [1, 64] " +
+ "range, " + maxEntryOversizeFactor + " given");
+ this.maxEntryOversizeFactor = maxEntryOversizeFactor;
+ return this;
+ }
+
+ int maxEntryOversizeFactor() {
+ if (maxEntryOversizeFactor < 0)
+ return keyBuilder.constantSizeMarshaller() && valueBuilder.constantSizeMarshaller() ?
+ 1 : 64;
+ return maxEntryOversizeFactor;
+ }
+
/**
* Configures alignment strategy of address in memory of entries and independently of address in
* memory of values within entries in ChronicleMaps, created by this builder.
@@ -111,115 +430,324 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIDefault is {@link Alignment#OF_4_BYTES} for Byteable values.
+ * If values couldn't reference off-heap memory (i. e. it is not {@link Byteable} or
+ * "data value generated"), alignment configuration makes no sense and forbidden.
+ *
+ *
Default is {@link Alignment#NO_ALIGNMENT} if values couldn't reference off-heap memory,
+ * otherwise chosen heuristically (configure explicitly for being sure and to compare
+ * performance in your case).
*
* @param alignment the new alignment of the maps constructed by this builder
* @return this {@code ChronicleMapOnHeapUpdatableBuilder} back
+ * @throws IllegalStateException if values of maps, created by this builder, couldn't reference
+ * off-heap memory
*/
- @Override
public ChronicleMapBuilder entryAndValueAlignment(Alignment alignment) {
- delegate.entryAndValueAlignment(alignment);
+ this.alignment = alignment;
+ checkAlignmentOnlyIfValuesPossiblyReferenceOffHeap();
return this;
}
+ private void checkAlignmentOnlyIfValuesPossiblyReferenceOffHeap() {
+ if (!valueBuilder.possibleOffHeapReferences() &&
+ (alignment == Alignment.OF_4_BYTES || alignment == Alignment.OF_8_BYTES))
+ throw new IllegalStateException("Entry and value alignment should be configured only " +
+ "if values might point to off-heap memory");
+ }
+
+ Alignment entryAndValueAlignment() {
+ if (alignment != null)
+ return alignment;
+ Class firstPrimitiveFieldType = firstPrimitiveFieldType(valueBuilder.eClass);
+ if (firstPrimitiveFieldType == long.class || firstPrimitiveFieldType == double.class)
+ return Alignment.OF_8_BYTES;
+ if (firstPrimitiveFieldType == int.class || firstPrimitiveFieldType == float.class)
+ return Alignment.OF_4_BYTES;
+ return Alignment.NO_ALIGNMENT;
+ }
+
/**
- * {@inheritDoc} Also, it overrides any previous {@link #prepareValueBytesOnAcquire}
- * configuration to this {@code ChronicleMapBuilder}.
- *
- * By default, the default value is not specified, default {@linkplain
- * #prepareValueBytesOnAcquire prepare value bytes routine} is specified instead.
- *
- * @see #defaultValueProvider(DefaultValueProvider)
- * @see #prepareValueBytesOnAcquire(PrepareValueBytes)
+ * Package-private for tests
*/
+
+
@Override
- public ChronicleMapBuilder defaultValue(V defaultValue) {
- delegate.defaultValue(defaultValue);
+ public ChronicleMapBuilder entries(long entries) {
+ if (entries <= 0L)
+ throw new IllegalArgumentException("Entries should be positive, " + entries + " given");
+ this.entries = entries;
return this;
}
+ long entries() {
+ return entries;
+ }
+
+ @Override
+ public ChronicleMapBuilder actualEntriesPerSegment(long actualEntriesPerSegment) {
+ if (actualEntriesPerSegment <= 0L)
+ throw new IllegalArgumentException("entries per segment should be positive, " +
+ actualEntriesPerSegment + " given");
+ if (tooManyEntriesPerSegment(actualEntriesPerSegment))
+ throw new IllegalArgumentException("max entries per segment is " +
+ MultiMapFactory.MAX_CAPACITY + ", " + actualEntriesPerSegment + " given");
+ this.actualEntriesPerSegment = actualEntriesPerSegment;
+ return this;
+ }
+
+ private boolean tooManyEntriesPerSegment(long entriesPerSegment) {
+ return entriesPerSegment > MultiMapFactory.MAX_CAPACITY;
+ }
+
+ long actualEntriesPerSegment() {
+ if (actualEntriesPerSegment > 0L)
+ return actualEntriesPerSegment;
+ int actualSegments = actualSegments();
+ long actualEntries = totalEntriesIfPoorDistribution(actualSegments);
+ long actualEntriesPerSegment = divideUpper(actualEntries, actualSegments);
+ if (tooManyEntriesPerSegment(actualEntriesPerSegment))
+ throw new IllegalStateException("max entries per segment is " +
+ MultiMapFactory.MAX_CAPACITY + " configured entries() and " +
+ "actualSegments() so that there should be " + actualEntriesPerSegment +
+ " entries per segment");
+ return actualEntriesPerSegment;
+ }
+
+ private long totalEntriesIfPoorDistribution(int segments) {
+ if (segments == 1)
+ return entries;
+ double poorDistEntriesScale = Math.log(segments) * entries;
+ return Math.min(entries * segments,
+ (long) (entries + poorDistEntriesScale * 0.14 + 32)); // 12% was min for tests
+ }
+
+ @Override
+ public ChronicleMapBuilder minSegments(int minSegments) {
+ checkSegments(minSegments);
+ this.minSegments = minSegments;
+ return this;
+ }
+
+ int minSegments() {
+ return Math.max(estimateSegments(), minSegments);
+ }
+
+ private int estimateSegments() {
+ return (int) Math.min(Maths.nextPower2(entries / 32, 1), estimateSegementBasedOnSize());
+ }
+
+ private int estimateSegementBasedOnSize() {
+ // based on entries with multimap of 100 bytes.
+ long size = (long) (Math.log10(entrySize + 32) / 2 * entries);
+ if (size < 10 * 1000)
+ return 512;
+ if (size < 100 * 1000)
+ return 1024;
+ if (size < 1000 * 1000)
+ return 2 * 1024;
+ if (size < 10 * 1000 * 1000)
+ return 4 * 1024;
+ if (size < 100 * 1000 * 1000)
+ return 8 * 1024;
+ return 16 * 1024;
+ }
+
+ @Override
+ public ChronicleMapBuilder actualSegments(int actualSegments) {
+ checkSegments(actualSegments);
+ this.actualSegments = actualSegments;
+ return this;
+ }
+
+ int actualSegments() {
+ if (actualSegments > 0)
+ return actualSegments;
+ long shortMMapSegments = trySegments(MultiMapFactory.I16_MAX_CAPACITY,
+ MAX_SEGMENTS_TO_CHAISE_COMPACT_MULTI_MAPS);
+ if (shortMMapSegments > 0L)
+ return (int) shortMMapSegments;
+ long intMMapSegments = trySegments(MultiMapFactory.MAX_CAPACITY, MAX_SEGMENTS);
+ if (intMMapSegments > 0L)
+ return (int) intMMapSegments;
+ throw new IllegalStateException("Max segments is " + MAX_SEGMENTS + ", configured so much" +
+ " entries() that builder automatically decided to use " +
+ (-intMMapSegments) + " segments");
+ }
+
+ private long trySegments(long maxSegmentCapacity, int maxSegments) {
+ long segments = divideUpper(totalEntriesIfPoorDistribution(minSegments()),
+ maxSegmentCapacity);
+ segments = Maths.nextPower2(Math.max(segments, minSegments()), 1L);
+ return segments <= maxSegments ? segments : -segments;
+ }
+
+ int segmentHeaderSize() {
+ int segments = actualSegments();
+ // reduce false sharing unless we have a lot of segments.
+ return segments <= 16 * 1024 ? 64 : 32;
+ }
+
+ MultiMapFactory multiMapFactory() {
+ return MultiMapFactory.forCapacity(actualEntriesPerSegment());
+ }
+
+ public ChronicleMapBuilder lockTimeOut(long lockTimeOut, TimeUnit unit) {
+ this.lockTimeOut = lockTimeOut;
+ lockTimeOutUnit = unit;
+ return this;
+ }
+
+ long lockTimeOut(TimeUnit unit) {
+ return unit.convert(lockTimeOut, lockTimeOutUnit);
+ }
+
+ @Override
+ public ChronicleMapBuilder errorListener(ChronicleHashErrorListener errorListener) {
+ this.errorListener = errorListener;
+ return this;
+ }
+
+ ChronicleHashErrorListener errorListener() {
+ return errorListener;
+ }
+
/**
- * Configures the procedure which is called on the bytes, which later the returned value is
- * pointing to, if the key is absent, on {@link ChronicleMap#acquireUsing(Object, Object)
- * acquireUsing()} call on maps, created by this builder. See {@link PrepareValueBytes} for more
- * information.
+ * Configures if the maps created by this builder should return {@code null} instead of previous
+ * mapped values on {@link ChronicleMap#put(Object, Object) ChornicleMap.put(key, value)} calls.
*
- * The default preparation callback zeroes out the value bytes.
+ *
{@link Map#put(Object, Object) Map.put()} returns the previous value, functionality which
+ * is rarely used but fairly cheap for {@link HashMap}. In the case, for an off heap collection,
+ * it has to create a new object and deserialize the data from off-heap memory. It's expensive
+ * for something you probably don't use.
*
- * @param prepareValueBytes what to do with the value bytes before assigning them into the
- * {@link net.openhft.lang.model.Byteable} value to return from {@code
- * acquireUsing()} call
+ *
By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract
+ * and returns the previous mapped value on {@code put()} calls.
+ *
+ * @param putReturnsNull {@code true} if you want {@link ChronicleMap#put(Object, Object)
+ * ChronicleMap.put()} to not return the value that was replaced but
+ * instead return {@code null}
* @return this builder back
- * @see PrepareValueBytes
- * @see #defaultValue(Object)
- * @see #defaultValueProvider(DefaultValueProvider)
+ * @see #removeReturnsNull(boolean)
*/
- @Override
- public ChronicleMapBuilder prepareValueBytesOnAcquire(@NotNull PrepareValueBytes prepareValueBytes) {
- delegate.prepareValueBytesOnAcquire(prepareValueBytes);
+ public ChronicleMapBuilder putReturnsNull(boolean putReturnsNull) {
+ this.putReturnsNull = putReturnsNull;
return this;
}
- @Override
- public ChronicleMapBuilder keySize(int keySize) {
- delegate.keySize(keySize);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder constantKeySizeBySample(K sampleKey) {
- delegate.constantKeySizeBySample(sampleKey);
- return this;
+ boolean putReturnsNull() {
+ return putReturnsNull;
}
/**
- * Configures the optimal number of bytes, taken by serialized form of values, put into maps,
- * created by this builder. If value size is always the same, call {@link
- * #constantValueSizeBySample(Object)} method instead of this one.
+ * Configures if the maps created by this builder should return {@code null} instead of the last
+ * mapped value on {@link ChronicleMap#remove(Object) ChronicleMap.remove(key)} calls.
*
- * If value is a boxed primitive type, i. e. if value size is known statically, it is
- * automatically accounted and shouldn't be specified by user.
+ *
{@link Map#remove(Object) Map.remove()} returns the previous value, functionality which is
+ * rarely used but fairly cheap for {@link HashMap}. In the case, for an off heap collection,
+ * it has to create a new object and deserialize the data from off-heap memory. It's expensive
+ * for something you probably don't use.
*
- *
If value size varies moderately, specify the size higher than average, but lower than the
- * maximum possible, to minimize average memory overuse. If value size varies in a wide range,
- * it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it
- * directly.
+ *
By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract
+ * and returns the mapped value on {@code remove()} calls.
*
- * @param valueSize number of bytes, taken by serialized form of values
- * @return this {@code ChronicleMapOnHeapUpdatableBuilder} back
- * @see #constantValueSizeBySample(Object)
- * @see #keySize(int)
- * @see #entrySize(int)
- */
- @Override
- public ChronicleMapBuilder valueSize(int valueSize) {
- delegate.valueSize(valueSize);
- return this;
- }
-
- /**
- * Configures the constant number of bytes, taken by serialized form of values, put into maps,
- * created by this builder. This is done by providing the {@code sampleValue}, all values should
- * take the same number of bytes in serialized form, as this sample object.
- *
- * If values are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass,
- * i. e. if value size is known statically, it is automatically accounted and this method
- * shouldn't be called.
- *
- *
If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be
- * called instead of this one.
- *
- * @param sampleValue the sample value
+ * @param removeReturnsNull {@code true} if you want {@link ChronicleMap#remove(Object)
+ * ChronicleMap.remove()} to not return the value of the removed entry
+ * but instead return {@code null}
* @return this builder back
- * @see #valueSize(int)
- * @see #constantKeySizeBySample(Object)
+ * @see #putReturnsNull(boolean)
*/
- @Override
- public ChronicleMapBuilder constantValueSizeBySample(@NotNull V sampleValue) {
- delegate.constantValueSizeBySample(sampleValue);
+ public ChronicleMapBuilder removeReturnsNull(boolean removeReturnsNull) {
+ this.removeReturnsNull = removeReturnsNull;
return this;
}
+ boolean removeReturnsNull() {
+ return removeReturnsNull;
+ }
+
+ @Override
+ public ChronicleMapBuilder metaDataBytes(int metaDataBytes) {
+ if (metaDataBytes < 0 || metaDataBytes > 255)
+ throw new IllegalArgumentException("MetaDataBytes must be [0..255] was " + metaDataBytes);
+ this.metaDataBytes = metaDataBytes;
+ return this;
+ }
+
+ int metaDataBytes() {
+ return metaDataBytes;
+ }
+
+ @Override
+ public String toString() {
+ return "ChronicleMapBuilder{" +
+ "name=" + name +
+ ", actualSegments=" + pretty(actualSegments) +
+ ", minSegments=" + pretty(minSegments) +
+ ", actualEntriesPerSegment=" + pretty(actualEntriesPerSegment) +
+ ", keySize=" + pretty(keySize) +
+ ", sampleKeyForConstantSizeComputation=" + pretty(sampleKey) +
+ ", valueSize=" + pretty(valueSize) +
+ ", sampleValueForConstantSizeComputation=" + pretty(sampleValue) +
+ ", entrySize=" + pretty(entrySize) +
+ ", entryAndValueAlignment=" + entryAndValueAlignment() +
+ ", entries=" + entries() +
+ ", lockTimeOut=" + lockTimeOut + " " + lockTimeOutUnit +
+ ", metaDataBytes=" + metaDataBytes() +
+ ", errorListener=" + errorListener() +
+ ", putReturnsNull=" + putReturnsNull() +
+ ", removeReturnsNull=" + removeReturnsNull() +
+ ", timeProvider=" + timeProvider() +
+ ", bytesMarshallerFactory=" + pretty(bytesMarshallerFactory) +
+ ", objectSerializer=" + pretty(objectSerializer) +
+ ", keyBuilder=" + keyBuilder +
+ ", valueBuilder=" + valueBuilder +
+ ", eventListener=" + eventListener +
+ ", defaultValue=" + defaultValue +
+ ", defaultValueProvider=" + pretty(defaultValueProvider) +
+ ", prepareValueBytes=" + pretty(prepareValueBytes) +
+ '}';
+ }
+
+ @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
+ @Override
+ public boolean equals(Object o) {
+ return builderEquals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ @Override
+ public ChronicleMapBuilder timeProvider(TimeProvider timeProvider) {
+ this.timeProvider = timeProvider;
+ return this;
+ }
+
+ TimeProvider timeProvider() {
+ return timeProvider;
+ }
+
+ BytesMarshallerFactory bytesMarshallerFactory() {
+ return bytesMarshallerFactory == null ?
+ bytesMarshallerFactory = new VanillaBytesMarshallerFactory() :
+ bytesMarshallerFactory;
+ }
+
+ @Override
+ public ChronicleMapBuilder bytesMarshallerFactory(
+ BytesMarshallerFactory bytesMarshallerFactory) {
+ this.bytesMarshallerFactory = bytesMarshallerFactory;
+ return this;
+ }
+
+ ObjectSerializer acquireObjectSerializer(ObjectSerializer defaultSerializer) {
+ return objectSerializer == null ?
+ BytesMarshallableSerializer.create(bytesMarshallerFactory(), defaultSerializer) :
+ objectSerializer;
+ }
+
/**
* {@inheritDoc}
*
@@ -230,81 +758,97 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderI
+ *
+ * This serializer is used to serialize both keys and values, if they both require this:
+ * loosely typed, nullable, and custom {@linkplain #keyMarshaller(BytesMarshaller) key} and
+ * {@linkplain #valueMarshaller(BytesMarshaller) value} marshallers are not configured.
*/
@Override
- public ChronicleMapBuilder objectSerializer(@NotNull ObjectSerializer objectSerializer) {
- delegate.objectSerializer(objectSerializer);
+ public ChronicleMapBuilder objectSerializer(ObjectSerializer objectSerializer) {
+ this.objectSerializer = objectSerializer;
+ return this;
+ }
+
+ @Override
+ public ChronicleMapBuilder keyMarshaller(
+ @NotNull BytesMarshaller super K> keyMarshaller) {
+ keyBuilder.marshaller(keyMarshaller);
+ return this;
+ }
+
+ @Override
+ public ChronicleMapBuilder keyMarshallers(
+ @NotNull BytesWriter keyWriter, @NotNull BytesReader keyReader) {
+ keyBuilder.writer(keyWriter);
+ keyBuilder.reader(keyReader);
+ return this;
+ }
+
+ @Override
+ public ChronicleMapBuilder keySizeMarshaller(@NotNull SizeMarshaller keySizeMarshaller) {
+ keyBuilder.sizeMarshaller(keySizeMarshaller);
return this;
}
/**
- * {@inheritDoc}
- *
- * @throws IllegalStateException if custom value marshaller is specified or value class is not
- * either {@code BytesMarshallable} or {@code Externalizable}
+ * @throws IllegalStateException {@inheritDoc}
+ * @see #valueDeserializationFactory(ObjectFactory)
*/
-
@Override
- public ChronicleMapBuilder valueDeserializationFactory(@NotNull ObjectFactory valueDeserializationFactory) {
- delegate.valueDeserializationFactory(valueDeserializationFactory);
+ public ChronicleMapBuilder keyDeserializationFactory(
+ @NotNull ObjectFactory keyDeserializationFactory) {
+ keyBuilder.factory(keyDeserializationFactory);
return this;
}
- /**
- * {@inheritDoc}
- *
- * By default, default value provider is not specified, {@link #defaultValue(Object) default
- * value} is specified instead.
- *
- * @see #defaultValue(Object)
- */
@Override
- public ChronicleMapBuilder defaultValueProvider(@NotNull DefaultValueProvider defaultValueProvider) {
- delegate.defaultValueProvider(defaultValueProvider);
+ public ChronicleMapBuilder immutableKeys() {
+ keyBuilder.instancesAreMutable(false);
return this;
}
/**
* Configures the {@code BytesMarshaller} used to serialize/deserialize values to/from off-heap
- * memory in maps, created by this builder. See the
- * section about serialization in ChronicleMap manual for more information.
+ * memory in maps, created by this builder. See the section about serialization
+ * in ChronicleMap manual for more information.
*
* @param valueMarshaller the marshaller used to serialize values
* @return this builder back
- * @see #valueMarshallers(net.openhft.chronicle.hash.serialization.BytesWriter,
- * net.openhft.chronicle.hash.serialization.BytesReader)
- * @see ChronicleMapBuilder
+ * @see #valueMarshallers(BytesWriter, BytesReader)
* @see #objectSerializer(ObjectSerializer)
+ * @see #keyMarshaller(BytesMarshaller)
*/
- @Override
public ChronicleMapBuilder valueMarshaller(
@NotNull BytesMarshaller super V> valueMarshaller) {
- delegate.valueMarshaller(valueMarshaller);
+ valueBuilder.marshaller(valueMarshaller);
return this;
}
/**
- * Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in
- * maps, created by this builder. See the
- * section about serialization in ChronicleMap manual for more information.
+ * Configures the marshallers, used to serialize/deserialize values to/from off-heap memory
+ * in maps, created by this builder. See the section about serialization
+ * in ChronicleMap manual for more information.
*
- * Configuring marshalling this way results to a little bit more compact in-memory layout of
- * the map, comparing to a single interface configuration: {@link #valueMarshaller(BytesMarshaller)}.
+ *
Configuring marshalling this way results to a little bit more compact in-memory layout
+ * of the map, comparing to a single interface configuration: {@link #valueMarshaller(
+ * BytesMarshaller)}.
*
- *
Passing {@link net.openhft.chronicle.hash.serialization.BytesInterop} instead of plain
- * {@link net.openhft.chronicle.hash.serialization.BytesWriter} is, of cause, possible, but
- * currently pointless for values.
+ *
Passing {@link BytesInterop} instead of plain {@link BytesWriter} is, of cause, possible,
+ * but currently pointless for values.
*
- * @param valueWriter the new value object → {@link net.openhft.lang.io.Bytes} writer
- * (interop) strategy
- * @param valueReader the new {@link net.openhft.lang.io.Bytes} → value object reader
- * strategy
+ * @param valueWriter the new value object → {@link Bytes} writer (interop) strategy
+ * @param valueReader the new {@link Bytes} → value object reader strategy
* @return this builder back
* @see #valueMarshaller(BytesMarshaller)
+ * @see #valueSizeMarshaller(SizeMarshaller)
+ * @see #keyMarshallers(BytesWriter, BytesReader)
*/
- @Override
- public ChronicleMapBuilder valueMarshallers(@NotNull BytesWriter valueWriter, @NotNull BytesReader valueReader) {
- delegate.valueMarshallers(valueWriter, valueReader);
+ public ChronicleMapBuilder valueMarshallers(
+ @NotNull BytesWriter valueWriter, @NotNull BytesReader valueReader) {
+ valueBuilder.writer(valueWriter);
+ valueBuilder.reader(valueReader);
return this;
}
@@ -312,218 +856,411 @@ public final class ChronicleMapBuilder implements ChronicleMapBuilderIDefault value size marshaller is so-called {@linkplain net.openhft.chronicle.hash.serialization.SizeMarshallers#stopBit()
- * stop bit encoding marshalling}. If {@linkplain #constantValueSizeBySample(Object) constant
- * value size} is configured, or defaulted if the value type is always constant and {@code
- * ChronicleHashBuilder} implementation knows about it, this configuration takes no effect,
- * because a special {@link net.openhft.chronicle.hash.serialization.SizeMarshaller}
- * implementation, which doesn't actually do any marshalling, and just returns the known
- * constant size on {@link net.openhft.chronicle.hash.serialization.SizeMarshaller#readSize(
- *net.openhft.lang.io.Bytes)} calls, is used instead of any {@code SizeMarshaller} configured
- * using this method.
+ * Default value size marshaller is so-called {@linkplain SizeMarshallers#stopBit() stop bit
+ * encoding marshalling}, unless {@link #constantValueSizeBySample(Object)} or the builder
+ * statically knows the value size is constant -- {@link SizeMarshallers#constant(long)} or
+ * equivalents are used by default in these cases.
*
- * @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes to
- * off-heap memory
+ * @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes
+ * to off-heap memory
* @return this builder back
+ * @see #keySizeMarshaller(SizeMarshaller)
*/
- @Override
- public ChronicleMapBuilder valueSizeMarshaller(@NotNull SizeMarshaller valueSizeMarshaller) {
- delegate.valueSizeMarshaller(valueSizeMarshaller);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder entries(long l) {
- delegate.entries(l);
+ public ChronicleMapBuilder valueSizeMarshaller(
+ @NotNull SizeMarshaller valueSizeMarshaller) {
+ valueBuilder.sizeMarshaller(valueSizeMarshaller);
return this;
}
/**
- * each map can be identified by a name
+ * Configures factory which is used to create a new value instance, if value class is either
+ * {@link Byteable}, {@link BytesMarshallable} or {@link Externalizable} subclass, or value type
+ * is eligible for data value generation, or configured custom value reader is an instance of
+ * {@link DeserializationFactoryConfigurableBytesReader}, in maps, created by this builder.
*
- * @param name the name of the map
- * @return self
+ * Default value deserialization factory is {@link NewInstanceObjectFactory}, which creates
+ * a new value instance using {@link Class#newInstance()} default constructor. You could provide
+ * an {@link AllocateInstanceObjectFactory}, which uses {@code Unsafe.allocateInstance(Class)}
+ * (you might want to do this for better performance or if you don't want to initialize fields),
+ * or a factory which calls a value class constructor with some arguments, or a factory which
+ * internally delegates to instance pool or {@link ThreadLocal}, to reduce allocations.
+ *
+ * @param valueDeserializationFactory the value factory used to produce instances to deserialize
+ * data in
+ * @return this builder back
+ * @throws IllegalStateException if it is not possible to apply deserialization factory to
+ * value deserializers, currently configured for this builder
+ * @see #keyDeserializationFactory(ObjectFactory)
*/
- @Override
- public ChronicleMapBuilder name(String name) {
- delegate.name(name);
+ public ChronicleMapBuilder valueDeserializationFactory(
+ @NotNull ObjectFactory valueDeserializationFactory) {
+ valueBuilder.factory(valueDeserializationFactory);
return this;
}
- @Override
- public String name() {
- return delegate.name();
- }
-
- @Override
- public ChronicleMapBuilder replication(SingleChronicleHashReplication withId) {
- delegate.replication(withId);
+ public ChronicleMapBuilder eventListener(MapEventListener eventListener) {
+ this.eventListener = eventListener;
return this;
}
- @Override
- public ChronicleMapBuilder pushTo(InetSocketAddress... addresses) {
- delegate.pushTo(addresses);
+ MapEventListener eventListener() {
+ return eventListener;
+ }
+
+ public ChronicleMapBuilder bytesEventListener(BytesMapEventListener eventListener) {
+ this.bytesEventListener = eventListener;
return this;
}
- @Override
- public ChronicleMap create() {
- return delegate.create();
+ BytesMapEventListener bytesEventListener() {
+ return bytesEventListener;
}
- @Override
- public ChronicleMapBuilder actualSegments(int actualSegments) {
- delegate.actualSegments(actualSegments);
+ /**
+ * Specifies the value to be put for each key queried in {@link ChronicleMap#acquireUsing
+ * acquireUsing()} method, if the key is absent in the map, created by this builder.
+ *
+ * This configuration overrides any previous {@link #defaultValueProvider(
+ * DefaultValueProvider)} and {@link #prepareDefaultValueBytes(PrepareValueBytes)}
+ * configurations to this builder.
+ *
+ * @param defaultValue the default value to be put to the map for absent keys during {@code
+ * acquireUsing()} calls
+ * @return this builder object back
+ * @see #defaultValueProvider(DefaultValueProvider)
+ * @see #prepareDefaultValueBytes(PrepareValueBytes)
+ */
+ public ChronicleMapBuilder defaultValue(V defaultValue) {
+ this.defaultValue = defaultValue;
+ this.defaultValueProvider = null;
+ if (defaultValue == null)
+ defaultValueProvider = NullValueProvider.INSTANCE;
+ this.prepareValueBytes = null;
return this;
}
- @Override
- public ChronicleMapBuilder minSegments(int minSegments) {
- delegate.minSegments(minSegments);
+ /**
+ * Specifies the function to obtain a value for the key during {@link ChronicleMap#acquireUsing
+ * acquireUsing()} calls, if the key is absent in the map, created by this builder.
+ *
+ * This configuration overrides any previous {@link #defaultValue(Object)} and {@link
+ * #prepareDefaultValueBytes(PrepareValueBytes)} configurations to this builder.
+ *
+ * @param defaultValueProvider the strategy to obtain a default value by the absent key
+ * @return this builder object back
+ * @see #defaultValue(Object)
+ * @see #prepareDefaultValueBytes(PrepareValueBytes)
+ */
+ public ChronicleMapBuilder defaultValueProvider(
+ @NotNull DefaultValueProvider defaultValueProvider) {
+ this.defaultValueProvider = defaultValueProvider;
+ this.defaultValue = null;
+ this.prepareValueBytes = null;
return this;
}
- @Override
- public ChronicleMapBuilder actualEntriesPerSegment(long actualEntriesPerSegment) {
- delegate.actualEntriesPerSegment(actualEntriesPerSegment);
+ /**
+ * Configures the procedure which is called on the bytes, which later the returned value is
+ * pointing to or deserialized from, if the key is absent, on {@link ChronicleMap#acquireUsing
+ * acquireUsing()} call on maps, created by this builder. See {@link PrepareValueBytes} for more
+ * information.
+ *
+ * This method of value initialization on {@code acquireUsing()} calls is allowed only if
+ * value size is constant. Otherwise you should use either {@link #defaultValue(Object)} or
+ * {@link #defaultValueProvider(DefaultValueProvider)} methods.
+ *
+ *
This configuration overrides any previous {@link #defaultValue(Object)} and {@link
+ * #defaultValueProvider(DefaultValueProvider)} configurations to this builder.
+ *
+ *
The default preparation callback zeroes out the value bytes.
+ *
+ * @param prepareValueBytes what to do with the value bytes before assigning them into the
+ * {@link Byteable} value or deserializing the value from,
+ * to return from {@code acquireUsing()} call
+ * @return this builder back
+ * @throws IllegalStateException is value size is not constant
+ * @see PrepareValueBytes
+ * @see #defaultValue(Object)
+ * @see #defaultValueProvider(DefaultValueProvider)
+ */
+ public ChronicleMapBuilder prepareDefaultValueBytes(
+ @NotNull PrepareValueBytes prepareValueBytes) {
+ this.prepareValueBytes = prepareValueBytes;
+ this.defaultValue = null;
+ this.defaultValueProvider = null;
+ checkPrepareValueBytesOnlyIfConstantValueSize();
return this;
}
+ private void checkPrepareValueBytesOnlyIfConstantValueSize() {
+ if (prepareValueBytes != null && !valueBuilder.constantSizeMarshaller())
+ throw new IllegalStateException("Prepare value bytes could be used only if " +
+ "value size is constant");
+ }
+
+ PrepareValueBytesAsWriter prepareValueBytesAsWriter() {
+ PrepareValueBytes prepareValueBytes = this.prepareValueBytes;
+ if ((prepareValueBytes == null && defaultValueProvider() != null) ||
+ !valueBuilder.constantSizeMarshaller())
+ return null;
+ if (prepareValueBytes == null)
+ prepareValueBytes = new ZeroOutValueBytes<>(valueSize());
+ return new PrepareValueBytesAsWriter<>(prepareValueBytes, valueSize());
+ }
+
+ /**
+ * Non-public because should be called only after {@link #preMapConstruction(boolean)}
+ */
+ DefaultValueProvider defaultValueProvider() {
+ if (defaultValueProvider != null)
+ return defaultValueProvider;
+ if (defaultValue == null)
+ return null;
+ Object originalValueWriter = valueBuilder.interop();
+ Provider writerProvider = Provider.of(originalValueWriter.getClass());
+ ThreadLocalCopies copies = writerProvider.getCopies(null);
+ Object valueWriter = writerProvider.get(copies, originalValueWriter);
+ MetaProvider metaWriterProvider = valueBuilder.metaInteropProvider();
+ copies = metaWriterProvider.getCopies(copies);
+ MetaBytesWriter metaValueWriter = metaWriterProvider.get(copies,
+ valueBuilder.metaInterop(), valueWriter, defaultValue);
+ return new ConstantValueProvider(defaultValue, metaValueWriter, valueWriter);
+ }
+
@Override
- public ChronicleMapBuilder lockTimeOut(long lockTimeOut, TimeUnit unit) {
- delegate.lockTimeOut(lockTimeOut, unit);
+ public ChronicleMapBuilder replication(SingleChronicleHashReplication replication) {
+ this.singleHashReplication = replication;
return this;
}
- @Override
- public ChronicleMapBuilder errorListener(ChronicleHashErrorListener errorListener) {
- delegate.errorListener(errorListener);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder metaDataBytes(int metaDataBytes) {
- delegate.metaDataBytes(metaDataBytes);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder timeProvider(TimeProvider timeProvider) {
- delegate.timeProvider(timeProvider);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory) {
- delegate.bytesMarshallerFactory(bytesMarshallerFactory);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder keyMarshaller(BytesMarshaller keyMarshaller) {
- delegate.keyMarshaller(keyMarshaller);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder keyMarshallers(BytesWriter keyWriter, BytesReader keyReader) {
- delegate.keyMarshallers(keyWriter, keyReader);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder keySizeMarshaller(SizeMarshaller keySizeMarshaller) {
- delegate.keySizeMarshaller(keySizeMarshaller);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder keyDeserializationFactory(ObjectFactory keyDeserializationFactory) {
- delegate.keyDeserializationFactory(keyDeserializationFactory);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder immutableKeys() {
- delegate.immutableKeys();
- return this;
- }
-
- @Override
- public StatelessClientConfig> statelessClient(InetSocketAddress remoteAddress) {
- return delegate.statelessClient(remoteAddress);
- }
-
- @Override
- public ChronicleHashInstanceConfig> instance() {
- return delegate.instance();
- }
-
@Override
public ChronicleMapBuilder replication(byte identifier) {
- delegate.replication(identifier);
- return this;
+ return replication(SingleChronicleHashReplication.builder().createWithId(identifier));
}
@Override
- public ChronicleMapBuilder replication(byte identifier, TcpTransportAndNetworkConfig tcpTransportAndNetwork) {
- delegate.replication(identifier, tcpTransportAndNetwork);
- return this;
+ public ChronicleMapBuilder replication(
+ byte identifier, TcpTransportAndNetworkConfig tcpTransportAndNetwork) {
+ return replication(SingleChronicleHashReplication.builder()
+ .tcpTransportAndNetwork(tcpTransportAndNetwork).createWithId(identifier));
}
@Override
- public ChronicleMapBuilder clone() {
- delegate.clone();
- return this;
+ public ChronicleHashInstanceConfig> instance() {
+ return new MapInstanceConfig<>(this.clone(), singleHashReplication, null, null, null,
+ new AtomicBoolean(false));
}
@Override
- public ChronicleMapBuilder putReturnsNull(boolean b) {
- delegate.putReturnsNull(b);
- return this;
+ public ChronicleMap createStatelessClient(InetSocketAddress serverAddress)
+ throws IOException {
+ return statelessClient(serverAddress).create();
}
- @Override
- public ChronicleMapBuilder removeReturnsNull(boolean b) {
- delegate.removeReturnsNull(b);
- return this;
- }
-
- @Override
- public ChronicleMapBuilder eventListener(MapEventListener eventListener) {
- delegate.eventListener(eventListener);
- return this;
- }
-
- @Override
- public ChronicleMapBuilderI bytesEventListener(BytesMapEventListener eventListener) {
- delegate.bytesEventListener(eventListener);
- return this;
+ ChronicleMap createStatelessClient(StatelessMapConfig statelessClientConfig)
+ throws IOException {
+ pushingToMapEventListener();
+ preMapConstruction(false);
+ return new StatelessChronicleMap<>(statelessClientConfig, this);
}
@Override
public ChronicleMap createPersistedTo(File file) throws IOException {
- return delegate.createPersistedTo(file);
+ // clone() to make this builder instance thread-safe, because createWithFile() method
+ // computes some state based on configurations, but doesn't synchronize on configuration
+ // changes.
+ return clone().createWithFile(file, singleHashReplication, null);
}
- /**
- * @param bootstrapOnlyLocalEntries if set to true - when a new node joins a TCP replication
- * grid, the new node will be populated with data, only for the
- * nodes that created that data. Otherwise, all the nodes will
- * publish all the data they have ( potentially swamping the
- * new node with duplicates ) however this does guarantee that
- * all the data is replicated over to the new node, and is
- * useful especially in the case that the originating node is
- * not currently running.
- */
- public ChronicleMapBuilder bootstrapOnlyLocalEntries(boolean bootstrapOnlyLocalEntries) {
- delegate.bootstrapOnlyLocalEntries(bootstrapOnlyLocalEntries);
- return this;
+ @Override
+ public StatelessClientConfig> statelessClient(
+ InetSocketAddress remoteAddress) {
+ return new StatelessMapConfig<>(clone(), remoteAddress, DEFAULT_STATELESS_CLIENT_TIMEOUT,
+ null, new AtomicBoolean(false));
}
- public ChronicleMapBuilder disableOversizedEntries() {
- delegate.disableOversizedEntries();
- return this;
+ @Override
+ public ChronicleMap create() {
+ // clone() to make this builder instance thread-safe, because createWithoutFile() method
+ // computes some state based on configurations, but doesn't synchronize on configuration
+ // changes.
+ return clone().createWithoutFile(singleHashReplication, null);
+ }
+
+ ChronicleMap create(MapInstanceConfig ib) throws IOException {
+ if (ib.file != null) {
+ return createWithFile(ib.file, ib.singleHashReplication, ib.channel);
+ } else {
+ return createWithoutFile(ib.singleHashReplication, ib.channel);
+ }
+ }
+
+ ChronicleMap createWithFile(File file, SingleChronicleHashReplication singleHashReplication,
+ ReplicationChannel channel) throws IOException {
+ pushingToMapEventListener();
+ for (int i = 0; i < 10; i++) {
+ if (file.exists() && file.length() > 0) {
+ try (FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis)) {
+ VanillaChronicleMap map =
+ (VanillaChronicleMap) ois.readObject();
+ map.headerSize = roundUpMapHeaderSize(fis.getChannel().position());
+ map.createMappedStoreAndSegments(file);
+ return establishReplication(map, singleHashReplication, channel);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e);
+ }
+ }
+ if (file.createNewFile() || file.length() == 0) {
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+ // new file
+ if (!file.exists())
+ throw new FileNotFoundException("Unable to create " + file);
+
+ VanillaChronicleMap map = newMap(singleHashReplication, channel);
+
+ try (FileOutputStream fos = new FileOutputStream(file);
+ ObjectOutputStream oos = new ObjectOutputStream(fos)) {
+ oos.writeObject(map);
+ oos.flush();
+ map.headerSize = roundUpMapHeaderSize(fos.getChannel().position());
+ map.createMappedStoreAndSegments(file);
+ }
+
+ return establishReplication(map, singleHashReplication, channel);
+ }
+
+ ChronicleMap createWithoutFile(
+ SingleChronicleHashReplication singleHashReplication, ReplicationChannel channel) {
+ try {
+ pushingToMapEventListener();
+ VanillaChronicleMap map = newMap(singleHashReplication, channel);
+ BytesStore bytesStore = new DirectStore(JDKObjectSerializer.INSTANCE,
+ map.sizeInBytes(), true);
+ map.createMappedStoreAndSegments(bytesStore);
+ return establishReplication(map, singleHashReplication, channel);
+ } catch (IOException e) {
+ // file-less version should never trigger an IOException.
+ throw new AssertionError(e);
+ }
+ }
+
+ private void pushingToMapEventListener() {
+ if (pushToAddresses == null || pushToAddresses.length == 0) {
+ return;
+ }
+ try {
+ Class> pmel = Class.forName(
+ "com.higherfrequencytrading.chronicle.enterprise.map.PushingMapEventListener");
+ Constructor> constructor = pmel.getConstructor(ChronicleMap[].class);
+ // create a stateless client for each address
+ ChronicleMap[] statelessClients = new ChronicleMap[pushToAddresses.length];
+ ChronicleMapBuilder cmb = clone();
+ cmb.pushTo((InetSocketAddress[]) null);
+ for (int i = 0; i < pushToAddresses.length; i++) {
+ statelessClients[i] = cmb.createStatelessClient(pushToAddresses[i]);
+ }
+ eventListener =
+ (MapEventListener) constructor.newInstance((Object) statelessClients);
+ } catch (ClassNotFoundException e) {
+ LoggerFactory.getLogger(getClass().getName())
+ .warn("Chronicle Enterprise not found in the class path");
+ } catch (Exception e) {
+ LoggerFactory.getLogger(getClass().getName())
+ .error("PushingMapEventListener failed to load", e);
+ }
+ }
+
+ private VanillaChronicleMap newMap(
+ SingleChronicleHashReplication singleHashReplication, ReplicationChannel channel) throws IOException {
+ boolean replicated = singleHashReplication != null || channel != null;
+ preMapConstruction(replicated);
+ if (replicated) {
+ AbstractReplication replication;
+ if (singleHashReplication != null) {
+ replication = singleHashReplication;
+ } else {
+ replication = channel.hub();
+ }
+ return new ReplicatedChronicleMap,
+ V, Object, MetaBytesInterop>(this, replication);
+ } else {
+ return new VanillaChronicleMap,
+ V, Object, MetaBytesInterop>(this);
+ }
+ }
+
+ void preMapConstruction(boolean replicated) {
+ keyBuilder.objectSerializer(acquireObjectSerializer(JDKObjectSerializer.INSTANCE));
+ valueBuilder.objectSerializer(acquireObjectSerializer(JDKObjectSerializer.INSTANCE));
+
+ long maxSize = (long) entrySize(replicated) * figureBufferAllocationFactor();
+ keyBuilder.maxSize(maxSize);
+ valueBuilder.maxSize(maxSize);
+
+ if (sampleKey != null)
+ keyBuilder.constantSizeBySample(sampleKey);
+ if (sampleValue != null)
+ valueBuilder.constantSizeBySample(sampleValue);
+ stateChecks();
+ }
+
+ private void stateChecks() {
+ checkAlignmentOnlyIfValuesPossiblyReferenceOffHeap();
+ checkPrepareValueBytesOnlyIfConstantValueSize();
+ }
+
+ private ChronicleMap establishReplication(
+ VanillaChronicleMap map,
+ SingleChronicleHashReplication singleHashReplication,
+ ReplicationChannel channel) throws IOException {
+ if (map instanceof ReplicatedChronicleMap) {
+ if (singleHashReplication != null && channel != null) {
+ throw new AssertionError("Only one non-null replication should be passed");
+ }
+ ReplicatedChronicleMap result = (ReplicatedChronicleMap) map;
+ List replicators = new ArrayList<>(2);
+ if (singleHashReplication != null) {
+ if (singleHashReplication.tcpTransportAndNetwork() != null)
+ replicators.add(Replicators.tcp(singleHashReplication));
+ if (singleHashReplication.udpTransport() != null)
+ replicators.add(Replicators.udp(singleHashReplication.udpTransport()));
+ } else {
+ ReplicationHub hub = channel.hub();
+ int entrySize = entrySize(true);
+ if (entrySize > hub.maxEntrySize()) {
+ throw new IllegalArgumentException("During ChannelProviderBuilder setup, " +
+ "maxEntrySize=" + hub.maxEntrySize() +
+ " was specified, but map with " +
+ "entrySize=" + entrySize + " is attempted to apply to the replicator");
+ }
+ ChannelProvider provider = ChannelProvider.getProvider(hub);
+ ChannelProvider.ChronicleChannel ch = provider.createChannel(channel.channelId());
+ replicators.add(ch);
+ }
+ for (Replicator replicator : replicators) {
+ Closeable token = replicator.applyTo(this, result, result, map);
+ if (replicators.size() == 1 && token.getClass() == UdpReplicator.class) {
+ LOG.warn(Replicators.ONLY_UDP_WARN_MESSAGE);
+ }
+ result.addCloseable(token);
+ }
+ }
+ return map;
+ }
+
+ private int figureBufferAllocationFactor() {
+ // if expected map size is about 1000, seems rather wasteful to allocate
+ // key and value serialization buffers each x64 of expected entry size..
+ return (int) Math.min(Math.max(2L, entries() >> 10),
+ VanillaChronicleMap.MAX_ENTRY_OVERSIZE_FACTOR);
}
}
+
diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java
deleted file mode 100755
index 99cc3455b..000000000
--- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilderI.java
+++ /dev/null
@@ -1,319 +0,0 @@
-package net.openhft.chronicle.map;
-
-import net.openhft.chronicle.hash.ChronicleHashErrorListener;
-import net.openhft.chronicle.hash.ChronicleHashInstanceConfig;
-import net.openhft.chronicle.hash.StatelessClientConfig;
-import net.openhft.chronicle.hash.replication.SingleChronicleHashReplication;
-import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig;
-import net.openhft.chronicle.hash.replication.TimeProvider;
-import net.openhft.chronicle.hash.serialization.BytesReader;
-import net.openhft.chronicle.hash.serialization.BytesWriter;
-import net.openhft.chronicle.hash.serialization.SizeMarshaller;
-import net.openhft.lang.io.serialization.BytesMarshaller;
-import net.openhft.lang.io.serialization.BytesMarshallerFactory;
-import net.openhft.lang.io.serialization.ObjectFactory;
-import net.openhft.lang.io.serialization.ObjectSerializer;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.InetSocketAddress;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author Rob Austin.
- */
-public interface ChronicleMapBuilderI extends Serializable {
-
- /**
- * {@inheritDoc} With respect to {@linkplain #entryAndValueAlignment(Alignment) alignment}.
- *
- * Note that the actual entrySize will be aligned to 4 (default {@linkplain
- * #entryAndValueAlignment(Alignment) entry alignment}). I. e. if you set entry size to 30, and
- * entry alignment is set to {@link Alignment#OF_4_BYTES}, the actual entry size will be 32 (30
- * aligned to 4 bytes).
- *
- * @see #entryAndValueAlignment(Alignment) // * @see #entries(long)
- */
- ChronicleMapBuilderI entrySize(int entrySize);
-
- /**
- * Configures alignment strategy of address in memory of entries and independently of address in
- * memory of values within entries in ChronicleMaps, created by this builder.
- *
- * Useful when values of the map are updated intensively, particularly fields with volatile
- * access, because it doesn't work well if the value crosses cache lines. Also, on some
- * (nowadays rare) architectures any misaligned memory access is more expensive than aligned.
- *
- *
Note that {@linkplain #entrySize(int) entry size} will be aligned according to this
- * alignment. I. e. if you set {@code entrySize(20)} and {@link Alignment#OF_8_BYTES}, actual
- * entry size will be 24 (20 aligned to 8 bytes).
- *
- *
Default is {@link Alignment#OF_4_BYTES} for Byteable values.
- *
- * @param alignment the new alignment of the maps constructed by this builder
- * @return this {@code ChronicleMapOnHeapUpdatableBuilder} back
- */
- ChronicleMapBuilderI entryAndValueAlignment(Alignment alignment);
-
- /**
- * {@inheritDoc} Also, it overrides any previous {@link #prepareValueBytesOnAcquire}
- * configuration to this {@code ChronicleMapBuilder}.
- *
- * By default, the default value is not specified, default {@linkplain
- * #prepareValueBytesOnAcquire prepare value bytes routine} is specified instead.
- *
- * @see #defaultValueProvider(DefaultValueProvider)
- * @see #prepareValueBytesOnAcquire(PrepareValueBytes)
- */
- ChronicleMapBuilderI defaultValue(V defaultValue);
-
- /**
- * Configures the procedure which is called on the bytes, which later the returned value is
- * pointing to, if the key is absent, on {@link ChronicleMap#acquireUsing(Object, Object)
- * acquireUsing()} call on maps, created by this builder. See {@link PrepareValueBytes} for more
- * information.
- *
- * The default preparation callback zeroes out the value bytes.
- *
- * @param prepareValueBytes what to do with the value bytes before assigning them into the
- * {@link net.openhft.lang.model.Byteable} value to return from {@code
- * acquireUsing()} call
- * @return this builder back
- * @see PrepareValueBytes
- * @see #defaultValue(Object)
- * @see #defaultValueProvider(DefaultValueProvider)
- */
- ChronicleMapBuilderI prepareValueBytesOnAcquire(
- @NotNull PrepareValueBytes prepareValueBytes);
-
- /**
- * {@inheritDoc}
- *
- * @see #constantKeySizeBySample(Object)
- * @see #valueSize(int)
- * @see #entrySize(int)
- */
- ChronicleMapBuilderI keySize(int keySize);
-
- /**
- * {@inheritDoc}
- *
- * @see #keySize(int)
- * @see #constantValueSizeBySample(Object)
- */
-
- ChronicleMapBuilderI constantKeySizeBySample(K sampleKey);
-
- /**
- * Configures the optimal number of bytes, taken by serialized form of values, put into maps,
- * created by this builder. If value size is always the same, call {@link
- * #constantValueSizeBySample(Object)} method instead of this one.
- *
- * If value is a boxed primitive type, i. e. if value size is known statically, it is
- * automatically accounted and shouldn't be specified by user.
- *
- *
If value size varies moderately, specify the size higher than average, but lower than the
- * maximum possible, to minimize average memory overuse. If value size varies in a wide range,
- * it's better to use {@linkplain #entrySize(int) entry size} in "chunk" mode and configure it
- * directly.
- *
- * @param valueSize number of bytes, taken by serialized form of values
- * @return this {@code ChronicleMapOnHeapUpdatableBuilder} back
- * @see #constantValueSizeBySample(Object)
- * @see #keySize(int)
- * @see #entrySize(int)
- */
-
- ChronicleMapBuilderI valueSize(int valueSize);
-
- /**
- * Configures the constant number of bytes, taken by serialized form of values, put into maps,
- * created by this builder. This is done by providing the {@code sampleValue}, all values should
- * take the same number of bytes in serialized form, as this sample object.
- *
- * If values are of boxed primitive type or {@link net.openhft.lang.model.Byteable} subclass,
- * i. e. if value size is known statically, it is automatically accounted and this method
- * shouldn't be called.
- *
- *
If value size varies, method {@link #valueSize(int)} or {@link #entrySize(int)} should be
- * called instead of this one.
- *
- * @param sampleValue the sample value
- * @return this builder back
- * @see #valueSize(int)
- * @see #constantKeySizeBySample(Object)
- */
-
- ChronicleMapBuilderI constantValueSizeBySample(
- @NotNull V sampleValue);
-
- /**
- * {@inheritDoc}
- *
- * Example:
{@code Map map =
- * ChronicleMapBuilder.of(Key.class, Value.class)
- * .entries(1_000_000)
- * .keySize(50).valueSize(200)
- * // this class hasn't implemented yet, just for example
- * .objectSerializer(new KryoObjectSerializer())
- * .create();}
- */
-
- ChronicleMapBuilderI objectSerializer(
- @NotNull ObjectSerializer objectSerializer);
-
- /**
- * {@inheritDoc}
- *
- * @throws IllegalStateException if custom value marshaller is specified or value class is not
- * either {@code BytesMarshallable} or {@code Externalizable}
- */
-
- ChronicleMapBuilderI valueDeserializationFactory(
- @NotNull ObjectFactory valueDeserializationFactory);
-
- /**
- * {@inheritDoc}
- *
- * By default, default value provider is not specified, {@link #defaultValue(Object) default
- * value} is specified instead.
- *
- * @see #defaultValue(Object)
- */
-
- ChronicleMapBuilderI defaultValueProvider(
- @NotNull DefaultValueProvider defaultValueProvider);
-
- /**
- * Configures the {@code BytesMarshaller} used to serialize/deserialize values to/from off-heap
- * memory in maps, created by this builder. See the
- * section about serialization in ChronicleMap manual for more information.
- *
- * @param valueMarshaller the marshaller used to serialize values
- * @return this builder back
- * @see #valueMarshallers(net.openhft.chronicle.hash.serialization.BytesWriter,
- * net.openhft.chronicle.hash.serialization.BytesReader)
- * @see ChronicleMapBuilder
- * @see #objectSerializer(ObjectSerializer)
- */
- ChronicleMapBuilderI valueMarshaller(
- @NotNull BytesMarshaller super V> valueMarshaller);
-
- /**
- * Configures the marshallers, used to serialize/deserialize values to/from off-heap memory in
- * maps, created by this builder. See the
- * section about serialization in ChronicleMap manual for more information.
- *
- * Configuring marshalling this way results to a little bit more compact in-memory layout of
- * the map, comparing to a single interface configuration: {@link #valueMarshaller(BytesMarshaller)}.
- *
- *
Passing {@link net.openhft.chronicle.hash.serialization.BytesInterop} instead of plain
- * {@link net.openhft.chronicle.hash.serialization.BytesWriter} is, of cause, possible, but
- * currently pointless for values.
- *
- * @param valueWriter the new value object → {@link net.openhft.lang.io.Bytes} writer
- * (interop) strategy
- * @param valueReader the new {@link net.openhft.lang.io.Bytes} → value object reader
- * strategy
- * @return this builder back
- * @see #valueMarshaller(BytesMarshaller)
- */
- ChronicleMapBuilderI valueMarshallers(
- @NotNull BytesWriter valueWriter, @NotNull BytesReader valueReader);
-
- /**
- * Configures the marshaller used to serialize actual value sizes to off-heap memory in maps,
- * created by this builder.
- *
- * Default value size marshaller is so-called {@linkplain net.openhft.chronicle.hash.serialization.SizeMarshallers#stopBit()
- * stop bit encoding marshalling}. If {@linkplain #constantValueSizeBySample(Object) constant
- * value size} is configured, or defaulted if the value type is always constant and {@code
- * ChronicleHashBuilder} implementation knows about it, this configuration takes no effect,
- * because a special {@link net.openhft.chronicle.hash.serialization.SizeMarshaller}
- * implementation, which doesn't actually do any marshalling, and just returns the known
- * constant size on {@link net.openhft.chronicle.hash.serialization.SizeMarshaller#readSize(
- *net.openhft.lang.io.Bytes)} calls, is used instead of any {@code SizeMarshaller} configured
- * using this method.
- *
- * @param valueSizeMarshaller the new marshaller, used to serialize actual value sizes to
- * off-heap memory
- * @return this builder back
- */
- ChronicleMapBuilderI valueSizeMarshaller(
- @NotNull SizeMarshaller valueSizeMarshaller);
-
- ChronicleMapBuilderI entries(long l);
-
- ChronicleMapBuilderI replication(SingleChronicleHashReplication withId);
-
- ChronicleMapBuilderI pushTo(InetSocketAddress... addresses);
-
- ChronicleMap create();
-
- ChronicleMapBuilderI actualSegments(int actualSegments);
-
- ChronicleMapBuilderI minSegments(int minSegments);
-
- ChronicleMapBuilderI actualEntriesPerSegment(long actualEntriesPerSegment);
-
- ChronicleMapBuilderI lockTimeOut(long lockTimeOut, TimeUnit unit);
-
- ChronicleMapBuilderI errorListener(ChronicleHashErrorListener errorListener);
-
- ChronicleMapBuilderI metaDataBytes(int metaDataBytes);
-
- ChronicleMapBuilderI timeProvider(TimeProvider timeProvider);
-
- ChronicleMapBuilderI bytesMarshallerFactory(BytesMarshallerFactory bytesMarshallerFactory);
-
- ChronicleMapBuilderI keyMarshaller(BytesMarshaller keyMarshaller);
-
- ChronicleMapBuilderI keyMarshallers(BytesWriter keyWriter, BytesReader keyReader);
-
- ChronicleMapBuilderI keySizeMarshaller(SizeMarshaller keySizeMarshaller);
-
- ChronicleMapBuilderI keyDeserializationFactory(ObjectFactory keyDeserializationFactory);
-
- ChronicleMapBuilderI immutableKeys();
-
- ChronicleMap createPersistedTo(File file) throws IOException;
-
- StatelessClientConfig