diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java index 0fc614406..60eb7b516 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/internal/CharSequenceReader.java @@ -33,7 +33,16 @@ public final class CharSequenceReader implements BytesReader, StatefulCopyable> { private static final long serialVersionUID = 0L; - private enum NoInterning implements CharSequenceInterner { + private enum NoInterningIdentity implements CharSequenceInterner { + INSTANCE; + + @Override + public CharSequence intern(CharSequence cs) { + return cs; + } + } + + private enum NoInterningToString implements CharSequenceInterner { INSTANCE; @Override public String intern(CharSequence cs) { @@ -41,10 +50,17 @@ public final class CharSequenceReader } } - private static final CharSequenceReader DEFAULT_READER = - new CharSequenceReader(NoInterning.INSTANCE, NoInterning.INSTANCE); + private static final CharSequenceReader STRING_READER = + new CharSequenceReader(NoInterningToString.INSTANCE, NoInterningToString.INSTANCE); - public static CharSequenceReader of() { + private static final CharSequenceReader DEFAULT_READER = + new CharSequenceReader(NoInterningIdentity.INSTANCE, NoInterningIdentity.INSTANCE); + + public static CharSequenceReader of() { + return STRING_READER; + } + + public static CharSequenceReader ofStringBuilder() { return DEFAULT_READER; } @@ -111,7 +127,9 @@ public final class CharSequenceReader } private Object readResolve() throws ObjectStreamException { - if (interner == NoInterning.INSTANCE) + if (interner == NoInterningToString.INSTANCE) + return STRING_READER; + if (interner == NoInterningIdentity.INSTANCE) return DEFAULT_READER; return this; } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index 0d3e701cc..d5c8268d4 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -104,6 +104,8 @@ public final class ChronicleMapBuilder implements Cloneable, LoggerFactory.getLogger(ChronicleMapBuilder.class.getName()); public static final long DEFAULT_STATELESS_CLIENT_TIMEOUT = TimeUnit.SECONDS.toMillis(10); + private static final StringBuilder EMTRY_STRING_BUILDER = new StringBuilder(); + SerializationBuilder keyBuilder; SerializationBuilder valueBuilder; @@ -148,6 +150,8 @@ public final class ChronicleMapBuilder implements Cloneable, if (CharSequence.class == valueClass) defaultValue = (V) ""; + if (StringBuilder.class == valueClass) + defaultValue = (V) EMTRY_STRING_BUILDER; } /** diff --git a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java index 87cb452db..a94e29540 100644 --- a/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/SerializationBuilder.java @@ -130,6 +130,9 @@ final class SerializationBuilder implements Cloneable, Serializable { } else if (eClass == CharSequence.class || eClass == String.class) { reader((BytesReader) CharSequenceReader.of()); writer((BytesWriter) CharSequenceWriter.instance()); + } else if (eClass == StringBuilder.class) { + reader((BytesReader) CharSequenceReader.ofStringBuilder()); + writer((BytesWriter) CharSequenceWriter.instance()); } else if (eClass == Void.class) { sizeMarshaller(VoidMarshaller.INSTANCE); reader((BytesReader) VoidMarshaller.INSTANCE); diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 3164a834c..3b7f70e95 100755 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -652,7 +652,7 @@ class VanillaChronicleMap, @Override public V newValueInstance() { - if (vClass.equals(CharSequence.class) || vClass.equals(StringBuilder.class)) { + if (vClass == CharSequence.class || vClass == StringBuilder.class) { return (V) new StringBuilder(); } @@ -2818,6 +2818,14 @@ class VanillaChronicleMap, @Override public void close() { + // TODO optimize -- keep replication bytes offset in SegmentState to jump directly + long pos = segmentState.pos; + long offset = segment.offsetFromPos(pos); + MultiStoreBytes entry = segment.reuse(segmentState.tmpBytes, offset); + long keySize = map.keySizeMarshaller.readSize(entry); + entry.skip(keySize); + segment.manageReplicationBytes(entry, true, false); + map.onPut(segment, pos); segmentState.close(); segment.writeUnlock(); } @@ -2849,9 +2857,25 @@ class VanillaChronicleMap, @Override public void close() { if (putOnClose) { - segment.putWithoutLock(copies, segmentState, - metaKeyInterop, keyInterop, key(), keySize, map.keyIdentity(), - map, value(), map.valueIdentity(), segmentHash, true, map, true); + // TODO optimize -- keep keySize, valueSizePos, entryEndAddr, etc. inside + // segmentState + long pos = segmentState.pos; + long offset = segment.offsetFromPos(pos); + MultiStoreBytes entry = segment.reuse(segmentState.tmpBytes, offset); + long keySize = map.keySizeMarshaller.readSize(entry); + entry.skip(keySize); + segment.manageReplicationBytes(entry, true, false); + long valueSizePos = entry.position(); + long valueSize = map.readValueSize(entry); + long entryEndAddr = entry.positionAddr() + valueSize; + VI valueInterop = map.valueInteropProvider.get(copies, map.originalValueInterop); + V value = value(); + MVI metaValueInterop = map.metaValueInteropProvider + .get(copies, map.originalMetaValueInterop, valueInterop, value); + long newValueSize = metaValueInterop.size(valueInterop, value); + segment.putValue(pos, offset, entry, valueSizePos, entryEndAddr, segmentState, + metaValueInterop, valueInterop, value, newValueSize, segment.hashLookup()); + map.onPut(segment, segmentState.pos); } putOnClose = true; segmentState.close(); diff --git a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java index 5683d0884..47f062435 100644 --- a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java +++ b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java @@ -460,10 +460,11 @@ public class CHMUseCasesTest { } } - @Ignore("HCOLL-248 Issue with acquireUsingLocked() for a StringBuilder Value") @Test public void testAcquireUsingWithIntValueKeyStringBuilderValue() throws IOException { + if (typeOfMap == TypeOfMap.STATELESS) + return; // acquireUsingLocked supported by the STATELESS client ChronicleMapBuilder builder = ChronicleMapBuilder .of(IntValue.class, StringBuilder.class); diff --git a/src/test/java/net/openhft/chronicle/map/TcpReplicationWithAcquireUsingLockedTest.java b/src/test/java/net/openhft/chronicle/map/TcpReplicationWithAcquireUsingLockedTest.java index d71615c9a..d802908fc 100644 --- a/src/test/java/net/openhft/chronicle/map/TcpReplicationWithAcquireUsingLockedTest.java +++ b/src/test/java/net/openhft/chronicle/map/TcpReplicationWithAcquireUsingLockedTest.java @@ -3,7 +3,6 @@ package net.openhft.chronicle.map; import eg.TestInstrumentVOInterface; import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import java.net.InetSocketAddress; @@ -14,7 +13,6 @@ import java.net.InetSocketAddress; public class TcpReplicationWithAcquireUsingLockedTest { - @Ignore("HCOLL-253 TcpReplication is not working when used with acquireUsingLocked") @Test public void testReplicationWithAcquireUsingLocked() throws Exception { @@ -31,10 +29,12 @@ public class TcpReplicationWithAcquireUsingLockedTest { map1a = map1; - TcpTransportAndNetworkConfig config2 = TcpTransportAndNetworkConfig.of(8077, new InetSocketAddress("127.0.0.1", 8076)); + TcpTransportAndNetworkConfig config2 = + TcpTransportAndNetworkConfig.of(8077, new InetSocketAddress("127.0.0.1", 8076)); - try (ChronicleMap map2 = ChronicleMapBuilder.of(CharSequence.class, + try (ChronicleMap map2 = + ChronicleMapBuilder.of(CharSequence.class, TestInstrumentVOInterface.class) .replication((byte) 2, config2) .create()) { @@ -45,9 +45,8 @@ public class TcpReplicationWithAcquireUsingLockedTest { TestInstrumentVOInterface instrumentVOInterface = map1.newValueInstance(); - try (WriteContext wc = map1 - .acquireUsingLocked - ("KEY1", instrumentVOInterface)) { + try (WriteContext wc = + map1.acquireUsingLocked("KEY1", instrumentVOInterface)) { instrumentVOInterface.setSymbol("Flyer"); instrumentVOInterface.setCurrencyCode("USA"); }