summaryrefslogtreecommitdiff
path: root/lang/src/main/java/net/openhft/lang/io
diff options
context:
space:
mode:
authorEmmanuel Bourg <ebourg@apache.org>2016-08-03 10:17:09 +0200
committerEmmanuel Bourg <ebourg@apache.org>2016-08-03 10:17:09 +0200
commit3d9b009255ffd897a9bd84ca3977cd3f553da8ef (patch)
tree13a8296e2989ff8da3fc50d335fd48b2dd0c6064 /lang/src/main/java/net/openhft/lang/io
parentb2ec1a2d459cfef3ff13133c1f7f5972e3740258 (diff)
Imported Upstream version 6.7.6
Diffstat (limited to 'lang/src/main/java/net/openhft/lang/io')
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/AbstractBytes.java1831
-rw-r--r--lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java269
-rw-r--r--lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java43
-rw-r--r--lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java53
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java292
-rw-r--r--lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java159
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java24
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/ByteStringParser.java88
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/Bytes.java31
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/BytesCommon.java183
-rw-r--r--lang/src/main/java/net/openhft/lang/io/BytesHasher.java37
-rw-r--r--lang/src/main/java/net/openhft/lang/io/BytesStore.java61
-rw-r--r--lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java122
-rw-r--r--lang/src/main/java/net/openhft/lang/io/CharBuffers.java28
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/CheckedBytes.java1313
-rw-r--r--lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java231
-rw-r--r--lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java83
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/DirectBytes.java55
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/DirectStore.java148
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java46
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java59
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java29
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/IOTools.java43
-rwxr-xr-x[-rw-r--r--]lang/src/main/java/net/openhft/lang/io/MappedFile.java105
-rw-r--r--lang/src/main/java/net/openhft/lang/io/MappedMemory.java82
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java730
-rw-r--r--lang/src/main/java/net/openhft/lang/io/MappedStore.java52
-rw-r--r--lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java61
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/MutableDecimal.java42
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/NativeBytes.java554
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/RandomDataInput.java297
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/RandomDataOutput.java270
-rw-r--r--lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java108
-rw-r--r--lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java56
-rw-r--r--lang/src/main/java/net/openhft/lang/io/Reuses.java34
-rw-r--r--lang/src/main/java/net/openhft/lang/io/SettableAtt.java21
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/StopCharTester.java28
-rw-r--r--[-rwxr-xr-x]lang/src/main/java/net/openhft/lang/io/StopCharTesters.java30
-rw-r--r--lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java84
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java89
-rw-r--r--lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java46
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java128
-rw-r--r--lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java137
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java141
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java161
-rw-r--r--lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java85
-rw-r--r--lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java26
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/WrappedBytes.java1056
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java22
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java196
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java49
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java26
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java35
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java38
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java71
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java23
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java65
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java104
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java29
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java89
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java22
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java63
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java50
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java72
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java68
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java71
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java103
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java72
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java46
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java72
-rwxr-xr-x[-rw-r--r--]lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java24
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java43
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java46
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java65
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java49
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java29
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java57
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java75
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java62
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java27
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java37
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java40
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java55
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java208
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java33
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java46
-rwxr-xr-xlang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java121
-rw-r--r--lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java85
-rw-r--r--lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java119
-rw-r--r--lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java109
90 files changed, 10667 insertions, 1700 deletions
diff --git a/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java b/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java
index e167835..15a0279 100755
--- a/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java
+++ b/lang/src/main/java/net/openhft/lang/io/AbstractBytes.java
@@ -1,66 +1,76 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
import net.openhft.lang.Jvm;
import net.openhft.lang.Maths;
-import net.openhft.lang.io.serialization.BytesMarshallable;
-import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.io.serialization.BytesMarshallableSerializer;
import net.openhft.lang.io.serialization.BytesMarshallerFactory;
-import net.openhft.lang.io.serialization.CompactBytesMarshaller;
-import net.openhft.lang.io.serialization.impl.NoMarshaller;
+import net.openhft.lang.io.serialization.JDKZObjectSerializer;
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.io.serialization.impl.StringBuilderPool;
import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
+import net.openhft.lang.io.view.BytesInputStream;
+import net.openhft.lang.io.view.BytesOutputStream;
+import net.openhft.lang.model.Byteable;
+import net.openhft.lang.pool.EnumInterner;
import net.openhft.lang.pool.StringInterner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
+import java.lang.reflect.Field;
import java.math.BigInteger;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
+import static java.lang.Long.numberOfTrailingZeros;
+
/**
* @author peter.lawrey
*/
@SuppressWarnings("MagicNumber")
public abstract class AbstractBytes implements Bytes {
- public static final long BUSY_LOCK_LIMIT = 10L * 1000 * 1000 * 1000;
- public static final int INT_LOCK_MASK = 0xFFFFFF;
- public static final int UNSIGNED_BYTE_MASK = 0xFF;
- public static final int UNSIGNED_SHORT_MASK = 0xFFFF;
+ public static final int END_OF_BUFFER = -1;
public static final long UNSIGNED_INT_MASK = 0xFFFFFFFFL;
+ public static final int SLEEP_THRESHOLD = 20 * 1000 * 1000;
+ // todo add tests before using in ChronicleMap
+ static final int RW_LOCK_LIMIT = 30;
+ static final long RW_READ_LOCKED = 1L << 0;
+ static final long RW_WRITE_WAITING = 1L << RW_LOCK_LIMIT;
+ static final long RW_WRITE_LOCKED = 1L << 2 * RW_LOCK_LIMIT;
+ static final int RW_LOCK_MASK = (1 << RW_LOCK_LIMIT) - 1;
+ static final char[] HEXI_DECIMAL = "0123456789ABCDEF".toCharArray();
+ private static final long BUSY_LOCK_LIMIT = 20L * 1000 * 1000 * 1000;
+ private static final int INT_LOCK_MASK;
+ private static final int UNSIGNED_BYTE_MASK = 0xFF;
+ private static final int UNSIGNED_SHORT_MASK = 0xFFFF;
+ private static final int USHORT_EXTENDED = UNSIGNED_SHORT_MASK;
// extra 1 for decimal place.
- static final int MAX_NUMBER_LENGTH = 1 + (int) Math.ceil(Math.log10(Long.MAX_VALUE));
- static final byte[] RADIX_PARSE = new byte[256];
-
- static {
- Arrays.fill(RADIX_PARSE, (byte) -1);
- for (int i = 0; i < 10; i++)
- RADIX_PARSE['0' + i] = (byte) i;
- for (int i = 0; i < 26; i++)
- RADIX_PARSE['A' + i] = RADIX_PARSE['a' + i] = (byte) (i + 10);
- }
-
- private static final Logger LOGGER = Logger.getLogger(AbstractBytes.class.getName());
+ private static final int MAX_NUMBER_LENGTH = 1 + (int) Math.ceil(Math.log10(Long.MAX_VALUE));
+ private static final byte[] RADIX_PARSE = new byte[256];
+ //private static final Logger LOGGER = Logger.getLogger(AbstractBytes.class.getName());
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private static final byte[] MIN_VALUE_TEXT = ("" + Long.MIN_VALUE).getBytes();
private static final byte[] Infinity = "Infinity".getBytes();
@@ -73,39 +83,49 @@ public abstract class AbstractBytes implements Bytes {
private static final short SHORT_MIN_VALUE = Short.MIN_VALUE;
private static final short SHORT_EXTENDED = Short.MIN_VALUE + 1;
private static final short SHORT_MAX_VALUE = Short.MIN_VALUE + 2;
- private static final int USHORT_EXTENDED = UNSIGNED_SHORT_MASK;
// RandomDataInput
private static final int INT_MIN_VALUE = Integer.MIN_VALUE;
private static final int INT_EXTENDED = Integer.MIN_VALUE + 1;
private static final int INT_MAX_VALUE = Integer.MIN_VALUE + 2;
private static final long MAX_VALUE_DIVIDE_10 = Long.MAX_VALUE / 10;
- private static final byte NULL = 'N';
- private static final byte ENUMED = 'E';
- private static final byte SERIALIZED = 'S';
private static final byte[] RADIX = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();
- static boolean ID_LIMIT_WARNED = false;
+ private static final StringBuilderPool sbp = new StringBuilderPool();
+ private static final ThreadLocal<DateCache> dateCacheTL = new ThreadLocal<DateCache>();
+ private static final FastStringOperations STRING_OPS = createFastStringOperations();
+ private static boolean ID_LIMIT_WARNED = false;
+
+ static {
+ Arrays.fill(RADIX_PARSE, (byte) -1);
+ for (int i = 0; i < 10; i++)
+ RADIX_PARSE['0' + i] = (byte) i;
+ for (int i = 0; i < 26; i++)
+ RADIX_PARSE['A' + i] = RADIX_PARSE['a' + i] = (byte) (i + 10);
+ INT_LOCK_MASK = 0xFFFFFF;
+ }
+
+ final AtomicInteger refCount;
private final byte[] numberBuffer = new byte[MAX_NUMBER_LENGTH];
protected boolean finished;
- protected BytesMarshallerFactory bytesMarshallerFactory;
+ volatile Thread singleThread = null;
+ private ObjectSerializer objectSerializer;
private StringInterner stringInterner = null;
- private BytesInputStream inputStream = null;
- private BytesOutputStream outputStream = null;
- private StringBuilder utfReader = null;
- private SimpleDateFormat dateFormat = null;
- private long lastDay = Long.MIN_VALUE;
- @Nullable
- private byte[] lastDateStr = null;
+ private boolean selfTerminating = false;
- protected AbstractBytes() {
- this(new VanillaBytesMarshallerFactory());
+ AbstractBytes() {
+ this(new VanillaBytesMarshallerFactory(), new AtomicInteger(1));
}
- protected AbstractBytes(BytesMarshallerFactory bytesMarshallerFactory) {
+ AbstractBytes(BytesMarshallerFactory bytesMarshallerFactory, AtomicInteger refCount) {
+ this(BytesMarshallableSerializer.create(bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE), refCount);
+ }
+
+ AbstractBytes(ObjectSerializer objectSerializer, AtomicInteger refCount) {
this.finished = false;
- this.bytesMarshallerFactory = bytesMarshallerFactory;
+ this.refCount = refCount;
+ setObjectSerializer(objectSerializer);
}
- static boolean equalsCaseIgnore(StringBuilder sb, String s) {
+ private static boolean equalsCaseIgnore(StringBuilder sb, String s) {
if (sb.length() != s.length())
return false;
for (int i = 0; i < s.length(); i++)
@@ -171,21 +191,490 @@ public abstract class AbstractBytes implements Bytes {
}
private static void warnIdLimit(long id) {
- LOGGER.log(Level.WARNING, "High thread id may result in collisions id: " + id);
+ LoggerHolder.LOGGER.log(Level.WARNING, "High thread id may result in collisions id: " + id);
ID_LIMIT_WARNED = true;
}
- protected StringInterner stringInterner() {
+ static int returnOrThrowEndOfBuffer(boolean selfTerminating) {
+ if (selfTerminating) return END_OF_BUFFER;
+ throw new BufferUnderflowException();
+ }
+
+ public static void readUTF2(Bytes bytes, @NotNull Appendable appendable, int utflen, int count) throws IOException {
+ while (count < utflen) {
+ int c = bytes.readUnsignedByte();
+ switch (c >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* 0xxxxxxx */
+ count++;
+ appendable.append((char) c);
+ break;
+
+ case 12:
+ case 13: {
+ /* 110x xxxx 10xx xxxx */
+ count += 2;
+ if (count > utflen)
+ throw new UTFDataFormatException(
+ "malformed input: partial character at end");
+ int char2 = bytes.readUnsignedByte();
+ if ((char2 & 0xC0) != 0x80)
+ throw new UTFDataFormatException(
+ "malformed input around byte " + count + " was " + char2);
+ int c2 = (char) (((c & 0x1F) << 6) |
+ (char2 & 0x3F));
+ appendable.append((char) c2);
+ break;
+ }
+
+ case 14: {
+ /* 1110 xxxx 10xx xxxx 10xx xxxx */
+ count += 3;
+ if (count > utflen)
+ throw new UTFDataFormatException(
+ "malformed input: partial character at end");
+ int char2 = bytes.readUnsignedByte();
+ int char3 = bytes.readUnsignedByte();
+
+ if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
+ throw new UTFDataFormatException(
+ "malformed input around byte " + (count - 1) + " was " + char2 + " " + char3);
+ int c3 = (char) (((c & 0x0F) << 12) |
+ ((char2 & 0x3F) << 6) |
+ (char3 & 0x3F));
+ appendable.append((char) c3);
+ break;
+ }
+
+ default:
+ /* 10xx xxxx, 1111 xxxx */
+ throw new UTFDataFormatException(
+ "malformed input around byte " + count);
+ }
+ }
+ }
+
+ public static long findUTFLength(@NotNull CharSequence str) {
+ if (str instanceof String)
+ return findUTFLength((String) str);
+ int strlen = str.length();
+ long utflen = strlen;
+
+ for (int i = 0; i < strlen; i++) {
+ char c = str.charAt(i);
+ if ((c > 0x007F)) {
+ if (c > 0x07FF) {
+ utflen += 2;
+
+ } else {
+ utflen += 1;
+ }
+ }
+ }
+ return utflen;
+ }
+
+ public static long findUTFLength(@NotNull CharSequence str, int strlen) {
+ long utflen = 0L;
+
+ for (int i = 0; i < strlen; ++i) {
+ long c = (long) str.charAt(i);
+ if (c >= 0L && c <= 127L) {
+ ++utflen;
+ } else if (c > 2047L) {
+ utflen += 3L;
+ } else {
+ utflen += 2L;
+ }
+ }
+
+ return utflen;
+ }
+
+ public static long findUTFLength(@NotNull String str) {
+ return STRING_OPS.getUtf8EncodedStringLength(str);
+ }
+
+ private static FastStringOperations createFastStringOperations() {
+ try {
+ return new FastStringOperations16();
+ } catch (Exception e) {
+ // do nothing
+ }
+
+ try {
+ return new FastStringOperations17();
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static void writeUTF0(Bytes bytes, @NotNull CharSequence str, int strlen) {
+ if (bytes instanceof DirectBytes) {
+ DirectBytes db = (DirectBytes) bytes;
+ if (str instanceof String)
+ writeUTF1(db, STRING_OPS.extractChars((String) str), strlen);
+ else
+ writeUTF1(db, str, strlen);
+ } else {
+ writeUTF1(bytes, str, strlen);
+ }
+ }
+
+ static void writeUTF1(DirectBytes bytes, @NotNull CharSequence str, int strlen) {
+ int c;
+ int i;
+ for (i = 0; i < strlen; i++) {
+ c = str.charAt(i);
+ if (!((c >= 0x0000) && (c <= 0x007F)))
+// if (c + Integer.MIN_VALUE - 1 <= Integer.MIN_VALUE + 0x007F-1)
+ break;
+ NativeBytes.UNSAFE.putByte(bytes.positionAddr + i, (byte) c);
+ }
+ bytes.skip(i);
+ if (i < strlen)
+ writeUTF2(bytes, str, strlen, i);
+ }
+
+ static void writeUTF1(DirectBytes bytes, @NotNull char[] chars, int strlen) {
+ int c;
+ int i;
+ ascii:
+ {
+ for (i = 0; i < strlen; i++) {
+ c = chars[i];
+ if (!((c >= 0x0000) && (c <= 0x007F)))
+ break ascii;
+ NativeBytes.UNSAFE.putByte(bytes.positionAddr + i, (byte) c);
+ }
+ bytes.skip(i);
+ return;
+ }
+ bytes.skip(i);
+ if (i < strlen)
+ writeUTF2(bytes, chars, strlen, i);
+ }
+
+ public static void writeUTF1(Bytes bytes, @NotNull CharSequence str, int strlen) {
+ int c;
+ int i;
+ for (i = 0; i < strlen; i++) {
+ c = str.charAt(i);
+ if (!((c >= 0x0000) && (c <= 0x007F)))
+ break;
+ bytes.write(c);
+ }
+ if (i < strlen)
+ writeUTF2(bytes, str, strlen, i);
+ }
+
+ private static void writeUTF2(Bytes bytes, CharSequence str, int strlen, int i) {
+ int c;
+ for (; i < strlen; i++) {
+ c = str.charAt(i);
+ writeUTFchar(bytes, c);
+ }
+ }
+
+ private static void writeUTFchar(Bytes bytes, int c) {
+ if ((c >= 0x0000) && (c <= 0x007F)) {
+ bytes.write(c);
+
+ } else if (c > 0x07FF) {
+ bytes.write((byte) (0xE0 | ((c >> 12) & 0x0F)));
+ bytes.write((byte) (0x80 | ((c >> 6) & 0x3F)));
+ bytes.write((byte) (0x80 | (c & 0x3F)));
+
+ } else {
+ bytes.write((byte) (0xC0 | ((c >> 6) & 0x1F)));
+ bytes.write((byte) (0x80 | c & 0x3F));
+ }
+ }
+
+ private static void writeUTF2(Bytes bytes, char[] str, int strlen, int i) {
+ int c;
+ for (; i < strlen; i++) {
+ c = str[i];
+ writeUTFchar(bytes, c);
+ }
+ }
+
+ static void checkArrayOffs(int arrayLength, int off, int len) {
+ if ((len | off) < 0 | ((off + len) & 0xffffffffL) > arrayLength)
+ throw new IndexOutOfBoundsException();
+ }
+
+ /**
+ * display the hex data of {@link Bytes} from the position() to the limit()
+ *
+ * @param buffer the buffer you wish to toString()
+ * @return hex representation of the buffer, from example [0D ,OA, FF]
+ */
+ public static String toHex(@NotNull final Bytes buffer) {
+ if (buffer.remaining() == 0)
+ return "";
+
+ final Bytes slice = buffer.slice();
+ final StringBuilder builder = new StringBuilder("[");
+
+ while (slice.remaining() > 0) {
+ final byte b = slice.readByte();
+ builder.append(((char) b) + "(" + String.format("%02X ", b).trim() + ")");
+ builder.append(",");
+ }
+
+ // remove the last comma
+ builder.deleteCharAt(builder.length() - 1);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * display the buffer as a string
+ *
+ * @param buffer the buffer you wish to toString()
+ * @return hex representation of the buffer, from example [0D ,OA, FF]
+ */
+ public static String toString(@NotNull final Bytes buffer) {
+ final Bytes slice = buffer.slice();
+ final StringBuilder builder = new StringBuilder("");
+
+ while (slice.remaining() > 0) {
+ final byte b = slice.readByte();
+ builder.append((char) b);
+ }
+ return builder.toString();
+ }
+
+ static int rwReadLocked(long lock) {
+ return (int) (lock & RW_LOCK_MASK);
+ }
+
+ static int rwWriteWaiting(long lock) {
+ return (int) ((lock >>> RW_LOCK_LIMIT) & RW_LOCK_MASK);
+ }
+
+ static int rwWriteLocked(long lock) {
+ return (int) (lock >>> (2 * RW_LOCK_LIMIT));
+ }
+
+ public static String toHexString(@NotNull final Bytes bytes, long offset, long len) throws BufferUnderflowException {
+ if (len == 0)
+ return "";
+
+ int width = 16;
+ int[] lastLine = new int[width];
+ String sep = "";
+ long position = bytes.position();
+ long limit = bytes.limit();
+
+ try {
+
+ bytes.limit(offset + len);
+ bytes.position(offset);
+
+ final StringBuilder builder = new StringBuilder();
+ long start = offset / width * width;
+ long end = (offset + len + width - 1) / width * width;
+ for (long i = start; i < end; i += width) {
+ // check for duplicate rows
+ if (i + width < end) {
+ boolean same = true;
+
+ for (int j = 0; j < width && i + j < offset + len; j++) {
+ int ch = bytes.readUnsignedByte(i + j);
+ same &= (ch == lastLine[j]);
+ lastLine[j] = ch;
+ }
+ if (i > start && same) {
+ sep = "........\n";
+ continue;
+ }
+ }
+ builder.append(sep);
+ sep = "";
+ String str = Long.toHexString(i);
+ for (int j = str.length(); j < 8; j++)
+ builder.append('0');
+ builder.append(str);
+ for (int j = 0; j < width; j++) {
+ if (j == width / 2)
+ builder.append(' ');
+ if (i + j < start || i + j >= offset + len) {
+ builder.append(" ");
+
+ } else {
+ builder.append(' ');
+ int ch = bytes.readUnsignedByte(i + j);
+ builder.append(HEXI_DECIMAL[ch >> 4]);
+ builder.append(HEXI_DECIMAL[ch & 15]);
+ }
+ }
+ builder.append(' ');
+ for (int j = 0; j < width; j++) {
+ if (j == width / 2)
+ builder.append(' ');
+ if (i + j < start || i + j >= offset + len) {
+ builder.append(' ');
+
+ } else {
+ int ch = bytes.readUnsignedByte(i + j);
+ if (ch < ' ' || ch > 126)
+ ch = '\u00B7';
+ builder.append((char) ch);
+ }
+ }
+ builder.append("\n");
+ }
+ return builder.toString();
+ } finally {
+ bytes.limit(limit);
+ bytes.position(position);
+ }
+ }
+
+ protected void setObjectSerializer(ObjectSerializer objectSerializer) {
+ this.objectSerializer = objectSerializer;
+ }
+
+ /**
+ * clearing the volatile singleThread is a write barrier.
+ */
+ @Override
+ public void clearThreadAssociation() {
+ singleThread = null;
+ }
+
+ boolean checkSingleThread() {
+ Thread t = Thread.currentThread();
+ if (singleThread != t)
+ setThreadOrThrowException(t);
+ return true;
+ }
+
+ private void setThreadOrThrowException(Thread t) {
+ if (singleThread == null)
+ singleThread = t;
+ else
+ throw new IllegalStateException("Altered by thread " + singleThread + " and " + t);
+ }
+
+ public void readUTF0(@NotNull Appendable appendable, int utflen)
+ throws IOException {
+ int count = 0;
+
+ while (count < utflen) {
+ int c = readUnsignedByteOrThrow();
+ if (c >= 128) {
+ position(position() - 1);
+ readUTF2(this, appendable, utflen, count);
+ break;
+ }
+ count++;
+ appendable.append((char) c);
+ }
+ }
+
+ @Override
+ public boolean read8bitText(@NotNull StringBuilder stringBuilder) throws StreamCorruptedException {
+ long len = readStopBit();
+ if (len < 1) {
+ stringBuilder.setLength(0);
+ if (len == -1)
+ return false;
+ if (len == 0)
+ return true;
+ throw new StreamCorruptedException("UTF length invalid " + len + " remaining: " + remaining());
+ }
+ if (len > remaining() || len > Integer.MAX_VALUE)
+ throw new StreamCorruptedException("UTF length invalid " + len + " remaining: " + remaining());
+ int ilen = (int) len;
+ for (int i = 0; i < ilen; i++)
+ stringBuilder.append((char) readUnsignedByte());
+ return true;
+ }
+
+ @Override
+ public void write8bitText(@Nullable CharSequence s) {
+ if (s == null) {
+ writeStopBit(-1);
+ return;
+ }
+ writeStopBit(s.length());
+ for (int i = 0; i < s.length(); i++)
+ writeUnsignedByte(s.charAt(i));
+ }
+
+ @Override
+ public long size() {
+ return capacity();
+ }
+
+ @Override
+ public void free() {
+ throw new UnsupportedOperationException("Forcing a free() via Bytes is unsafe, try reserve() + release()");
+ }
+
+ @Override
+ public void reserve() {
+ if (refCount.get() < 1) throw new IllegalStateException();
+ refCount.incrementAndGet();
+ }
+
+ @Override
+ public boolean release() {
+ if (refCount.get() < 1) throw new IllegalStateException();
+ if (refCount.decrementAndGet() > 0) return false;
+ cleanup();
+ return true;
+ }
+
+ protected abstract void cleanup();
+
+ @Override
+ public int refCount() {
+ return refCount.get();
+ }
+
+ StringInterner stringInterner() {
if (stringInterner == null)
stringInterner = new StringInterner(8 * 1024);
return stringInterner;
}
@Override
+ public void selfTerminating(boolean selfTerminating) {
+ this.selfTerminating = selfTerminating;
+ }
+
+ // RandomDataOutput
+
+ @Override
+ public boolean selfTerminating() {
+ return selfTerminating;
+ }
+
+ @Override
+ public int readUnsignedByteOrThrow() throws BufferUnderflowException {
+ return readByteOrThrow(selfTerminating);
+ }
+
+ public int readByteOrThrow(boolean selfTerminating) throws BufferUnderflowException {
+ return remaining() < 1 ? returnOrThrowEndOfBuffer(selfTerminating) : readUnsignedByte();
+ }
+
+ @Override
public Boolean parseBoolean(@NotNull StopCharTester tester) {
- StringBuilder sb = acquireUtfReader();
- parseUTF(sb, tester);
+ StringBuilder sb = acquireStringBuilder();
+ parseUtf8(sb, tester);
if (sb.length() == 0)
return null;
switch (sb.charAt(0)) {
@@ -215,16 +704,18 @@ public abstract class AbstractBytes implements Bytes {
}
@Override
+ public void readFully(@NotNull char[] data) {
+ readFully(data, 0, data.length);
+ }
+
+ @Override
public int skipBytes(int n) {
- long position = position();
- int n2 = (int) Math.min(n, capacity() - position);
- position(position + n2);
- return n2;
+ return (int) skip(n);
}
@Override
public boolean readBoolean() {
- return readByte() != 0;
+ return readByteOrThrow(false) != 0;
}
@Override
@@ -255,11 +746,12 @@ public abstract class AbstractBytes implements Bytes {
@NotNull
@Override
public String readLine() {
- StringBuilder input = acquireUtfReader();
+ StringBuilder input = acquireStringBuilder();
EOL:
while (position() < capacity()) {
- int c = readUnsignedByte();
+ int c = readUnsignedByteOrThrow();
switch (c) {
+ case END_OF_BUFFER:
case '\n':
break EOL;
case '\r':
@@ -278,14 +770,17 @@ public abstract class AbstractBytes implements Bytes {
@Nullable
@Override
public String readUTFΔ() {
- if (readUTFΔ(acquireUtfReader()))
+ StringBuilder utfReader = acquireStringBuilder();
+ if (readUTFΔ(utfReader))
return utfReader.length() == 0 ? "" : stringInterner().intern(utfReader);
return null;
}
+ // locking at it temporarily changes position.
+ // todo write a version without changing the position.
@Nullable
@Override
- public String readUTFΔ(long offset) throws IllegalStateException {
+ public synchronized String readUTFΔ(long offset) throws IllegalStateException {
long position = position();
try {
position(offset);
@@ -295,15 +790,9 @@ public abstract class AbstractBytes implements Bytes {
}
}
- // RandomDataOutput
-
@NotNull
- private StringBuilder acquireUtfReader() {
- if (utfReader == null)
- utfReader = new StringBuilder();
- else
- utfReader.setLength(0);
- return utfReader;
+ private StringBuilder acquireStringBuilder() {
+ return sbp.acquireStringBuilder();
}
@Override
@@ -319,93 +808,27 @@ public abstract class AbstractBytes implements Bytes {
@SuppressWarnings("MagicNumber")
private boolean appendUTF0(@NotNull Appendable appendable) throws IOException {
long len = readStopBit();
- if (len < -1 || len > Integer.MAX_VALUE)
- throw new StreamCorruptedException("UTF length invalid " + len);
if (len == -1)
return false;
+ else if (len == 0)
+ return true;
+ if (len < -1 || len > remaining())
+ throw new StreamCorruptedException("UTF length invalid " + len + " remaining: " + remaining());
int utflen = (int) len;
readUTF0(appendable, utflen);
return true;
}
- private void readUTF0(@NotNull Appendable appendable, int utflen) throws IOException {
- int count = 0;
- while (count < utflen) {
- int c = readByte();
- if (c < 0) {
- position(position() - 1);
- break;
- }
- count++;
- appendable.append((char) c);
- }
-
- while (count < utflen) {
- int c = readUnsignedByte();
- switch (c >> 4) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- /* 0xxxxxxx */
- count++;
- appendable.append((char) c);
- break;
- case 12:
- case 13: {
- /* 110x xxxx 10xx xxxx */
- count += 2;
- if (count > utflen)
- throw new UTFDataFormatException(
- "malformed input: partial character at end");
- int char2 = readUnsignedByte();
- if ((char2 & 0xC0) != 0x80)
- throw new UTFDataFormatException(
- "malformed input around byte " + count);
- int c2 = (char) (((c & 0x1F) << 6) |
- (char2 & 0x3F));
- appendable.append((char) c2);
- break;
- }
- case 14: {
- /* 1110 xxxx 10xx xxxx 10xx xxxx */
- count += 3;
- if (count > utflen)
- throw new UTFDataFormatException(
- "malformed input: partial character at end");
- int char2 = readUnsignedByte();
- int char3 = readUnsignedByte();
-
- if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
- throw new UTFDataFormatException(
- "malformed input around byte " + (count - 1));
- int c3 = (char) (((c & 0x0F) << 12) |
- ((char2 & 0x3F) << 6) |
- (char3 & 0x3F));
- appendable.append((char) c3);
- break;
- }
- default:
- /* 10xx xxxx, 1111 xxxx */
- throw new UTFDataFormatException(
- "malformed input around byte " + count);
- }
- }
- }
-
@NotNull
@Override
- public String parseUTF(@NotNull StopCharTester tester) {
- parseUTF(acquireUtfReader(), tester);
+ public String parseUtf8(@NotNull StopCharTester tester) {
+ StringBuilder utfReader = acquireStringBuilder();
+ parseUtf8(utfReader, tester);
return stringInterner().intern(utfReader);
}
@Override
- public void parseUTF(@NotNull StringBuilder builder, @NotNull StopCharTester tester) {
+ public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) {
builder.setLength(0);
try {
readUTF0(builder, tester);
@@ -415,19 +838,23 @@ public abstract class AbstractBytes implements Bytes {
}
private void readUTF0(@NotNull Appendable appendable, @NotNull StopCharTester tester) throws IOException {
- while (remaining() > 0) {
- int c = readByte();
- if (c < 0) {
+ while (true) {
+ int c = readUnsignedByteOrThrow();
+ if (c >= 128) {
position(position() - 1);
+ readUTF2(appendable, tester);
break;
}
if (tester.isStopChar(c))
return;
appendable.append((char) c);
}
+ }
- while (remaining() > 0) {
- int c = readUnsignedByte();
+ // used by Chronicle Map 3.x
+ public void readUTF2(@NotNull Appendable appendable, @NotNull StopCharTester tester) throws IOException {
+ while (true) {
+ int c = readUnsignedByteOrThrow();
switch (c >> 4) {
case 0:
case 1:
@@ -442,6 +869,7 @@ public abstract class AbstractBytes implements Bytes {
return;
appendable.append((char) c);
break;
+
case 12:
case 13: {
/* 110x xxxx 10xx xxxx */
@@ -456,9 +884,9 @@ public abstract class AbstractBytes implements Bytes {
appendable.append((char) c2);
break;
}
+
case 14: {
/* 1110 xxxx 10xx xxxx 10xx xxxx */
-
int char2 = readUnsignedByte();
int char3 = readUnsignedByte();
@@ -473,6 +901,7 @@ public abstract class AbstractBytes implements Bytes {
appendable.append((char) c3);
break;
}
+
default:
/* 10xx xxxx, 1111 xxxx */
throw new UTFDataFormatException(
@@ -491,7 +920,7 @@ public abstract class AbstractBytes implements Bytes {
@Override
public boolean skipTo(@NotNull StopCharTester tester) {
while (remaining() > 0) {
- int ch = readByte();
+ int ch = readUnsignedByteOrThrow();
if (tester.isStopChar(ch))
return true;
}
@@ -503,7 +932,8 @@ public abstract class AbstractBytes implements Bytes {
public String readUTF() {
try {
int len = readUnsignedShort();
- readUTF0(acquireUtfReader(), len);
+ StringBuilder utfReader = acquireStringBuilder();
+ readUTF0(utfReader, len);
return utfReader.length() == 0 ? "" : stringInterner().intern(utfReader);
} catch (IOException unexpected) {
throw new AssertionError(unexpected);
@@ -622,16 +1052,47 @@ public abstract class AbstractBytes implements Bytes {
}
@Override
+ public long readIncompleteLong(long offset) {
+ long left = remaining();
+ if (left >= 8)
+ return readLong(offset);
+ if (left == 4)
+ return readInt(offset);
+ long l = 0;
+ for (int i = 0, remaining = (int) left; i < remaining; i++) {
+ l |= (long) readUnsignedByte(offset + i) << (i * 8);
+ }
+ return l;
+ }
+
+ @Override
public long readStopBit() {
- long l = 0, b;
- int count = 0;
+ long l;
+ if ((l = readByte()) >= 0)
+ return l;
+ return readStopBit0(l);
+ }
+
+ private long readStopBit0(long l) {
+ l &= 0x7FL;
+ long b;
+ int count = 7;
while ((b = readByte()) < 0) {
l |= (b & 0x7FL) << count;
count += 7;
}
- if (b == 0 && count > 0)
+ if (b != 0) {
+ if (count > 56)
+ throw new IllegalStateException(
+ "Cannot read more than 9 stop bits of positive value");
+ return l | (b << count);
+
+ } else {
+ if (count > 63)
+ throw new IllegalStateException(
+ "Cannot read more than 10 stop bits of negative value");
return ~l;
- return l | (b << count);
+ }
}
@Override
@@ -644,7 +1105,12 @@ public abstract class AbstractBytes implements Bytes {
@Override
public void read(@NotNull ByteBuffer bb) {
- int len = (int) Math.min(bb.remaining(), remaining());
+ read(bb, bb.remaining());
+ }
+
+ @Override
+ public void read(@NotNull ByteBuffer bb, int length) {
+ int len = (int) Math.min(length, remaining());
if (bb.order() == byteOrder()) {
while (len >= 8) {
bb.putLong(readLong());
@@ -660,24 +1126,22 @@ public abstract class AbstractBytes implements Bytes {
// // RandomOutputStream
@Override
public void write(@NotNull byte[] bytes) {
- int length = bytes.length;
- checkWrite(length);
- write(bytes, 0, length);
+ write(bytes, 0, bytes.length);
}
- private void checkWrite(int length) {
+ private void checkWrite(long length) {
if (length > remaining())
throw new IllegalStateException("Cannot write " + length + " only " + remaining() + " remaining");
}
@Override
public void writeBoolean(boolean v) {
- write(v ? -1 : 0);
+ write(v ? 'Y' : 0);
}
@Override
public void writeBoolean(long offset, boolean v) {
- writeByte(offset, v ? -1 : 0);
+ writeByte(offset, v ? 'Y' : 0);
}
@Override
@@ -689,50 +1153,58 @@ public abstract class AbstractBytes implements Bytes {
@Override
public void writeChars(@NotNull String s) {
- int len = s.length();
+ writeChars((CharSequence) s);
+ }
+
+ @Override
+ public void writeChars(@NotNull CharSequence cs) {
+ int len = cs.length();
for (int i = 0; i < len; i++)
- writeChar(s.charAt(i));
+ writeChar(cs.charAt(i));
}
@Override
public void writeUTF(@NotNull String str) {
- long strlen = str.length();
- long utflen = findUTFLength(str, strlen);
+ long utflen = findUTFLength(str);
if (utflen > 65535)
throw new IllegalStateException("String too long " + utflen + " when encoded, max: 65535");
writeUnsignedShort((int) utflen);
- writeUTF0(str, strlen);
+ checkUFTLength(utflen);
+ writeUTF0(this, str, str.length());
}
@Override
- public void writeUTFΔ(@Nullable CharSequence str) {
+ public void writeUTFΔ(@Nullable CharSequence str) throws IllegalArgumentException {
if (str == null) {
writeStopBit(-1);
return;
}
- long strlen = str.length();
- long utflen = findUTFLength(str, strlen);
+ long utflen = findUTFLength(str);
writeStopBit(utflen);
- writeUTF0(str, strlen);
+ checkUFTLength(utflen);
+ writeUTF0(this, str, str.length());
}
+ // locking at it temporarily changes position.
+ // todo write a version without changing the position.
@Override
- public void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException {
+ public synchronized void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException {
assert maxSize > 1;
- if (s == null) {
- writeStopBit(-1);
- return;
- }
- long strlen = s.length();
- long utflen = findUTFLength(s, strlen);
- long totalSize = IOTools.stopBitLength(utflen) + utflen;
- if (totalSize > maxSize)
- throw new IllegalStateException("Attempted to write " + totalSize + " byte String, when only " + maxSize + " allowed");
long position = position();
try {
position(offset);
+ if (s == null) {
+ writeStopBit(-1);
+ return;
+ }
+ long utflen = findUTFLength(s);
+ long totalSize = IOTools.stopBitLength(utflen) + utflen;
+ if (totalSize > maxSize)
+ throw new IllegalStateException("Attempted to write " + totalSize + " byte String, when only " + maxSize + " allowed");
+
writeStopBit(utflen);
- writeUTF0(s, strlen);
+ writeUTF0(this, s, s.length());
+ zeroOut(position(), offset + maxSize);
} finally {
position(position);
}
@@ -740,56 +1212,15 @@ public abstract class AbstractBytes implements Bytes {
@NotNull
public ByteStringAppender append(@NotNull CharSequence str) {
- if (str == null)
- return this;
- long strlen = str.length();
- writeUTF0(str, strlen);
+ int strlen = str.length();
+ writeUTF0(this, str, strlen);
return this;
}
- private long findUTFLength(@NotNull CharSequence str, long strlen) {
- long utflen = 0, c;/* use charAt instead of copying String to char array */
- for (int i = 0; i < strlen; i++) {
- c = str.charAt(i);
- if ((c >= 0x0000) && (c <= 0x007F)) {
- utflen++;
- } else if (c > 0x07FF) {
- utflen += 3;
- } else {
- utflen += 2;
- }
- }
-
+ private void checkUFTLength(long utflen) throws IllegalArgumentException {
if (utflen > remaining())
throw new IllegalArgumentException(
"encoded string too long: " + utflen + " bytes, remaining=" + remaining());
- return utflen;
- }
-
- private void writeUTF0(@NotNull CharSequence str, long strlen) {
- int c;
- int i;
- for (i = 0; i < strlen; i++) {
- c = str.charAt(i);
- if (!((c >= 0x0000) && (c <= 0x007F)))
- break;
- write(c);
- }
-
- for (; i < strlen; i++) {
- c = str.charAt(i);
- if ((c >= 0x0000) && (c <= 0x007F)) {
- write(c);
-
- } else if (c > 0x07FF) {
- write((byte) (0xE0 | ((c >> 12) & 0x0F)));
- write((byte) (0x80 | ((c >> 6) & 0x3F)));
- write((byte) (0x80 | (c & 0x3F)));
- } else {
- write((byte) (0xC0 | ((c >> 6) & 0x1F)));
- write((byte) (0x80 | c & 0x3F));
- }
- }
}
@Override
@@ -815,14 +1246,49 @@ public abstract class AbstractBytes implements Bytes {
}
@Override
+ public void write(long offset, Bytes bytes) {
+ long length = bytes.remaining();
+ checkWrite(length);
+ long i;
+ for (i = 0; i < length - 7; i += 8)
+ writeLong(offset + i, bytes.readLong());
+ for (; i < length; i++)
+ writeByte(offset + i, bytes.readByte());
+ }
+
+ @Override
public void write(byte[] bytes, int off, int len) {
- checkWrite(bytes.length);
+ checkArrayOffs(bytes.length, off, len);
+ checkWrite(len);
for (int i = 0; i < len; i++)
write(bytes[off + i]);
}
@Override
+ public void write(long offset, byte[] bytes, int off, int len) {
+ checkArrayOffs(bytes.length, off, len);
+ checkWrite(len);
+
+ for (int i = 0; i < len; i++)
+ writeByte(offset + i, bytes[off + i]);
+ }
+
+ @Override
+ public void write(@NotNull char[] data) {
+ write(data, 0, data.length);
+ }
+
+ @Override
+ public void write(@NotNull char[] data, int off, int len) {
+ checkArrayOffs(data.length, off, len);
+ checkWrite(len * 2);
+
+ for (int i = 0; i < len; i++)
+ writeChar(data[off + i]);
+ }
+
+ @Override
public void writeUnsignedShort(int v) {
writeShort(v);
}
@@ -841,6 +1307,7 @@ public abstract class AbstractBytes implements Bytes {
case Short.MIN_VALUE:
writeByte(BYTE_MIN_VALUE);
break;
+
case Short.MAX_VALUE:
writeByte(BYTE_MAX_VALUE);
break;
@@ -855,6 +1322,7 @@ public abstract class AbstractBytes implements Bytes {
public void writeCompactUnsignedShort(int v) {
if (v >= 0 && v < USHORT_EXTENDED) {
writeByte(v);
+
} else {
writeUnsignedShort(USHORT_EXTENDED);
writeUnsignedShort(v);
@@ -866,6 +1334,7 @@ public abstract class AbstractBytes implements Bytes {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedByte(v >>> 16);
writeUnsignedShort(v);
+
} else {
writeUnsignedByte(v);
writeUnsignedShort(v >>> 8);
@@ -877,6 +1346,7 @@ public abstract class AbstractBytes implements Bytes {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedByte(offset, v >>> 16);
writeUnsignedShort(offset + 1, v);
+
} else {
writeUnsignedByte(offset, v);
writeUnsignedShort(offset + 1, v >>> 8);
@@ -902,6 +1372,7 @@ public abstract class AbstractBytes implements Bytes {
case Integer.MIN_VALUE:
writeShort(SHORT_MIN_VALUE);
break;
+
case Integer.MAX_VALUE:
writeShort(SHORT_MAX_VALUE);
break;
@@ -916,6 +1387,7 @@ public abstract class AbstractBytes implements Bytes {
public void writeCompactUnsignedInt(long v) {
if (v >= 0 && v < USHORT_EXTENDED) {
writeShort((int) v);
+
} else {
writeShort(USHORT_EXTENDED);
writeUnsignedInt(v);
@@ -927,6 +1399,7 @@ public abstract class AbstractBytes implements Bytes {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedShort((int) (v >>> 32));
writeUnsignedInt(v);
+
} else {
writeUnsignedShort((int) v);
writeUnsignedInt(v >>> 16);
@@ -938,6 +1411,7 @@ public abstract class AbstractBytes implements Bytes {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedShort(offset, (int) (v >>> 32));
writeUnsignedInt(offset + 2, v);
+
} else {
writeUnsignedShort(offset, (int) v);
writeUnsignedInt(offset + 2, v >>> 16);
@@ -958,31 +1432,42 @@ public abstract class AbstractBytes implements Bytes {
} else {
writeInt(INT_EXTENDED);
writeLong(v);
-
}
}
@Override
public void writeStopBit(long n) {
+ if ((n & ~0x7F) == 0) {
+ write((int) (n & 0x7f));
+ return;
+ }
+ if ((n & ~0x3FFF) == 0) {
+ write((int) ((n & 0x7f) | 0x80));
+ write((int) (n >> 7));
+ return;
+ }
+ writeStopBit0(n);
+ }
+
+ private void writeStopBit0(long n) {
boolean neg = false;
if (n < 0) {
neg = true;
n = ~n;
}
- while (true) {
- long n2 = n >>> 7;
- if (n2 != 0) {
- writeByte((byte) (0x80 | (n & 0x7F)));
- n = n2;
- } else {
- if (neg) {
- writeByte((byte) (0x80 | (n & 0x7F)));
- writeByte(0);
- } else {
- writeByte((byte) (n & 0x7F));
- }
- break;
- }
+
+ long n2;
+ while ((n2 = n >>> 7) != 0) {
+ write((byte) (0x80L | n));
+ n = n2;
+ }
+ // final byte
+ if (!neg) {
+ write((byte) n);
+
+ } else {
+ write((byte) (0x80L | n));
+ write(0);
}
}
@@ -991,6 +1476,7 @@ public abstract class AbstractBytes implements Bytes {
float f = (float) v;
if (f == v) {
writeFloat(f);
+
} else {
writeFloat(Float.NaN);
writeDouble(v);
@@ -1006,23 +1492,7 @@ public abstract class AbstractBytes implements Bytes {
writeByte(bb.get());
}
- @Deprecated
- @Override
- public void writeStartToPosition(@NotNull Bytes bb) {
- final long position = bb.position();
- long offset = 0;
- if (position > remaining())
- throw new IndexOutOfBoundsException("Trying to write " + position + " when only " + remaining() + " left");
- // TODO optimise this to use Unsafe copy memory
- while (position - offset >= 8) {
- writeLong(bb.readLong(offset));
- offset += 8;
- }
- while (position - offset >= 1)
- writeByte(bb.readByte(offset++));
- }
-
- // // ByteStringAppender
+ // ByteStringAppender
@NotNull
@Override
public ByteStringAppender append(@NotNull CharSequence s, int start, int end) {
@@ -1105,18 +1575,19 @@ public abstract class AbstractBytes implements Bytes {
@NotNull
@Override
public ByteStringAppender appendDateMillis(long timeInMS) {
- if (dateFormat == null) {
- dateFormat = new SimpleDateFormat("yyyy/MM/dd");
- dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ DateCache dateCache = dateCacheTL.get();
+ if (dateCache == null) {
+ dateCacheTL.set(dateCache = new DateCache());
}
long date = timeInMS / 86400000;
- if (lastDay != date) {
- lastDateStr = dateFormat.format(new Date(timeInMS)).getBytes(ISO_8859_1);
- lastDay = date;
+ if (dateCache.lastDay != date) {
+ dateCache.lastDateStr = dateCache.dateFormat.format(new Date(timeInMS)).getBytes(ISO_8859_1);
+ dateCache.lastDay = date;
+
} else {
- assert lastDateStr != null;
+ assert dateCache.lastDateStr != null;
}
- write(lastDateStr);
+ write(dateCache.lastDateStr);
return this;
}
@@ -1168,13 +1639,16 @@ public abstract class AbstractBytes implements Bytes {
if (exp == 0 && mantissa == 0) {
writeByte('0');
return this;
+
} else if (exp == 2047) {
if (mantissa == 0) {
write(Infinity);
+
} else {
write(NaN);
}
return this;
+
} else if (exp > 0) {
mantissa += 1L << 52;
}
@@ -1283,8 +1757,26 @@ public abstract class AbstractBytes implements Bytes {
int exp = 0;
boolean negative = false;
int decimalPlaces = Integer.MIN_VALUE;
+ int ch = readUnsignedByteOrThrow();
+ switch (ch) {
+ case 'N':
+ if (compareRest("aN"))
+ return Double.NaN;
+ skip(-1);
+ return Double.NaN;
+ case 'I':
+ if (compareRest("nfinity"))
+ return Double.POSITIVE_INFINITY;
+ skip(-1);
+ return Double.NaN;
+ case '-':
+ if (compareRest("Infinity"))
+ return Double.NEGATIVE_INFINITY;
+ negative = true;
+ ch = readUnsignedByteOrThrow();
+ break;
+ }
while (true) {
- byte ch = readByte();
if (ch >= '0' && ch <= '9') {
while (value >= MAX_VALUE_DIVIDE_10) {
value >>>= 1;
@@ -1292,18 +1784,31 @@ public abstract class AbstractBytes implements Bytes {
}
value = value * 10 + (ch - '0');
decimalPlaces++;
- } else if (ch == '-') {
- negative = true;
+
} else if (ch == '.') {
decimalPlaces = 0;
+
} else {
break;
}
+ ch = readUnsignedByteOrThrow();
}
return asDouble(value, exp, negative, decimalPlaces);
}
+ protected boolean compareRest(String s) {
+ if (s.length() > remaining())
+ return false;
+ long position = position();
+ for (int i = 0; i < s.length(); i++) {
+ if (readUnsignedByte(position + i) != s.charAt(i))
+ return false;
+ }
+ skip(s.length());
+ return true;
+ }
+
@NotNull
@Override
public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) {
@@ -1321,7 +1826,7 @@ public abstract class AbstractBytes implements Bytes {
}
@NotNull
- public <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) {
+ <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) {
for (int i = 0; i < list.size(); i++) {
if (i > 0)
append(separator);
@@ -1338,15 +1843,18 @@ public abstract class AbstractBytes implements Bytes {
long num = 0, scale = Long.MIN_VALUE;
boolean negative = false;
while (true) {
- byte b = readByte();
+ int b = readUnsignedByteOrThrow();
// if (b >= '0' && b <= '9')
if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE) {
num = num * 10 + b - '0';
scale++;
+
} else if (b == '.') {
scale = 0;
+
} else if (b == '-') {
negative = true;
+
} else {
break;
}
@@ -1362,7 +1870,7 @@ public abstract class AbstractBytes implements Bytes {
long num = 0;
boolean negative = false;
while (true) {
- byte b = readByte();
+ int b = readUnsignedByteOrThrow();
// if (b >= '0' && b <= '9')
if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE)
num = num * 10 + b - '0';
@@ -1381,10 +1889,11 @@ public abstract class AbstractBytes implements Bytes {
long num = 0;
boolean negative = false;
while (true) {
- byte b = readByte();
+ int b = readUnsignedByteOrThrow();
byte rp = RADIX_PARSE[b];
if (rp >= 0 && rp < base) {
num = num * base + rp;
+
} else if (b == '-')
negative = true;
else
@@ -1475,7 +1984,6 @@ public abstract class AbstractBytes implements Bytes {
if (num <= 0)
return 2;
numberBuffer[1] = (byte) (num % 10L + '0');
- num /= 10;
return 1;
}
@@ -1640,7 +2148,7 @@ public abstract class AbstractBytes implements Bytes {
@NotNull
@Override
public ByteStringAppender append(@NotNull MutableDecimal md) {
- StringBuilder sb = acquireUtfReader();
+ StringBuilder sb = acquireStringBuilder();
md.toString(sb);
append(sb);
return this;
@@ -1649,51 +2157,125 @@ public abstract class AbstractBytes implements Bytes {
@NotNull
@Override
public InputStream inputStream() {
- if (inputStream == null)
- inputStream = new BytesInputStream();
- return inputStream;
+ return new BytesInputStream(this);
}
@NotNull
@Override
public OutputStream outputStream() {
- if (outputStream == null)
- outputStream = new BytesOutputStream();
- return outputStream;
+ return new BytesOutputStream(this);
}
@NotNull
@Override
- public BytesMarshallerFactory bytesMarshallerFactory() {
- return bytesMarshallerFactory == null ? bytesMarshallerFactory = new VanillaBytesMarshallerFactory() : bytesMarshallerFactory;
+ public ObjectSerializer objectSerializer() {
+ return objectSerializer;
}
@SuppressWarnings("unchecked")
@Override
public <E> void writeEnum(@Nullable E e) {
- Class aClass;
- if (e == null || e instanceof CharSequence)
- aClass = String.class;
- else
- aClass = (Class) e.getClass();
- BytesMarshaller<E> em = bytesMarshallerFactory().acquireMarshaller(aClass, true);
- em.write(this, e);
+ if (e == null) {
+ write8bitText(null);
+ return;
+ }
+ if (e instanceof CharSequence) {
+ write8bitText((CharSequence) e);
+ return;
+ }
+ if (e instanceof Enum) {
+ write8bitText(e.toString());
+ return;
+ }
+
+ Class aClass = (Class) e.getClass();
+ writeInstance(aClass, e);
+ }
+
+ @Override
+ public void writeEnum(long offset, int len, Object e) {
+ long pos = position();
+ long lim = limit();
+ try {
+ position(offset);
+ limit(offset + len);
+ writeEnum(e);
+ } finally {
+ limit(lim);
+ position(pos);
+ }
}
@SuppressWarnings("unchecked")
@Override
public <E> E readEnum(@NotNull Class<E> eClass) {
- BytesMarshaller<E> em = bytesMarshallerFactory().acquireMarshaller(eClass, true);
- return em.read(this);
+ if (Enum.class.isAssignableFrom(eClass))
+ return (E) readEnum2((Class<Enum>) (Class) eClass);
+ if (String.class.isAssignableFrom(eClass))
+ return (E) readUTFΔ();
+ return readInstance(eClass, null);
+ }
+
+ @Override
+ public <E> E readEnum(long offset, int maxSize, Class<E> eClass) {
+ long pos = position();
+ long lim = limit();
+ try {
+ position(offset);
+ limit(offset + maxSize);
+ if (Enum.class.isAssignableFrom(eClass))
+ return (E) readEnum2((Class<Enum>) (Class) eClass);
+ if (String.class.isAssignableFrom(eClass))
+ return (E) readUTFΔ();
+ return readInstance(eClass, null);
+ } finally {
+ limit(lim);
+ position(pos);
+ }
+ }
+
+ @Override
+ public long nextSetBit(long fromIndex) {
+ if (fromIndex < 0)
+ throw new IndexOutOfBoundsException();
+ long maxBit = capacity() << 3;
+ long fromLongIndex = fromIndex & ~63;
+ if (fromLongIndex >= maxBit)
+ return -1;
+ long firstByte = fromLongIndex >>> 3;
+ if ((fromIndex & 63) != 0) {
+ long l = readVolatileLong(firstByte) >>> fromIndex;
+ if (l != 0) {
+ return fromIndex + numberOfTrailingZeros(l);
+ }
+ firstByte += 8;
+ }
+ for (long i = firstByte; i < capacity(); i += 8) {
+ long l = readLong(i);
+ if (l != 0)
+ return (i << 3) + numberOfTrailingZeros(l);
+ }
+ return -1;
+ }
+
+ private <E extends Enum<E>> E readEnum2(Class<E> eClass) {
+ try {
+ StringBuilder sb = acquireStringBuilder();
+ if (read8bitText(sb))
+ return EnumInterner.intern(eClass, sb);
+ return null;
+ } catch (StreamCorruptedException e) {
+ throw new IllegalStateException(e);
+ }
}
@SuppressWarnings("unchecked")
@Override
public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) {
- String text = parseUTF(tester);
+ String text = parseUtf8(tester);
if (text.isEmpty())
return null;
- return Enum.valueOf(eClass, text);
+ return EnumInterner.intern(eClass, text);
}
@Override
@@ -1714,11 +2296,13 @@ public abstract class AbstractBytes implements Bytes {
@Override
public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) {
- int len = (int) readStopBit();
+ long len = readStopBit();
+ if (len < 0 || len > Integer.MAX_VALUE)
+ throw new IllegalStateException("Invalid length: " + len);
list.clear();
for (int i = 0; i < len; i++) {
@SuppressWarnings("unchecked")
- E e = (E) readEnum(eClass);
+ E e = readEnum(eClass);
list.add(e);
}
}
@@ -1726,7 +2310,10 @@ public abstract class AbstractBytes implements Bytes {
@Override
@NotNull
public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) {
- int len = (int) readStopBit();
+ long len = readStopBit();
+ if (len < 0 || len > Integer.MAX_VALUE)
+ throw new IllegalStateException("Invalid length: " + len);
+
map.clear();
for (int i = 0; i < len; i++)
map.put(readEnum(kClass), readEnum(vClass));
@@ -1740,7 +2327,7 @@ public abstract class AbstractBytes implements Bytes {
@Override
public int read() {
- return remaining() > 0 ? readByte() : -1;
+ return remaining() > 0 ? readUnsignedByte() : -1;
}
@Override
@@ -1753,11 +2340,11 @@ public abstract class AbstractBytes implements Bytes {
@Override
public long skip(long n) {
- if (n < 0)
+ if (n < -position())
throw new IllegalArgumentException("Skip bytes out of range, was " + n);
if (n > remaining())
n = remaining();
- skipBytes((int) n);
+ position(position() + n);
return n;
}
@@ -1784,9 +2371,18 @@ public abstract class AbstractBytes implements Bytes {
}
@Override
- public void reset() {
+ public AbstractBytes clear() {
finished = false;
position(0L);
+ limit(capacity());
+ return this;
+ }
+
+ @Override
+ public Bytes flip() {
+ limit(position());
+ position(0);
+ return this;
}
@Override
@@ -1797,26 +2393,10 @@ public abstract class AbstractBytes implements Bytes {
@Nullable
@Override
public Object readObject() {
- byte type = readByte();
- switch (type) {
- case NULL:
- return null;
- case ENUMED: {
- Class clazz = readEnum(Class.class);
- return readEnum(clazz);
- }
- case SERIALIZED: {
- try {
- return new ObjectInputStream(this.inputStream()).readObject();
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }
- default:
- BytesMarshaller<Object> m = bytesMarshallerFactory().getMarshaller(type);
- if (m == null)
- throw new IllegalStateException("Unknown type " + (char) type);
- return m.read(this);
+ try {
+ return objectSerializer.readSerializable(this, null, null);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
}
}
@@ -1829,82 +2409,83 @@ public abstract class AbstractBytes implements Bytes {
throw new ClassCastException("Cannot convert " + o.getClass().getName() + " to " + tClass.getName() + " was " + o);
}
- @SuppressWarnings("unchecked")
+ @Nullable
@Override
- public void writeObject(@Nullable Object obj) {
- if (obj == null) {
- writeByte(NULL);
- return;
+ @SuppressWarnings("unchecked")
+ public <T> T readInstance(@NotNull Class<T> objClass, T obj) {
+ try {
+ return objectSerializer.readSerializable(this, objClass, obj);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
}
+ }
- Class<?> clazz = obj.getClass();
- final BytesMarshallerFactory bytesMarshallerFactory = bytesMarshallerFactory();
- BytesMarshaller em = bytesMarshallerFactory.acquireMarshaller(clazz, false);
- if (em == NoMarshaller.INSTANCE && autoGenerateMarshaller(obj))
- em = bytesMarshallerFactory.acquireMarshaller(clazz, true);
-
- if (em != NoMarshaller.INSTANCE) {
- if (em instanceof CompactBytesMarshaller) {
- writeByte(((CompactBytesMarshaller) em).code());
- em.write(this, obj);
- return;
- }
- writeByte(ENUMED);
- writeEnum(clazz);
- em.write(this, obj);
- return;
- }
- writeByte(SERIALIZED);
- // TODO this is the lame implementation, but it works.
+ @SuppressWarnings("unchecked")
+ @Override
+ public void writeObject(@Nullable Object obj) {
try {
- ObjectOutputStream oos = new ObjectOutputStream(this.outputStream());
- oos.writeObject(obj);
- } catch (IOException e) {
+ objectSerializer.writeSerializable(this, obj, null);
+ } catch (Exception e) {
throw new IllegalStateException(e);
}
checkEndOfBuffer();
}
- protected boolean autoGenerateMarshaller(Object obj) {
- return (obj instanceof Comparable && obj.getClass().getPackage().getName().startsWith("java"))
- || obj instanceof Externalizable
- || obj instanceof BytesMarshallable;
+ @Override
+ public <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj) {
+ try {
+ objectSerializer.writeSerializable(this, obj, objClass);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ checkEndOfBuffer();
}
@Override
public boolean tryLockInt(long offset) {
- long id = shortThreadId();
- return tryLockNanos4a(offset, (int) id);
+ return tryLockNanos4a(offset);
}
@Override
public boolean tryLockNanosInt(long offset, long nanos) {
- long id = shortThreadId();
int limit = nanos <= 10000 ? (int) nanos / 10 : 1000;
for (int i = 0; i < limit; i++)
- if (tryLockNanos4a(offset, (int) id))
+ if (tryLockNanos4a(offset))
return true;
if (nanos <= 10000)
return false;
long end = System.nanoTime() + nanos - 10000;
do {
- if (tryLockNanos4a(offset, (int) id))
+ if (tryLockNanos4a(offset))
return true;
} while (end > System.nanoTime() && !currentThread().isInterrupted());
return false;
}
- private boolean tryLockNanos4a(long offset, int id) {
+ private boolean tryLockNanos4a(long offset) {
+ //lowId = bottom 24 bytes of the thread id
int lowId = shortThreadId();
+ //Use the top 8 bytes as a counter, and the bottom 24 bytes as the thread id
int firstValue = ((1 << 24) | lowId);
+ //If the cas works, it was unlocked and we now atomically have the lock
if (compareAndSwapInt(offset, 0, firstValue))
return true;
- long currentValue = readUnsignedInt(offset);
+ //The cas failed so get the value of the current lock
+ int currentValue = readInt(offset);
+ //if the bottom 24 bytes match our thread id ...
+ // TODO but what if we're in a different process?
if ((currentValue & INT_LOCK_MASK) == lowId) {
- if (currentValue >= (255L << 24))
- throw new IllegalStateException("Reentred 255 times without an unlock");
+ //then if the counter in the top 8 bytes is 255, throw an exception
+ if ((currentValue >>> 24) >= 255)
+ throw new IllegalStateException("Reentered 255 times without an unlock - if you are using this to lock across processes, there could be a thread id conflict letting one process 'steal' the lock from another process. To avoid this, call AffinitySupport.setThreadId() during startup which will make all threads have unique ids");
+ //otherwise increase the counter in the top 8 bytes by one
currentValue += 1 << 24;
- writeOrderedInt(offset, (int) currentValue);
+ //and store it - no other threads can successfully write at this point
+ //because their cas will fail (the value is not 0), so no update concurrency
+ //conflict, but we do want other threads to read the value we write
+ writeOrderedInt(offset, currentValue);
+ //we've got the lock - and incremented it, so return true
+ return true;
}
return false;
}
@@ -1925,18 +2506,25 @@ public abstract class AbstractBytes implements Bytes {
int firstValue = ((1 << 24) | lowId);
if (compareAndSwapInt(offset, firstValue, 0))
return;
- // try to chek the lowId and the count.
+ // try to cheek the lowId and the count.
unlockFailedInt(offset, lowId);
}
- private Thread currentThread;
- private int shortThreadId = Integer.MIN_VALUE;
+ @Override
+ public void resetLockInt(long offset) {
+ writeOrderedInt(offset, 0);
+ }
+
+ @Override
+ public int threadIdForLockInt(long offset) {
+ return readVolatileInt(offset) & INT_LOCK_MASK;
+ }
- public int shortThreadId() {
- return shortThreadId > 0 ? shortThreadId : shortThreadId0();
+ int shortThreadId() {
+ return shortThreadId0();
}
- protected int shortThreadId0() {
+ int shortThreadId0() {
final int tid = (int) getId() & INT_LOCK_MASK;
if (!ID_LIMIT_WARNED && tid > 1 << 24) {
warnIdLimit(tid);
@@ -1944,13 +2532,8 @@ public abstract class AbstractBytes implements Bytes {
return tid;
}
- public void setCurrentThread() {
- currentThread = Thread.currentThread();
- shortThreadId = shortThreadId0();
- }
-
- public Thread currentThread() {
- return currentThread == null ? Thread.currentThread() : currentThread;
+ Thread currentThread() {
+ return Thread.currentThread();
}
@Override
@@ -1959,7 +2542,7 @@ public abstract class AbstractBytes implements Bytes {
return tryLockNanos8a(offset, id);
}
- public long uniqueTid() {
+ long uniqueTid() {
return Jvm.getUniqueTid(currentThread());
}
@@ -1972,11 +2555,41 @@ public abstract class AbstractBytes implements Bytes {
return true;
if (nanos <= 10000)
return false;
- long end = System.nanoTime() + nanos - 10000;
+ return tryLockNanosLong0(offset, nanos, id);
+ }
+
+ private boolean tryLockNanosLong0(long offset, long nanos, long id) {
+ long nanos0 = Math.min(nanos, SLEEP_THRESHOLD);
+ long start = System.nanoTime();
+ long end0 = start + nanos0 - 10000;
do {
if (tryLockNanos8a(offset, id))
return true;
- } while (end > System.nanoTime() && !currentThread().isInterrupted());
+ } while (end0 > System.nanoTime() && !currentThread().isInterrupted());
+
+ long end = start + nanos - SLEEP_THRESHOLD;
+ if (LoggerHolder.LOGGER.isLoggable(Level.FINE)) {
+ LoggerHolder.LOGGER.log(Level.FINE, Thread.currentThread().getName() + ", waiting for lock");
+ }
+
+ try {
+ do {
+ if (tryLockNanos8a(offset, id)) {
+ long millis = (System.nanoTime() - start) / 1000000;
+ if (millis > 200) {
+ LoggerHolder.LOGGER.log(Level.WARNING,
+ Thread.currentThread().getName() +
+ ", to obtain a lock took " +
+ millis / 1e3 + " seconds"
+ );
+ }
+ return true;
+ }
+ Thread.sleep(1);
+ } while (end > System.nanoTime());
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
return false;
}
@@ -1985,9 +2598,16 @@ public abstract class AbstractBytes implements Bytes {
if (compareAndSwapLong(offset, 0, firstValue))
return true;
long currentValue = readLong(offset);
- if ((currentValue & (1L << 48) - 1) == id) {
+ long lockedId = currentValue & ((1L << 48) - 1);
+ if (lockedId == 0) {
+ int count = (int) (currentValue >>> 48);
+ if (count != 0)
+ LoggerHolder.LOGGER.log(Level.WARNING, "Lock held by threadId 0 !?");
+ return compareAndSwapLong(offset, currentValue, firstValue);
+ }
+ if (lockedId == id) {
if (currentValue >>> 48 == 65535)
- throw new IllegalStateException("Reentred 65535 times without an unlock");
+ throw new IllegalStateException("Reentered 65535 times without an unlock");
currentValue += 1L << 48;
writeOrderedLong(offset, currentValue);
return true;
@@ -2015,18 +2635,30 @@ public abstract class AbstractBytes implements Bytes {
unlockFailedLong(offset, id);
}
- protected long getId() {
+ @Override
+ public void resetLockLong(long offset) {
+ writeOrderedLong(offset, 0L);
+ }
+
+ @Override
+ public long threadIdForLockLong(long offset) {
+ return readVolatileLong(offset);
+ }
+
+ long getId() {
return currentThread().getId();
}
private void unlockFailedInt(long offset, int lowId) throws IllegalMonitorStateException {
- long currentValue = readUnsignedInt(offset);
+ long currentValue = readInt(offset);
long holderId = currentValue & INT_LOCK_MASK;
if (holderId == lowId) {
currentValue -= 1 << 24;
writeOrderedInt(offset, (int) currentValue);
+
} else if (currentValue == 0) {
- throw new IllegalMonitorStateException("No thread holds this lock");
+ LoggerHolder.LOGGER.log(Level.WARNING, "No thread holds this lock, threadId: " + shortThreadId());
+
} else {
throw new IllegalMonitorStateException("Thread " + holderId + " holds this lock, " + (currentValue >>> 24) + " times");
}
@@ -2038,8 +2670,10 @@ public abstract class AbstractBytes implements Bytes {
if (holderId == id) {
currentValue -= 1L << 48;
writeOrderedLong(offset, currentValue);
+
} else if (currentValue == 0) {
throw new IllegalMonitorStateException("No thread holds this lock");
+
} else {
throw new IllegalMonitorStateException("Process " + ((currentValue >>> 32) & 0xFFFF)
+ " thread " + (holderId & (-1L >>> 32))
@@ -2089,7 +2723,7 @@ public abstract class AbstractBytes implements Bytes {
public short addShort(long offset, short s) {
short s2 = readShort(offset);
s2 += s;
- writeByte(offset, s2);
+ writeShort(offset, s2);
return s2;
}
@@ -2198,20 +2832,19 @@ public abstract class AbstractBytes implements Bytes {
@Override
public int length() {
- return (int) Math.min(Integer.MAX_VALUE, remaining());
+ if (position() == 0)
+ return (int) Math.min(limit(), Integer.MAX_VALUE);
+ else if (position() == limit() || limit() == capacity())
+ return (int) Math.min(position(), Integer.MAX_VALUE);
+ else
+ throw new IllegalStateException();
}
@Override
public char charAt(int index) {
- return (char) readUnsignedByte(index);
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- StringBuilder sb = new StringBuilder(end - start + 1);
- for (int i = start; i < end; i++)
- sb.append(charAt(i));
- return sb;
+ if (index < 0 || index >= length())
+ throw new IndexOutOfBoundsException();
+ return (char) readUnsignedByte(position() + index);
}
@Override
@@ -2225,99 +2858,407 @@ public abstract class AbstractBytes implements Bytes {
@Override
public void writeMarshallable(@NotNull Bytes out) {
out.write(this, position(), remaining());
+ }
+ @Override
+ public void write(RandomDataInput bytes) {
+ long toWrite = bytes.remaining();
+ write(bytes, bytes.position(), toWrite);
+ bytes.skip(toWrite);
}
@Override
- public void write(BytesCommon bytes, long position, long length) {
- if (length > bytes.remaining())
+ public void write(RandomDataInput bytes, long position, long length) {
+ if (length > remaining())
throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining");
- RandomDataInput rdi = (RandomDataInput) bytes;
if (bytes.byteOrder() == byteOrder()) {
while (length >= 8) {
- writeLong(rdi.readLong(position));
+ writeLong(bytes.readLong(position));
position += 8;
length -= 8;
}
}
while (length >= 1) {
- writeByte(rdi.readByte(position));
+ writeByte(bytes.readByte(position));
position++;
length--;
}
}
- protected class BytesInputStream extends InputStream {
- private long mark = 0;
+ @Override
+ public void write(@NotNull Byteable byteable) {
+ if (byteable.bytes() == null) {
+ throw new IllegalArgumentException("Attempt to write an unitialized Byteable object");
+ }
- @Override
- public int available() throws IOException {
- long remaining = remaining();
- return (int) Math.min(Integer.MAX_VALUE, remaining);
+ write(byteable.bytes(), byteable.offset(), byteable.maxSize());
+ }
+
+ @Override
+ public boolean startsWith(RandomDataInput input) {
+ return compare(position(), input, input.position(), input.remaining());
+ }
+
+ @Override
+ public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) {
+ if (offset < 0 || inputOffset < 0 || len < 0)
+ throw new IndexOutOfBoundsException();
+ if (offset + len < 0 || offset + len > capacity() || inputOffset + len < 0 ||
+ inputOffset + len > input.capacity()) {
+ return false;
+ }
+ long i = 0L;
+ for (; i < len - 7L; i += 8L) {
+ if (readLong(offset + i) != input.readLong(inputOffset + i))
+ return false;
+ }
+ if (i < len - 3L) {
+ if (readInt(offset + i) != input.readInt(inputOffset + i))
+ return false;
+ i += 4L;
+ }
+ if (i < len - 1L) {
+ if (readChar(offset + i) != input.readChar(inputOffset + i))
+ return false;
+ i += 2L;
+ }
+ if (i < len) {
+ if (readByte(offset + i) != input.readByte(inputOffset + i))
+ return false;
}
+ return true;
+ }
- @Override
- public void close() throws IOException {
- finish();
+ @NotNull
+ @Override
+ public String toString() {
+ long remaining = remaining();
+ if (remaining < 0 || remaining > 1L << 48)
+ return "invalid remaining: " + remaining();
+ if (remaining > 1 << 20)
+ remaining = 1 << 20;
+ char[] chars = new char[(int) remaining];
+ long pos = position();
+ for (int i = 0; i < remaining; i++) {
+ chars[i] = (char) readUnsignedByte(i + pos);
}
+ return new String(chars);
+ }
- @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod")
- @Override
- public void mark(int readlimit) {
- mark = position();
+ @NotNull
+ @Override
+ public String toDebugString() {
+ return toDebugString(64);
+ }
+
+ @NotNull
+ @Override
+ public String toDebugString(long limit) {
+ StringBuilder sb = new StringBuilder(200);
+ sb.append("[pos: ").append(position()).append(", lim: ").append(limit()).append(", cap: ")
+ .append(capacity()).append(" ] ");
+ toString(sb, position() - limit, position(), position() + limit);
+
+ return sb.toString();
+ }
+
+ @Override
+ public String toHexString(long limit) {
+ return toHexString(this, position(), Math.min(remaining(), limit));
+ }
+
+ @Override
+ public void toString(Appendable sb, long start, long position, long end) {
+ try {
+ // before
+ if (start < 0) start = 0;
+ if (position > start) {
+ for (long i = start; i < position; i++) {
+ append(sb, i);
+ }
+ sb.append('\u2016');
+ }
+ if (end > limit())
+ end = limit();
+ // after
+ for (long i = position; i < end; i++) {
+ append(sb, i);
+ }
+ } catch (IOException e) {
+ try {
+ sb.append(e.toString());
+ } catch (IOException e1) {
+ throw new AssertionError(e);
+ }
}
+ }
- @Override
- public boolean markSupported() {
- return true;
+ @Override
+ public ByteBuffer sliceAsByteBuffer(@Nullable ByteBuffer toReuse) {
+ throw new UnsupportedOperationException();
+ }
+
+ private void append(Appendable sb, long i) throws IOException {
+ int b = readUnsignedByte(i);
+ if (b == 0)
+ sb.append('\u0660');
+ else if (b < 21)
+ sb.append((char) (b + 0x2487));
+ else
+ sb.append((char) b);
+ }
+
+ @Override
+ public void asString(Appendable appendable) {
+ try {
+ for (long i = position(); i < limit(); i++)
+ append(appendable, i);
+ } catch (IOException e) {
+ throw new AssertionError(e);
}
+ }
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return AbstractBytes.this.read(b, off, len);
+ @Override
+ public CharSequence asString() {
+ StringBuilder sb = new StringBuilder();
+ asString(sb);
+ return sb;
+ }
+
+ @Override
+ public boolean compareAndSwapDouble(long offset, double expected, double value) {
+ long exp = Double.doubleToRawLongBits(expected);
+ long val = Double.doubleToRawLongBits(value);
+ return compareAndSwapLong(offset, exp, val);
+ }
+
+ public File file() {
+ return null;
+ }
+
+ // read/write lock support.
+ // short path in a small method so it can be inlined.
+ public boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ long lock = readVolatileLong(offset);
+ int writersWaiting = rwWriteWaiting(lock);
+ int writersLocked = rwWriteLocked(lock);
+ // readers wait for waiting writers
+ if (writersLocked <= 0 && writersWaiting <= 0) {
+ // increment readers locked.
+ int readersLocked = rwReadLocked(lock);
+ if (readersLocked >= RW_LOCK_MASK)
+ throw new IllegalStateException("readersLocked has reached a limit of " + readersLocked);
+ if (compareAndSwapLong(offset, lock, lock + RW_READ_LOCKED))
+ return true;
}
+ return tryRWReadLock0(offset, timeOutNS);
+ }
- @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod")
- @Override
- public void reset() throws IOException {
- position(mark);
+ private boolean tryRWReadLock0(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ long end = System.nanoTime() + timeOutNS;
+ // wait for no write locks, nor waiting writes.
+ for (; ; ) {
+ long lock = readVolatileLong(offset);
+ int writersWaiting = rwWriteWaiting(lock);
+ int writersLocked = rwWriteLocked(lock);
+ if (writersLocked <= 0 && writersWaiting <= 0) {
+ // increment readers locked.
+ int readersLocked = rwReadLocked(lock);
+ if (readersLocked >= RW_LOCK_MASK)
+ throw new IllegalStateException("readersLocked has reached a limit of " + readersLocked);
+ // add to the readLock count and decrease the readWaiting count.
+ if (compareAndSwapLong(offset, lock, lock + RW_READ_LOCKED))
+ return true;
+ }
+ if (System.nanoTime() > end)
+ return false;
+
+ if (currentThread().isInterrupted())
+ throw new InterruptedException("Unable to obtain lock, interrupted");
}
+ }
- @Override
- public long skip(long n) throws IOException {
- if (n > Integer.MAX_VALUE)
- throw new IOException("Skip too large");
- return skipBytes((int) n);
+ public boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ long lock = readVolatileLong(offset);
+ int readersLocked = rwReadLocked(lock);
+ int writersLocked = rwWriteLocked(lock);
+ // writers don't wait for waiting readers.
+ if (readersLocked <= 0 && writersLocked <= 0) {
+ if (compareAndSwapLong(offset, lock, lock + RW_WRITE_LOCKED))
+ return true;
}
+ return tryRWWriteLock0(offset, timeOutNS);
+ }
- @Override
- public int read() throws IOException {
- if (remaining() > 0)
- return readUnsignedByte();
- return -1;
+ private boolean tryRWWriteLock0(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ for (; ; ) {
+ long lock = readVolatileLong(offset);
+ int writersWaiting = rwWriteWaiting(lock);
+ if (writersWaiting >= RW_LOCK_MASK)
+ throw new IllegalStateException("writersWaiting has reached a limit of " + writersWaiting);
+ if (compareAndSwapLong(offset, lock, lock + RW_WRITE_WAITING))
+ break;
+ }
+ long end = System.nanoTime() + timeOutNS;
+ // wait for no write locks.
+ for (; ; ) {
+ long lock = readVolatileLong(offset);
+ int readersLocked = rwReadLocked(lock);
+ int writersWaiting = rwWriteWaiting(lock);
+ int writersLocked = rwWriteLocked(lock);
+ if (readersLocked <= 0 && writersLocked <= 0) {
+ // increment readers locked.
+ if (writersWaiting <= 0) {
+ System.err.println("writersWaiting has underflowed");
+ return false;
+ }
+ // add to the readLock count and decrease the readWaiting count.
+ if (compareAndSwapLong(offset, lock, lock + RW_WRITE_LOCKED - RW_WRITE_WAITING))
+ return true;
+ }
+ boolean interrupted = currentThread().isInterrupted();
+ if (interrupted || System.nanoTime() > end) {
+ // release waiting
+ for (; ; ) {
+ if (writersWaiting <= 0)
+ throw new IllegalStateException("writersWaiting has underflowed");
+ if (compareAndSwapLong(offset, lock, lock - RW_WRITE_WAITING))
+ break;
+ lock = readVolatileLong(offset);
+ writersWaiting = rwWriteWaiting(lock);
+ }
+ if (interrupted)
+ throw new InterruptedException("Unable to obtain lock, interrupted");
+ return false;
+ }
}
}
- protected class BytesOutputStream extends OutputStream {
- @Override
- public void close() throws IOException {
- finish();
+ public void unlockRWReadLock(long offset) {
+ for (; ; ) {
+ long lock = readVolatileLong(offset);
+ int readersLocked = rwReadLocked(lock);
+ if (readersLocked <= 0)
+ throw new IllegalMonitorStateException("readerLock underflow");
+ if (compareAndSwapLong(offset, lock, lock - RW_READ_LOCKED))
+ return;
}
+ }
- @Override
- public void write(@NotNull byte[] b) throws IOException {
- AbstractBytes.this.write(b);
+ public void unlockRWWriteLock(long offset) {
+ for (; ; ) {
+ long lock = readVolatileLong(offset);
+ int writersLocked = rwWriteLocked(lock);
+ if (writersLocked != 1)
+ throw new IllegalMonitorStateException("writersLock underflow " + writersLocked);
+ if (compareAndSwapLong(offset, lock, lock - RW_WRITE_LOCKED))
+ return;
+ }
+ }
+
+ String dumpRWLock(long offset) {
+ long lock = readVolatileLong(offset);
+ int readersLocked = rwReadLocked(lock);
+ int writersWaiting = rwWriteWaiting(lock);
+ int writersLocked = rwWriteLocked(lock);
+ return "writerLocked: " + writersLocked
+ + ", writersWaiting: " + writersWaiting
+ + ", readersLocked: " + readersLocked;
+ }
+
+ static class DateCache {
+ final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
+ private long lastDay = Long.MIN_VALUE;
+ @Nullable
+ private byte[] lastDateStr = null;
+
+ DateCache() {
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+ }
+
+ static class LoggerHolder {
+ public static final Logger LOGGER = Logger.getLogger(AbstractBytes.class.getName());
+ }
+
+ abstract static class FastStringOperations {
+ abstract long getUtf8EncodedStringLength(@NotNull String string);
+
+ final int getUtf8CharSize(char c) {
+ if ((c > 0x007F)) {
+ if (c > 0x07FF) {
+ return 3;
+ } else {
+ return 2;
+ }
+ }
+ return 1;
+ }
+
+ abstract char[] extractChars(String string);
+ }
+
+ private static class FastStringOperations17 extends FastStringOperations {
+ final Field valueField;
+
+ private FastStringOperations17() throws SecurityException, NoSuchFieldException {
+ valueField = String.class.getDeclaredField("value");
+ valueField.setAccessible(true);
+ }
+
+ char[] extractChars(String string) {
+ try {
+ return (char[]) valueField.get(string);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ }
}
@Override
- public void write(byte[] b, int off, int len) throws IOException {
- AbstractBytes.this.write(b, off, len);
+ long getUtf8EncodedStringLength(String string) {
+ char[] chars = extractChars(string);
+ long utflen = chars.length;
+ for (char c : chars) {
+ if ((c > 0x007F)) {
+ if (c > 0x07FF) {
+ utflen += 2;
+ } else {
+ utflen++;
+ }
+ }
+ }
+ return utflen;
+ }
+ }
+
+ private static final class FastStringOperations16 extends FastStringOperations17 {
+ private final Field offsetField, countField;
+
+ private FastStringOperations16() throws SecurityException, NoSuchFieldException {
+ super();
+ offsetField = String.class.getDeclaredField("offset");
+ offsetField.setAccessible(true);
+ countField = String.class.getDeclaredField("count");
+ countField.setAccessible(true);
}
@Override
- public void write(int b) throws IOException {
- checkWrite(1);
- writeUnsignedByte(b);
+ long getUtf8EncodedStringLength(String string) {
+ final char[] chars = extractChars(string);
+ final int startIndex, endIndex;
+
+ try {
+ startIndex = offsetField.getInt(string);
+ endIndex = startIndex + countField.getInt(string);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ }
+
+ long utflen = 0;
+ for (int i = startIndex; i < endIndex; ++i) {
+ utflen += getUtf8CharSize(chars[i]);
+ }
+ return utflen;
}
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java b/lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java
new file mode 100644
index 0000000..3708e1b
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/AbstractMappedStore.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.channels.FileChannel;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.model.constraints.NotNull;
+import sun.misc.Cleaner;
+import sun.nio.ch.FileChannelImpl;
+
+abstract class AbstractMappedStore implements BytesStore, Closeable {
+ private static final int MAP_RO = 0;
+ private static final int MAP_RW = 1;
+ private static final int MAP_PV = 2;
+
+ // retain to prevent GC.
+ private final File file;
+ private final RandomAccessFile raf;
+ private final Cleaner cleaner;
+ private final AtomicInteger refCount = new AtomicInteger(1);
+ private final FileChannel.MapMode mode;
+ protected final MmapInfoHolder mmapInfoHolder;
+ private ObjectSerializer objectSerializer;
+
+ AbstractMappedStore(MmapInfoHolder mmapInfoHolder, File file, FileChannel.MapMode mode,
+ long startInFile, long size, ObjectSerializer objectSerializer)
+ throws IOException {
+ validateSize(size);
+ this.file = file;
+ this.mmapInfoHolder = mmapInfoHolder;
+ this.mmapInfoHolder.setSize(size);
+ this.objectSerializer = objectSerializer;
+ this.mode = mode;
+
+ try {
+ this.raf = new RandomAccessFile(file, accesModeFor(mode));
+ resizeIfNeeded(startInFile, size);
+ map(startInFile);
+ this.cleaner = Cleaner.create(this, new Unmapper(mmapInfoHolder, raf));
+ } catch (Exception e) {
+ throw wrap(e);
+ }
+ }
+
+ protected static void validateSize(long size) {
+ if (size <= 0 || size > 128L << 40) {
+ throw new IllegalArgumentException("invalid size: " + size);
+ }
+ }
+
+ protected final void resizeIfNeeded(long startInFile, long newSize) throws IOException {
+ if (file.getAbsolutePath().startsWith("/dev/")) {
+ return;
+ }
+ if (startInFile > 0) {
+ if (raf.length() >= startInFile + newSize) {
+ return;
+ }
+ } else if (startInFile == 0) {
+ if (raf.length() == newSize) {
+ return;
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Start offset in file needs to be positive: " + startInFile);
+ }
+ if (mode != FileChannel.MapMode.READ_WRITE) {
+ throw new IOException(
+ "Cannot resize file to " + newSize + " as mode is not READ_WRITE");
+ }
+
+ raf.setLength(startInFile + newSize);
+ }
+
+ protected final void map(long startInFile) throws IOException {
+ try {
+ mmapInfoHolder.setAddress(
+ map0(raf.getChannel(), imodeFor(mode), startInFile, mmapInfoHolder.getSize()));
+ } catch (Exception e) {
+ throw wrap(e);
+ }
+ }
+
+ protected final void unmapAndSyncToDisk() throws IOException {
+ unmap0(mmapInfoHolder.getAddress(), mmapInfoHolder.getSize());
+ syncToDisk();
+ }
+
+ public final void syncToDisk() throws IOException {
+ raf.getChannel().force(true);
+ }
+
+ private static long map0(FileChannel fileChannel, int imode, long start, long size)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ Method map0 = fileChannel.getClass().getDeclaredMethod(
+ "map0", int.class, long.class, long.class);
+ map0.setAccessible(true);
+ return (Long) map0.invoke(fileChannel, imode, start, size);
+ }
+
+ private static void unmap0(long address, long size) throws IOException {
+ try {
+ Method unmap0 = FileChannelImpl.class.getDeclaredMethod(
+ "unmap0", long.class, long.class);
+ unmap0.setAccessible(true);
+ unmap0.invoke(null, address, size);
+ } catch (Exception e) {
+ throw wrap(e);
+ }
+ }
+
+ private static IOException wrap(Throwable e) {
+ if (e instanceof InvocationTargetException)
+ e = e.getCause();
+ if (e instanceof IOException)
+ return (IOException) e;
+ return new IOException(e);
+ }
+
+ private static String accesModeFor(FileChannel.MapMode mode) {
+ return mode == FileChannel.MapMode.READ_WRITE ? "rw" : "r";
+ }
+
+ private static int imodeFor(FileChannel.MapMode mode) {
+ int imode = -1;
+ if (mode == FileChannel.MapMode.READ_ONLY)
+ imode = MAP_RO;
+ else if (mode == FileChannel.MapMode.READ_WRITE)
+ imode = MAP_RW;
+ else if (mode == FileChannel.MapMode.PRIVATE)
+ imode = MAP_PV;
+ assert (imode >= 0);
+ return imode;
+ }
+
+ @Override
+ public final ObjectSerializer objectSerializer() {
+ return objectSerializer;
+ }
+
+ @Override
+ public final long address() {
+ return mmapInfoHolder.getAddress();
+ }
+
+ @Override
+ public final long size() {
+ return mmapInfoHolder.getSize();
+ }
+
+ @Override
+ public final void free() {
+ cleaner.clean();
+ }
+
+ @Override
+ public final void close() {
+ free();
+ }
+
+ @NotNull
+ public final DirectBytes bytes() {
+ return new DirectBytes(this, refCount);
+ }
+
+ @NotNull
+ public final DirectBytes bytes(long offset, long length) {
+ return new DirectBytes(this, refCount, offset, length);
+ }
+
+ public final File file() {
+ return file;
+ }
+
+ static final class MmapInfoHolder {
+ private long address, size;
+ private volatile boolean locked;
+
+ private void checkLock() {
+ if (locked) {
+ throw new IllegalStateException();
+ }
+ }
+
+ void lock() {
+ this.locked = true;
+ }
+
+ void setAddress(long address) {
+ checkLock();
+ this.address = address;
+ }
+
+ long getAddress() {
+ return address;
+ }
+
+ void setSize(long size) {
+ checkLock();
+ this.size = size;
+ }
+
+ long getSize() {
+ return size;
+ }
+ }
+
+ private static final class Unmapper implements Runnable {
+ private final MmapInfoHolder mmapInfoHolder;
+ private final RandomAccessFile raf;
+ /*
+ * This is not for synchronization (since calling this from multiple
+ * threads through .free / .close is an user error!) but rather to make
+ * sure that if an explicit cleanup was performed, the cleaner does not
+ * retry cleaning up the resources.
+ */
+ private volatile boolean cleanedUp;
+
+ Unmapper(MmapInfoHolder mmapInfo, RandomAccessFile raf) {
+ this.mmapInfoHolder = mmapInfo;
+ this.raf = raf;
+ }
+
+ public void run() {
+ if (cleanedUp) {
+ return;
+ }
+ cleanedUp = true;
+
+ try {
+ unmap0(mmapInfoHolder.getAddress(), mmapInfoHolder.getSize());
+ raf.getChannel().force(true);
+ // this also closes the underlying channel as per the documentation
+ raf.close();
+ } catch (IOException e) {
+ UnmapperLoggerHolder.LOGGER.log(Level.SEVERE,
+ "An exception has occurred while cleaning up a MappedStore instance: " +
+ e.getMessage(), e);
+ }
+ }
+ }
+
+ private static final class UnmapperLoggerHolder {
+ private static final Logger LOGGER = Logger.getLogger(Unmapper.class.getName());
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java
new file mode 100644
index 0000000..eaa7062
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingDirectBytes.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.model.constraints.NotNull;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unlike {@link net.openhft.lang.io.NativeBytes}, always throw check bounds and exceptions on all write methods,
+ * including that write a single primitive, e. g. {@link #writeInt(int)}. {@code NativeBytes} throw exceptions only if
+ * Java assertions enabled.
+ */
+public class BoundsCheckingDirectBytes extends DirectBytes {
+
+ public BoundsCheckingDirectBytes(@NotNull BytesStore store, AtomicInteger refCount) {
+ super(store, refCount);
+ }
+
+ @Override
+ void positionChecks(long positionAddr) {
+ actualPositionChecks(positionAddr);
+ }
+
+ @Override
+ void offsetChecks(long offset, long len) {
+ actualOffsetChecks(offset, len);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java
new file mode 100644
index 0000000..1deef28
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/BoundsCheckingNativeBytes.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.ObjectSerializer;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unlike {@link NativeBytes}, always throw check bounds and exceptions on
+ * all write methods, including that write a single primitive, e. g. {@link #writeInt(int)}.
+ * {@code NativeBytes} throw exceptions only if Java assertions enabled.
+ */
+public class BoundsCheckingNativeBytes extends NativeBytes {
+
+ public BoundsCheckingNativeBytes(long startAddr, long capacityAddr) {
+ super(startAddr, capacityAddr);
+ }
+
+ public BoundsCheckingNativeBytes(ObjectSerializer objectSerializer,
+ long startAddr, long capacityAddr,
+ AtomicInteger refCount) {
+ super(objectSerializer, startAddr, capacityAddr, refCount);
+ }
+
+ public BoundsCheckingNativeBytes(NativeBytes bytes) {
+ super(bytes);
+ }
+
+ @Override
+ void positionChecks(long positionAddr) {
+ actualPositionChecks(positionAddr);
+ }
+
+ @Override
+ void offsetChecks(long offset, long len) {
+ actualOffsetChecks(offset, len);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java
index 5b85e87..f23a8df 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java
+++ b/lang/src/main/java/net/openhft/lang/io/ByteBufferBytes.java
@@ -1,65 +1,187 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.io.serialization.BytesMarshallableSerializer;
+import net.openhft.lang.io.serialization.JDKZObjectSerializer;
+import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
+import net.openhft.lang.model.constraints.NotNull;
import sun.nio.ch.DirectBuffer;
import java.io.EOFException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* @author peter.lawrey
*/
-public class ByteBufferBytes extends AbstractBytes {
- protected final ByteBuffer buffer;
- protected int start, position, limit;
- protected AtomicBoolean barrier;
-
+public class ByteBufferBytes extends AbstractBytes implements IByteBufferBytes {
+ private final ByteBuffer buffer;
+ private final int start;
+ private final int capacity;
+ private int position;
+ private int limit;
+ private AtomicBoolean barrier;
+
+ /**
+ * Use the ByteBufferBytes.wrap(ByteBuffer) as DirectByteBuffer more efficient
+ */
+ @Deprecated
public ByteBufferBytes(ByteBuffer buffer) {
this(buffer, 0, buffer.capacity());
}
- public ByteBufferBytes(ByteBuffer buffer, int start, int limit) {
+ /**
+ * Use the ByteBufferBytes.wrap(ByteBuffer) as DirectByteBuffer more efficient
+ *
+ * @param buffer the buffer to populate
+ * @param start start of buffer
+ * * @param capacity len of buffer
+ */
+ @Deprecated
+ public ByteBufferBytes(ByteBuffer buffer, int start, int capacity) {
+ super(BytesMarshallableSerializer.create(new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE), new AtomicInteger(1));
+ // We should set order to native, because compare-and-swap operations
+ // end up with native ops. Bytes interfaces handles only native order.
+ buffer.order(ByteOrder.nativeOrder());
this.buffer = buffer;
this.start = position = start;
- this.limit = limit;
+ this.capacity = limit = (capacity + start);
+ }
+
+ public static IByteBufferBytes wrap(ByteBuffer buffer) {
+ if (buffer instanceof DirectBuffer) {
+ return new DirectByteBufferBytes(buffer);
+ }
+
+ return new ByteBufferBytes(buffer.slice());
+ }
+
+ public static IByteBufferBytes wrap(ByteBuffer buffer, int start, int capacity) {
+ if (buffer instanceof DirectBuffer) {
+ return new DirectByteBufferBytes(buffer, start, capacity);
+ }
+
+ return new ByteBufferBytes(buffer.slice(), start, capacity);
+ }
+
+ @Override
+ public ByteBufferBytes slice() {
+ return new ByteBufferBytes(buffer(), position, limit - position);
+ }
+
+ @Override
+ public ByteBufferBytes slice(long offset, long length) {
+ long sliceStart = position + offset;
+ assert sliceStart >= start && sliceStart < capacity;
+ long sliceEnd = sliceStart + length;
+ assert sliceEnd > sliceStart && sliceEnd <= capacity;
+ return new ByteBufferBytes(buffer(), (int) sliceStart, (int) length);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ int subStart = position + start;
+ if (subStart < position || subStart > limit)
+ throw new IndexOutOfBoundsException();
+ int subEnd = position + end;
+ if (subEnd < subStart || subEnd > limit)
+ throw new IndexOutOfBoundsException();
+ if (start == end)
+ return "";
+ return new ByteBufferBytes(buffer(), subStart, end - start);
+ }
+
+ @Override
+ public ByteBufferBytes bytes() {
+ return new ByteBufferBytes(buffer(), start, capacity - start);
+ }
+
+ @Override
+ public ByteBufferBytes bytes(long offset, long length) {
+ long sliceStart = start + offset;
+ assert sliceStart >= start && sliceStart < capacity;
+ long sliceEnd = sliceStart + length;
+ assert sliceEnd > sliceStart && sliceEnd <= capacity;
+ return new ByteBufferBytes(buffer(), (int) sliceStart, (int) length);
+ }
+
+ @Override
+ public long address() {
+ if (buffer instanceof DirectBuffer) {
+ long address = ((DirectBuffer) buffer).address();
+ if (address == 0)
+ throw new IllegalStateException("This buffer has no address, is it empty?");
+ return address;
+ }
+ throw new IllegalStateException("A heap ByteBuffer doesn't have a fixed address");
+ }
+
+ @Override
+ public Bytes zeroOut() {
+ clear();
+ int i = start;
+ for (; i < capacity - 7; i++)
+ buffer.putLong(i, 0L);
+ for (; i < capacity; i++)
+ buffer.put(i, (byte) 0);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end) {
+ if (start < 0 || end > limit())
+ throw new IllegalArgumentException("start: " + start + ", end: " + end);
+ if (start >= end)
+ return this;
+ int i = (int) (this.start + start);
+ int j = (int) (this.start + end);
+ for (; i < j - 7; i++)
+ buffer.putLong(i, 0L);
+ for (; i < j; i++)
+ buffer.put(i, (byte) 0);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end, boolean ifNotZero) {
+ // ByteBuffers are allocated in memory eagerly.
+ return zeroOut(start, end);
}
public ByteBuffer buffer() {
return buffer;
}
- protected void readBarrier() {
+ void readBarrier() {
if (barrier == null) barrier = new AtomicBoolean();
barrier.get();
}
- protected void writeBarrier() {
+ void writeBarrier() {
if (barrier == null) barrier = new AtomicBoolean();
barrier.lazySet(false);
}
@Override
public int read(@NotNull byte[] bytes, int off, int len) {
- if (len < 0 || off < 0 || off + len > bytes.length)
- throw new IllegalArgumentException();
+ checkArrayOffs(bytes.length, off, len);
long left = remaining();
if (left <= 0) return -1;
int len2 = (int) Math.min(left, len);
@@ -70,7 +192,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public byte readByte() {
- if (position < limit)
+ if (position < capacity)
return buffer.get(position++);
throw new IndexOutOfBoundsException();
}
@@ -78,15 +200,14 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public byte readByte(long offset) {
int pos = (int) (start + offset);
- if (pos < limit)
+ if (pos < capacity)
return buffer.get(pos);
throw new IndexOutOfBoundsException();
}
@Override
public void readFully(@NotNull byte[] b, int off, int len) {
- if (len < 0 || off < 0 || off + len > b.length)
- throw new IllegalArgumentException();
+ checkArrayOffs(b.length, off, len);
long left = remaining();
if (left < len)
throw new IllegalStateException(new EOFException());
@@ -95,8 +216,28 @@ public class ByteBufferBytes extends AbstractBytes {
}
@Override
+ public void readFully(@NotNull char[] data, int off, int len) {
+ checkArrayOffs(data.length, off, len);
+ long left = remaining();
+ if (left < len * 2L)
+ throw new IllegalStateException(new EOFException());
+ for (int i = 0; i < len; i++)
+ data[off + i] = readChar();
+ }
+
+ @Override
+ public void readFully(long offset, byte[] bytes, int off, int len) {
+ checkArrayOffs(bytes.length, off, len);
+ long left = remaining();
+ if (left < len)
+ throw new IllegalStateException(new EOFException());
+ for (int i = 0; i < len; i++)
+ bytes[off + i] = readByte(offset + i);
+ }
+
+ @Override
public short readShort() {
- if (position + 2 <= limit) {
+ if (position + 2 <= capacity) {
short s = buffer.getShort(position);
position += 2;
return s;
@@ -107,14 +248,14 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public short readShort(long offset) {
int pos = (int) (start + offset);
- if (pos + 2 <= limit)
+ if (pos + 2 <= capacity)
return buffer.getShort(pos);
throw new IndexOutOfBoundsException();
}
@Override
public char readChar() {
- if (position + 2 <= limit) {
+ if (position + 2 <= capacity) {
char ch = buffer.getChar(position);
position += 2;
return ch;
@@ -125,14 +266,14 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public char readChar(long offset) {
int pos = (int) (start + offset);
- if (pos + 2 <= limit)
+ if (pos + 2 <= capacity)
return buffer.getChar(pos);
throw new IndexOutOfBoundsException();
}
@Override
public int readInt() {
- if (position + 4 <= limit) {
+ if (position + 4 <= capacity) {
int i = buffer.getInt(position);
position += 4;
return i;
@@ -143,7 +284,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public int readInt(long offset) {
int pos = (int) (start + offset);
- if (pos + 4 <= limit)
+ if (pos + 4 <= capacity)
return buffer.getInt(pos);
throw new IndexOutOfBoundsException();
}
@@ -162,7 +303,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public long readLong() {
- if (position + 8 <= limit) {
+ if (position + 8 <= capacity) {
long l = buffer.getLong(position);
position += 8;
return l;
@@ -173,7 +314,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public long readLong(long offset) {
int pos = (int) (start + offset);
- if (pos + 8 <= limit)
+ if (pos + 8 <= capacity)
return buffer.getLong(pos);
throw new IndexOutOfBoundsException();
}
@@ -192,7 +333,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public float readFloat() {
- if (position + 4 <= limit) {
+ if (position + 4 <= capacity) {
float f = buffer.getFloat(position);
position += 4;
return f;
@@ -203,14 +344,14 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public float readFloat(long offset) {
int pos = (int) (start + offset);
- if (pos + 4 <= limit)
+ if (pos + 4 <= capacity)
return buffer.getFloat(pos);
throw new IndexOutOfBoundsException();
}
@Override
public double readDouble() {
- if (position + 8 <= limit) {
+ if (position + 8 <= capacity) {
double d = buffer.getDouble(position);
position += 8;
return d;
@@ -221,14 +362,14 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public double readDouble(long offset) {
int pos = (int) (start + offset);
- if (pos + 8 <= limit)
+ if (pos + 8 <= capacity)
return buffer.getDouble(pos);
throw new IndexOutOfBoundsException();
}
@Override
public void write(int b) {
- if (position < limit)
+ if (position < capacity)
buffer.put(position++, (byte) b);
else
throw new IndexOutOfBoundsException();
@@ -237,7 +378,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeByte(long offset, int b) {
int pos = (int) (start + offset);
- if (pos < limit)
+ if (pos < capacity)
buffer.put(pos, (byte) b);
else
throw new IndexOutOfBoundsException();
@@ -245,9 +386,10 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeShort(int v) {
- if (position + 2 <= limit) {
+ if (position + 2 <= capacity) {
buffer.putShort(position, (short) v);
position += 2;
+
} else {
throw new IndexOutOfBoundsException();
}
@@ -256,7 +398,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeShort(long offset, int v) {
int pos = (int) (start + offset);
- if (pos + 2 <= limit)
+ if (pos + 2 <= capacity)
buffer.putShort(pos, (short) v);
else
throw new IndexOutOfBoundsException();
@@ -264,9 +406,10 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeChar(int v) {
- if (position + 2 <= limit) {
+ if (position + 2 <= capacity) {
buffer.putChar(position, (char) v);
position += 2;
+
} else {
throw new IndexOutOfBoundsException();
}
@@ -275,7 +418,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeChar(long offset, int v) {
int pos = (int) (start + offset);
- if (pos + 2 <= limit)
+ if (pos + 2 <= capacity)
buffer.putChar(pos, (char) v);
else
throw new IndexOutOfBoundsException();
@@ -283,9 +426,10 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeInt(int v) {
- if (position + 4 <= limit) {
+ if (position + 4 <= capacity) {
buffer.putInt(position, v);
position += 4;
+
} else {
throw new IndexOutOfBoundsException();
}
@@ -294,7 +438,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeInt(long offset, int v) {
int pos = (int) (start + offset);
- if (pos + 4 <= limit)
+ if (pos + 4 <= capacity)
buffer.putInt(pos, v);
else
throw new IndexOutOfBoundsException();
@@ -321,9 +465,10 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeLong(long v) {
- if (position + 8 <= limit) {
+ if (position + 8 <= capacity) {
buffer.putLong(position, v);
position += 8;
+
} else {
throw new IndexOutOfBoundsException();
}
@@ -332,7 +477,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeLong(long offset, long v) {
int pos = (int) (start + offset);
- if (pos + 8 <= limit)
+ if (pos + 8 <= capacity)
buffer.putLong(pos, v);
else
throw new IndexOutOfBoundsException();
@@ -359,9 +504,10 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeFloat(float v) {
- if (position + 4 <= limit) {
+ if (position + 4 <= capacity) {
buffer.putFloat(position, v);
position += 4;
+
} else {
throw new IndexOutOfBoundsException();
}
@@ -370,7 +516,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeFloat(long offset, float v) {
int pos = (int) (start + offset);
- if (pos + 4 <= limit)
+ if (pos + 4 <= capacity)
buffer.putFloat(pos, v);
else
throw new IndexOutOfBoundsException();
@@ -378,9 +524,10 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeDouble(double v) {
- if (position + 8 <= limit) {
+ if (position + 8 <= capacity) {
buffer.putDouble(position, v);
position += 8;
+
} else {
throw new IndexOutOfBoundsException();
}
@@ -389,7 +536,7 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void writeDouble(long offset, double v) {
int pos = (int) (start + offset);
- if (pos + 8 <= limit)
+ if (pos + 8 <= capacity)
buffer.putDouble(pos, v);
else
throw new IndexOutOfBoundsException();
@@ -411,15 +558,19 @@ public class ByteBufferBytes extends AbstractBytes {
}
@Override
- public void position(long position) {
+ public ByteBufferBytes position(long position) {
if (start + position > Integer.MAX_VALUE)
- throw new IndexOutOfBoundsException("Position to large");
+ throw new IllegalArgumentException("Position to large");
+ if (position < 0)
+ throw new IllegalArgumentException("Position to small");
+
this.position = (int) (start + position);
+ return this;
}
@Override
public long capacity() {
- return limit - start;
+ return capacity - start;
}
@Override
@@ -427,6 +578,17 @@ public class ByteBufferBytes extends AbstractBytes {
return limit - position;
}
+ @Override
+ public long limit() {
+ return limit - start;
+ }
+
+ @Override
+ public ByteBufferBytes limit(long limit) {
+ this.limit = (int) (start + limit);
+ return this;
+ }
+
@NotNull
@Override
public ByteOrder byteOrder() {
@@ -435,7 +597,25 @@ public class ByteBufferBytes extends AbstractBytes {
@Override
public void checkEndOfBuffer() throws IndexOutOfBoundsException {
- if (position < start || position > limit)
+ if (position < start || position > limit()) {
throw new IndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ protected void cleanup() {
+ IOTools.clean(buffer);
+ }
+
+ @Override
+ public Bytes load() {
+ int pageSize = NativeBytes.UNSAFE.pageSize();
+ for (int offset = start; offset < capacity; offset += pageSize)
+ buffer.get(offset);
+ return this;
+ }
+
+ public void alignPositionAddr(int powerOf2) {
+ position = (position + powerOf2 - 1) & ~(powerOf2 - 1);
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java b/lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java
new file mode 100644
index 0000000..e14c9e0
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/ByteBufferReuse.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import sun.misc.Unsafe;
+
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+
+interface ByteBufferReuse {
+ ByteBufferReuse INSTANCE = Inner.getReuse();
+
+ ByteBuffer reuse(long addr, int cap, Object att, ByteBuffer toReuse);
+
+ class Inner extends Reuses implements Opcodes {
+ private static ByteBufferReuse getReuse() {
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ final String reuseImplClassName = "net/openhft/lang/io/ByteBufferReuseImpl";
+ cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, reuseImplClassName, null,
+ "sun/reflect/MagicAccessorImpl",
+ new String[]{"net/openhft/lang/io/ByteBufferReuse"});
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+
+ String attachedBufferFieldName = getAttachedBufferFieldName();
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "reuse",
+ "(JILjava/lang/Object;Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;",
+ null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 5);
+ String directByteBuffer = "java/nio/DirectByteBuffer";
+ mv.visitTypeInsn(INSTANCEOF, directByteBuffer);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IFEQ, l0);
+ mv.visitVarInsn(ALOAD, 5);
+ mv.visitTypeInsn(CHECKCAST, directByteBuffer);
+ mv.visitVarInsn(ASTORE, 6);
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitFieldInsn(GETFIELD, directByteBuffer, attachedBufferFieldName, "Ljava/lang/Object;");
+ String settableAtt = "net/openhft/lang/io/SettableAtt";
+ mv.visitTypeInsn(INSTANCEOF, settableAtt);
+ mv.visitJumpInsn(IFEQ, l0);
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitVarInsn(LLOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, directByteBuffer, "address", "J");
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitInsn(ICONST_M1);
+ mv.visitFieldInsn(PUTFIELD, directByteBuffer, "mark", "I");
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitInsn(ICONST_0);
+ mv.visitFieldInsn(PUTFIELD, directByteBuffer, "position", "I");
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitFieldInsn(PUTFIELD, directByteBuffer, "limit", "I");
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitFieldInsn(PUTFIELD, directByteBuffer, "capacity", "I");
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitFieldInsn(GETFIELD, directByteBuffer, attachedBufferFieldName, "Ljava/lang/Object;");
+ mv.visitTypeInsn(CHECKCAST, settableAtt);
+ mv.visitVarInsn(ALOAD, 4);
+ mv.visitFieldInsn(PUTFIELD, settableAtt, "att", "Ljava/lang/Object;");
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l0);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitTypeInsn(NEW, settableAtt);
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, settableAtt, "<init>", "()V", false);
+ mv.visitVarInsn(ASTORE, 6);
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitVarInsn(ALOAD, 4);
+ mv.visitFieldInsn(PUTFIELD, settableAtt, "att", "Ljava/lang/Object;");
+ mv.visitTypeInsn(NEW, directByteBuffer);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(LLOAD, 1);
+ mv.visitVarInsn(ILOAD, 3);
+ mv.visitVarInsn(ALOAD, 6);
+ mv.visitMethodInsn(INVOKESPECIAL, directByteBuffer, "<init>",
+ "(JILjava/lang/Object;)V", false);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(6, 7);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ final byte[] impl = cw.toByteArray();
+
+ final Unsafe unsafe = NativeBytes.UNSAFE;
+ Class clazz = AccessController.doPrivileged(new PrivilegedAction<Class>() {
+ @Override
+ public Class run() {
+ ClassLoader cl = MAGIC_CLASS_LOADER;
+ return unsafe.defineClass(reuseImplClassName, impl, 0, impl.length, cl, null);
+ }
+ });
+ try {
+ return (ByteBufferReuse) clazz.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String getAttachedBufferFieldName() {
+ try {
+ Class<?> clazz = Class.forName("java.nio.DirectByteBuffer");
+ String[] possibleFieldNames = new String[] { "att",
+ "viewedBuffer" };
+ for (String possibleFieldName : possibleFieldNames) {
+ try {
+ clazz.getDeclaredField(possibleFieldName);
+ return possibleFieldName;
+ } catch (Exception e) {
+ continue;
+ }
+ }
+
+ throw new RuntimeException(
+ "Failed to find any of the possible field names on DirectByteBuffer: "
+ + Arrays.toString(possibleFieldNames));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java b/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java
index fd36102..281ba9a 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java
+++ b/lang/src/main/java/net/openhft/lang/io/ByteStringAppender.java
@@ -1,23 +1,23 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
/**
* @author peter.lawrey
diff --git a/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java b/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java
index 10132cc..d80c3bf 100755
--- a/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java
+++ b/lang/src/main/java/net/openhft/lang/io/ByteStringParser.java
@@ -1,69 +1,91 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.nio.BufferUnderflowException;
/**
* @author peter.lawrey
*/
public interface ByteStringParser extends BytesCommon {
/**
- * Return true or false, or null if it could not be detected as true or false. Case is not important
- * <p/>
- * false: f, false, n, no, 0
- * <p/>
- * true: t, true, y, yes, 1
+ * If set to true, the end of the Bytes will be the end of any consuming String, double or long.
+ * If false, incomplete reads will throw a BufferUnderflowException
+
+ * @param selfTerminate if true, the end of the Buffer is silent.
+ */
+ void selfTerminating(boolean selfTerminate);
+
+ /**
+ * @return if this Bytes self terminates.
+ */
+ boolean selfTerminating();
+
+ /**
+ * @return the next unsigned byte or -1 if selfTerminating and the end is reached.
+ * @throws BufferUnderflowException if the end is reached and selfTerminating is false.
+ */
+ int readUnsignedByteOrThrow() throws BufferUnderflowException;
+
+ /**
+ * Return true or false, or null if it could not be detected
+ * as true or false. Case is not important
+ *
+ * <p>false: f, false, n, no, 0
+ *
+ * <p>true: t, true, y, yes, 1
*
* @param tester to detect the end of the text.
* @return true, false, or null if neither.
*/
- Boolean parseBoolean(@NotNull StopCharTester tester);
+ Boolean parseBoolean(@NotNull StopCharTester tester) throws BufferUnderflowException;
/**
* Populate a StringBuilder with the UTF encoded text until the end.
*
- * @param builder to clear and append to.
+ * @param builder to zeroOut and append to.
* @param tester to detect when to stop.
*/
- void parseUTF(@NotNull StringBuilder builder, @NotNull StopCharTester tester);
+ void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) throws BufferUnderflowException;
@NotNull
- String parseUTF(@NotNull StopCharTester tester);
+ String parseUtf8(@NotNull StopCharTester tester) throws BufferUnderflowException;
@Nullable
- <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester);
+ <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) throws BufferUnderflowException;
@NotNull
- MutableDecimal parseDecimal(@NotNull MutableDecimal decimal);
+ MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) throws BufferUnderflowException;
/**
* @return the next long, stopping at the first invalid character
*/
- long parseLong();
+ long parseLong() throws BufferUnderflowException;
/**
* @param base to use.
* @return the next long, stopping at the first invalid character
*/
- long parseLong(int base);
+ long parseLong(int base) throws BufferUnderflowException;
- double parseDouble();
+ double parseDouble() throws BufferUnderflowException;
/**
* Make sure we just read a stop character
@@ -80,4 +102,18 @@ public interface ByteStringParser extends BytesCommon {
* @return true if we stopped at a stop character, false if we ran out of data.
*/
boolean skipTo(@NotNull StopCharTester tester);
+
+ /**
+ * Dump the contents of this Bytes as text in the Appendable.
+ *
+ * @param appendable to append to
+ */
+ void asString(Appendable appendable);
+
+ /**
+ * Dump the contents of Bytes as a CharSequence
+ *
+ * @return the CharSequence for these Bytes.
+ */
+ CharSequence asString();
}
diff --git a/lang/src/main/java/net/openhft/lang/io/Bytes.java b/lang/src/main/java/net/openhft/lang/io/Bytes.java
index c520613..e9a21be 100755
--- a/lang/src/main/java/net/openhft/lang/io/Bytes.java
+++ b/lang/src/main/java/net/openhft/lang/io/Bytes.java
@@ -1,26 +1,33 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
+import net.openhft.lang.ReferenceCounted;
import net.openhft.lang.io.serialization.BytesMarshallable;
/**
* @author peter.lawrey
*/
-public interface Bytes extends RandomDataInput, RandomDataOutput, RandomDataUpdate,
- ByteStringAppender, ByteStringParser, CharSequence, BytesMarshallable {
+public interface Bytes extends RandomDataInput, RandomDataOutput, RandomDataUpdate, BytesStore,
+ ByteStringAppender, ByteStringParser, CharSequence, BytesMarshallable, ReferenceCounted {
+
+ /**
+ * Needed if the buffer is created in one thread and used in another.
+ */
+ void clearThreadAssociation();
+
}
diff --git a/lang/src/main/java/net/openhft/lang/io/BytesCommon.java b/lang/src/main/java/net/openhft/lang/io/BytesCommon.java
index 0549f61..88eb529 100755
--- a/lang/src/main/java/net/openhft/lang/io/BytesCommon.java
+++ b/lang/src/main/java/net/openhft/lang/io/BytesCommon.java
@@ -1,26 +1,28 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import net.openhft.lang.io.serialization.BytesMarshallerFactory;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.model.constraints.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
@@ -28,14 +30,27 @@ import java.nio.ByteOrder;
*/
public interface BytesCommon {
/**
- * @return the offset read/written so far
+ * @return the offset read/written which must be &gt;= limit()
*/
long position();
/**
* @param position to skip to
+ * @return this bytes object back
+ * @throws java.lang.IllegalArgumentException if positions &lt; 0 || position &gt;= limit
*/
- void position(long position);
+ Bytes position(long position) throws IllegalArgumentException;
+
+ /**
+ * @return the current limit which must be &gt;= capacity()
+ */
+ long limit();
+
+ /**
+ * @param limit the new limit which must be &gt;= capacity()
+ * @return this bytes object back
+ */
+ Bytes limit(long limit);
/**
* @return space available
@@ -60,9 +75,46 @@ public interface BytesCommon {
boolean isFinished();
/**
- * Start again, unfinished, position() == 0
+ * Clears this buffer. The position is set to zero, the limit is set to
+ * the capacity, and the mark is discarded.
+ *
+ * <p> Invoke this method before using a sequence of channel-read or
+ * <i>put</i> operations to fill this buffer. For example:
+ * <pre>{@code
+ * buf.clear(); // Prepare buffer for reading
+ * in.read(buf); // Read data
+ * }</pre>
+ *
+ * <p>This method does not actually erase the data in the buffer, but it
+ * is named as if it did because it will most often be used in situations
+ * in which that might as well be the case. </p>
+ *
+ * @return This buffer
*/
- void reset();
+ Bytes clear();
+
+ /**
+ * Flips this buffer. The limit is set to the current position and then
+ * the position is set to zero. If the mark is defined then it is
+ * discarded.
+ *
+ * <p> After a sequence of channel-read or <i>put</i> operations, invoke
+ * this method to prepare for a sequence of channel-write or relative
+ * <i>get</i> operations. For example:
+ * <pre>{@code
+ * buf.put(magic); // Prepend header
+ * in.read(buf); // Read data into rest of buffer
+ * buf.flip(); // Flip buffer
+ * out.write(buf); // Write header + data to channel
+ * }</pre>
+ *
+ * <p> This method is often used in conjunction with the {@link
+ * java.nio.ByteBuffer#compact compact} method when transferring data from
+ * one place to another. </p>
+ *
+ * @return This buffer
+ */
+ Bytes flip();
/**
* @return Byte order for reading binary
@@ -86,7 +138,7 @@ public interface BytesCommon {
* @return the factory for marshallers.
*/
@NotNull
- BytesMarshallerFactory bytesMarshallerFactory();
+ ObjectSerializer objectSerializer();
/**
* @throws IndexOutOfBoundsException if the bounds of the Bytes has been exceeded.
@@ -94,11 +146,98 @@ public interface BytesCommon {
void checkEndOfBuffer() throws IndexOutOfBoundsException;
/**
- * Copy from one Bytes to another, moves the position by length
+ * Access every page to ensure those pages are in memory.
+ *
+ * @return this.
+ */
+ Bytes load();
+
+ /**
+ * Write a portion of the Bytes to an Appendable for printing.
+ *
+ * @param sb to append to
+ * @param start first byte
+ * @param position where to place a cursor or 0 = none.
+ * @param end last byte to append.
+ */
+ void toString(Appendable sb, long start, long position, long end);
+
+ /**
+ * Align the position address to a power of 2.
*
- * @param bytes to copy
- * @param position to copy from
- * @param length to copy
+ * @param alignment power of 2 to align to.
*/
- void write(BytesCommon bytes, long position, long length);
+ void alignPositionAddr(int alignment);
+
+ /**
+ * Creates a new bytes whose content is a shared subsequence of this bytes'
+ * content.
+ *
+ * <p>The content of the new bytes will start at this bytes' current
+ * position. Changes to this bytes' content will be visible in the new bytes,
+ * and vice versa; the two bytes' position and limit values will be
+ * independent.
+ *
+ * <p>The new bytes' position will be zero, its capacity and its limit
+ * will be the number of bytes remaining in this bytes.
+ *
+ * <p>{@code slice()} is equivalent of {@code slice(0, remaining())}.
+ *
+ * @return the new bytes
+ * @see #slice(long, long)
+ */
+ Bytes slice();
+
+ /**
+ * Returns a {@code ByteBuffer} whose content is a shared subsequence of this bytes' content.
+ *
+ * <p>The content of the returned {@code ByteBuffer} will start at this bytes' current
+ * position. Changes to this bytes' content will be visible in the returned {@code ByteBuffer},
+ * and vice versa; this bytes' and the returned {@code ByteBuffer}'s position and limit values
+ * will be independent.
+ *
+ * <p>The returned {@code ByteBuffer}'s position will be zero, its capacity and its limit
+ * will be the number of bytes remaining in this bytes.
+ *
+ * <p>If this bytes object is able to reuse to given {@code toReuse} {@code ByteBuffer}, it will
+ * be reused and returned back from this method, otherwise a new {@code ByteBuffer} instance
+ * is created and returned.
+ *
+ * @param toReuse a {@code ByteBuffer} to reuse
+ * @return a {@code ByteBuffer} view of this {@code Bytes}
+ * @see #slice()
+ * @see ByteBuffer#slice()
+ */
+ ByteBuffer sliceAsByteBuffer(@Nullable ByteBuffer toReuse);
+
+ /**
+ * Creates a new bytes whose content is a shared subsequence of this bytes'
+ * content.
+ *
+ * <p>The content of the new bytes will start at this bytes' current
+ * {@link #position()}{@code + offset}. Changes to this bytes' content
+ * will be visible in the new bytes, and vice versa; the two bytes'
+ * position and limit values will be independent.
+ *
+ * <p>The new bytes' position will be zero, its capacity and its limit
+ * will be equal to {@code length}.
+ *
+ * <p>{@code offset} can be negative (if current bytes' position is positive)
+ * and {@code length} can run out of current bytes' limit, the restriction
+ * is that new bytes' should be within this bytes' absolute bounds.
+ *
+ * @param offset relative offset of the new bytes from the current bytes'
+ * position
+ * @param length capacity of the new bytes
+ * @return the new bytes
+ * @see #slice()
+ */
+ Bytes slice(long offset, long length);
+
+ @NotNull
+ String toDebugString();
+
+ String toDebugString(long limit);
+
+ String toHexString(long limit);
}
diff --git a/lang/src/main/java/net/openhft/lang/io/BytesHasher.java b/lang/src/main/java/net/openhft/lang/io/BytesHasher.java
new file mode 100644
index 0000000..cb9535f
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/BytesHasher.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+public interface BytesHasher {
+ /**
+ * Provide a 64-bit hash for the bytes in Bytes between the bytes.position() and bytes.limit();
+ *
+ * @param bytes to hash
+ * @return 64-bit hash
+ */
+ long hash(Bytes bytes);
+
+ /**
+ * Provide a 64-bit hash for the bytes between offset and limit
+ *
+ * @param bytes to hash
+ * @param offset the start inclusive
+ * @param limit the end exclusive
+ * @return 64-bit hash.
+ */
+ long hash(Bytes bytes, long offset, long limit);
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/BytesStore.java b/lang/src/main/java/net/openhft/lang/io/BytesStore.java
new file mode 100644
index 0000000..b6ec3a9
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/BytesStore.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.ObjectSerializer;
+
+import java.io.File;
+
+public interface BytesStore {
+ /**
+ * Create a bytes whose content is the whole bytes store. Call of this
+ * method is equivalent to {@code bytes(0, size())} call.
+ *
+ * @return the new bytes
+ * @see #bytes(long, long)
+ */
+ Bytes bytes();
+
+ /**
+ * Slice a {@code Bytes} object with start address of
+ * {@link #address() address}{@code + offset} and capacity of {@code length}.
+ *
+ * <p>If this {@code BytesStore} is {@code Bytes} itself rather than natural
+ * {@code BytesStore} object, this method will offset the new bytes from the
+ * bytes' start, not from bytes' position like
+ * {@link Bytes#slice(long, long)}.
+ *
+ * <p>{@code offset} should be non-negative, {@code length} should be positive,
+ * {@code offset + length} should be less or equal to {@link #size() size}.
+ *
+ * @param offset offset of the new bytes from the bytes store address
+ * @param length capacity and limit of the new bytes
+ * @return the sliced {@code Bytes}
+ * @see #bytes()
+ */
+ Bytes bytes(long offset, long length);
+
+ long address();
+
+ long size();
+
+ void free();
+
+ ObjectSerializer objectSerializer();
+
+ File file();
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java b/lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java
new file mode 100644
index 0000000..89ef031
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/CharBufferReuse.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import sun.misc.Unsafe;
+
+import java.nio.CharBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+interface CharBufferReuse {
+ CharBufferReuse INSTANCE = Inner.getReuse();
+
+ CharBuffer reuse(CharSequence cs, CharBuffer toReuse);
+
+ class Inner extends Reuses implements Opcodes {
+ private static CharBufferReuse getReuse() {
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ final String reuseImplClassName = "net/openhft/lang/io/CharBufferReuseImpl";
+ cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, reuseImplClassName, null,
+ "sun/reflect/MagicAccessorImpl",
+ new String[] {"net/openhft/lang/io/CharBufferReuse"});
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "reuse",
+ "(Ljava/lang/CharSequence;Ljava/nio/CharBuffer;)Ljava/nio/CharBuffer;",
+ null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 2);
+ String stringCharBuffer = "java/nio/StringCharBuffer";
+ mv.visitTypeInsn(INSTANCEOF, stringCharBuffer);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IFEQ, l0);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, stringCharBuffer);
+ mv.visitVarInsn(ASTORE, 3);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitInsn(ICONST_M1);
+ mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "mark", "I");
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitInsn(ICONST_0);
+ mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "position", "I");
+ mv.visitVarInsn(ALOAD, 1);
+ String charSequence = "java/lang/CharSequence";
+ mv.visitMethodInsn(INVOKEINTERFACE, charSequence, "length", "()I", true);
+ mv.visitVarInsn(ISTORE, 4);
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "limit", "I");
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitVarInsn(ILOAD, 4);
+ mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "capacity", "I");
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitFieldInsn(PUTFIELD, stringCharBuffer, "str", "Ljava/lang/CharSequence;");
+ mv.visitVarInsn(ALOAD, 3);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l0);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitTypeInsn(NEW, stringCharBuffer);
+ mv.visitInsn(DUP);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitInsn(ICONST_0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKEINTERFACE, charSequence, "length", "()I", true);
+ mv.visitMethodInsn(INVOKESPECIAL, stringCharBuffer, "<init>",
+ "(Ljava/lang/CharSequence;II)V", false);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(5, 5);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ final byte[] impl = cw.toByteArray();
+
+ final Unsafe unsafe = NativeBytes.UNSAFE;
+ Class clazz = AccessController.doPrivileged(new PrivilegedAction<Class>() {
+ @Override
+ public Class run() {
+ ClassLoader cl = MAGIC_CLASS_LOADER;
+ return unsafe.defineClass(reuseImplClassName, impl, 0, impl.length, cl, null);
+ }
+ });
+ try {
+ return (CharBufferReuse) clazz.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/CharBuffers.java b/lang/src/main/java/net/openhft/lang/io/CharBuffers.java
new file mode 100644
index 0000000..546aeb6
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/CharBuffers.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import java.nio.CharBuffer;
+
+public final class CharBuffers {
+
+ public static CharBuffer wrap(CharSequence cs, CharBuffer toReuse) {
+ return CharBufferReuse.INSTANCE.reuse(cs, toReuse);
+ }
+
+ private CharBuffers() {}
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/CheckedBytes.java b/lang/src/main/java/net/openhft/lang/io/CheckedBytes.java
new file mode 100755
index 0000000..31e7526
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/CheckedBytes.java
@@ -0,0 +1,1313 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.model.Byteable;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+public class CheckedBytes implements Bytes {
+ private final Bytes bytes;
+ volatile boolean isClosed = false;
+
+ public CheckedBytes(Bytes bytes) {
+ this.checkNotClosed();
+ this.bytes = bytes;
+ }
+
+ public void clearThreadAssociation() {
+ this.checkNotClosed();
+ this.bytes.clearThreadAssociation();
+ }
+
+ public long size() {
+ this.checkNotClosed();
+ return this.bytes.size();
+ }
+
+ void checkNotClosed() {
+ if(this.isClosed) {
+ System.err.print("Thread " + Thread.currentThread().getName() + " performing processing " + "after free()");
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
+ ThreadInfo[] arr$ = threads;
+ int len$ = threads.length;
+
+ for(int i$ = 0; i$ < len$; ++i$) {
+ ThreadInfo info = arr$[i$];
+ System.out.print(info);
+ }
+
+ System.exit(-1);
+ }
+ }
+ public void free() {
+ this.isClosed = true;
+ this.bytes.free();
+ }
+
+ public void reserve() {
+ this.checkNotClosed();
+ this.bytes.reserve();
+ }
+
+ public boolean release() {
+ this.checkNotClosed();
+ return this.bytes.release();
+ }
+
+ public int refCount() {
+ this.checkNotClosed();
+ return this.bytes.refCount();
+ }
+
+ public void selfTerminating(boolean selfTerminating) {
+ this.checkNotClosed();
+ this.bytes.selfTerminating(selfTerminating);
+ }
+
+ public boolean selfTerminating() {
+ this.checkNotClosed();
+ return this.bytes.selfTerminating();
+ }
+
+ public int readUnsignedByteOrThrow() throws BufferUnderflowException {
+ return this.bytes.readUnsignedByteOrThrow();
+ }
+
+ public void write(long offset, Bytes bytes) {
+ bytes.write(offset, bytes);
+ }
+
+ public Boolean parseBoolean(@NotNull StopCharTester tester) {
+ this.checkNotClosed();
+ return this.bytes.parseBoolean(tester);
+ }
+
+ public void readFully(@NotNull byte[] bytes) {
+ this.checkNotClosed();
+ this.bytes.readFully(bytes);
+ }
+
+ public void readFully(@NotNull char[] data) {
+ this.checkNotClosed();
+ this.bytes.readFully(data);
+ }
+
+ public int skipBytes(int n) {
+ this.checkNotClosed();
+ return this.bytes.skipBytes(n);
+ }
+
+ public boolean readBoolean() {
+ this.checkNotClosed();
+ return this.bytes.readBoolean();
+ }
+
+ public boolean readBoolean(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readBoolean(offset);
+ }
+
+ public int readUnsignedByte() {
+ this.checkNotClosed();
+ return this.bytes.readUnsignedByte();
+ }
+
+ public int readUnsignedByte(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readUnsignedByte(offset);
+ }
+
+ public int readUnsignedShort() {
+ this.checkNotClosed();
+ return this.bytes.readUnsignedShort();
+ }
+
+ public int readUnsignedShort(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readUnsignedShort(offset);
+ }
+
+ @NotNull
+ public String readLine() {
+ this.checkNotClosed();
+ return this.bytes.readLine();
+ }
+
+ @Nullable
+ public String readUTFΔ() {
+ this.checkNotClosed();
+ return this.bytes.readUTFΔ();
+ }
+
+ @Nullable
+ public String readUTFΔ(long offset) throws IllegalStateException {
+ return this.bytes.readUTFΔ(offset);
+ }
+
+ public boolean readUTFΔ(@NotNull StringBuilder stringBuilder) {
+ this.checkNotClosed();
+ return this.bytes.readUTFΔ(stringBuilder);
+ }
+
+ @Override
+ public boolean read8bitText(@NotNull StringBuilder stringBuilder) throws StreamCorruptedException {
+ return false;
+ }
+
+ @NotNull
+ public String parseUtf8(@NotNull StopCharTester tester) {
+ this.checkNotClosed();
+ return this.bytes.parseUtf8(tester);
+ }
+
+ public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) {
+ this.checkNotClosed();
+ this.bytes.parseUtf8(builder, tester);
+ }
+
+ public boolean stepBackAndSkipTo(@NotNull StopCharTester tester) {
+ this.checkNotClosed();
+ return this.bytes.stepBackAndSkipTo(tester);
+ }
+
+ public boolean skipTo(@NotNull StopCharTester tester) {
+ this.checkNotClosed();
+ return this.bytes.skipTo(tester);
+ }
+
+ @NotNull
+ public String readUTF() {
+ this.checkNotClosed();
+ return this.bytes.readUTF();
+ }
+
+ public short readCompactShort() {
+ this.checkNotClosed();
+ return this.bytes.readCompactShort();
+ }
+
+ public int readCompactUnsignedShort() {
+ this.checkNotClosed();
+ return this.bytes.readCompactUnsignedShort();
+ }
+
+ public int readInt24() {
+ this.checkNotClosed();
+ return this.bytes.readInt24();
+ }
+
+ public int readInt24(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readInt24(offset);
+ }
+
+ public long readUnsignedInt() {
+ this.checkNotClosed();
+ return this.bytes.readUnsignedInt();
+ }
+
+ public long readUnsignedInt(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readUnsignedInt(offset);
+ }
+
+ public int readCompactInt() {
+ this.checkNotClosed();
+ return this.bytes.readCompactInt();
+ }
+
+ public long readCompactUnsignedInt() {
+ this.checkNotClosed();
+ return this.bytes.readCompactUnsignedInt();
+ }
+
+ public long readInt48() {
+ this.checkNotClosed();
+ return this.bytes.readInt48();
+ }
+
+ public long readInt48(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readInt48(offset);
+ }
+
+ public long readCompactLong() {
+ this.checkNotClosed();
+ return this.bytes.readCompactLong();
+ }
+
+ public long readStopBit() {
+ this.checkNotClosed();
+ return this.bytes.readStopBit();
+ }
+
+ public double readCompactDouble() {
+ this.checkNotClosed();
+ return this.bytes.readCompactDouble();
+ }
+
+ public void read(@NotNull ByteBuffer bb) {
+ this.checkNotClosed();
+ this.bytes.read(bb);
+ }
+
+ public void read(@NotNull ByteBuffer bb, int length) {
+ this.checkNotClosed();
+ this.bytes.read(bb, length);
+ }
+
+ public void write(@NotNull byte[] bytes) {
+ this.checkNotClosed();
+ this.bytes.write(bytes);
+ }
+
+ public void writeBoolean(boolean v) {
+ this.checkNotClosed();
+ this.bytes.writeBoolean(v);
+ }
+
+ public void writeBoolean(long offset, boolean v) {
+ this.checkNotClosed();
+ this.bytes.writeBoolean(offset, v);
+ }
+
+ public void writeBytes(@NotNull String s) {
+ this.checkNotClosed();
+ this.bytes.writeBytes(s);
+ }
+
+ public void writeChars(@NotNull String s) {
+ this.checkNotClosed();
+ this.bytes.writeChars(s);
+ }
+
+ public void writeChars(@NotNull CharSequence cs) {
+ this.checkNotClosed();
+ this.bytes.writeChars(cs);
+ }
+
+ public void writeUTF(@NotNull String str) {
+ this.checkNotClosed();
+ this.bytes.writeUTF(str);
+ }
+
+ public void writeUTFΔ(@Nullable CharSequence str) throws IllegalArgumentException {
+ this.bytes.writeUTFΔ(str);
+ }
+
+ public void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException {
+ this.bytes.writeUTFΔ(offset, maxSize, s);
+ }
+
+ @Override
+ public void write8bitText(@Nullable CharSequence s) {
+
+ }
+
+ @NotNull
+ public ByteStringAppender append(@NotNull CharSequence str) {
+ this.checkNotClosed();
+ return this.bytes.append(str);
+ }
+
+ public void writeByte(int v) {
+ this.checkNotClosed();
+ this.bytes.writeByte(v);
+ }
+
+ public void writeUnsignedByte(int v) {
+ this.checkNotClosed();
+ this.bytes.writeUnsignedByte(v);
+ }
+
+ public void writeUnsignedByte(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeUnsignedByte(offset, v);
+ }
+
+ public void write(@NotNull char[] data) {
+ this.checkNotClosed();
+ this.bytes.write(data);
+ }
+
+ public void write(@NotNull char[] data, int off, int len) {
+ this.checkNotClosed();
+ this.bytes.write(data, off, len);
+ }
+
+ public void writeUnsignedShort(int v) {
+ this.checkNotClosed();
+ this.bytes.writeUnsignedShort(v);
+ }
+
+ public void writeUnsignedShort(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeUnsignedShort(offset, v);
+ }
+
+ public void writeCompactShort(int v) {
+ this.checkNotClosed();
+ this.bytes.writeCompactShort(v);
+ }
+
+ public void writeCompactUnsignedShort(int v) {
+ this.checkNotClosed();
+ this.bytes.writeCompactUnsignedShort(v);
+ }
+
+ public void writeInt24(int v) {
+ this.checkNotClosed();
+ this.bytes.writeInt24(v);
+ }
+
+ public void writeInt24(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeInt24(offset, v);
+ }
+
+ public void writeUnsignedInt(long v) {
+ this.checkNotClosed();
+ this.bytes.writeUnsignedInt(v);
+ }
+
+ public void writeUnsignedInt(long offset, long v) {
+ this.checkNotClosed();
+ this.bytes.writeUnsignedInt(offset, v);
+ }
+
+ public void writeCompactInt(int v) {
+ this.checkNotClosed();
+ this.bytes.writeCompactInt(v);
+ }
+
+ public void writeCompactUnsignedInt(long v) {
+ this.checkNotClosed();
+ this.bytes.writeCompactUnsignedInt(v);
+ }
+
+ public void writeInt48(long v) {
+ this.checkNotClosed();
+ this.bytes.writeInt48(v);
+ }
+
+ public void writeInt48(long offset, long v) {
+ this.checkNotClosed();
+ this.bytes.writeInt48(offset, v);
+ }
+
+ public void writeCompactLong(long v) {
+ this.checkNotClosed();
+ this.bytes.writeCompactLong(v);
+ }
+
+ public void writeStopBit(long n) {
+ this.checkNotClosed();
+ this.bytes.writeStopBit(n);
+ }
+
+ public void writeCompactDouble(double v) {
+ this.checkNotClosed();
+ this.bytes.writeCompactDouble(v);
+ }
+
+ public void write(@NotNull ByteBuffer bb) {
+ this.checkNotClosed();
+ this.bytes.write(bb);
+ }
+
+ @NotNull
+ public ByteStringAppender append(@NotNull CharSequence s, int start, int end) {
+ this.checkNotClosed();
+ return this.bytes.append(s, start, end);
+ }
+
+ @NotNull
+ public ByteStringAppender append(@Nullable Enum value) {
+ this.checkNotClosed();
+ return this.bytes.append(value);
+ }
+
+ @NotNull
+ public ByteStringAppender append(boolean b) {
+ this.checkNotClosed();
+ return this.bytes.append(b);
+ }
+
+ @NotNull
+ public ByteStringAppender append(char c) {
+ this.checkNotClosed();
+ return this.bytes.append(c);
+ }
+
+ @NotNull
+ public ByteStringAppender append(int num) {
+ this.checkNotClosed();
+ return this.bytes.append(num);
+ }
+
+ @NotNull
+ public ByteStringAppender append(long num) {
+ this.checkNotClosed();
+ return this.bytes.append(num);
+ }
+
+ @NotNull
+ public ByteStringAppender append(long num, int base) {
+ this.checkNotClosed();
+ return this.bytes.append(num, base);
+ }
+
+ @NotNull
+ public ByteStringAppender appendDateMillis(long timeInMS) {
+ this.checkNotClosed();
+ return this.bytes.appendDateMillis(timeInMS);
+ }
+
+ @NotNull
+ public ByteStringAppender appendDateTimeMillis(long timeInMS) {
+ this.checkNotClosed();
+ return this.bytes.appendDateTimeMillis(timeInMS);
+ }
+
+ @NotNull
+ public ByteStringAppender appendTimeMillis(long timeInMS) {
+ this.checkNotClosed();
+ return this.bytes.appendTimeMillis(timeInMS);
+ }
+
+ @NotNull
+ public ByteStringAppender append(double d) {
+ this.checkNotClosed();
+ return this.bytes.append(d);
+ }
+
+ public double parseDouble() {
+ this.checkNotClosed();
+ return this.bytes.parseDouble();
+ }
+
+ @NotNull
+ public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) {
+ this.checkNotClosed();
+ return this.bytes.append(list, separator);
+ }
+
+ @NotNull
+ <E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) {
+ this.checkNotClosed();
+ return this.bytes.append(list, separator);
+ }
+
+ @NotNull
+ public MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) {
+ this.checkNotClosed();
+ return this.bytes.parseDecimal(decimal);
+ }
+
+ public long parseLong() {
+ this.checkNotClosed();
+ return this.bytes.parseLong();
+ }
+
+ public long parseLong(int base) {
+ this.checkNotClosed();
+ return this.bytes.parseLong(base);
+ }
+
+ @NotNull
+ public ByteStringAppender append(double d, int precision) {
+ this.checkNotClosed();
+ return this.bytes.append(d, precision);
+ }
+
+ @NotNull
+ public ByteStringAppender append(@NotNull MutableDecimal md) {
+ this.checkNotClosed();
+ return this.bytes.append(md);
+ }
+
+ @NotNull
+ public InputStream inputStream() {
+ this.checkNotClosed();
+ return this.bytes.inputStream();
+ }
+
+ @NotNull
+ public OutputStream outputStream() {
+ this.checkNotClosed();
+ return this.bytes.outputStream();
+ }
+
+ @NotNull
+ public ObjectSerializer objectSerializer() {
+ this.checkNotClosed();
+ return this.bytes.objectSerializer();
+ }
+
+ public <E> void writeEnum(@Nullable E e) {
+ this.checkNotClosed();
+ this.bytes.writeEnum(e);
+ }
+
+ public <E> E readEnum(@NotNull Class<E> eClass) {
+ this.checkNotClosed();
+ return this.bytes.readEnum(eClass);
+ }
+
+ @Override
+ public <E> E readEnum(long offset, int maxSize, Class<E> eClass) {
+ this.checkNotClosed();
+ return this.bytes.readEnum(offset, maxSize, eClass);
+ }
+
+ @Override
+ public void writeEnum(long offset, int maxSize, Object object) {
+ this.checkNotClosed();
+ this.bytes.writeEnum(offset, maxSize, object);
+ }
+
+ public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) {
+ this.checkNotClosed();
+ return this.bytes.parseEnum(eClass, tester);
+ }
+
+ public <E> void writeList(@NotNull Collection<E> list) {
+ this.checkNotClosed();
+ this.bytes.writeList(list);
+ }
+
+ public <K, V> void writeMap(@NotNull Map<K, V> map) {
+ this.checkNotClosed();
+ this.bytes.writeMap(map);
+ }
+
+ public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) {
+ this.checkNotClosed();
+ this.bytes.readList(list, eClass);
+ }
+
+ @NotNull
+ public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) {
+ this.checkNotClosed();
+ return this.bytes.readMap(map, kClass, vClass);
+ }
+
+ public int available() {
+ this.checkNotClosed();
+ return this.bytes.available();
+ }
+
+ public int read() {
+ this.checkNotClosed();
+ return this.bytes.read();
+ }
+
+ public int read(@NotNull byte[] bytes) {
+ this.checkNotClosed();
+ return this.bytes.read(bytes);
+ }
+
+ public long skip(long n) {
+ this.checkNotClosed();
+ return this.bytes.skip(n);
+ }
+
+ public void close() {
+ this.checkNotClosed();
+ this.bytes.close();
+ }
+
+ public void finish() throws IndexOutOfBoundsException {
+ this.bytes.finish();
+ }
+
+ public boolean isFinished() {
+ this.checkNotClosed();
+ return this.bytes.isFinished();
+ }
+
+ public Bytes clear() {
+ this.checkNotClosed();
+ return this.bytes.clear();
+ }
+
+ public Bytes flip() {
+ this.checkNotClosed();
+ return this.bytes.flip();
+ }
+
+ public void flush() {
+ this.checkNotClosed();
+ this.bytes.flush();
+ }
+
+ @Nullable
+ public Object readObject() {
+ this.checkNotClosed();
+ return this.bytes.readObject();
+ }
+
+ @Nullable
+ public <T> T readObject(Class<T> tClass) throws IllegalStateException {
+ return this.bytes.readObject(tClass);
+ }
+
+ @Nullable
+ public <T> T readInstance(@NotNull Class<T> objClass, T obj) {
+ this.checkNotClosed();
+ return this.bytes.readInstance(objClass, obj);
+ }
+
+ public void writeObject(@Nullable Object obj) {
+ this.checkNotClosed();
+ this.bytes.writeObject(obj);
+ }
+
+ public <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj) {
+ this.checkNotClosed();
+ this.bytes.writeInstance(objClass, obj);
+ }
+
+ public boolean tryLockInt(long offset) {
+ this.checkNotClosed();
+ return this.bytes.tryLockInt(offset);
+ }
+
+ public boolean tryLockNanosInt(long offset, long nanos) {
+ this.checkNotClosed();
+ return this.bytes.tryLockNanosInt(offset, nanos);
+ }
+
+ public void busyLockInt(long offset) throws InterruptedException, IllegalStateException {
+ this.bytes.busyLockInt(offset);
+ }
+
+ public void unlockInt(long offset) throws IllegalMonitorStateException {
+ this.bytes.unlockInt(offset);
+ }
+
+ public void resetLockInt(long offset) {
+ this.checkNotClosed();
+ this.bytes.resetLockInt(offset);
+ }
+
+ public int threadIdForLockInt(long offset) {
+ this.checkNotClosed();
+ return this.bytes.threadIdForLockInt(offset);
+ }
+
+ public boolean tryLockLong(long offset) {
+ this.checkNotClosed();
+ return this.bytes.tryLockLong(offset);
+ }
+
+ public boolean tryLockNanosLong(long offset, long nanos) {
+ this.checkNotClosed();
+ return this.bytes.tryLockNanosLong(offset, nanos);
+ }
+
+ public void busyLockLong(long offset) throws InterruptedException, IllegalStateException {
+ this.bytes.busyLockLong(offset);
+ }
+
+ public void unlockLong(long offset) throws IllegalMonitorStateException {
+ this.bytes.unlockLong(offset);
+ }
+
+ public void resetLockLong(long offset) {
+ this.checkNotClosed();
+ this.bytes.resetLockLong(offset);
+ }
+
+ public long threadIdForLockLong(long offset) {
+ this.checkNotClosed();
+ return this.bytes.threadIdForLockLong(offset);
+ }
+
+ public int getAndAdd(long offset, int delta) {
+ this.checkNotClosed();
+ return this.bytes.getAndAdd(offset, delta);
+ }
+
+ public int addAndGetInt(long offset, int delta) {
+ this.checkNotClosed();
+ return this.bytes.addAndGetInt(offset, delta);
+ }
+
+ public byte addByte(long offset, byte b) {
+ this.checkNotClosed();
+ return this.bytes.addByte(offset, b);
+ }
+
+ public int addUnsignedByte(long offset, int i) {
+ this.checkNotClosed();
+ return this.bytes.addUnsignedByte(offset, i);
+ }
+
+ public short addShort(long offset, short s) {
+ this.checkNotClosed();
+ return this.bytes.addShort(offset, s);
+ }
+
+ public int addUnsignedShort(long offset, int i) {
+ this.checkNotClosed();
+ return this.bytes.addUnsignedShort(offset, i);
+ }
+
+ public int addInt(long offset, int i) {
+ this.checkNotClosed();
+ return this.bytes.addInt(offset, i);
+ }
+
+ public long addUnsignedInt(long offset, long i) {
+ this.checkNotClosed();
+ return this.bytes.addUnsignedInt(offset, i);
+ }
+
+ public long addLong(long offset, long i) {
+ this.checkNotClosed();
+ return this.bytes.addLong(offset, i);
+ }
+
+ public float addFloat(long offset, float f) {
+ this.checkNotClosed();
+ return this.bytes.addFloat(offset, f);
+ }
+
+ public double addDouble(long offset, double d) {
+ this.checkNotClosed();
+ return this.bytes.addDouble(offset, d);
+ }
+
+ public int addAtomicInt(long offset, int i) {
+ this.checkNotClosed();
+ return this.bytes.addAtomicInt(offset, i);
+ }
+
+ public long addAtomicLong(long offset, long delta) {
+ this.checkNotClosed();
+ return this.bytes.addAtomicLong(offset, delta);
+ }
+
+ public float addAtomicFloat(long offset, float delta) {
+ this.checkNotClosed();
+ return this.bytes.addAtomicFloat(offset, delta);
+ }
+
+ public double addAtomicDouble(long offset, double delta) {
+ this.checkNotClosed();
+ return this.bytes.addAtomicDouble(offset, delta);
+ }
+
+ public float readVolatileFloat(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readVolatileFloat(offset);
+ }
+
+ public double readVolatileDouble(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readVolatileDouble(offset);
+ }
+
+ public void writeOrderedFloat(long offset, float v) {
+ this.checkNotClosed();
+ this.bytes.writeOrderedFloat(offset, v);
+ }
+
+ public void writeOrderedDouble(long offset, double v) {
+ this.checkNotClosed();
+ this.bytes.writeOrderedDouble(offset, v);
+ }
+
+ public int length() {
+ this.checkNotClosed();
+ return this.bytes.length();
+ }
+
+ public char charAt(int index) {
+ this.checkNotClosed();
+ return this.bytes.charAt(index);
+ }
+
+ public void readMarshallable(@NotNull Bytes in) throws IllegalStateException {
+ this.bytes.readMarshallable(in);
+ }
+
+ public void writeMarshallable(@NotNull Bytes out) {
+ this.checkNotClosed();
+ this.bytes.writeMarshallable(out);
+ }
+
+ public void write(RandomDataInput bytes) {
+ this.checkNotClosed();
+ this.bytes.write(bytes);
+ }
+
+ public void write(Byteable byteable) {
+ this.checkNotClosed();
+ this.bytes.write(byteable);
+ }
+
+ public boolean startsWith(RandomDataInput input) {
+ this.checkNotClosed();
+ return this.bytes.startsWith(input);
+ }
+
+ @NotNull
+ public String toString() {
+ this.checkNotClosed();
+ return this.bytes.toString();
+ }
+
+ @NotNull
+ public String toDebugString() {
+ this.checkNotClosed();
+ return this.bytes.toDebugString();
+ }
+
+ @NotNull
+ public String toDebugString(long limit) {
+ this.checkNotClosed();
+ return this.bytes.toDebugString(limit);
+ }
+
+ @Override
+ public String toHexString(long limit) {
+ this.checkNotClosed();
+ return this.bytes.toHexString(limit);
+ }
+
+ public void toString(Appendable sb, long start, long position, long end) {
+ this.checkNotClosed();
+ this.bytes.toString(sb, start, position, end);
+ }
+
+ public void asString(Appendable appendable) {
+ this.checkNotClosed();
+ this.bytes.asString(appendable);
+ }
+
+ public CharSequence asString() {
+ this.checkNotClosed();
+ return this.bytes.asString();
+ }
+
+ public boolean compareAndSwapDouble(long offset, double expected, double value) {
+ this.checkNotClosed();
+ return this.bytes.compareAndSwapDouble(offset, expected, value);
+ }
+
+ public File file() {
+ this.checkNotClosed();
+ return this.bytes.file();
+ }
+
+ public boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ return this.bytes.tryRWReadLock(offset, timeOutNS);
+ }
+
+ public boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ return this.bytes.tryRWWriteLock(offset, timeOutNS);
+ }
+
+ public void unlockRWReadLock(long offset) {
+ this.checkNotClosed();
+ this.bytes.unlockRWReadLock(offset);
+ }
+
+ public void unlockRWWriteLock(long offset) {
+ this.checkNotClosed();
+ this.bytes.unlockRWWriteLock(offset);
+ }
+
+ public Bytes slice() {
+ this.checkNotClosed();
+ return this.bytes.slice();
+ }
+
+ public Bytes slice(long offset, long length) {
+ this.checkNotClosed();
+ return this.bytes.slice(offset, length);
+ }
+
+ public CharSequence subSequence(int start, int end) {
+ this.checkNotClosed();
+ return this.bytes.subSequence(start, end);
+ }
+
+ public Bytes bytes() {
+ this.checkNotClosed();
+ return this.bytes.bytes();
+ }
+
+ public Bytes bytes(long offset, long length) {
+ this.checkNotClosed();
+ return this.bytes.bytes(offset, length);
+ }
+
+ public long address() {
+ this.checkNotClosed();
+ return this.bytes.address();
+ }
+
+ public Bytes zeroOut() {
+ this.checkNotClosed();
+ return this.bytes.zeroOut();
+ }
+
+ public Bytes zeroOut(long start, long end) {
+ this.checkNotClosed();
+ return this.bytes.zeroOut(start, end);
+ }
+
+ public Bytes zeroOut(long start, long end, boolean ifNotZero) {
+ this.checkNotClosed();
+ return this.bytes.zeroOut(start, end, ifNotZero);
+ }
+
+ public int read(@NotNull byte[] bytes, int off, int len) {
+ this.checkNotClosed();
+ return this.bytes.read(bytes, off, len);
+ }
+
+ public byte readByte() {
+ this.checkNotClosed();
+ return this.bytes.readByte();
+ }
+
+ public byte readByte(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readByte(offset);
+ }
+
+ public void readFully(@NotNull byte[] b, int off, int len) {
+ this.checkNotClosed();
+ this.bytes.readFully(b, off, len);
+ }
+
+ public void readFully(long offset, byte[] bytes, int off, int len) {
+ this.checkNotClosed();
+ this.bytes.readFully(offset, bytes, off, len);
+ }
+
+ public void readFully(@NotNull char[] data, int off, int len) {
+ this.checkNotClosed();
+ this.bytes.readFully(data, off, len);
+ }
+
+ public short readShort() {
+ this.checkNotClosed();
+ return this.bytes.readShort();
+ }
+
+ public short readShort(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readShort(offset);
+ }
+
+ public char readChar() {
+ this.checkNotClosed();
+ return this.bytes.readChar();
+ }
+
+ public char readChar(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readChar(offset);
+ }
+
+ public int readInt() {
+ this.checkNotClosed();
+ return this.bytes.readInt();
+ }
+
+ public int readInt(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readInt(offset);
+ }
+
+ public int readVolatileInt() {
+ this.checkNotClosed();
+ return this.bytes.readVolatileInt();
+ }
+
+ public int readVolatileInt(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readVolatileInt(offset);
+ }
+
+ public long readLong() {
+ this.checkNotClosed();
+ return this.bytes.readLong();
+ }
+
+ @Override
+ public long readIncompleteLong(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readIncompleteLong(offset);
+ }
+
+ public long readLong(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readLong(offset);
+ }
+
+ public long readVolatileLong() {
+ this.checkNotClosed();
+ return this.bytes.readVolatileLong();
+ }
+
+ public long readVolatileLong(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readVolatileLong(offset);
+ }
+
+ public float readFloat() {
+ this.checkNotClosed();
+ return this.bytes.readFloat();
+ }
+
+ public float readFloat(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readFloat(offset);
+ }
+
+ public double readDouble() {
+ this.checkNotClosed();
+ return this.bytes.readDouble();
+ }
+
+ public double readDouble(long offset) {
+ this.checkNotClosed();
+ return this.bytes.readDouble(offset);
+ }
+
+ public void write(int b) {
+ this.checkNotClosed();
+ this.bytes.write(b);
+ }
+
+ public void writeByte(long offset, int b) {
+ this.checkNotClosed();
+ this.bytes.writeByte(offset, b);
+ }
+
+ public void write(long offset, @NotNull byte[] bytes) {
+ this.checkNotClosed();
+ this.bytes.write(offset, bytes);
+ }
+
+ public void write(byte[] bytes, int off, int len) {
+ this.checkNotClosed();
+ this.bytes.write(bytes, off, len);
+ }
+
+ public void write(long offset, byte[] bytes, int off, int len) {
+ this.checkNotClosed();
+ this.bytes.write(offset, bytes, off, len);
+ }
+
+ public void writeShort(int v) {
+ this.checkNotClosed();
+ this.bytes.writeShort(v);
+ }
+
+ public void writeShort(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeShort(offset, v);
+ }
+
+ public void writeChar(int v) {
+ this.checkNotClosed();
+ this.bytes.writeChar(v);
+ }
+
+ public void writeChar(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeChar(offset, v);
+ }
+
+ public void writeInt(int v) {
+ this.checkNotClosed();
+ this.bytes.writeInt(v);
+ }
+
+ public void writeInt(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeInt(offset, v);
+ }
+
+ public void writeOrderedInt(int v) {
+ this.checkNotClosed();
+ this.bytes.writeOrderedInt(v);
+ }
+
+ public void writeOrderedInt(long offset, int v) {
+ this.checkNotClosed();
+ this.bytes.writeOrderedInt(offset, v);
+ }
+
+ public boolean compareAndSwapInt(long offset, int expected, int x) {
+ this.checkNotClosed();
+ return this.bytes.compareAndSwapInt(offset, expected, x);
+ }
+
+ public void writeLong(long v) {
+ this.checkNotClosed();
+ this.bytes.writeLong(v);
+ }
+
+ public void writeLong(long offset, long v) {
+ this.checkNotClosed();
+ this.bytes.writeLong(offset, v);
+ }
+
+ public void writeOrderedLong(long v) {
+ this.checkNotClosed();
+ this.bytes.writeOrderedLong(v);
+ }
+
+ public void writeOrderedLong(long offset, long v) {
+ this.checkNotClosed();
+ this.bytes.writeOrderedLong(offset, v);
+ }
+
+ public boolean compareAndSwapLong(long offset, long expected, long x) {
+ this.checkNotClosed();
+ return this.bytes.compareAndSwapLong(offset, expected, x);
+ }
+
+ public void writeFloat(float v) {
+ this.checkNotClosed();
+ this.bytes.writeFloat(v);
+ }
+
+ public void writeFloat(long offset, float v) {
+ this.checkNotClosed();
+ this.bytes.writeFloat(offset, v);
+ }
+
+ public void writeDouble(double v) {
+ this.checkNotClosed();
+ this.bytes.writeDouble(v);
+ }
+
+ public void writeDouble(long offset, double v) {
+ this.checkNotClosed();
+ this.bytes.writeDouble(offset, v);
+ }
+
+ public void readObject(Object object, int start, int end) {
+ this.checkNotClosed();
+ this.bytes.readObject(object, start, end);
+ }
+
+ public void writeObject(Object object, int start, int end) {
+ this.checkNotClosed();
+ this.bytes.writeObject(object, start, end);
+ }
+
+ public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) {
+ this.checkNotClosed();
+ return this.bytes.compare(offset, input, inputOffset, len);
+ }
+
+ public long position() {
+ this.checkNotClosed();
+ return this.bytes.position();
+ }
+
+ public Bytes position(long position) {
+ this.checkNotClosed();
+ return this.bytes.position(position);
+ }
+
+ public void write(RandomDataInput bytes, long position, long length) {
+ this.checkNotClosed();
+ this.bytes.write(bytes, position, length);
+ }
+
+ public long capacity() {
+ this.checkNotClosed();
+ return this.bytes.capacity();
+ }
+
+ public long remaining() {
+ this.checkNotClosed();
+ return this.bytes.remaining();
+ }
+
+ public long limit() {
+ this.checkNotClosed();
+ return this.bytes.limit();
+ }
+
+ public Bytes limit(long limit) {
+ this.checkNotClosed();
+ return this.bytes.limit(limit);
+ }
+
+ @NotNull
+ public ByteOrder byteOrder() {
+ this.checkNotClosed();
+ return this.bytes.byteOrder();
+ }
+
+ public void checkEndOfBuffer() throws IndexOutOfBoundsException {
+ this.bytes.checkEndOfBuffer();
+ }
+
+ public Bytes load() {
+ this.checkNotClosed();
+ return this.bytes.load();
+ }
+
+ public void alignPositionAddr(int powerOf2) {
+ this.checkNotClosed();
+ this.bytes.alignPositionAddr(powerOf2);
+ }
+
+ public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) {
+ this.checkNotClosed();
+ return this.bytes.sliceAsByteBuffer(toReuse);
+ }
+
+ @Override
+ public long nextSetBit(long fromIndex) {
+ this.checkNotClosed();
+ return this.bytes.nextSetBit(fromIndex);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java b/lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java
new file mode 100644
index 0000000..4f1576b
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/ChronicleUnsafe.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.model.constraints.NotNull;
+import sun.misc.Unsafe;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+/**
+ * Created by Rob Austin
+ */
+public class ChronicleUnsafe {
+
+ private final MappedFile mappedFile;
+ private MappedMemory mappedMemory = null;
+ public static final Unsafe UNSAFE;
+
+ static {
+ try {
+ @SuppressWarnings("ALL")
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ UNSAFE = (Unsafe) theUnsafe.get(null);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private long chunkSize;
+ private long offset;
+ private final long mask;
+ private long last = -1;
+
+ /**
+ * @param mappedFile a until that able to map block of memory to a file
+ * @throws IllegalStateException if the block size is not a power of 2
+ */
+ public ChronicleUnsafe(@NotNull MappedFile mappedFile) {
+ long blockSize = mappedFile.blockSize();
+
+ if (((blockSize & -blockSize) != blockSize))
+ throw new IllegalStateException("the block size has to be a power of 2");
+
+ this.mappedFile = mappedFile;
+ this.chunkSize = mappedFile.blockSize();
+
+ long shift = (int) (Math.log(blockSize) / Math.log(2));
+
+ mask = ~((1L << shift) - 1L);
+ }
+
+ public long toAddress(long address) {
+ return (mask & address ^ this.last) == 0 ? address + offset : toAddress0(address);
+ }
+
+ public long toAddress0(long address) {
+ int index = (int) ((address / chunkSize));
+ long remainder = address - (((long) index) * chunkSize);
+
+ // index == 0 is the header, so we wont reference count the header
+ if (mappedMemory != null && mappedMemory.index() != 0)
+ mappedFile.release(mappedMemory);
+
+ try {
+ this.mappedMemory = mappedFile.acquire(index);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ long result = mappedMemory.bytes().address() + remainder;
+ this.offset = result - address;
+ this.last = mask & address;
+ return result;
+ }
+
+ public long toRemainingInChunk(long address) {
+ int chunk = (int) ((address / chunkSize));
+ long remainder = address - (((long) chunk) * chunkSize);
+
+ return mappedMemory.bytes().capacity() - remainder;
+ }
+
+ public int arrayBaseOffset(Class<?> aClass) {
+ return UNSAFE.arrayBaseOffset(aClass);
+ }
+
+ public int pageSize() {
+ throw new UnsupportedOperationException("todo (pageSize)");
+ }
+
+ public long allocateMemory(int aVoid) {
+ throw new UnsupportedOperationException("todo (allocateMemory)");
+ }
+
+ public long getLong(byte[] bytes, long address) {
+ return UNSAFE.getLong(bytes, toAddress(address));
+ }
+
+ public long getLong(Object object, long address) {
+ return UNSAFE.getLong(object, toAddress(address));
+ }
+
+ public void setMemory(long startAddress, long len, byte defaultValue) {
+ long remaining = len;
+ while (remaining > 0) {
+ long address = toAddress(startAddress);
+ long remainingInChunk = toRemainingInChunk(startAddress);
+ if (remainingInChunk > remaining)
+ remainingInChunk = remaining;
+ UNSAFE.setMemory(address, remainingInChunk, defaultValue);
+ startAddress += remainingInChunk;
+ remaining -= remainingInChunk;
+ }
+ }
+
+ public byte getByte(long address) {
+ return UNSAFE.getByte(toAddress(address));
+ }
+
+ public void putByte(long address, byte value) {
+ UNSAFE.putByte(toAddress(address), value);
+ }
+
+ public void putLong(long address, long value) {
+ UNSAFE.putLong(toAddress(address), value);
+ }
+
+ public long getLong(long address) {
+ return UNSAFE.getLong(toAddress(address));
+ }
+
+ public void copyMemory(Object o, long positionAddr, Object bytes, long i, long len2) {
+ throw new UnsupportedOperationException("todo (copyMemory)");
+ }
+
+ public short getShort(long address) {
+ return UNSAFE.getShort(toAddress(address));
+ }
+
+ public char getChar(long address) {
+ return UNSAFE.getChar(toAddress(address));
+ }
+
+ public int getInt(long address) {
+ return UNSAFE.getInt(toAddress(address));
+ }
+
+ public int getIntVolatile(Object o, long address) {
+ return UNSAFE.getIntVolatile(o, toAddress(address));
+ }
+
+ public long getLongVolatile(Object o, long address) {
+ return UNSAFE.getLongVolatile(o, toAddress(address));
+ }
+
+ public float getFloat(long address) {
+ return UNSAFE.getFloat(toAddress(address));
+ }
+
+ public double getDouble(long address) {
+ return UNSAFE.getDouble(toAddress(address));
+ }
+
+ public void putShort(long address, short v) {
+ UNSAFE.putShort(toAddress(address), v);
+ }
+
+ public void putChar(long address, char v) {
+ UNSAFE.putChar(toAddress(address), v);
+ }
+
+ public void putInt(long address, int v) {
+ UNSAFE.putInt(toAddress(address), v);
+ }
+
+ public void putOrderedInt(Object o, long address, int v) {
+ UNSAFE.putOrderedInt(o, toAddress(address), v);
+ }
+
+ public boolean compareAndSwapInt(Object o, long address, int expected, int v) {
+ return UNSAFE.compareAndSwapInt(o, toAddress(address), expected, v);
+ }
+
+ public void putOrderedLong(Object o, long address, long v) {
+ UNSAFE.putOrderedLong(o, toAddress(address), v);
+ }
+
+ public boolean compareAndSwapLong(Object o, long address, long expected, long v) {
+ return UNSAFE.compareAndSwapLong(o, toAddress(address), expected, v);
+ }
+
+ public void putFloat(long address, float v) {
+ UNSAFE.putFloat(toAddress(address), v);
+ }
+
+ public void putDouble(long address, double v) {
+ UNSAFE.putDouble(toAddress(address), v);
+ }
+
+ public void putLong(Object o, long address, long aLong) {
+ UNSAFE.putLong(o, toAddress(address), aLong);
+ }
+
+ public void putByte(Object o, long address, byte aByte) {
+ UNSAFE.putByte(o, toAddress(address), aByte);
+ }
+
+ public byte getByte(Object o, long address) {
+ return UNSAFE.getByte(o, toAddress(address));
+ }
+
+ public void copyMemory(long l, long positionAddr, long length) {
+ throw new UnsupportedOperationException("todo (copyMemory)");
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java
new file mode 100644
index 0000000..938dd74
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/DirectByteBufferBytes.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import sun.nio.ch.DirectBuffer;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class DirectByteBufferBytes extends NativeBytes implements IByteBufferBytes {
+ private ByteBuffer buffer;
+
+ public DirectByteBufferBytes(int capacity) {
+ this(ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()), 0, capacity);
+ }
+
+ public DirectByteBufferBytes(final ByteBuffer buffer) {
+ this(buffer, 0, buffer.capacity());
+ }
+
+ public DirectByteBufferBytes(final ByteBuffer buffer, int start, int capacity) {
+ super(
+ ((DirectBuffer) buffer).address() + start,
+ ((DirectBuffer) buffer).address() + capacity
+ );
+
+ this.buffer = buffer;
+ }
+
+ @Override
+ public ByteBuffer buffer() {
+ return buffer;
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) {
+ return sliceAsByteBuffer(toReuse, buffer);
+ }
+
+ protected DirectByteBufferBytes resize(int newCapacity, boolean cleanup, boolean preserveData) {
+ if(newCapacity != capacity()) {
+ final ByteBuffer oldBuffer = this.buffer;
+ final long oldAddress = this.startAddr;
+ final long oldPosition = position();
+
+ if(preserveData && (oldPosition > newCapacity)) {
+ throw new IllegalArgumentException(
+ "Capacity can't be less than currently used data (size=" + oldPosition
+ + ", capacity=" + newCapacity + ")"
+ );
+ }
+
+ this.buffer = ByteBuffer.allocateDirect(newCapacity).order(ByteOrder.nativeOrder());
+ setStartPositionAddress(((DirectBuffer) buffer).address());
+ this.capacityAddr = this.startAddr + newCapacity;
+ this.limitAddr = this.capacityAddr;
+
+ if (preserveData && (oldPosition > 0)) {
+ UNSAFE.copyMemory(oldAddress, this.startAddr, Math.min(newCapacity, oldPosition));
+ this.positionAddr = this.startAddr + Math.min(newCapacity, oldPosition);
+ }
+
+ if(cleanup) {
+ IOTools.clean(oldBuffer);
+ }
+ }
+
+ return this;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/DirectBytes.java b/lang/src/main/java/net/openhft/lang/io/DirectBytes.java
index 7e783e4..0f83371 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/DirectBytes.java
+++ b/lang/src/main/java/net/openhft/lang/io/DirectBytes.java
@@ -1,43 +1,62 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* @author peter.lawrey
*/
public class DirectBytes extends NativeBytes {
@NotNull
- private final DirectStore store;
+ private final BytesStore store;
- DirectBytes(@NotNull DirectStore store) {
- super(store.bytesMarshallerFactory, store.address, store.address, store.address + store.size);
+ public DirectBytes(@NotNull BytesStore store, AtomicInteger refCount) {
+ super(store.objectSerializer(), store.address(), store.address() + store.size(), refCount);
+ this.store = store;
+ }
+
+ public DirectBytes(@NotNull BytesStore store, AtomicInteger refCount, long offset, long length) {
+ super(store.objectSerializer(), store.address() + offset, store.address() + offset + length, refCount);
this.store = store;
}
public void positionAndSize(long offset, long size) {
- if (offset < 0 || size < 0 || offset + size > store.size)
+ if (offset < 0 || size < 0 || offset + size > store.size())
throw new IllegalArgumentException();
- startAddr = positionAddr = store.address + offset;
- limitAddr = startAddr + size;
+
+ setStartPositionAddress(store.address() + offset);
+ capacityAddr = limitAddr = startAddr + size;
}
- public DirectStore store() {
+ public BytesStore store() {
return store;
}
+
+ @Override
+ protected void cleanup() {
+ store.free();
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) {
+ return sliceAsByteBuffer(toReuse, store);
+ }
}
diff --git a/lang/src/main/java/net/openhft/lang/io/DirectStore.java b/lang/src/main/java/net/openhft/lang/io/DirectStore.java
index 7099165..ca1b4a3 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/DirectStore.java
+++ b/lang/src/main/java/net/openhft/lang/io/DirectStore.java
@@ -1,45 +1,57 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
+import net.openhft.lang.io.serialization.BytesMarshallableSerializer;
import net.openhft.lang.io.serialization.BytesMarshallerFactory;
+import net.openhft.lang.io.serialization.JDKZObjectSerializer;
+import net.openhft.lang.io.serialization.ObjectSerializer;
import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
import sun.misc.Cleaner;
+import java.io.File;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* @author peter.lawrey
*/
-public class DirectStore {
- protected BytesMarshallerFactory bytesMarshallerFactory;
+public class DirectStore implements BytesStore, AutoCloseable {
+ private final ObjectSerializer objectSerializer;
private final Cleaner cleaner;
- protected long address;
- protected long size;
+ private final Deallocator deallocator;
+ private long address;
+ private long size;
+ private final AtomicInteger refCount = new AtomicInteger(1);
public DirectStore(long size) {
this(new VanillaBytesMarshallerFactory(), size);
}
- public DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size) {
+ private DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size) {
this(bytesMarshallerFactory, size, true);
}
- public DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size, boolean zeroOut) {
- this.bytesMarshallerFactory = bytesMarshallerFactory;
+ private DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size, boolean zeroOut) {
+ this(BytesMarshallableSerializer.create(bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE), size, zeroOut);
+ }
+
+ public DirectStore(ObjectSerializer objectSerializer, long size, boolean zeroOut) {
+ this.objectSerializer = objectSerializer;
address = NativeBytes.UNSAFE.allocateMemory(size);
// System.out.println("old value " + Integer.toHexString(NativeBytes.UNSAFE.getInt(null, address)));
@@ -49,36 +61,65 @@ public class DirectStore {
}
this.size = size;
- cleaner = Cleaner.create(this, new Runnable() {
- @Override
- public void run() {
- if (address != 0)
- NativeBytes.UNSAFE.freeMemory(address);
- address = DirectStore.this.size = 0;
- }
- });
+ deallocator = new Deallocator(address);
+ cleaner = Cleaner.create(this, deallocator);
+ }
+
+ @Override
+ public ObjectSerializer objectSerializer() {
+ return objectSerializer;
}
@NotNull
public static DirectStore allocate(long size) {
- return new DirectStore(null, size);
+ return new DirectStore(new VanillaBytesMarshallerFactory(), size);
}
@NotNull
public static DirectStore allocateLazy(long size) {
- return new DirectStore(null, size, false);
+ return new DirectStore(new VanillaBytesMarshallerFactory(), size, false);
}
-/* public void resize(long newSize) {
- if (newSize == size)
- return;
- address = NativeBytes.UNSAFE.reallocateMemory(address, newSize);
+ /**
+ * Resizes this {@code DirectStore} to the {@code newSize}.
+ *
+ * <p>If {@code zeroOut} is {@code false}, the memory past the old size is not zeroed out and
+ * will generally be garbage.
+ *
+ * <p>{@code DirectStore} don't keep track of the child {@code DirectBytes} instances, so after
+ * the resize they might point to the wrong memory. Use at your own risk.
+ *
+ * @param newSize new size of this {@code DirectStore}
+ * @param zeroOut if the memory past the old size should be zeroed out on increasing resize
+ * @throws IllegalArgumentException if the {@code newSize} is not positive
+ */
+ public void resize(long newSize, boolean zeroOut) {
+ if (newSize <= 0)
+ throw new IllegalArgumentException("Given newSize is " + newSize +
+ " but should be positive");
+ address = deallocator.address = NativeBytes.UNSAFE.reallocateMemory(address, newSize);
+ if (zeroOut && newSize > size) {
+ NativeBytes.UNSAFE.setMemory(address + size, newSize - size, (byte) 0);
+ }
size = newSize;
- }*/
+ }
+ @SuppressWarnings("ConstantConditions")
@NotNull
- public DirectBytes createSlice() {
- return new DirectBytes(this);
+ public DirectBytes bytes() {
+ boolean debug = false;
+ assert debug = true;
+ return debug ? new BoundsCheckingDirectBytes(this, refCount) : new DirectBytes(this, refCount);
+ }
+
+ @NotNull
+ public DirectBytes bytes(long offset, long length) {
+ return new DirectBytes(this, refCount, offset, length);
+ }
+
+ @Override
+ public long address() {
+ return address;
}
public void free() {
@@ -89,8 +130,41 @@ public class DirectStore {
return size;
}
- public BytesMarshallerFactory bytesMarshallerFactory() {
- return bytesMarshallerFactory;
+ public static BytesStore allocateLazy(long sizeInBytes, ObjectSerializer objectSerializer) {
+ return new DirectStore(objectSerializer, sizeInBytes, false);
+ }
+
+ @Override
+ public File file() {
+ return null;
+ }
+
+ /**
+ * calls free
+ */
+ @Override
+ public void close() {
+ free();
}
+ /**
+ * Static nested class instead of anonymous because the latter would hold a strong reference to
+ * this DirectStore preventing it from becoming phantom-reachable.
+ */
+ private static class Deallocator implements Runnable {
+ private volatile long address;
+
+ Deallocator(long address) {
+ assert address != 0;
+ this.address = address;
+ }
+
+ @Override
+ public void run() {
+ if (address == 0)
+ return;
+ NativeBytes.UNSAFE.freeMemory(address);
+ address = 0;
+ }
+ }
}
diff --git a/lang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java b/lang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java
new file mode 100755
index 0000000..235f55e
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/EscapingStopCharTester.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+/**
+ * Created by peter.lawrey on 16/01/15.
+ */
+public class EscapingStopCharTester implements StopCharTester {
+ private final StopCharTester sct;
+ private boolean escaped = false;
+
+ EscapingStopCharTester(StopCharTester sct) {
+ this.sct = sct;
+ }
+
+ public static StopCharTester escaping(StopCharTester sct) {
+ return new EscapingStopCharTester(sct);
+ }
+
+ @Override
+ public boolean isStopChar(int ch) throws IllegalStateException {
+ if (escaped) {
+ escaped = false;
+ return false;
+ }
+ if (ch == '\\') {
+ escaped = true;
+ return false;
+ }
+ return sct.isStopChar(ch);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java b/lang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java
new file mode 100755
index 0000000..71ddec4
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/FileLifecycleListener.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+/**
+ * Created by peter.lawrey on 05/08/2015.
+ */
+public interface FileLifecycleListener {
+ enum EventType {
+ NEW,
+ MMAP,
+ UNMAP,
+ GROW,
+ SYNC,
+ DELETE,
+ CLOSE
+ }
+
+ enum FileLifecycleListeners implements FileLifecycleListener {
+ IGNORE {
+ @Override
+ public void onEvent(EventType type, File file, long timeInNanos) {
+ }
+ },
+ CONSOLE {
+ @Override
+ public void onEvent(EventType type, File file, long timeInNanos) {
+ System.out.println(
+ "File " + file + " took " + timeInNanos / 1000 / 1e3 + " ms. to " + type);
+ }
+ },
+ LOG {
+ @Override
+ public void onEvent(EventType type, File file, long timeInNanos) {
+ LoggerFactory.getLogger(FileLifecycleListeners.class).info(
+ "File " + file + " took " + timeInNanos / 1000 / 1e3 + " ms. to " + type);
+ }
+ }
+ }
+
+ void onEvent(EventType type, File file, long timeInNanos);
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java b/lang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java
new file mode 100755
index 0000000..43997b2
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/IByteBufferBytes.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Created by peter.lawrey on 27/01/15.
+ */
+public interface IByteBufferBytes extends Bytes {
+ /**
+ * Obtain the underlying ByteBuffer
+ */
+ ByteBuffer buffer();
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/IOTools.java b/lang/src/main/java/net/openhft/lang/io/IOTools.java
index 854e8fe..b87cb68 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/IOTools.java
+++ b/lang/src/main/java/net/openhft/lang/io/IOTools.java
@@ -1,29 +1,32 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
+import org.slf4j.LoggerFactory;
+import sun.misc.Cleaner;
+import sun.nio.ch.DirectBuffer;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-import java.util.logging.Logger;
/**
* @author peter.lawrey
@@ -61,7 +64,7 @@ public enum IOTools {
deleteDir(new File(dirPath));
}
- public static void deleteDir(File dir) {
+ private static void deleteDir(File dir) {
// delete one level.
if (dir.isDirectory()) {
File[] files = dir.listFiles();
@@ -69,11 +72,19 @@ public enum IOTools {
for (File file : files)
if (file.isDirectory()) {
deleteDir(file);
+
} else if (!file.delete()) {
- Logger.getLogger(IOTools.class.getName()).info("... unable to delete " + file);
+ LoggerFactory.getLogger(IOTools.class).info("... unable to delete {}", file);
}
-
}
dir.delete();
}
+
+ public static void clean(ByteBuffer bb) {
+ if (bb instanceof DirectBuffer) {
+ Cleaner cl = ((DirectBuffer) bb).cleaner();
+ if (cl != null)
+ cl.clean();
+ }
+ }
}
diff --git a/lang/src/main/java/net/openhft/lang/io/MappedFile.java b/lang/src/main/java/net/openhft/lang/io/MappedFile.java
index f4907b9..6174cbd 100644..100755
--- a/lang/src/main/java/net/openhft/lang/io/MappedFile.java
+++ b/lang/src/main/java/net/openhft/lang/io/MappedFile.java
@@ -1,22 +1,23 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
+import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -26,29 +27,29 @@ import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
-import java.util.logging.Logger;
/**
* This manages the full life cycle of a file and its mappings.
*/
public class MappedFile {
- private final FileChannel fileChannel;
- private final String basePath;
+ private final String filename;
private final long blockSize;
private final long overlapSize;
+
+ private final FileChannel fileChannel;
private final List<MappedMemory> maps = new ArrayList<MappedMemory>();
// short list of the last two mappings.
private volatile MappedMemory map0, map1;
- public MappedFile(String basePath, long blockSize) throws FileNotFoundException {
- this(basePath, blockSize, 0L);
+ public MappedFile(String filename, long blockSize) throws FileNotFoundException {
+ this(filename, blockSize, 0L);
}
- public MappedFile(String basePath, long blockSize, long overlapSize) throws FileNotFoundException {
- this.basePath = basePath;
+ public MappedFile(String filename, long blockSize, long overlapSize) throws FileNotFoundException {
+ this.filename = filename;
this.blockSize = blockSize;
this.overlapSize = overlapSize;
- fileChannel = new RandomAccessFile(basePath, "rw").getChannel();
+ fileChannel = new RandomAccessFile(filename, "rw").getChannel();
}
public static MappedByteBuffer getMap(@NotNull FileChannel fileChannel, long start, int size) throws IOException {
@@ -66,26 +67,22 @@ public class MappedFile {
if (e.getMessage() == null || !e.getMessage().endsWith("user-mapped section open")) {
throw e;
}
- if (i < 10)
- //noinspection CallToThreadYield
- Thread.yield();
- else
- try {
- //noinspection BusyWait
- Thread.sleep(1);
- } catch (InterruptedException ignored) {
- Thread.currentThread().interrupt();
- throw e;
- }
+ try {
+ //noinspection BusyWait
+ Thread.sleep(1);
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ throw e;
+ }
}
}
}
- public MappedMemory acquire(long index) throws IOException {
- return acquire(index, false);
+ public String name() {
+ return filename;
}
- public MappedMemory acquire(long index, boolean prefetch) throws IOException {
+ public MappedMemory acquire(long index) throws IOException {
MappedMemory map0 = this.map0, map1 = this.map1;
if (map0 != null && map0.index() == index) {
map0.reserve();
@@ -95,10 +92,29 @@ public class MappedFile {
map1.reserve();
return map1;
}
- return acquire0(index, prefetch);
+ return acquire0(index);
+ }
+
+ /**
+ * gets the refCount a given index, or returns 0, if the there is no mapping for this index
+ *
+ * @param index
+ * @return the mapping at this {@code index}
+ * @throws IndexOutOfBoundsException if the index is out of range
+ */
+ public int getRefCount(long index) {
+ try {
+ for (MappedMemory m : maps) {
+ if (m.index() == index)
+ return m.refCount();
+ }
+ } catch (Exception e) {
+ return 0;
+ }
+ return 0;
}
- private synchronized MappedMemory acquire0(long index, boolean prefetch) throws IOException {
+ private synchronized MappedMemory acquire0(long index) throws IOException {
if (map1 != null)
map1.release();
map1 = map0;
@@ -130,8 +146,10 @@ public class MappedFile {
count++;
}
}
- if (count > 1)
- Logger.getLogger(MappedFile.class.getName()).info(basePath + " memory mappings left unreleased, num= " + count);
+ if (count > 1) {
+ LoggerFactory.getLogger(MappedFile.class).info("{} memory mappings left unreleased, num= {}", filename, count);
+ }
+
maps.clear();
fileChannel.close();
}
@@ -143,4 +161,17 @@ public class MappedFile {
return 0;
}
}
+
+ public long blockSize() {
+ return blockSize;
+ }
+
+ public void release(MappedMemory mappedMemory) {
+ if (mappedMemory.release()) {
+ if (map0 == mappedMemory)
+ map0 = null;
+ if (map1 == mappedMemory)
+ map1 = null;
+ }
+ }
}
diff --git a/lang/src/main/java/net/openhft/lang/io/MappedMemory.java b/lang/src/main/java/net/openhft/lang/io/MappedMemory.java
index df97ef5..6fe488d 100644
--- a/lang/src/main/java/net/openhft/lang/io/MappedMemory.java
+++ b/lang/src/main/java/net/openhft/lang/io/MappedMemory.java
@@ -1,57 +1,66 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import sun.misc.Cleaner;
+import net.openhft.lang.ReferenceCounted;
+import net.openhft.lang.io.serialization.ObjectSerializer;
import sun.nio.ch.DirectBuffer;
+import java.io.File;
import java.nio.MappedByteBuffer;
import java.util.concurrent.atomic.AtomicInteger;
-public class MappedMemory {
+public class MappedMemory implements ReferenceCounted, BytesStore {
private final MappedByteBuffer buffer;
+ private final DirectByteBufferBytes bytes;
private final long index;
- private final AtomicInteger refCount = new AtomicInteger(1);
+ private final AtomicInteger refCount = new AtomicInteger(0);
private volatile boolean unmapped = false;
public MappedMemory(MappedByteBuffer buffer, long index) {
this.buffer = buffer;
this.index = index;
- }
-
- private static void unmap(MappedByteBuffer bb) {
- Cleaner cl = ((DirectBuffer) bb).cleaner();
- if (cl != null)
- cl.clean();
+ bytes = new DirectByteBufferBytes(buffer);
}
public long index() {
return index;
}
+ @Override
public void reserve() {
if (unmapped) throw new IllegalStateException();
refCount.incrementAndGet();
}
- public void release() {
+ /**
+ * @return true if release cause the ref count to drop to zero and the resource was freed
+ */
+ @Override
+ public boolean release() {
if (unmapped) throw new IllegalStateException();
- if (refCount.decrementAndGet() > 0) return;
+ if (refCount.decrementAndGet() > 0) return false;
close();
+ return true;
+ }
+
+ @Override
+ public int refCount() {
+ return refCount.get();
}
public MappedByteBuffer buffer() {
@@ -62,8 +71,33 @@ public class MappedMemory {
return ((DirectBuffer) buffer).address();
}
- public int refCount() {
- return refCount.get();
+ @Override
+ public long size() {
+ return bytes.capacity();
+ }
+
+ @Override
+ public void free() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ObjectSerializer objectSerializer() {
+ return null;
+ }
+
+ @Override
+ public File file() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Bytes bytes() {
+ return bytes;
+ }
+
+ @Override
+ public Bytes bytes(long offset, long length) {
+ throw new UnsupportedOperationException();
}
public static void release(MappedMemory mapmem) {
@@ -77,7 +111,7 @@ public class MappedMemory {
}
public void close() {
- unmap(buffer);
+ IOTools.clean(buffer);
unmapped = true;
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java b/lang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java
new file mode 100755
index 0000000..79f4e9c
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/MappedNativeBytes.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import org.jetbrains.annotations.NotNull;
+import sun.misc.Unsafe;
+
+import java.io.EOFException;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * works in conjunction with a MappedFile to map additional memory blocks when required This
+ * implementation is very similar to {@code NativeBytes}, accept that the memory address resolution
+ * is left to ChronicleUnsafe, rather than being part of this class.
+ */
+public class MappedNativeBytes extends AbstractBytes {
+
+ @NotNull
+ public final ThreadLocal<ChronicleUnsafe> threadLocal = new ThreadLocal<ChronicleUnsafe>();
+
+ static final int BYTES_OFFSET;
+ static final int CHARS_OFFSET;
+
+ static {
+ try {
+ @SuppressWarnings("ALL")
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ Unsafe unsafe = (Unsafe) theUnsafe.get(null);
+ BYTES_OFFSET = unsafe.arrayBaseOffset(byte[].class);
+ CHARS_OFFSET = unsafe.arrayBaseOffset(char[].class);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+ private final boolean isSingleThreaded;
+
+ protected long start;
+ protected long position;
+ protected long limit;
+ protected long capacity;
+ private final MappedFile mappedFile;
+ private final ChronicleUnsafe chronicleUnsafe;
+
+ public MappedNativeBytes(@NotNull final MappedFile mappedFile, boolean isSingleThreaded) {
+ this.isSingleThreaded = isSingleThreaded;
+ this.mappedFile = mappedFile;
+ this.start = 0;
+ this.position = start;
+ this.limit = this.capacity = Long.MAX_VALUE;
+ this.chronicleUnsafe = (isSingleThreaded) ? new ChronicleUnsafe(mappedFile) : null;
+ }
+
+ public MappedNativeBytes(ObjectSerializer objectSerializer,
+ long sliceStart,
+ long capacity,
+ @NotNull AtomicInteger refCount,
+ @NotNull MappedFile mappedFile,
+ boolean singleThreaded) {
+ this.isSingleThreaded = singleThreaded;
+ setObjectSerializer(objectSerializer);
+ this.start = sliceStart;
+ this.position = 0;
+ this.capacity = capacity;
+ this.refCount.set(refCount.get());
+ this.mappedFile = mappedFile;
+ this.chronicleUnsafe = (isSingleThreaded) ? new ChronicleUnsafe(mappedFile) : null;
+ }
+
+ @Override
+ public MappedNativeBytes slice() {
+ return new MappedNativeBytes(objectSerializer(), position, limit, refCount, mappedFile, isSingleThreaded);
+ }
+
+ @Override
+ public MappedNativeBytes slice(long offset, long length) {
+ long sliceStart = position + offset;
+ assert sliceStart >= start && sliceStart < capacity;
+ long sliceEnd = sliceStart + length;
+ assert sliceEnd > sliceStart && sliceEnd <= capacity;
+ return new MappedNativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount, mappedFile, isSingleThreaded);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ long subStart = position + start;
+ if (subStart < position || subStart > limit)
+ throw new IndexOutOfBoundsException();
+ long subEnd = position + end;
+ if (subEnd < subStart || subEnd > limit)
+ throw new IndexOutOfBoundsException();
+ if (start == end)
+ return "";
+ return new MappedNativeBytes(objectSerializer(), subStart, subEnd, refCount, mappedFile, isSingleThreaded);
+ }
+
+ @Override
+ public MappedNativeBytes bytes() {
+ return new MappedNativeBytes(objectSerializer(), start, capacity, refCount, mappedFile, isSingleThreaded);
+ }
+
+ @Override
+ public MappedNativeBytes bytes(long offset, long length) {
+ long sliceStart = start + offset;
+ assert sliceStart >= start && sliceStart < capacity;
+ long sliceEnd = sliceStart + length;
+ assert sliceEnd > sliceStart && sliceEnd <= capacity;
+ return new MappedNativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount, mappedFile, isSingleThreaded);
+ }
+
+ @Override
+ public long address() {
+ return start;
+ }
+
+ @Override
+ public Bytes zeroOut() {
+ clear();
+ getChronicleUnsafe().setMemory(start, capacity(), (byte) 0);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end) {
+ if (start < 0 || end > limit())
+ throw new IllegalArgumentException("start: " + start + ", end: " + end);
+ if (start >= end)
+ return this;
+ getChronicleUnsafe().setMemory(this.start + start, end - start, (byte) 0);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end, boolean ifNotZero) {
+ return ifNotZero ? zeroOutDirty(start, end) : zeroOut(start, end);
+ }
+
+ private Bytes zeroOutDirty(long start, long end) {
+ if (start < 0 || end > limit())
+ throw new IllegalArgumentException("start: " + start + ", end: " + end);
+ if (start >= end)
+ return this;
+ // get unaligned leading bytes
+ ChronicleUnsafe unsafe = getChronicleUnsafe();
+ while (start < end && (start & 7) != 0) {
+ byte b = unsafe.getByte(this.start + start);
+ if (b != 0)
+ unsafe.putByte(this.start + start, (byte) 0);
+ start++;
+ }
+ // check 64-bit aligned access
+ while (start < end - 7) {
+ long l = unsafe.getLong(this.start + start);
+ if (l != 0)
+ unsafe.putLong(this.start + start, 0L);
+ start += 8;
+ }
+ // check unaligned tail
+ while (start < end) {
+ byte b = unsafe.getByte(this.start + start);
+ if (b != 0)
+ unsafe.putByte(this.start + start, (byte) 0);
+ start++;
+ }
+ return this;
+ }
+
+ @Override
+ public int read(@NotNull byte[] bytes, int off, int len) {
+ if (len < 0 || off < 0 || off + len > bytes.length)
+ throw new IllegalArgumentException();
+ long left = remaining();
+ if (left <= 0) return -1;
+ int len2 = (int) Math.min(len, left);
+ getChronicleUnsafe().copyMemory(null, position, bytes, BYTES_OFFSET + off, len2);
+ addPosition(len2);
+ return len2;
+ }
+
+ @Override
+ public byte readByte() {
+ byte aByte = getChronicleUnsafe().getByte(position);
+ addPosition(1);
+ return aByte;
+ }
+
+ @Override
+ public byte readByte(long offset) {
+ return getChronicleUnsafe().getByte(start + offset);
+ }
+
+ @Override
+ public void readFully(@NotNull byte[] b, int off, int len) {
+ checkArrayOffs(b.length, off, len);
+ long left = remaining();
+ if (left < len)
+ throw new IllegalStateException(new EOFException());
+ getChronicleUnsafe().copyMemory(null, position, b, BYTES_OFFSET + off, len);
+ addPosition(len);
+ }
+
+ @Override
+ public void readFully(long offset, byte[] bytes, int off, int len) {
+ checkArrayOffs(bytes.length, off, len);
+ getChronicleUnsafe().copyMemory(null, start + offset, bytes, BYTES_OFFSET + off, len);
+ }
+
+ @Override
+ public void readFully(@NotNull char[] data, int off, int len) {
+ checkArrayOffs(data.length, off, len);
+ long bytesOff = off * 2L;
+ long bytesLen = len * 2L;
+ long left = remaining();
+ if (left < bytesLen)
+ throw new IllegalStateException(new EOFException());
+ getChronicleUnsafe().copyMemory(null, position, data, BYTES_OFFSET + bytesOff, bytesLen);
+ addPosition(bytesLen);
+ }
+
+ @Override
+ public short readShort() {
+ short s = getChronicleUnsafe().getShort(position);
+ addPosition(2);
+ return s;
+ }
+
+ @Override
+ public short readShort(long offset) {
+ return getChronicleUnsafe().getShort(start + offset);
+ }
+
+ @Override
+ public char readChar() {
+ char ch = getChronicleUnsafe().getChar(position);
+ addPosition(2);
+ return ch;
+ }
+
+ @Override
+ public char readChar(long offset) {
+ return getChronicleUnsafe().getChar(start + offset);
+ }
+
+ @Override
+ public int readInt() {
+ int i = getChronicleUnsafe().getInt(position);
+ addPosition(4);
+ return i;
+ }
+
+ @Override
+ public int readInt(long offset) {
+ return getChronicleUnsafe().getInt(start + offset);
+ }
+
+ @Override
+ public int readVolatileInt() {
+ int i = getChronicleUnsafe().getIntVolatile(null, position);
+ addPosition(4);
+ return i;
+ }
+
+ @Override
+ public int readVolatileInt(long offset) {
+ return getChronicleUnsafe().getIntVolatile(null, start + offset);
+ }
+
+ @Override
+ public long readLong() {
+ long l = getChronicleUnsafe().getLong(position);
+ addPosition(8);
+ return l;
+ }
+
+ @Override
+ public long readLong(long offset) {
+ return getChronicleUnsafe().getLong(start + offset);
+ }
+
+ @Override
+ public long readVolatileLong() {
+ long l = getChronicleUnsafe().getLongVolatile(null, position);
+ addPosition(8);
+ return l;
+ }
+
+ @Override
+ public long readVolatileLong(long offset) {
+ return getChronicleUnsafe().getLongVolatile(null, start + offset);
+ }
+
+ @Override
+ public float readFloat() {
+ float f = getChronicleUnsafe().getFloat(position);
+ addPosition(4);
+ return f;
+ }
+
+ @Override
+ public float readFloat(long offset) {
+ return getChronicleUnsafe().getFloat(start + offset);
+ }
+
+ @Override
+ public double readDouble() {
+ double d = getChronicleUnsafe().getDouble(position);
+ addPosition(8);
+ return d;
+ }
+
+ @Override
+ public double readDouble(long offset) {
+ return getChronicleUnsafe().getDouble(start + offset);
+ }
+
+ @Override
+ public void write(int b) {
+ getChronicleUnsafe().putByte(position, (byte) b);
+ incrementPositionAddr(1);
+ }
+
+ @Override
+ public void writeByte(long offset, int b) {
+ offsetChecks(offset, 1L);
+ getChronicleUnsafe().putByte(start + offset, (byte) b);
+ }
+
+ @Override
+ public void write(long offset, @NotNull byte[] bytes) {
+ if (offset < 0 || offset + bytes.length > capacity())
+ throw new IllegalArgumentException();
+ getChronicleUnsafe().copyMemory(bytes, BYTES_OFFSET, null, start + offset, bytes.length);
+ addPosition(bytes.length);
+ }
+
+ @Override
+ public void write(byte[] bytes, int off, int len) {
+ if (off < 0 || off + len > bytes.length || len > remaining())
+ throw new IllegalArgumentException();
+ getChronicleUnsafe().copyMemory(bytes, BYTES_OFFSET + off, null, position, len);
+ addPosition(len);
+ }
+
+ @Override
+ public void write(long offset, byte[] bytes, int off, int len) {
+ if (offset < 0 || off + len > bytes.length || offset + len > capacity())
+ throw new IllegalArgumentException();
+ getChronicleUnsafe().copyMemory(bytes, BYTES_OFFSET + off, null, start + offset, len);
+ }
+
+ @Override
+ public void writeShort(int v) {
+ positionChecks(position + 2L);
+ getChronicleUnsafe().putShort(position, (short) v);
+ position += 2L;
+ }
+
+ private long incrementPositionAddr(long value) {
+ positionAddr(positionAddr() + value);
+ return positionAddr();
+ }
+
+ @Override
+ public void writeShort(long offset, int v) {
+ offsetChecks(offset, 2L);
+ getChronicleUnsafe().putShort(start + offset, (short) v);
+ }
+
+ @Override
+ public void writeChar(int v) {
+ positionChecks(position + 2L);
+ getChronicleUnsafe().putChar(position, (char) v);
+ position += 2L;
+ }
+
+ void addPosition(long delta) {
+ positionAddr(positionAddr() + delta);
+ }
+
+ @Override
+ public void writeChar(long offset, int v) {
+ offsetChecks(offset, 2L);
+ getChronicleUnsafe().putChar(start + offset, (char) v);
+ }
+
+ @Override
+ public void writeInt(int v) {
+ positionChecks(position + 4L);
+ getChronicleUnsafe().putInt(position, v);
+ position += 4L;
+ }
+
+ @Override
+ public void writeInt(long offset, int v) {
+ offsetChecks(offset, 4L);
+ getChronicleUnsafe().putInt(start + offset, v);
+ }
+
+ @Override
+ public void writeOrderedInt(int v) {
+ positionChecks(position + 4L);
+ getChronicleUnsafe().putOrderedInt(null, position, v);
+ position += 4L;
+ }
+
+ @Override
+ public void writeOrderedInt(long offset, int v) {
+ offsetChecks(offset, 4L);
+ getChronicleUnsafe().putOrderedInt(null, start + offset, v);
+ }
+
+ @Override
+ public boolean compareAndSwapInt(long offset, int expected, int x) {
+ offsetChecks(offset, 4L);
+ return getChronicleUnsafe().compareAndSwapInt(null, start + offset, expected, x);
+ }
+
+ @Override
+ public void writeLong(long v) {
+ positionChecks(position + 8L);
+ getChronicleUnsafe().putLong(position, v);
+ position += 8L;
+ }
+
+ @Override
+ public void writeLong(long offset, long v) {
+ offsetChecks(offset, 8L);
+ getChronicleUnsafe().putLong(start + offset, v);
+ }
+
+ @Override
+ public void writeOrderedLong(long v) {
+ positionChecks(position + 8L);
+ getChronicleUnsafe().putOrderedLong(null, position, v);
+ position += 8L;
+ }
+
+ @Override
+ public void writeOrderedLong(long offset, long v) {
+ offsetChecks(offset, 8L);
+ getChronicleUnsafe().putOrderedLong(null, start + offset, v);
+ }
+
+ @Override
+ public boolean compareAndSwapLong(long offset, long expected, long x) {
+ offsetChecks(offset, 8L);
+ return getChronicleUnsafe().compareAndSwapLong(null, start + offset, expected, x);
+ }
+
+ @Override
+ public void writeFloat(float v) {
+ positionChecks(position + 4L);
+ getChronicleUnsafe().putFloat(position, v);
+ position += 4L;
+ }
+
+ @Override
+ public void writeFloat(long offset, float v) {
+ offsetChecks(offset, 4L);
+ getChronicleUnsafe().putFloat(start + offset, v);
+ }
+
+ @Override
+ public void writeDouble(double v) {
+ positionChecks(position + 8L);
+ getChronicleUnsafe().putDouble(position, v);
+ position += 8L;
+ }
+
+ @Override
+ public void writeDouble(long offset, double v) {
+ offsetChecks(offset, 8L);
+ getChronicleUnsafe().putDouble(start + offset, v);
+ }
+
+ @Override
+ public void readObject(Object object, int start, int end) {
+ int len = end - start;
+ if (position + len >= limit)
+ throw new IndexOutOfBoundsException("Length out of bounds len: " + len);
+
+ ChronicleUnsafe unsafe = getChronicleUnsafe();
+ for (; len >= 8; len -= 8) {
+ unsafe.putLong(object, (long) start, unsafe.getLong(position));
+ incrementPositionAddr(8L);
+ start += 8;
+ }
+ for (; len > 0; len--) {
+ unsafe.putByte(object, (long) start, unsafe.getByte(position));
+ incrementPositionAddr(1L);
+ start++;
+ }
+ }
+
+ @Override
+ public void writeObject(Object object, int start, int end) {
+ int len = end - start;
+
+ ChronicleUnsafe unsafe = getChronicleUnsafe();
+ for (; len >= 8; len -= 8) {
+ positionChecks(position + 8L);
+ unsafe.putLong(position, unsafe.getLong(object, (long) start));
+ position += 8;
+ start += 8;
+ }
+ for (; len > 0; len--) {
+ positionChecks(position + 1L);
+ unsafe.putByte(position, unsafe.getByte(object, (long) start));
+ position++;
+ start++;
+ }
+ }
+
+ @Override
+ public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) {
+ if (offset < 0 || inputOffset < 0 || len < 0)
+ throw new IndexOutOfBoundsException();
+ if (offset + len < 0 || offset + len > capacity() || inputOffset + len < 0 ||
+ inputOffset + len > input.capacity()) {
+ return false;
+ }
+ long i = 0L;
+ ChronicleUnsafe unsafe = getChronicleUnsafe();
+ for (; i < len - 7L; i += 8L) {
+ if (unsafe.getLong(start + offset + i) != input.readLong(inputOffset + i))
+ return false;
+ }
+ if (i < len - 3L) {
+ if (unsafe.getInt(start + offset + i) != input.readInt(inputOffset + i))
+ return false;
+ i += 4L;
+ }
+ if (i < len - 1L) {
+ if (unsafe.getChar(start + offset + i) != input.readChar(inputOffset + i))
+ return false;
+ i += 2L;
+ }
+ if (i < len) {
+ if (unsafe.getByte(start + offset + i) != input.readByte(inputOffset + i))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public long position() {
+ return (position - start);
+ }
+
+ @Override
+ public MappedNativeBytes position(long position) {
+ if (position < 0 || position > limit())
+ throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit());
+
+ positionAddr(start + position);
+ return this;
+ }
+
+ /**
+ * Change the position acknowleging there is no thread safety assumptions. Best effort setting
+ * is fine. *
+ *
+ * @param position to set if we can.
+ * @return this
+ */
+ public MappedNativeBytes lazyPosition(long position) {
+ if (position < 0 || position > limit())
+ throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit());
+
+ // assume we don't need to no check thread safety.
+
+ positionAddr(start + position);
+ return this;
+ }
+
+ @Override
+ public void write(RandomDataInput bytes, long position, long length) {
+ if (length > remaining())
+ throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining");
+ if (bytes instanceof MappedNativeBytes) {
+ getChronicleUnsafe().copyMemory(((MappedNativeBytes) bytes).start + position, this.position, length);
+ skip(length);
+
+ } else {
+ super.write(bytes, position, length);
+ }
+ }
+
+ @Override
+ public long capacity() {
+ return (capacity - start);
+ }
+
+ @Override
+ public long remaining() {
+ return (limit - position);
+ }
+
+ @Override
+ public long limit() {
+ return (limit - start);
+ }
+
+ @Override
+ public MappedNativeBytes limit(long limit) {
+ if (limit < 0 || limit > capacity()) {
+ throw new IllegalArgumentException("limit: " + limit + " capacity: " + capacity());
+ }
+
+ this.limit = start + limit;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public ByteOrder byteOrder() {
+ return ByteOrder.nativeOrder();
+ }
+
+ @Override
+ public void checkEndOfBuffer() throws IndexOutOfBoundsException {
+ if (position() > limit()) {
+ throw new IndexOutOfBoundsException(
+ "position is beyond the end of the buffer " + position() + " > " + limit());
+ }
+ }
+
+ public long startAddr() {
+ return start;
+ }
+
+ long capacityAddr() {
+ return capacity;
+ }
+
+ @Override
+ protected void cleanup() {
+ // TODO nothing to do.
+ }
+
+ @Override
+ public Bytes load() {
+ ChronicleUnsafe unsafe = getChronicleUnsafe();
+ int pageSize = unsafe.pageSize();
+ for (long addr = start; addr < capacity; addr += pageSize)
+ unsafe.getByte(addr);
+ return this;
+ }
+
+ public void alignPositionAddr(int powerOf2) {
+ long value = (position + powerOf2 - 1) & ~(powerOf2 - 1);
+ positionAddr(value);
+ }
+
+ public void positionAddr(long positionAddr) {
+ positionChecks(positionAddr);
+ this.position = positionAddr;
+ }
+
+ void positionChecks(long positionAddr) {
+ assert actualPositionChecks(positionAddr);
+ }
+
+ boolean actualPositionChecks(long positionAddr) {
+ if (positionAddr < start)
+ throw new IndexOutOfBoundsException("position before the start by " + (start - positionAddr) + " bytes");
+ if (positionAddr > limit)
+ throw new IndexOutOfBoundsException("position after the limit by " + (positionAddr - limit) + " bytes");
+
+ return true;
+ }
+
+ void offsetChecks(long offset, long len) {
+ assert actualOffsetChecks(offset, len);
+ }
+
+ boolean actualOffsetChecks(long offset, long len) {
+ if (offset < 0L || offset + len > capacity())
+ throw new IndexOutOfBoundsException("offset out of bounds: " + offset + ", len: " +
+ len + ", capacity: " + capacity());
+ return true;
+ }
+
+ public long positionAddr() {
+ return position;
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) {
+ return sliceAsByteBuffer(toReuse, null);
+ }
+
+ protected ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse, Object att) {
+ return ByteBufferReuse.INSTANCE.reuse(position, (int) remaining(), att, toReuse);
+ }
+
+ // todo : we should move this lookup further up the stack, so that it can be done, just ONCE, for example once by a single threaded appender
+ // todo : hence the constructor should be give then instance of chronicleUnsafe to use
+ @NotNull
+ public ChronicleUnsafe getChronicleUnsafe() {
+ if (isSingleThreaded)
+ return chronicleUnsafe;
+
+ ChronicleUnsafe chronicleUnsafe = threadLocal.get();
+ if (chronicleUnsafe == null) {
+ chronicleUnsafe = new ChronicleUnsafe(mappedFile);
+ threadLocal.set(chronicleUnsafe);
+ }
+
+ return chronicleUnsafe;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/MappedStore.java b/lang/src/main/java/net/openhft/lang/io/MappedStore.java
new file mode 100644
index 0000000..df6b9f2
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/MappedStore.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.BytesMarshallableSerializer;
+import net.openhft.lang.io.serialization.BytesMarshallerFactory;
+import net.openhft.lang.io.serialization.JDKZObjectSerializer;
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+public class MappedStore extends AbstractMappedStore {
+ public MappedStore(File file, FileChannel.MapMode mode, long size) throws IOException {
+ this(file, mode, size, new VanillaBytesMarshallerFactory());
+ }
+
+ @Deprecated
+ public MappedStore(File file, FileChannel.MapMode mode, long size,
+ BytesMarshallerFactory bytesMarshallerFactory) throws IOException {
+ this(file, mode, size, BytesMarshallableSerializer.create(
+ bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE));
+ }
+
+ public MappedStore(File file, FileChannel.MapMode mode, long size,
+ ObjectSerializer objectSerializer) throws IOException {
+ this(file, mode, 0L, size, objectSerializer);
+ }
+
+ public MappedStore(File file, FileChannel.MapMode mode, long startInFile, long size,
+ ObjectSerializer objectSerializer) throws IOException {
+ super(new MmapInfoHolder(), file, mode, startInFile, size, objectSerializer);
+ mmapInfoHolder.lock();
+ }
+}
+
diff --git a/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java b/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java
index c2b6679..b9107e0 100644
--- a/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java
+++ b/lang/src/main/java/net/openhft/lang/io/MultiStoreBytes.java
@@ -1,40 +1,57 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
public class MultiStoreBytes extends NativeBytes {
- private DirectStore store;
+ private Bytes underlyingBytes;
+ private long underlyingOffset;
public MultiStoreBytes() {
- super(NO_PAGE, NO_PAGE, NO_PAGE);
- this.store = store;
+ super(NO_PAGE, NO_PAGE);
}
- public void storePositionAndSize(DirectStore store, long offset, long size) {
- if (offset < 0 || size < 0 || offset + size > store.size)
- throw new IllegalArgumentException();
- this.store = store;
- this.bytesMarshallerFactory = store.bytesMarshallerFactory;
- startAddr = positionAddr = store.address + offset;
- limitAddr = startAddr + size;
+ public void storePositionAndSize(BytesStore store, long offset, long size) {
+ if (offset < 0 || size < 0 || offset + size > store.size())
+ throw new IllegalArgumentException("offset: " + offset + ", size: " + size + ", store.size: " + store.size());
+ setObjectSerializer(store.objectSerializer());
+
+ setStartPositionAddress(store.address() + offset);
+ capacityAddr = limitAddr = startAddr + size;
+ underlyingBytes = null;
+ underlyingOffset = 0;
+ }
+
+ public void setBytesOffset(Bytes bytes, long offset) {
+ setObjectSerializer(bytes.objectSerializer());
+
+ long bytesAddr = bytes.address();
+ setStartPositionAddress(bytesAddr + offset);
+ capacityAddr = limitAddr = bytesAddr + bytes.capacity();
+ underlyingBytes = bytes;
+ underlyingOffset = offset;
+ }
+
+ public Bytes underlyingBytes() {
+ if (underlyingBytes == null) throw new IllegalStateException();
+ return underlyingBytes;
}
- public DirectStore store() {
- return store;
+ public long underlyingOffset() {
+ return underlyingOffset;
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java b/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java
index 773ce07..c3037c1 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java
+++ b/lang/src/main/java/net/openhft/lang/io/MutableDecimal.java
@@ -1,32 +1,31 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
import java.math.BigDecimal;
-import java.math.RoundingMode;
/**
* @author peter.lawrey
*/
@SuppressWarnings({"CompareToUsesNonFinalVariable", "NonFinalFieldReferenceInEquals", "NonFinalFieldReferencedInHashCode"})
public class MutableDecimal extends Number implements Comparable<MutableDecimal> {
- static final double[] TENS = new double[16];
+ private static final double[] TENS = new double[16];
static {
TENS[0] = 1;
@@ -54,15 +53,13 @@ public class MutableDecimal extends Number implements Comparable<MutableDecimal>
set(d, precision);
}
- public void set(double d, int precision) {
- double d2 = precision > 0 ? d * tens(precision) : d / tens(-precision);
- scale = precision;
- if (Math.abs(d2) < 1e16) {
- value = Math.round(d2);
- } else {
- BigDecimal bd = BigDecimal.valueOf(d).setScale(precision, RoundingMode.HALF_UP);
- value = bd.unscaledValue().longValue();
+ void set(double d, int precision) {
+ while (d > Long.MAX_VALUE) {
+ d /= 10;
+ precision++;
}
+ value = Math.round(d);
+ this.scale = precision;
}
public void set(long value, int scale) {
@@ -74,7 +71,7 @@ public class MutableDecimal extends Number implements Comparable<MutableDecimal>
return value;
}
- public int scale() {
+ int scale() {
return scale;
}
@@ -135,6 +132,7 @@ public class MutableDecimal extends Number implements Comparable<MutableDecimal>
if (digit < 0) {
digit = 8;
v = (v >>> 1) / 5;
+
} else {
v /= 10;
}
diff --git a/lang/src/main/java/net/openhft/lang/io/NativeBytes.java b/lang/src/main/java/net/openhft/lang/io/NativeBytes.java
index 2a974d7..96aba3a 100755
--- a/lang/src/main/java/net/openhft/lang/io/NativeBytes.java
+++ b/lang/src/main/java/net/openhft/lang/io/NativeBytes.java
@@ -1,28 +1,35 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
import net.openhft.lang.io.serialization.BytesMarshallerFactory;
+import net.openhft.lang.io.serialization.ObjectSerializer;
import org.jetbrains.annotations.NotNull;
import sun.misc.Unsafe;
import java.io.EOFException;
+import java.io.IOException;
import java.lang.reflect.Field;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.lang.Long.numberOfTrailingZeros;
/**
* @author peter.lawrey
@@ -34,7 +41,9 @@ public class NativeBytes extends AbstractBytes {
@NotNull
@SuppressWarnings("ALL")
public static final Unsafe UNSAFE;
+ protected static final long NO_PAGE;
static final int BYTES_OFFSET;
+ static final int CHARS_OFFSET;
static {
try {
@@ -43,36 +52,64 @@ public class NativeBytes extends AbstractBytes {
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
BYTES_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
-
+ CHARS_OFFSET = UNSAFE.arrayBaseOffset(char[].class);
} catch (Exception e) {
throw new AssertionError(e);
}
+ NO_PAGE = UNSAFE.allocateMemory(UNSAFE.pageSize());
}
- protected static final long NO_PAGE = UNSAFE.allocateMemory(UNSAFE.pageSize());
-
protected long startAddr;
protected long positionAddr;
protected long limitAddr;
+ protected long capacityAddr;
- public NativeBytes(long startAddr, long positionAddr, long limitAddr) {
- this.startAddr = startAddr;
- this.positionAddr = positionAddr;
- this.limitAddr = limitAddr;
+ public NativeBytes(long startAddr, long capacityAddr) {
+ super();
+ setStartPositionAddress(startAddr);
+ if (startAddr > capacityAddr)
+ throw new IllegalArgumentException("Missorted capacity address");
+ this.limitAddr =
+ this.capacityAddr = capacityAddr;
+ positionChecks(positionAddr);
}
- public NativeBytes(BytesMarshallerFactory bytesMarshallerFactory, long startAddr, long positionAddr, long limitAddr) {
- super(bytesMarshallerFactory);
- this.startAddr = startAddr;
- this.positionAddr = positionAddr;
- this.limitAddr = limitAddr;
+ /**
+ * @deprecated Use {@link #NativeBytes(ObjectSerializer, long, long, AtomicInteger)} instead
+ */
+ @Deprecated
+ public NativeBytes(BytesMarshallerFactory bytesMarshallerFactory,
+ long startAddr, long capacityAddr, AtomicInteger refCount) {
+ super(bytesMarshallerFactory, refCount);
+
+ setStartPositionAddress(startAddr);
+ this.limitAddr =
+ this.capacityAddr = capacityAddr;
+ positionChecks(positionAddr);
+ }
+
+ public NativeBytes(ObjectSerializer objectSerializer,
+ long startAddr, long capacityAddr, AtomicInteger refCount) {
+ super(objectSerializer, refCount);
+
+ setStartPositionAddress(startAddr);
+ this.limitAddr =
+ this.capacityAddr = capacityAddr;
+ positionChecks(positionAddr);
}
public NativeBytes(NativeBytes bytes) {
- super(bytes.bytesMarshallerFactory());
- this.startAddr = bytes.startAddr;
+ super(bytes.objectSerializer(), new AtomicInteger(1));
+ setStartPositionAddress(bytes.startAddr);
+
this.positionAddr = bytes.positionAddr;
this.limitAddr = bytes.limitAddr;
+ this.capacityAddr = bytes.capacityAddr;
+ positionChecks(positionAddr);
+ }
+
+ public static NativeBytes wrap(long address, long capacity) {
+ return new NativeBytes(address, address + capacity);
}
public static long longHash(byte[] bytes, int off, int len) {
@@ -85,6 +122,177 @@ public class NativeBytes extends AbstractBytes {
return hash;
}
+ static long nextSetBit0(int firstByte, int maximum, long startAddr) {
+ for (int i = firstByte; i < maximum; i += 8) {
+ long l = UNSAFE.getLong(startAddr + i);
+ if (l != 0)
+ return (i << 3) + numberOfTrailingZeros(l);
+ }
+ return -1;
+ }
+
+ static long nextSetBit0(long firstByte, long maximum, long startAddr) {
+ for (long i = firstByte; i < maximum; i += 8) {
+ long l = UNSAFE.getLong(startAddr + i);
+ if (l != 0)
+ return (i << 3) + numberOfTrailingZeros(l);
+ }
+ return -1;
+ }
+
+ public void setStartPositionAddress(long startAddr) {
+ if ((startAddr & ~0x3fff) == 0)
+ throw new AssertionError("Invalid address " + Long.toHexString(startAddr));
+ this.positionAddr =
+ this.startAddr = startAddr;
+ }
+
+ // optimised to reduce overhead.
+ public void readUTF0(@NotNull Appendable appendable, int utflen)
+ throws IOException {
+ if (utflen > remaining())
+ throw new BufferUnderflowException();
+ if (appendable instanceof StringBuilder)
+ readUTF1((StringBuilder) appendable, utflen);
+ else
+ readUTF1(appendable, utflen);
+ }
+
+ private void readUTF1(@NotNull StringBuilder sb, int utflen)
+ throws IOException {
+ int count = 0;
+ sb.ensureCapacity(utflen);
+ char[] chars = StringBuilderUtils.extractChars(sb);
+ ascii:
+ try {
+ while (count < utflen) {
+ int c = UNSAFE.getByte(positionAddr++) & 0xFF;
+ if (c >= 128) {
+ break ascii;
+ }
+ chars[count++] = (char) c;
+ }
+ return;
+ } finally {
+ StringBuilderUtils.setCount(sb, count);
+ }
+
+ positionAddr--;
+ readUTF2(this, sb, utflen, count);
+ }
+
+ private void readUTF1(@NotNull Appendable appendable, int utflen)
+ throws IOException {
+ int count = 0;
+ while (count < utflen) {
+ int c = UNSAFE.getByte(positionAddr++) & 0xFF;
+ if (c >= 128) {
+ positionAddr--;
+ readUTF2(this, appendable, utflen, count);
+ break;
+ }
+ count++;
+ appendable.append((char) c);
+ }
+ }
+
+ @Override
+ public NativeBytes slice() {
+ return new NativeBytes(objectSerializer(), positionAddr, limitAddr, refCount);
+ }
+
+ @Override
+ public NativeBytes slice(long offset, long length) {
+ long sliceStart = positionAddr + offset;
+ assert sliceStart >= startAddr && sliceStart < capacityAddr;
+ long sliceEnd = sliceStart + length;
+ assert sliceEnd > sliceStart && sliceEnd <= capacityAddr;
+ return new NativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ long subStart = positionAddr + start;
+ if (subStart < positionAddr || subStart > limitAddr)
+ throw new IndexOutOfBoundsException();
+ long subEnd = positionAddr + end;
+ if (subEnd < subStart || subEnd > limitAddr)
+ throw new IndexOutOfBoundsException();
+ if (start == end)
+ return "";
+ return new NativeBytes(objectSerializer(), subStart, subEnd, refCount);
+ }
+
+ @Override
+ public NativeBytes bytes() {
+ return new NativeBytes(objectSerializer(), startAddr, capacityAddr, refCount);
+ }
+
+ @Override
+ public NativeBytes bytes(long offset, long length) {
+ long sliceStart = startAddr + offset;
+ assert sliceStart >= startAddr && sliceStart < capacityAddr;
+ long sliceEnd = sliceStart + length;
+ assert sliceEnd > sliceStart && sliceEnd <= capacityAddr;
+ return new NativeBytes(objectSerializer(), sliceStart, sliceEnd, refCount);
+ }
+
+ @Override
+ public long address() {
+ return startAddr;
+ }
+
+ @Override
+ public Bytes zeroOut() {
+ clear();
+ UNSAFE.setMemory(startAddr, capacity(), (byte) 0);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end) {
+ if (start < 0 || end > limit())
+ throw new IllegalArgumentException("start: " + start + ", end: " + end);
+ if (start >= end)
+ return this;
+ UNSAFE.setMemory(startAddr + start, end - start, (byte) 0);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end, boolean ifNotZero) {
+ return ifNotZero ? zeroOutDirty(start, end) : zeroOut(start, end);
+ }
+
+ private Bytes zeroOutDirty(long start, long end) {
+ if (start < 0 || end > limit())
+ throw new IllegalArgumentException("start: " + start + ", end: " + end);
+ if (start >= end)
+ return this;
+ // get unaligned leading bytes
+ while (start < end && (start & 7) != 0) {
+ byte b = UNSAFE.getByte(startAddr + start);
+ if (b != 0)
+ UNSAFE.putByte(startAddr + start, (byte) 0);
+ start++;
+ }
+ // check 64-bit aligned access
+ while (start < end - 7) {
+ long l = UNSAFE.getLong(startAddr + start);
+ if (l != 0)
+ UNSAFE.putLong(startAddr + start, 0L);
+ start += 8;
+ }
+ // check unaligned tail
+ while (start < end) {
+ byte b = UNSAFE.getByte(startAddr + start);
+ if (b != 0)
+ UNSAFE.putByte(startAddr + start, (byte) 0);
+ start++;
+ }
+ return this;
+ }
+
@Override
public int read(@NotNull byte[] bytes, int off, int len) {
if (len < 0 || off < 0 || off + len > bytes.length)
@@ -93,13 +301,15 @@ public class NativeBytes extends AbstractBytes {
if (left <= 0) return -1;
int len2 = (int) Math.min(len, left);
UNSAFE.copyMemory(null, positionAddr, bytes, BYTES_OFFSET + off, len2);
- positionAddr += len2;
+ addPosition(len2);
return len2;
}
@Override
public byte readByte() {
- return UNSAFE.getByte(positionAddr++);
+ byte aByte = UNSAFE.getByte(positionAddr);
+ addPosition(1);
+ return aByte;
}
@Override
@@ -109,19 +319,36 @@ public class NativeBytes extends AbstractBytes {
@Override
public void readFully(@NotNull byte[] b, int off, int len) {
- if (len < 0 || off < 0 || off + len > b.length)
- throw new IllegalArgumentException();
+ checkArrayOffs(b.length, off, len);
long left = remaining();
if (left < len)
throw new IllegalStateException(new EOFException());
UNSAFE.copyMemory(null, positionAddr, b, BYTES_OFFSET + off, len);
- positionAddr += len;
+ addPosition(len);
+ }
+
+ @Override
+ public void readFully(long offset, byte[] bytes, int off, int len) {
+ checkArrayOffs(bytes.length, off, len);
+ UNSAFE.copyMemory(null, startAddr + offset, bytes, BYTES_OFFSET + off, len);
+ }
+
+ @Override
+ public void readFully(@NotNull char[] data, int off, int len) {
+ checkArrayOffs(data.length, off, len);
+ long bytesOff = off * 2L;
+ long bytesLen = len * 2L;
+ long left = remaining();
+ if (left < bytesLen)
+ throw new IllegalStateException(new EOFException());
+ UNSAFE.copyMemory(null, positionAddr, data, BYTES_OFFSET + bytesOff, bytesLen);
+ addPosition(bytesLen);
}
@Override
public short readShort() {
short s = UNSAFE.getShort(positionAddr);
- positionAddr += 2;
+ addPosition(2);
return s;
}
@@ -133,7 +360,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public char readChar() {
char ch = UNSAFE.getChar(positionAddr);
- positionAddr += 2;
+ addPosition(2);
return ch;
}
@@ -145,7 +372,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public int readInt() {
int i = UNSAFE.getInt(positionAddr);
- positionAddr += 4;
+ addPosition(4);
return i;
}
@@ -157,7 +384,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public int readVolatileInt() {
int i = UNSAFE.getIntVolatile(null, positionAddr);
- positionAddr += 4;
+ addPosition(4);
return i;
}
@@ -169,7 +396,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public long readLong() {
long l = UNSAFE.getLong(positionAddr);
- positionAddr += 8;
+ addPosition(8);
return l;
}
@@ -181,7 +408,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public long readVolatileLong() {
long l = UNSAFE.getLongVolatile(null, positionAddr);
- positionAddr += 8;
+ addPosition(8);
return l;
}
@@ -193,7 +420,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public float readFloat() {
float f = UNSAFE.getFloat(positionAddr);
- positionAddr += 4;
+ addPosition(4);
return f;
}
@@ -205,7 +432,7 @@ public class NativeBytes extends AbstractBytes {
@Override
public double readDouble() {
double d = UNSAFE.getDouble(positionAddr);
- positionAddr += 8;
+ addPosition(8);
return d;
}
@@ -216,11 +443,13 @@ public class NativeBytes extends AbstractBytes {
@Override
public void write(int b) {
- UNSAFE.putByte(positionAddr++, (byte) b);
+ UNSAFE.putByte(positionAddr, (byte) b);
+ incrementPositionAddr(1);
}
@Override
public void writeByte(long offset, int b) {
+ offsetChecks(offset, 1L);
UNSAFE.putByte(startAddr + offset, (byte) b);
}
@@ -229,124 +458,163 @@ public class NativeBytes extends AbstractBytes {
if (offset < 0 || offset + bytes.length > capacity())
throw new IllegalArgumentException();
UNSAFE.copyMemory(bytes, BYTES_OFFSET, null, startAddr + offset, bytes.length);
- positionAddr += bytes.length;
+ addPosition(bytes.length);
}
@Override
public void write(byte[] bytes, int off, int len) {
+ if (off < 0 || off + len > bytes.length || len > remaining())
+ throw new IllegalArgumentException();
UNSAFE.copyMemory(bytes, BYTES_OFFSET + off, null, positionAddr, len);
- positionAddr += len;
+ addPosition(len);
+ }
+
+ @Override
+ public void write(long offset, byte[] bytes, int off, int len) {
+ if (offset < 0 || off + len > bytes.length || offset + len > capacity())
+ throw new IllegalArgumentException();
+ UNSAFE.copyMemory(bytes, BYTES_OFFSET + off, null, startAddr + offset, len);
}
@Override
public void writeShort(int v) {
+ positionChecks(positionAddr + 2L);
UNSAFE.putShort(positionAddr, (short) v);
- positionAddr += 2;
+ positionAddr += 2L;
+ }
+
+ private long incrementPositionAddr(long value) {
+ positionAddr(positionAddr() + value);
+ return positionAddr();
}
@Override
public void writeShort(long offset, int v) {
+ offsetChecks(offset, 2L);
UNSAFE.putShort(startAddr + offset, (short) v);
}
@Override
public void writeChar(int v) {
+ positionChecks(positionAddr + 2L);
UNSAFE.putChar(positionAddr, (char) v);
- positionAddr += 2;
+ positionAddr += 2L;
+ }
+
+ void addPosition(long delta) {
+ positionAddr(positionAddr() + delta);
}
@Override
public void writeChar(long offset, int v) {
+ offsetChecks(offset, 2L);
UNSAFE.putChar(startAddr + offset, (char) v);
}
@Override
public void writeInt(int v) {
- UNSAFE.putInt(null, positionAddr, v);
- positionAddr += 4;
+ positionChecks(positionAddr + 4L);
+ UNSAFE.putInt(positionAddr, v);
+ positionAddr += 4L;
}
@Override
public void writeInt(long offset, int v) {
+ offsetChecks(offset, 4L);
UNSAFE.putInt(startAddr + offset, v);
}
@Override
public void writeOrderedInt(int v) {
+ positionChecks(positionAddr + 4L);
UNSAFE.putOrderedInt(null, positionAddr, v);
- positionAddr += 4;
+ positionAddr += 4L;
}
@Override
public void writeOrderedInt(long offset, int v) {
+ offsetChecks(offset, 4L);
UNSAFE.putOrderedInt(null, startAddr + offset, v);
}
@Override
public boolean compareAndSwapInt(long offset, int expected, int x) {
+ offsetChecks(offset, 4L);
return UNSAFE.compareAndSwapInt(null, startAddr + offset, expected, x);
}
@Override
public void writeLong(long v) {
+ positionChecks(positionAddr + 8L);
UNSAFE.putLong(positionAddr, v);
- positionAddr += 8;
+ positionAddr += 8L;
}
@Override
public void writeLong(long offset, long v) {
+ offsetChecks(offset, 8L);
UNSAFE.putLong(startAddr + offset, v);
}
@Override
public void writeOrderedLong(long v) {
+ positionChecks(positionAddr + 8L);
UNSAFE.putOrderedLong(null, positionAddr, v);
- positionAddr += 8;
+ positionAddr += 8L;
}
@Override
public void writeOrderedLong(long offset, long v) {
+ offsetChecks(offset, 8L);
UNSAFE.putOrderedLong(null, startAddr + offset, v);
}
@Override
public boolean compareAndSwapLong(long offset, long expected, long x) {
+ offsetChecks(offset, 8L);
return UNSAFE.compareAndSwapLong(null, startAddr + offset, expected, x);
}
@Override
public void writeFloat(float v) {
+ positionChecks(positionAddr + 4L);
UNSAFE.putFloat(positionAddr, v);
- positionAddr += 4;
+ positionAddr += 4L;
}
@Override
public void writeFloat(long offset, float v) {
+ offsetChecks(offset, 4L);
UNSAFE.putFloat(startAddr + offset, v);
}
@Override
public void writeDouble(double v) {
+ positionChecks(positionAddr + 8L);
UNSAFE.putDouble(positionAddr, v);
- positionAddr += 8;
+ positionAddr += 8L;
}
@Override
public void writeDouble(long offset, double v) {
+ offsetChecks(offset, 8L);
UNSAFE.putDouble(startAddr + offset, v);
}
@Override
public void readObject(Object object, int start, int end) {
int len = end - start;
+ if (positionAddr + len >= limitAddr)
+ throw new IndexOutOfBoundsException("Length out of bounds len: " + len);
+
for (; len >= 8; len -= 8) {
UNSAFE.putLong(object, (long) start, UNSAFE.getLong(positionAddr));
- positionAddr += 8;
+ incrementPositionAddr(8L);
start += 8;
}
for (; len > 0; len--) {
UNSAFE.putByte(object, (long) start, UNSAFE.getByte(positionAddr));
- positionAddr++;
+ incrementPositionAddr(1L);
start++;
}
}
@@ -354,12 +622,15 @@ public class NativeBytes extends AbstractBytes {
@Override
public void writeObject(Object object, int start, int end) {
int len = end - start;
+
for (; len >= 8; len -= 8) {
+ positionChecks(positionAddr + 8L);
UNSAFE.putLong(positionAddr, UNSAFE.getLong(object, (long) start));
positionAddr += 8;
start += 8;
}
for (; len > 0; len--) {
+ positionChecks(positionAddr + 1L);
UNSAFE.putByte(positionAddr, UNSAFE.getByte(object, (long) start));
positionAddr++;
start++;
@@ -367,18 +638,82 @@ public class NativeBytes extends AbstractBytes {
}
@Override
+ public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) {
+ if (offset < 0 || inputOffset < 0 || len < 0)
+ throw new IndexOutOfBoundsException();
+ if (offset + len < 0 || offset + len > capacity() || inputOffset + len < 0 ||
+ inputOffset + len > input.capacity()) {
+ return false;
+ }
+ long i = 0L;
+ for (; i < len - 7L; i += 8L) {
+ if (UNSAFE.getLong(startAddr + offset + i) != input.readLong(inputOffset + i))
+ return false;
+ }
+ if (i < len - 3L) {
+ if (UNSAFE.getInt(startAddr + offset + i) != input.readInt(inputOffset + i))
+ return false;
+ i += 4L;
+ }
+ if (i < len - 1L) {
+ if (UNSAFE.getChar(startAddr + offset + i) != input.readChar(inputOffset + i))
+ return false;
+ i += 2L;
+ }
+ if (i < len) {
+ if (UNSAFE.getByte(startAddr + offset + i) != input.readByte(inputOffset + i))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
public long position() {
return (positionAddr - startAddr);
}
@Override
- public void position(long position) {
- this.positionAddr = startAddr + position;
+ public NativeBytes position(long position) {
+ if (position < 0 || position > limit())
+ throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit());
+
+ positionAddr(startAddr + position);
+ return this;
+ }
+
+ /**
+ * Change the position acknowleging there is no thread safety assumptions. Best effort setting
+ * is fine. *
+ *
+ * @param position to set if we can.
+ * @return this
+ */
+ public NativeBytes lazyPosition(long position) {
+ if (position < 0 || position > limit())
+ throw new IndexOutOfBoundsException("position: " + position + " limit: " + limit());
+
+ // assume we don't need to no check thread safety.
+
+ positionAddr(startAddr + position);
+ return this;
+ }
+
+ @Override
+ public void write(RandomDataInput bytes, long position, long length) {
+ if (length > remaining())
+ throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining");
+ if (bytes instanceof NativeBytes) {
+ UNSAFE.copyMemory(((NativeBytes) bytes).startAddr + position, positionAddr, length);
+ skip(length);
+
+ } else {
+ super.write(bytes, position, length);
+ }
}
@Override
public long capacity() {
- return (limitAddr - startAddr);
+ return (capacityAddr - startAddr);
}
@Override
@@ -386,6 +721,21 @@ public class NativeBytes extends AbstractBytes {
return (limitAddr - positionAddr);
}
+ @Override
+ public long limit() {
+ return (limitAddr - startAddr);
+ }
+
+ @Override
+ public NativeBytes limit(long limit) {
+ if (limit < 0 || limit > capacity()) {
+ throw new IllegalArgumentException("limit: " + limit + " capacity: " + capacity());
+ }
+
+ limitAddr = startAddr + limit;
+ return this;
+ }
+
@NotNull
@Override
public ByteOrder byteOrder() {
@@ -394,23 +744,107 @@ public class NativeBytes extends AbstractBytes {
@Override
public void checkEndOfBuffer() throws IndexOutOfBoundsException {
- if (position() > capacity())
- throw new IndexOutOfBoundsException("position is beyond the end of the buffer " + position() + " > " + capacity());
+ if (position() > limit()) {
+ throw new IndexOutOfBoundsException(
+ "position is beyond the end of the buffer " + position() + " > " + limit());
+ }
}
public long startAddr() {
return startAddr;
}
- public long positionAddr() {
- return positionAddr;
+ long capacityAddr() {
+ return capacityAddr;
+ }
+
+ @Override
+ protected void cleanup() {
+ // TODO nothing to do.
+ }
+
+ @Override
+ public Bytes load() {
+ int pageSize = UNSAFE.pageSize();
+ for (long addr = startAddr; addr < capacityAddr; addr += pageSize)
+ UNSAFE.getByte(addr);
+ return this;
+ }
+
+ public void alignPositionAddr(int powerOf2) {
+ long value = (positionAddr + powerOf2 - 1) & ~(powerOf2 - 1);
+ positionAddr(value);
}
public void positionAddr(long positionAddr) {
+ positionChecks(positionAddr);
this.positionAddr = positionAddr;
}
- public long limitAddr() {
- return limitAddr;
+ void positionChecks(long positionAddr) {
+ assert actualPositionChecks(positionAddr);
+ }
+
+ boolean actualPositionChecks(long positionAddr) {
+ if (positionAddr < startAddr)
+ throw new IndexOutOfBoundsException("position before the start by " + (startAddr - positionAddr) + " bytes");
+ if (positionAddr > limitAddr)
+ throw new IndexOutOfBoundsException("position after the limit by " + (positionAddr - limitAddr) + " bytes");
+
+ return true;
+ }
+
+ void offsetChecks(long offset, long len) {
+ assert actualOffsetChecks(offset, len);
+ }
+
+ boolean actualOffsetChecks(long offset, long len) {
+ if (offset < 0L || offset + len > capacity())
+ throw new IndexOutOfBoundsException("offset out of bounds: " + offset + ", len: " +
+ len + ", capacity: " + capacity());
+ return true;
+ }
+
+ public long positionAddr() {
+ return positionAddr;
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) {
+ return sliceAsByteBuffer(toReuse, null);
+ }
+
+ protected ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse, Object att) {
+ return ByteBufferReuse.INSTANCE.reuse(positionAddr, (int) remaining(), att, toReuse);
+ }
+
+ void address(long address) {
+ setStartPositionAddress(address);
+ }
+
+ void capacity(long capacity) {
+ this.limitAddr = this.capacityAddr = capacity;
+ }
+
+ @Override
+ public long nextSetBit(long fromIndex) {
+ if (fromIndex < 0)
+ throw new IndexOutOfBoundsException();
+ long capacity = capacity();
+ long maxBit = capacity << 3;
+ long fromLongIndex = fromIndex & ~63;
+ if (fromLongIndex >= maxBit)
+ return -1;
+ long firstByte = fromLongIndex >>> 3;
+ if ((fromIndex & 63) != 0) {
+ long l = UNSAFE.getLongVolatile(null, startAddr + firstByte) >>> fromIndex;
+ if (l != 0) {
+ return fromIndex + numberOfTrailingZeros(l);
+ }
+ firstByte += 8;
+ }
+ if (capacity < Integer.MAX_VALUE)
+ return nextSetBit0((int) firstByte, (int) capacity, startAddr);
+ return nextSetBit0(firstByte, capacity, startAddr);
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java b/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java
index 3581b0f..10f97e3 100755
--- a/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java
+++ b/lang/src/main/java/net/openhft/lang/io/RandomDataInput.java
@@ -1,17 +1,17 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ObjectInput;
+import java.io.StreamCorruptedException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
@@ -30,21 +31,22 @@ import java.util.RandomAccess;
*/
public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon {
/**
- * Reads some bytes from an input stream and stores them into the buffer array <code>b</code>. The number of bytes
+ * <p>Reads some bytes from an input stream and stores them into the buffer array <code>b</code>. The number of bytes
* read is equal to the length of <code>b</code>.
- * <p/>
- * This method blocks until one of the following conditions occurs:<p> <ul> <li><code>b.length</code> bytes of input
- * data are available, in which case a normal return is made.
- * <p/>
- * <li>End of file is detected, in which case an <code>EOFException</code> is thrown.
- * <p/>
- * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is
- * thrown. </ul>
- * <p/>
- * If <code>b</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>b.length</code>
- * is zero, then no bytes are read. Otherwise, the first byte read is stored into element <code>b[0]</code>, the
- * next one into <code>b[1]</code>, and so on. If an exception is thrown from this method, then it may be that some
- * but not all bytes of <code>b</code> have been updated with data from the input stream.
+ * </p><p>
+ * This method blocks until one of the following conditions occurs:
+ * </p>
+ * <ul>
+ * <li><code>b.length</code> bytes of input data are available, in which case a normal return is made. </li>
+ * <li>End of file is detected, in which case an <code>EOFException</code> is thrown.</li>
+ * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is thrown.</li>
+ * </ul>
+ * <p>
+ * If <code>bytes</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>bytes.length</code>
+ * is zero, then no bytes are read. Otherwise, the first byte read is stored into element <code>bytes[0]</code>, the
+ * next one into <code>bytes[1]</code>, and so on. If an exception is thrown from this method, then it may be that some
+ * but not all bytes of <code>bytes</code> have been updated with data from the input stream.
+ * </p>
*
* @param bytes the buffer into which the data is read.
*/
@@ -52,21 +54,22 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
void readFully(@NotNull byte[] bytes);
/**
- * Reads <code>len</code> bytes from an input stream.
- * <p/>
- * This method blocks until one of the following conditions occurs:<p> <ul> <li><code>len</code> bytes of input data
- * are available, in which case a normal return is made.
- * <p/>
- * <li>End of file is detected, in which case an <code>EOFException</code> is thrown.
- * <p/>
- * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is
- * thrown. </ul>
- * <p/>
- * If <code>b</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>off</code> is
+ * <p>Reads <code>len</code> bytes from an input stream.
+ * </p><p>
+ * This method blocks until one of the following conditions occurs:
+ * </p>
+ * <ul>
+ * <li><code>len</code> bytes of input data are available, in which case a normal return is made.</li>
+ * <li>End of file is detected, in which case an <code>EOFException</code> is thrown.</li>
+ * <li>An I/O error occurs, in which case an <code>IOException</code> other than <code>EOFException</code> is thrown. </li>
+ * </ul>
+ * <p>
+ * If <code>bytes</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>off</code> is
* negative, or <code>len</code> is negative, or <code>off+len</code> is greater than the length of the array
- * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is thrown. If <code>len</code> is zero, then no
- * bytes are read. Otherwise, the first byte read is stored into element <code>b[off]</code>, the next one into
- * <code>b[off+1]</code>, and so on. The number of bytes read is, at most, equal to <code>len</code>.
+ * <code>bytes</code>, then an <code>IndexOutOfBoundsException</code> is thrown. If <code>len</code> is zero, then no
+ * bytes are read. Otherwise, the first byte read is stored into element <code>bytes[off]</code>, the next one into
+ * <code>bytes[off+1]</code>, and so on. The number of bytes read is, at most, equal to <code>len</code>.
+ * </p>
*
* @param bytes the buffer into which the data is read.
* @param off an int specifying the offset into the data.
@@ -75,6 +78,12 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
@Override
void readFully(@NotNull byte[] bytes, int off, int len);
+ void readFully(long offset, @NotNull byte[] bytes, int off, int len);
+
+ void readFully(@NotNull char[] data);
+
+ void readFully(@NotNull char[] data, int off, int len);
+
/**
* Makes an attempt to skip over <code>n</code> bytes of data from the input stream, discarding the skipped bytes.
* However, it may skip over some smaller number of bytes, possibly zero. This may result from any of a number of
@@ -153,7 +162,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads two input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read and
* <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value
* returned is:
- * <p><pre><code>(short)((a &lt;&lt; 8) | (b &amp; 0xff))
+ * <pre><code>(short)((a &lt;&lt; 8) | (b &amp; 0xff))
* </code></pre>
* This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface
* <code>DataOutput</code>.
@@ -167,7 +176,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads two input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read and
* <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value
* returned is:
- * <p><pre><code>
+ * <pre><code>
* (short)((a &lt;&lt; 8) | (b &amp; 0xff))
* </code></pre>
* This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface
@@ -182,7 +191,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads two input bytes and returns an <code>int</code> value in the range <code>0</code> through
* <code>65535</code>. Let <code>a</code> be the first byte read and <code>b</code> be the second byte on big endian
* machines, and the opposite on little endian machines. The value returned is:
- * <p><pre><code>
+ * <pre><code>
* (((a &amp; 0xff) &lt;&lt; 8) | (b &amp; 0xff))
* </code></pre>
* This method is suitable for reading the bytes written by the <code>writeUnsignedShort</code> method of interface
@@ -198,7 +207,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads two input bytes and returns an <code>int</code> value in the range <code>0</code> through
* <code>65535</code>. Let <code>a</code> be the first byte read and <code>b</code> be the second byte on big endian
* machines, and the opposite on little endian machines. The value returned is:
- * <p><pre><code>
+ * <pre><code>
* (((a &amp; 0xff) &lt;&lt; 8) | (b &amp; 0xff))
* </code></pre>
* This method is suitable for reading the bytes written by the <code>writeShort</code> method of interface
@@ -214,8 +223,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads one or three input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read.
* This mapped as follows; Byte.MIN_VALUE =&gt; Short.MIN_VALUE, Byte.MAX_VALUE =&gt; Short.MAX_VALUE, Byte.MIN_VALUE+2 to
* Byte.MAX_VALUE-1 =&gt; same as short value, Byte.MIN_VALUE+1 =&gt; readShort().
- * <p/>
- * This method is suitable for reading the bytes written by the <code>writeCompactShort</code> method of interface
+ *
+ * <p>This method is suitable for reading the bytes written by the <code>writeCompactShort</code> method of interface
* <code>RandomDataOutput</code>.
*
* @return the 16-bit value read.
@@ -225,8 +234,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads one or three input bytes and returns a <code>short</code> value. Let <code>a</code> be the first byte read.
* This mapped as follows; -1 =&gt; readUnsignedShort(), default =&gt; (a &amp; 0xFF)
- * <p/>
- * This method is suitable for reading the bytes written by the <code>writeCompactUnsignedShort</code> method of
+ *
+ * <p>This method is suitable for reading the bytes written by the <code>writeCompactUnsignedShort</code> method of
* interface <code>RandomDataOutput</code>.
*
* @return the unsigned 16-bit value read.
@@ -237,7 +246,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads two input bytes and returns a <code>char</code> value. Let <code>a</code> be the first byte read and
* <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value
* returned is:
- * <p><pre><code>(char)((a &lt;&lt; 8) | (b &amp; 0xff))
+ * <pre><code>(char)((a &lt;&lt; 8) | (b &amp; 0xff))
* </code></pre>
* This method is suitable for reading bytes written by the <code>writeChar</code> method of interface
* <code>DataOutput</code>.
@@ -251,7 +260,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads two input bytes and returns a <code>char</code> value. Let <code>a</code> be the first byte read and
* <code>b</code> be the second byte on big endian machines, and the opposite on little endian machines. The value
* returned is:
- * <p><pre><code>(char)((a &lt;&lt; 8) | (b &amp; 0xff))
+ * <pre><code>(char)((a &lt;&lt; 8) | (b &amp; 0xff))
* </code></pre>
* This method is suitable for reading bytes written by the <code>writeChar</code> method of interface
* <code>RandomDataOutput</code>.
@@ -264,7 +273,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads three input bytes and returns a 24-bit <code>int</code> value. Let <code>a-c</code> be the first through
* third bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* ((((a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8))) &gt;&gt; 8
* </code></pre>
@@ -278,7 +287,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads three input bytes and returns a 24-bit <code>int</code> value. Let <code>a-c</code> be the first through
* third bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* ((((a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8))) &gt;&gt; 8
* </code></pre>
@@ -293,7 +302,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* (((a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 0xff))
* </code></pre>
@@ -308,7 +317,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* (((a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 0xff))
* </code></pre>
@@ -324,7 +333,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* This is the same as readInt() except a read barrier is performed first. <p> Reads four input bytes and returns
* an <code>int</code> value. Let <code>a-d</code> be the first through fourth bytes read on big endian machines,
* and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* (((a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 0xff))
* </code></pre>
@@ -339,7 +348,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* This is the same as readInt() except a read barrier is performed first. <p> Reads four input bytes and returns
* an <code>int</code> value. Let <code>a-d</code> be the first through fourth bytes read on big endian machines,
* and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* (((a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp; 0xff))
* </code></pre>
@@ -354,7 +363,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* ((((long) a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp;
* 0xff))
@@ -369,7 +378,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads four input bytes and returns an <code>int</code> value. Let <code>a-d</code> be the first through fourth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre>
+ * <pre>
* <code>
* ((((long) a &amp; 0xff) &lt;&lt; 24) | ((b &amp; 0xff) &lt;&lt; 16) | ((c &amp; 0xff) &lt;&lt; 8) | (d &amp;
* 0xff))
@@ -387,8 +396,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* with readShort(). This mapped as follows; Short.MIN_VALUE =&gt; Integer.MIN_VALUE, Short.MAX_VALUE =&gt;
* Integer.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 =&gt; same as short value, Short.MIN_VALUE+1 =&gt;
* readInt().
- * <p/>
- * This method is suitable for reading the bytes written by the <code>writeCompactInt</code> method of interface
+ *
+ * <p>This method is suitable for reading the bytes written by the <code>writeCompactInt</code> method of interface
* <code>RandomDataOutput</code>.
*
* @return the 32-bit value read.
@@ -398,8 +407,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads two or six input bytes and returns an <code>int</code> value. Let <code>a</code> be the first short read
* with readShort(). This mapped as follows; -1 =&gt; readUnsignedInt(), default =&gt; (a &amp; 0xFFFF)
- * <p/>
- * This method is suitable for reading the bytes written by the <code>writeCompactUnsignedInt</code> method of
+ *
+ * <p>This method is suitable for reading the bytes written by the <code>writeCompactUnsignedInt</code> method of
* interface <code>RandomDataOutput</code>.
*
* @return the unsigned 32-bit value read.
@@ -409,7 +418,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre> <code>
+ * <pre> <code>
* (((long)(a &amp; 0xff) &lt;&lt; 56) |
* ((long)(b &amp; 0xff) &lt;&lt; 48) |
* ((long)(c &amp; 0xff) &lt;&lt; 40) |
@@ -419,8 +428,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* ((long)(g &amp; 0xff) &lt;&lt; 8) |
* ((long)(h &amp; 0xff)))
* </code></pre>
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeLong</code> method of interface
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeLong</code> method of interface
* <code>DataOutput</code>.
*
* @return the <code>long</code> value read.
@@ -429,9 +438,17 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
long readLong();
/**
+ * Same as readLong except the remaining() can be less than 8.
+ *
+ * @param offset base
+ * @return long.
+ */
+ long readIncompleteLong(long offset);
+
+ /**
* Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre> <code>
+ * <pre> <code>
* (((long)(a &amp; 0xff) &lt;&lt; 56) |
* ((long)(b &amp; 0xff) &lt;&lt; 48) |
* ((long)(c &amp; 0xff) &lt;&lt; 40) |
@@ -441,8 +458,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* ((long)(g &amp; 0xff) &lt;&lt; 8) |
* ((long)(h &amp; 0xff)))
* </code></pre>
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeLong</code> method of interface
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeLong</code> method of interface
* <code>RandomDataOutput</code>.
*
* @param offset of the long to read
@@ -452,10 +469,10 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* This is the same readLong() except a dread barrier is performed first
- * <p/>
- * Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth
+ *
+ * <p>Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre> <code>
+ * <pre> <code>
* (((long)(a &amp; 0xff) &lt;&lt; 56) |
* ((long)(b &amp; 0xff) &lt;&lt; 48) |
* ((long)(c &amp; 0xff) &lt;&lt; 40) |
@@ -465,8 +482,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* ((long)(g &amp; 0xff) &lt;&lt; 8) |
* ((long)(h &amp; 0xff)))
* </code></pre>
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or
* <code>writeVolatileLong</code> method of interface <code>RandomDataOutput</code>.
*
* @return the <code>long</code> value read.
@@ -475,10 +492,10 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* This is the same readLong() except a dread barrier is performed first
- * <p/>
- * Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth
+ *
+ * <p>Reads eight input bytes and returns a <code>long</code> value. Let <code>a-h</code> be the first through eighth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre> <code>
+ * <pre> <code>
* (((long)(a &amp; 0xff) &lt;&lt; 56) |
* ((long)(b &amp; 0xff) &lt;&lt; 48) |
* ((long)(c &amp; 0xff) &lt;&lt; 40) |
@@ -488,8 +505,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* ((long)(g &amp; 0xff) &lt;&lt; 8) |
* ((long)(h &amp; 0xff)))
* </code></pre>
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeOrderedLong</code> or
* <code>writeVolatileLong</code> method of interface <code>RandomDataOutput</code>.
*
* @param offset of the long to read
@@ -500,7 +517,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads six input bytes and returns a <code>long</code> value. Let <code>a-f</code> be the first through sixth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre> <code>
+ * <pre> <code>
* (((long)(a &amp; 0xff) &lt;&lt; 56) |
* ((long)(b &amp; 0xff) &lt;&lt; 48) |
* ((long)(c &amp; 0xff) &lt;&lt; 40) |
@@ -508,8 +525,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* ((long)(e &amp; 0xff) &lt;&lt; 24) |
* ((long)(f &amp; 0xff) &lt;&lt; 16)) &gt;&gt; 16
* </code></pre>
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface
* <code>RandomDataOutput</code>.
*
* @return the <code>long</code> value read.
@@ -519,7 +536,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads six input bytes and returns a <code>long</code> value. Let <code>a-f</code> be the first through sixth
* bytes read on big endian machines, and the opposite on little endian machines. The value returned is:
- * <p><pre> <code>
+ * <pre> <code>
* (((long)(a &amp; 0xff) &lt;&lt; 56) |
* ((long)(b &amp; 0xff) &lt;&lt; 48) |
* ((long)(c &amp; 0xff) &lt;&lt; 40) |
@@ -527,8 +544,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* ((long)(e &amp; 0xff) &lt;&lt; 24) |
* ((long)(f &amp; 0xff) &lt;&lt; 16)) &gt;&gt; 16
* </code></pre>
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeInt48</code> method of interface
* <code>RandomDataOutput</code>.
*
* @param offset of the long to read
@@ -540,8 +557,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads four or twelve input bytes and returns a <code>long</code> value. Let <code>a</code> be the first int read
* with readInt(). This mapped as follows; Integer.MIN_VALUE =&gt; Long.MIN_VALUE, Integer.MAX_VALUE =&gt; Long.MAX_VALUE,
* Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 =&gt; same as short value, Integer.MIN_VALUE+1 =&gt; readLong().
- * <p/>
- * This method is suitable for reading the bytes written by the <code>writeCompactLong</code> method of interface
+ *
+ * <p>This method is suitable for reading the bytes written by the <code>writeCompactLong</code> method of interface
* <code>RandomDataOutput</code>.
*
* @return the 64-bit value read.
@@ -550,14 +567,14 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Reads between one and ten bytes with are stop encoded with support for negative numbers
- * <p><pre><code>
+ * <pre><code>
* long l = 0, b;
* int count = 0;
* while ((b = readByte()) &lt; 0) {
* l |= (b &amp; 0x7FL) &lt;&lt; count;
* count += 7;
* }
- * if (b == 0 && count &gt; 0)
+ * if (b == 0 &amp;&amp; count &gt; 0)
* return ~l;
* return l | (b &lt;&lt; count);
* </code></pre>
@@ -593,8 +610,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* This is the same as readFloat() except a read barrier is performed first. <p> Reads four input bytes and returns
* a <code>float</code> value.
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>.
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>.
*
* @param offset to read from
* @return the <code>int</code> value read.
@@ -635,8 +652,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* This is the same as readDouble() except a read barrier is performed first. <p> Reads four input bytes and returns
* a <code>float</code> value.
- * <p/>
- * This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>.
+ *
+ * <p>This method is suitable for reading bytes written by the <code>writeOrderedFloat</code> method of interface <code>RandomDataOutput</code>.
*
* @param offset to read from
* @return the <code>int</code> value read.
@@ -648,8 +665,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* into a character, until it encounters a line terminator or end of file; the characters read are then returned as
* a <code>String</code>. Note that because this method processes bytes, it does not support input of the full
* Unicode character set.
- * <p/>
- * If end of file is encountered before even one byte can be read, then <code>null</code> is returned. Otherwise,
+ *
+ * <p>If end of file is encountered before even one byte can be read, then <code>null</code> is returned. Otherwise,
* each byte that is read is converted to type <code>char</code> by zero-extension. If the character
* <code>'\n'</code> is encountered, it is discarded and reading ceases. If the character <code>'\r'</code> is
* encountered, it is discarded and, if the following byte converts &#32;to the character <code>'\n'</code>, then
@@ -660,7 +677,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* <code>(char)256</code>.
*
* @return the next line of text from the input stream, or <CODE>null</CODE> if the end of file is encountered
- * before a byte can be read.
+ * before a byte can be read.
*/
@Override
@Nullable
@@ -670,43 +687,43 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
* Reads in a string that has been encoded using a <a href="#modified-utf-8">modified UTF-8</a> format. The general
* contract of <code>readUTF</code> is that it reads a representation of a Unicode character string encoded in
* modified UTF-8 format; this string of characters is then returned as a <code>String</code>.
- * <p/>
- * First, two bytes are read and used to construct an unsigned 16-bit integer in exactly the manner of the
+ *
+ * <p>First, two bytes are read and used to construct an unsigned 16-bit integer in exactly the manner of the
* <code>readUnsignedShort</code> method . This integer value is called the <i>UTF length</i> and specifies the
* number of additional bytes to be read. These bytes are then converted to characters by considering them in
* groups. The length of each group is computed from the value of the first byte of the group. The byte following a
* group, if any, is the first byte of the next group.
- * <p/>
- * If the first byte of a group matches the bit pattern <code>0xxxxxxx</code> (where <code>x</code> means "may be
+ *
+ * <p>If the first byte of a group matches the bit pattern <code>0xxxxxxx</code> (where <code>x</code> means "may be
* <code>0</code> or <code>1</code>"), then the group consists of just that byte. The byte is zero-extended to form
* a character.
- * <p/>
- * If the first byte of a group matches the bit pattern <code>110xxxxx</code>, then the group consists of that byte
+ *
+ * <p>If the first byte of a group matches the bit pattern <code>110xxxxx</code>, then the group consists of that byte
* <code>a</code> and a second byte <code>b</code>. If there is no byte <code>b</code> (because byte <code>a</code>
* was the last of the bytes to be read), or if byte <code>b</code> does not match the bit pattern
* <code>10xxxxxx</code>, then a <code>UTFDataFormatException</code> is thrown. Otherwise, the group is converted to
- * the character:<p>
+ * the character:
* <pre><code>(char)(((a&amp; 0x1F) &lt;&lt; 6) | (b &amp; 0x3F))
* </code></pre>
* If the first byte of a group matches the bit pattern <code>1110xxxx</code>, then the group consists of that byte
* <code>a</code> and two more bytes <code>b</code> and <code>c</code>. If there is no byte <code>c</code> (because
* byte <code>a</code> was one of the last two of the bytes to be read), or either byte <code>b</code> or byte
* <code>c</code> does not match the bit pattern <code>10xxxxxx</code>, then a <code>UTFDataFormatException</code>
- * is thrown. Otherwise, the group is converted to the character:<p>
+ * is thrown. Otherwise, the group is converted to the character:
* <pre><code>
* (char)(((a &amp; 0x0F) &lt;&lt; 12) | ((b &amp; 0x3F) &lt;&lt; 6) | (c &amp; 0x3F))
* </code></pre>
* If the first byte of a group matches the pattern <code>1111xxxx</code> or the pattern <code>10xxxxxx</code>, then
* a <code>UTFDataFormatException</code> is thrown.
- * <p/>
- * If end of file is encountered at any time during this entire process, then an <code>EOFException</code> is
+ *
+ * <p>If end of file is encountered at any time during this entire process, then an <code>EOFException</code> is
* thrown.
- * <p/>
- * After every group has been converted to a character by this process, the characters are gathered, in the same
+ *
+ * <p>After every group has been converted to a character by this process, the characters are gathered, in the same
* order in which their corresponding groups were read from the input stream, to form a <code>String</code>, which
* is returned.
- * <p/>
- * The <code>writeUTF</code> method of interface <code>DataOutput</code> may be used to write data that is suitable
+ *
+ * <p>The <code>writeUTF</code> method of interface <code>DataOutput</code> may be used to write data that is suitable
* for reading by this method.
*
* @return a Unicode string.
@@ -726,7 +743,7 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
String readUTFΔ();
/**
- * The same as readUTFΔ() except an offset and maximum length is given.
+ * The same as readUTFΔ() except an offset is given.
*
* @param offset to read from
* @return a Unicode string or <code>null</code> if <code>writeUTFΔ(null)</code> was called
@@ -738,10 +755,13 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* The same as readUTFΔ() except the chars are copied to a truncated StringBuilder.
*
+ * @param stringBuilder to copy chars to
* @return <code>true</code> if there was a String, or <code>false</code> if it was <code>null</code>
*/
boolean readUTFΔ(@NotNull StringBuilder stringBuilder);
+ boolean read8bitText(@NotNull StringBuilder stringBuilder) throws StreamCorruptedException;
+
/**
* Copy bytes into a ByteBuffer to the minimum of the length <code>remaining()</code> in the ByteBuffer or the
* Excerpt.
@@ -751,12 +771,22 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
void read(@NotNull ByteBuffer bb);
/**
+ * Copy bytes into a ByteBuffer to the minimum of the length in the ByteBuffer or the
+ * Excerpt.
+ *
+ * @param bb to copy into
+ * @param length number of bytes to copy
+ */
+ void read(@NotNull ByteBuffer bb, int length);
+
+ /**
* Read a String with <code>readUTFΔ</code> which is converted to an enumerable type. i.e. where there is a one to
* one mapping between an object and it's toString().
- * <p/>
- * This is suitable to read an object written using <code>writeEnum()</code> in the <code>RandomDataOutput</code>
+ *
+ * <p>This is suitable to read an object written using <code>writeEnum()</code> in the <code>RandomDataOutput</code>
* interface
*
+ * @param <E> the enum class
* @param eClass to decode the String as
* @return the decoded value. <code>null</code> with be return if null was written.
*/
@@ -764,21 +794,27 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
<E> E readEnum(@NotNull Class<E> eClass);
/**
- * Read a stop bit encoded length and populates this Collection after clear()ing it.
- * <p/>
- * This is suitable to reading a list written using <code>writeList()</code> in the <code>RandomDataOutput</code>
+ * Read a stop bit encoded length and populates this Collection after zeroOut()ing it.
+ *
+ * <p>This is suitable to reading a list written using <code>writeList()</code> in the <code>RandomDataOutput</code>
* interface
*
+ * @param <E> the list element class
+ * @param eClass the list element class
* @param list to populate
*/
<E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass);
/**
- * Read a stop bit encoded length and populates this Map after clear()ing it.
- * <p/>
- * This is suitable to reading a list written using <code>writeMap()</code> in the <code>RandomDataOutput</code>
+ * Read a stop bit encoded length and populates this Map after zeroOut()ing it.
+ *
+ * <p>This is suitable to reading a list written using <code>writeMap()</code> in the <code>RandomDataOutput</code>
* interface
*
+ * @param <K> the map key class
+ * @param <V> the map value class
+ * @param kClass the map key class
+ * @param vClass the map value class
* @param map to populate
* @return the map
*/
@@ -800,6 +836,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
/**
* Read and return an object. The class that implements this interface defines where the object is "read" from.
*
+ * @param <T> the class of the object to read
+ * @param tClass the class of the object to read
* @return the object read from the stream
* @throws IllegalStateException the class of a serialized object cannot be found or any of the usual Input/Output
* related exceptions occur.
@@ -809,7 +847,18 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
<T> T readObject(Class<T> tClass) throws IllegalStateException;
/**
- * Reads a byte of data. This method will block if no input is available.
+ * Read an instance of a class assuming objClass was provided when written.
+ *
+ * @param <T> the class of the object to read
+ * @param objClass class to write
+ * @param obj to reuse or null if a new object is needed
+ * @return the object read from the stream
+ */
+ @Nullable
+ <T> T readInstance(@NotNull Class<T> objClass, T obj);
+
+ /**
+ * Reads a byte of data. This method is non blocking.
*
* @return the byte read, or -1 if the end of the stream is reached.
*/
@@ -865,4 +914,18 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon
*/
@Override
void close();
+
+ boolean startsWith(RandomDataInput input);
+
+ boolean compare(long offset, RandomDataInput input, long inputOffset, long len);
+
+ <E> E readEnum(long offset, int maxSize, Class<E> eClass);
+
+ /**
+ * From a given bit index, find the next bit with is set.
+ *
+ * @param fromIndex first bit to scan.
+ * @return first bit equals or after it which is set.
+ */
+ long nextSetBit(long fromIndex);
}
diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java b/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java
index 92a1a4c..d10f430 100755
--- a/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java
+++ b/lang/src/main/java/net/openhft/lang/io/RandomDataOutput.java
@@ -1,21 +1,22 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
+import net.openhft.lang.model.Byteable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,14 +30,50 @@ import java.util.RandomAccess;
* @author peter.lawrey
*/
public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommon {
+
+ /**
+ * Copies the contents of a RandomDataInput from the position to the limit.
+ *
+ * <p> This method transfers the bytes remaining in the given source
+ * buffer into this buffer. If there are more bytes remaining in the
+ * source buffer than in this buffer, that is, if
+ * <tt>src.remaining()</tt>&nbsp;<tt>&gt;</tt>&nbsp;<tt>remaining()</tt>,
+ * then no bytes are transferred and a {@link
+ * java.nio.BufferOverflowException} is thrown.
+ *
+ * <p> Otherwise, this method copies
+ * <i>n</i>&nbsp;=&nbsp;<tt>src.remaining()</tt> bytes from the given
+ * buffer into this buffer, starting at each buffer's current position.
+ * The positions of both buffers are then incremented by <i>n</i>.
+ *
+ * <p> In other words, an invocation of this method of the form
+ * <tt>dst.write(src)</tt> has exactly the same effect as the loop
+ *
+ * <pre>
+ * while (src.hasRemaining())
+ * dst.writeByte(src.readByte());
+ * </pre>
+
+ * @param bytes to copy
+ */
+ void write(RandomDataInput bytes);
+
+ /**
+ * Copy from one Bytes to another, moves the position of "this" RandomDataOutput by the length.
+ * The position of the RandomDataInput is not used or altered.
+ *
+ * @param bytes to copy
+ * @param position to copy from
+ * @param length to copy
+ */
+ void write(RandomDataInput bytes, long position, long length);
+
/**
- * Copy from one Bytes to another. Copied from the start to the current position.
+ * Copies the contents of a Byteable from the offset for maxSize bytes, moves the position of "this" RandomDataOutput by the maxSize
*
- * @param bytes to copy from
- * @deprecated Use write(BytesCommon bytes, long position, long length) instead.
+ * @param byteable to copy
*/
- @Deprecated
- void writeStartToPosition(Bytes bytes);
+ void write(@NotNull Byteable byteable);
/**
* Writes to the output stream the eight low-order bits of the argument <code>b</code>. The 24 high-order bits of
@@ -113,6 +150,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
*/
void write(long offset, byte[] bytes);
+ void write(long offset, Bytes bytes);
+
/**
* Writes <code>len</code> bytes from array <code>bytes</code>, in order, to the output stream. If
* <code>bytes</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. If <code>off</code> is
@@ -128,6 +167,12 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
@Override
void write(byte[] bytes, int off, int len);
+ void write(long offset, byte[] bytes, int off, int len);
+
+ void write(@NotNull char[] data);
+
+ void write(@NotNull char[] data, int off, int len);
+
/**
* Writes a <code>boolean</code> value to this output stream. If the argument <code>v</code> is <code>true</code>,
* the value <code>(byte)1</code> is written; if <code>v</code> is <code>false</code>, the value
@@ -150,12 +195,11 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* @param offset to write boolean
* @param v the boolean to be written.
*/
-
void writeBoolean(long offset, boolean v);
/**
* Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in
- * the order shown for big endian machines and the opposite for little endian, are: <p>
+ * the order shown for big endian machines and the opposite for little endian, are:
* <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 8))
* (byte)(0xff &amp; v)
@@ -170,7 +214,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in
- * the order shown for big endian machines and the opposite for little endian, are: <p>
+ * the order shown for big endian machines and the opposite for little endian, are:
* <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 8))
* (byte)(0xff &amp; v)
@@ -185,7 +229,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in
- * the order shown for big endian machines and the opposite for little endian, are: <p>
+ * the order shown for big endian machines and the opposite for little endian, are:
* <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 8))
* (byte)(0xff &amp; v)
@@ -199,7 +243,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes two bytes to the output stream to represent the value of the argument. The byte values to be written, in
- * the order shown for big endian machines and the opposite for little endian, are: <p>
+ * the order shown for big endian machines and the opposite for little endian, are:
* <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 8))
* (byte)(0xff &amp; v)
@@ -216,8 +260,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Writes one or three bytes as follows; Short.MIN_VALUE =&gt; Byte.MIN_VALUE, Short.MAX_VALUE =&gt; Byte.MAX_VALUE,
* Short.MIN_VALUE+2 to Short.MAX_VALUE-1 =&gt; writeByte(x), default =&gt; writeByte(Byte.MIN_VALUE+1;
* writeShort(x)
- * <p/>
- * The bytes written by this method may be read by the <code>readCompactShort</code> method of interface
+ *
+ * <p>The bytes written by this method may be read by the <code>readCompactShort</code> method of interface
* <code>RandomDataInput</code> , which will then return a <code>short</code> equal to <code>(short)v</code>.
*
* @param v the <code>short</code> value to be written.
@@ -226,8 +270,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes one or three bytes as follows; 0 to 254 =&gt; writeByte(x); otherwise writeByte(255); writeByteShort(x);
- * <p/>
- * The bytes written by this method may be read by the <code>readCompactUnsignedShort</code> method of interface
+ *
+ * <p>The bytes written by this method may be read by the <code>readCompactUnsignedShort</code> method of interface
* <code>RandomDataInput</code> , which will then return a <code>short</code> equal to <code>v &amp; 0xFFFF</code>.
*
* @param v the unsigned <code>short</code> value to be written.
@@ -237,7 +281,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes a <code>char</code> value, which is comprised of two bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 8))
* (byte)(0xff &amp; v)
* </code></pre><p>
@@ -252,7 +296,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes a <code>char</code> value, which is comprised of two bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 8))
* (byte)(0xff &amp; v)
* </code></pre><p>
@@ -267,7 +311,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes an <code>int</code> value, which is comprised of three bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 16))
* (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
* (byte)(0xff &amp; v)
@@ -282,7 +326,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes an <code>int</code> value, which is comprised of three bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 16))
* (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
* (byte)(0xff &amp; v)
@@ -298,7 +342,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 24))
* (byte)(0xff &amp; (v &gt;&gt; 16))
* (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
@@ -315,7 +359,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 24))
* (byte)(0xff &amp; (v &gt;&gt; 16))
* (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
@@ -332,7 +376,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 24))
* (byte)(0xff &amp; (v &gt;&gt; 16))
* (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
@@ -349,7 +393,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes an <code>int</code> value, which is comprised of four bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 24))
* (byte)(0xff &amp; (v &gt;&gt; 16))
* (byte)(0xff &amp; (v &gt;&gt; &#32; &#32;8))
@@ -368,8 +412,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Writes two or six bytes as follows; Integer.MIN_VALUE =&gt; Short.MIN_VALUE, Integer.MAX_VALUE =&gt;
* Short.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 =&gt; writeShort(x), default =&gt;
* writeShort(Short.MIN_VALUE+1; writeInt(x)
- * <p/>
- * The bytes written by this method may be read by the <code>readCompactInt</code> method of interface
+ *
+ * <p>The bytes written by this method may be read by the <code>readCompactInt</code> method of interface
* <code>RandomDataInput</code> , which will then return a <code>int</code> equal to <code>v</code>.
*
* @param v the <code>int</code> value to be written.
@@ -379,8 +423,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes two or six bytes as follows; 0 to (1 &lt;&lt; 16) - 2 =&gt; writeInt(x), otherwise writeShort(-1);
* writeInt(x)
- * <p/>
- * The bytes written by this method may be read by the <code>readCompactUnsignedInt</code> method of interface
+ *
+ * <p>The bytes written by this method may be read by the <code>readCompactUnsignedInt</code> method of interface
* <code>RandomDataInput</code> , which will then return a <code>int</code> equal to <code>v &amp;
* 0xFFFFFFFF</code>.
*
@@ -392,8 +436,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Same as writeInt but include an ordered write barrier. This means all writes will be visible on a read barrier
* if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read
* could see an old value
- * <p/>
- * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
+ *
+ * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
* same time.
*
* @param v value to write
@@ -404,8 +448,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Same as writeInt but include an ordered write barrier. This means all writes will be visible on a read barrier
* if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read
* could see an old value
- * <p/>
- * This is much faster than <code>writeVolatileInt</code> as the volatile write stalls the pipeline. The data is
+ *
+ * <p>This is much faster than <code>writeVolatileInt</code> as the volatile write stalls the pipeline. The data is
* visible to other threads at the same time.
*
* @param offset to write to
@@ -445,7 +489,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 40))
* (byte)(0xff &amp; (v &gt;&gt; 32))
* (byte)(0xff &amp; (v &gt;&gt; 24))
@@ -454,7 +498,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* (byte)(0xff &amp; v)
* </code></pre><p>
* The bytes written by this method may be read by the <code>readInt48</code> method of interface
- * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v &amp; ((1L &lt;&lt 48)
+ * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v &amp; ((1L &lt;&lt; 48)
* - 1)</code>.
*
* @param v the <code>long</code> value to be written.
@@ -464,7 +508,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 40))
* (byte)(0xff &amp; (v &gt;&gt; 32))
* (byte)(0xff &amp; (v &gt;&gt; 24))
@@ -473,7 +517,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* (byte)(0xff &amp; v)
* </code></pre><p>
* The bytes written by this method may be read by the <code>readInt48</code> method of interface
- * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v &amp; ((1L &lt;&lt 48)
+ * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v &amp; ((1L &lt;&lt; 48)
* - 1)</code>.
*
* @param offset to be written to
@@ -484,7 +528,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 56))
* (byte)(0xff &amp; (v &gt;&gt; 48))
* (byte)(0xff &amp; (v &gt;&gt; 40))
@@ -505,7 +549,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Writes a <code>long</code> value, which is comprised of eight bytes, to the output stream. The byte values to be
* written, in the order shown for big endian machines and the opposite for little endian, are:
- * <p><pre><code>
+ * <pre><code>
* (byte)(0xff &amp; (v &gt;&gt; 56))
* (byte)(0xff &amp; (v &gt;&gt; 48))
* (byte)(0xff &amp; (v &gt;&gt; 40))
@@ -527,8 +571,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Writes four or twelve bytes as follows Long.MIN_VALUE =&gt; Integer.MIN_VALUE, Long.MAX_VALUE =&gt;
* Integer.MAX_VALUE, Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 =&gt; writeInt(x), default =&gt;
* writeInt(Integer.MIN_VALUE+1; writeLong(x)
- * <p/>
- * The bytes written by this method may be read by the <code>readCompactLong</code> method of interface
+ *
+ * <p>The bytes written by this method may be read by the <code>readCompactLong</code> method of interface
* <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v</code>.
*
* @param v the <code>long</code> value to be written.
@@ -539,8 +583,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Same as writeLong but include an ordered write barrier. This means all writes will be visible on a read barrier
* if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read
* could see an old value
- * <p/>
- * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
+ *
+ * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
* same time.
*
* @param v value to write
@@ -551,8 +595,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Same as writeLong but include an ordered write barrier. This means all writes will be visible on a read barrier
* if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read
* could see an old value
- * <p/>
- * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
+ *
+ * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
* same time.
*
* @param offset to be written to
@@ -572,7 +616,21 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
boolean compareAndSwapLong(long offset, long expected, long x);
/**
- * Stop bit encoding numbers. This will write the same number of bytes whether you used a byte, short or int.
+ * Perform a compare and set operation. The value is set to <code>x</code> provided the <code>expected</code> value
+ * is set already. This operation is atomic.
+ *
+ * @param offset to write to.
+ * @param expected to expect
+ * @param x to set if expected was found
+ * @return true if set, false if the value was not expected
+ */
+ boolean compareAndSwapDouble(long offset, double expected, double x);
+
+ /**
+ * Stop bit encoding numbers. This will write the same number of bytes
+ * whether you used a byte, short or int.
+ *
+ * @param n the number to write
*/
void writeStopBit(long n);
@@ -604,11 +662,12 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Same as writeFloat but include an ordered write barrier. This means all writes will be visible on a read barrier
* if this write is visible. This might not be visible to be same thread for some clock cycles so an immediate read
* could see an old value
- * <p/>
- * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
+ *
+ * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
* same time.
*
- * @param v value to write
+ * @param offset to write to
+ * @param v value to write
*/
void writeOrderedFloat(long offset, float v);
@@ -639,15 +698,16 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
void writeDouble(long offset, double v);
/**
+ * <p>
* Writes four or twelve bytes as follow;
- * <p><pre><code>
+ * </p><pre><code>
* if ((float) d == d) {
* writeFloat((float) d);
* } else {
* writeFloat(Float.NaN);
* writeDouble(d);
* }
- * <p/>
+ * </code></pre>
* The bytes written by this method may be read by the <code>readCompactDouble</code> method of interface
* <code>RandomDataInput</code> , which will then return a <code>double</code> equal to <code>v</code>.
*
@@ -659,11 +719,12 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* Same as writeDouble but include an ordered write barrier. This means all writes will be visible on a read
* barrier if this write is visible. This might not be visible to be same thread for some clock cycles so an
* immediate read could see an old value
- * <p/>
- * This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
+ *
+ * <p>This is much faster than a volatile write which stalls the pipeline. The data is visible to other threads at the
* same time.
*
- * @param v value to write
+ * @param offset to write to
+ * @param v value to write
*/
void writeOrderedDouble(long offset, double v);
@@ -688,27 +749,36 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* bytes are actually written, high-order byte first, in exactly the manner of the <code>writeChar</code> method.
*
* @param s the string value to be written. Cannot be null.
+ * @see #writeChars(CharSequence)
*/
@Override
void writeChars(@NotNull String s);
/**
+ * Writes chars of the given {@code CharSequence} to the bytes, without encoding.
+ *
+ * @param cs the {@code CharSequence} to be written. Cannot be null.
+ * @see #writeChars(String)
+ */
+ void writeChars(@NotNull CharSequence cs);
+
+ /**
* Writes two bytes of length information to the output stream, followed by the <a
* href="DataInput.html#modified-utf-8">modified UTF-8</a> representation of every character in the string
* <code>s</code>. If <code>s</code> is <code>null</code>, a <code>NullPointerException</code> is thrown. Each
* character in the string <code>s</code> is converted to a group of one, two, or three bytes, depending on the
* value of the character.<p> If a character <code>c</code> is in the range <code>&#92;u0001</code> through
- * <code>&#92;u007f</code>, it is represented by one byte:<p>
+ * <code>&#92;u007f</code>, it is represented by one byte:
* <pre>(byte)c </pre> <p>
* If a character <code>c</code> is <code>&#92;u0000</code> or is in the range <code>&#92;u0080</code> through
* <code>&#92;u07ff</code>, then it is represented by two bytes, to be written
- * in the order shown:<p> <pre><code>
+ * in the order shown:<pre><code>
* (byte)(0xc0 | (0x1f &amp; (c &gt;&gt; 6)))
* (byte)(0x80 | (0x3f &amp; c))
* </code></pre> <p> If a character
* <code>c</code> is in the range <code>&#92;u0800</code> through <code>uffff</code>, then it is represented by
* three bytes, to be written
- * in the order shown:<p> <pre><code>
+ * in the order shown:<pre><code>
* (byte)(0xe0 | (0x0f &amp; (c &gt;&gt; 12)))
* (byte)(0x80 | (0x3f &amp; (c &gt;&gt; 6)))
* (byte)(0x80 | (0x3f &amp; c))
@@ -730,9 +800,9 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* i.e. one byte longer for short strings, but is not limited in length. 2) The string can be null.
*
* @param s the string value to be written. Can be null.
+ * @throws IllegalArgumentException if there is not enough space left
*/
- void writeUTFΔ(@Nullable CharSequence s);
-
+ void writeUTFΔ(@Nullable CharSequence s) throws IllegalArgumentException;
/**
* Write the same encoding as <code>writeUTF</code> with the following changes. 1) The length is stop bit encoded
@@ -745,8 +815,29 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
*/
void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException;
+ void write8bitText(@Nullable CharSequence s);
/**
- * Copies the contents of a ByteBuffer from the potision ot the limit.
+ * Copies the contents of a ByteBuffer from the position to the limit.
+ *
+ * <p> This method transfers the bytes remaining in the given source
+ * buffer into this buffer. If there are more bytes remaining in the
+ * source buffer than in this buffer, that is, if
+ * <tt>src.remaining()</tt>&nbsp;<tt>&gt;</tt>&nbsp;<tt>remaining()</tt>,
+ * then no bytes are transferred and a {@link
+ * java.nio.BufferOverflowException} is thrown.
+ *
+ * <p> Otherwise, this method copies
+ * <i>n</i>&nbsp;=&nbsp;<tt>src.remaining()</tt> bytes from the given
+ * buffer into this buffer, starting at each buffer's current position.
+ * The positions of both buffers are then incremented by <i>n</i>.
+ *
+ * <p> In other words, an invocation of this method of the form
+ * <tt>dst.write(src)</tt> has exactly the same effect as the loop
+ *
+ * <pre>
+ * while (src.hasRemaining())
+ * dst.writeByte(src.get());
+ * </pre>
*
* @param bb to copy.
*/
@@ -766,10 +857,11 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
/**
* Write an ordered collection of "enumerable objects" (See writeEnum). This writes the stop bit encoded length,
* followed by multiple calls to <code>writeEnum</code> All the elements must be of the same type.
- * <p/>
- * This can be read by the <code>readList</code> method of <code>RandomInputStream</code> and the reader must know
+ *
+ * <p>This can be read by the <code>readList</code> method of <code>RandomInputStream</code> and the reader must know
* the type of each element. You can send the class first by using <code>writeEnum</code> of the element class
*
+ * @param <E> the class of the list elements
* @param list to be written
*/
<E> void writeList(@NotNull Collection<E> list);
@@ -779,9 +871,10 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
* length, followed by multiple calls to <code>writeEnum</code> for each key and value. All the keys must be of the
* same type. All values must be of the same type.
*
+ * @param <K> the class of the map keys
+ * @param <V> the class of the map values
* @param map to write out
*/
-
<K, V> void writeMap(@NotNull Map<K, V> map);
// ObjectOutput
@@ -797,6 +890,15 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
void writeObject(@Nullable Object object);
/**
+ * Write an object with the assumption that the objClass will be provided when the class is read.
+ *
+ * @param <OBJ> the class of the object to write
+ * @param objClass class to write
+ * @param obj to write
+ */
+ <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj);
+
+ /**
* Copy data from an Object from bytes start to end.
*
* @param object to copy from
@@ -806,6 +908,32 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
void writeObject(Object object, int start, int end);
/**
+ * fill the Bytes with zeros, and clear the position.
+ *
+ * @return this
+ */
+ Bytes zeroOut();
+
+ /**
+ * fill the Bytes with zeros.
+ *
+ * @param start first byte to zero out
+ * @param end the first byte after the last to zero out (exclusive bound)
+ * @return this
+ */
+ Bytes zeroOut(long start, long end);
+
+ /**
+ * fill the Bytes with zeros, and clear the position, avoiding touching pages unnecessarily
+ *
+ * @param start first byte to zero out
+ * @param end the first byte after the last to zero out (exclusive bound)
+ * @param ifNotZero only set to zero after checking the value is not zero.
+ * @return this
+ */
+ Bytes zeroOut(long start, long end, boolean ifNotZero);
+
+ /**
* Check the end of the stream has not overflowed. Otherwise this doesn't do anything.
*/
@Override
@@ -816,4 +944,6 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo
*/
@Override
void close();
+
+ void writeEnum(long offset, int len, Object object);
}
diff --git a/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java b/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java
index c019276..686cd69 100644
--- a/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java
+++ b/lang/src/main/java/net/openhft/lang/io/RandomDataUpdate.java
@@ -1,17 +1,17 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
@@ -90,9 +90,25 @@ public interface RandomDataUpdate {
void unlockInt(long offset) throws IllegalMonitorStateException;
/**
+ * Lock which uses 4 bytes. Reset forces the lock to be cleared. Use this only when the program believes the
+ * locked thread is dead.
+ *
+ * @param offset of the start of the 4-byte lock
+ */
+ void resetLockInt(long offset);
+
+ /**
+ * Lock which uses 4 bytes. This returns the lower bytes which contain the threadId.
+ *
+ * @param offset of the start of the 4-byte lock
+ * @return the threadId or 0 if no thread holds the lock.
+ */
+ int threadIdForLockInt(long offset);
+
+ /**
* Lock across processes
- * <p/>
- * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
+ *
+ * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
* means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times
* you will get an ISE
*
@@ -103,8 +119,8 @@ public interface RandomDataUpdate {
/**
* Lock across processes
- * <p/>
- * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
+ *
+ * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
* means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times
* you will get an ISE
*
@@ -116,8 +132,8 @@ public interface RandomDataUpdate {
/**
* Lock across processes
- * <p/>
- * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
+ *
+ * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
* means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times
* you will get an ISE
*
@@ -129,8 +145,8 @@ public interface RandomDataUpdate {
/**
* Lock across processes
- * <p/>
- * Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
+ *
+ * <p>Lock which uses 8 bytes. It store the lower 32 bits of the Thread Id, 16 bits are the process id and the re-entrant count as 16 bit. This
* means if you create more than 16 million threads you can get a collision, and if you try to re-enter 65535 times
* you will get an ISE
*
@@ -138,4 +154,60 @@ public interface RandomDataUpdate {
* @throws IllegalMonitorStateException if this thread doesn't hold the lock
*/
void unlockLong(long offset) throws IllegalMonitorStateException;
+
+ /**
+ * Lock which uses 8 bytes. Reset forces the lock to be cleared. Use this only when the program believes the
+ * locked thread is dead.
+ *
+ * @param offset of the start of the 8-byte lock
+ */
+ void resetLockLong(long offset);
+
+ /**
+ * Lock which uses 8 bytes. This returns the lower bytes which contain the threadId.
+ *
+ * @param offset of the start of the 8-byte lock
+ * @return the threadId or 0 if no thread holds the lock.
+ */
+ long threadIdForLockLong(long offset);
+
+ /**
+ * Uses the 64-bit long at the offset as a non-reentrant read/write lock.
+ * There can be up to 2^20-1 threads reading, or waiting to read on a lock.
+ *
+ * @param offset of the long monitor
+ * @param timeOutNS length of time to busy wait for the lock.
+ * @return if the lock could be obtained in time.
+ * @throws java.lang.IllegalStateException if the monitor is in an illegal state
+ */
+ boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException;
+
+ /**
+ * Uses the 64-bit long at the offset as a non-reentrant read/write lock.
+ * There can be up to 2^20-1 threads reading, or waiting to read on a lock.
+ *
+ * @param offset of the long monitor
+ * @param timeOutNS length of time to busy wait for the lock.
+ * @return if the lock could be obtained in time.
+ * @throws java.lang.IllegalStateException if the monitor is in an illegal state
+ */
+ boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException;
+
+ /**
+ * Uses the 64-bit long at the offset as a non-reentrant read/write lock.
+ * There can be up to 2^20-1 threads reading, or waiting to read on a lock.
+ *
+ * @param offset of the long monitor
+ * @throws java.lang.IllegalStateException if the monitor is in an illegal state
+ */
+ void unlockRWReadLock(long offset) throws IllegalStateException;
+
+ /**
+ * Uses the 64-bit long at the offset as a non-reentrant read/write lock.
+ * There can be up to 2^20-1 threads reading, or waiting to read on a lock.
+ *
+ * @param offset of the long monitor
+ * @throws java.lang.IllegalStateException if the monitor is in an illegal state
+ */
+ void unlockRWWriteLock(long offset) throws IllegalStateException;
}
diff --git a/lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java b/lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java
new file mode 100644
index 0000000..6580b51
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/ResizeableMappedStore.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.BytesMarshallableSerializer;
+import net.openhft.lang.io.serialization.JDKZObjectSerializer;
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+public final class ResizeableMappedStore extends AbstractMappedStore {
+ public ResizeableMappedStore(File file, FileChannel.MapMode mode, long size)
+ throws IOException {
+ this(file, mode, size, BytesMarshallableSerializer.create(
+ new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE));
+ }
+
+ public ResizeableMappedStore(File file, FileChannel.MapMode mode, long size,
+ ObjectSerializer objectSerializer) throws IOException {
+ super(new MmapInfoHolder(), file, mode, 0L, size, objectSerializer);
+ }
+
+ /**
+ * Resizes the underlying file and re-maps it. Warning! After this call
+ * instances of {@link Bytes} obtained through {@link #bytes()} or
+ * {@link #bytes(long, long)} are invalid and using them can lead to reading
+ * arbitrary data or JVM crash! It is the callers responsibility to ensure
+ * that these instances are not used after the method call.
+ *
+ * @param newSize
+ * @throws IOException
+ */
+ public void resize(long newSize) throws IOException {
+ validateSize(newSize);
+ unmapAndSyncToDisk();
+ resizeIfNeeded(0L, newSize);
+ this.mmapInfoHolder.setSize(newSize);
+ map(0L);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/Reuses.java b/lang/src/main/java/net/openhft/lang/io/Reuses.java
new file mode 100644
index 0000000..7740fb3
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/Reuses.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+class Reuses {
+ static final ClassLoader MAGIC_CLASS_LOADER = findMagicClassLoader();
+
+ private static ClassLoader findMagicClassLoader() {
+ try {
+ Class<?> clazz = Class.forName("sun.reflect.ConstructorAccessor");
+ ClassLoader cl = clazz.getClassLoader();
+ if (cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ return cl;
+ } catch (Throwable ignore) {
+ }
+ return null;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/SettableAtt.java b/lang/src/main/java/net/openhft/lang/io/SettableAtt.java
new file mode 100644
index 0000000..1adb5f4
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/SettableAtt.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+final class SettableAtt {
+ Object att;
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/StopCharTester.java b/lang/src/main/java/net/openhft/lang/io/StopCharTester.java
index 8f965e3..52c5c21 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/StopCharTester.java
+++ b/lang/src/main/java/net/openhft/lang/io/StopCharTester.java
@@ -1,17 +1,17 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
@@ -22,10 +22,10 @@ package net.openhft.lang.io;
public interface StopCharTester {
/**
* Detect which byte stops the string to be parsed
- * <p/>
- * This should be changed to support char instead.
- * <p/>
- * Note: for safety reasons, you should stop on a 0 byte or throw an IllegalStateException.
+ *
+ * <p>This should be changed to support char instead.
+ *
+ * <p>Note: for safety reasons, you should stop on a 0 byte or throw an IllegalStateException.
*
* @param ch to test, 0 should return true or throw an exception.
* @return if this byte is a stop character.
diff --git a/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java b/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java
index f711706..f463222 100755..100644
--- a/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java
+++ b/lang/src/main/java/net/openhft/lang/io/StopCharTesters.java
@@ -1,22 +1,22 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
/**
* @author peter.lawrey
@@ -50,6 +50,12 @@ public enum StopCharTesters implements StopCharTester {
public boolean isStopChar(int ch) {
return ch <= 1;
}
+ },
+ ALL {
+ @Override
+ public boolean isStopChar(int ch) {
+ return ch < 0;
+ }
};
@NotNull
@@ -60,7 +66,7 @@ public enum StopCharTesters implements StopCharTester {
}
@NotNull
- public static StopCharTester forChar(char ch) {
+ private static StopCharTester forChar(char ch) {
return new CharCSTester(ch);
}
diff --git a/lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java b/lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java
new file mode 100644
index 0000000..f11ed90
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/StringBuilderUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Field;
+
+import static java.lang.Character.toLowerCase;
+
+/**
+ * Created by Rob Austin
+ */
+public enum StringBuilderUtils {
+ ;
+
+ private static final Field SB_VALUE, SB_COUNT;
+
+ static {
+ try {
+ SB_VALUE = Class.forName("java.lang.AbstractStringBuilder").getDeclaredField("value");
+ SB_VALUE.setAccessible(true);
+ SB_COUNT = Class.forName("java.lang.AbstractStringBuilder").getDeclaredField("count");
+ SB_COUNT.setAccessible(true);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static boolean endsWith(@NotNull final CharSequence source,
+ @NotNull final String endsWith) {
+ for (int i = 1; i <= endsWith.length(); i++) {
+ if (toLowerCase(source.charAt(source.length() - i)) !=
+ toLowerCase(endsWith.charAt(endsWith.length() - i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean isEqual(CharSequence s, CharSequence cs) {
+ if (s == null) return false;
+ if (s.length() != cs.length()) return false;
+ for (int i = 0; i < cs.length(); i++)
+ if (s.charAt(i) != cs.charAt(i))
+ return false;
+ return true;
+ }
+
+ public static String toString(Object o) {
+ return o == null ? null : o.toString();
+ }
+
+ public static char[] extractChars(StringBuilder sb) {
+ try {
+ return (char[]) SB_VALUE.get(sb);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static void setCount(StringBuilder sb, int count) {
+ try {
+ SB_COUNT.setInt(sb, count);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ }
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java
new file mode 100755
index 0000000..493ae2b
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHash.java
@@ -0,0 +1,89 @@
+package net.openhft.lang.io;
+
+import java.nio.ByteOrder;
+
+public enum VanillaBytesHash implements BytesHasher {
+ INSTANCE;
+
+ public static final int K0 = 0x6d0f27bd;
+ public static final int K1 = 0xc1f3bfc9;
+ public static final int K2 = 0x6b192397;
+ public static final int K3 = 0x6b915657;
+ public static final int M0 = 0x5bc80bad;
+ public static final int M1 = 0xea7585d7;
+ public static final int M2 = 0x7a646e19;
+ public static final int M3 = 0x855dd4db;
+ private static final int HI_BYTES = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? 4 : 0;
+
+ public static long agitate(long l) {
+ l += l >>> 22;
+ l ^= Long.rotateRight(l, 17);
+ return l;
+ }
+
+ @Override
+ public long hash(Bytes bytes, long offset, long limit) {
+ long start = offset;
+ int remaining = (int) (limit - offset);
+ // use two hashes so that when they are combined the 64-bit hash is more random.
+ long h0 = (long) remaining * K0;
+ long h1 = 0, h2 = 0, h3 = 0;
+ int i;
+ // optimise chunks of 32 bytes but this is the same as the next loop.
+ for (i = 0; i < remaining - 31; i += 32) {
+ if (i > 0) {
+ h0 *= K0;
+ h1 *= K1;
+ h2 *= K2;
+ h3 *= K3;
+ }
+ long addrI = start + i;
+ long l0 = bytes.readLong(addrI);
+ int l0a = bytes.readInt(addrI + HI_BYTES);
+ long l1 = bytes.readLong(addrI + 8);
+ int l1a = bytes.readInt(addrI + 8 + HI_BYTES);
+ long l2 = bytes.readLong(addrI + 16);
+ int l2a = bytes.readInt(addrI + 16 + HI_BYTES);
+ long l3 = bytes.readLong(addrI + 24);
+ int l3a = bytes.readInt(addrI + 24 + HI_BYTES);
+
+ h0 += (l0 + l1a - l2a) * M0;
+ h1 += (l1 + l2a - l3a) * M1;
+ h2 += (l2 + l3a - l0a) * M2;
+ h3 += (l3 + l0a - l1a) * M3;
+ }
+
+ // perform a hash of the end.
+ int left = remaining - i;
+ if (left > 0) {
+ if (i > 0) {
+ h0 *= K0;
+ h1 *= K1;
+ h2 *= K2;
+ h3 *= K3;
+ }
+
+ long addrI = start + i;
+ long l0 = bytes.readIncompleteLong(addrI);
+ int l0a = (int) (l0 >> 32);
+ long l1 = bytes.readIncompleteLong(addrI + 8);
+ int l1a = (int) (l1 >> 32);
+ long l2 = bytes.readIncompleteLong(addrI + 16);
+ int l2a = (int) (l2 >> 32);
+ long l3 = bytes.readIncompleteLong(addrI + 24);
+ int l3a = (int) (l3 >> 32);
+
+ h0 += (l0 + l1a - l2a) * M0;
+ h1 += (l1 + l2a - l3a) * M1;
+ h2 += (l2 + l3a - l0a) * M2;
+ h3 += (l3 + l0a - l1a) * M3;
+ }
+ return agitate(h0) ^ agitate(h1)
+ ^ agitate(h2) ^ agitate(h3);
+ }
+
+ @Override
+ public long hash(Bytes bytes) {
+ return hash(bytes, bytes.position(), bytes.limit());
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java
new file mode 100644
index 0000000..774fc1a
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaBytesHasher.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.Maths;
+
+public enum VanillaBytesHasher implements BytesHasher {
+ INSTANCE;
+ private static final long LONG_LEVEL_PRIME_MULTIPLE = 0x9ddfea08eb382d69L;
+ private static final short SHORT_LEVEL_PRIME_MULTIPLE = 0x404f;
+ private static final byte BYTE_LEVEL_PRIME_MULTIPLE = 0x57;
+
+ public long hash(Bytes bytes) {
+ return hash(bytes, bytes.position(), bytes.limit());
+ }
+
+ public long hash(Bytes bytes, long offset, long limit) {
+ return Maths.hash(limit - offset == 8 ? bytes.readLong(offset) : hash0(bytes, offset, limit));
+ }
+
+ private long hash0(Bytes bytes, long offset, long limit) {
+ long h = 0;
+ long i = offset;
+ for (; i < limit - 7; i += 8)
+ h = LONG_LEVEL_PRIME_MULTIPLE * h + bytes.readLong(i);
+ for (; i < limit - 1; i += 2)
+ h = SHORT_LEVEL_PRIME_MULTIPLE * h + bytes.readShort(i);
+ if (i < limit)
+ h = BYTE_LEVEL_PRIME_MULTIPLE * h + bytes.readByte(i);
+ return h;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java
new file mode 100755
index 0000000..1cdd254
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBlocks.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class VanillaMappedBlocks implements VanillaMappedResource {
+ private final VanillaMappedFile mappedFile;
+ private final List<VanillaMappedBytes> bytes;
+ private final long blockSize;
+ private final FileLifecycleListener fileLifecycleListener;
+
+ private VanillaMappedBytes mb0;
+ private VanillaMappedBytes mb1;
+
+ public VanillaMappedBlocks(final File path, VanillaMappedMode mode, long blockSize, long overlapSize) throws IOException {
+ this(path, mode, blockSize + overlapSize, null);
+ }
+
+ public VanillaMappedBlocks(final File path, VanillaMappedMode mode, long blockSize,
+ FileLifecycleListener fileLifecycleListener) throws IOException {
+ this.fileLifecycleListener = fileLifecycleListener;
+ this.mappedFile = new VanillaMappedFile(path, mode, -1, fileLifecycleListener);
+ this.bytes = new ArrayList<VanillaMappedBytes>();
+ this.blockSize = blockSize;
+ this.mb0 = null;
+ this.mb1 = null;
+ }
+
+ public synchronized VanillaMappedBytes acquire(long index) throws IOException {
+ if (this.mb0 != null && this.mb0.index() == index) {
+ this.mb0.reserve();
+ return this.mb0;
+ }
+
+ if (this.mb1 != null && this.mb1.index() == index) {
+ this.mb1.reserve();
+ return this.mb1;
+ }
+
+ return acquire0(index);
+ }
+
+ protected VanillaMappedBytes acquire0(long index) throws IOException {
+
+ if (this.mb1 != null) {
+ this.mb1.release();
+ }
+
+ this.mb1 = this.mb0;
+ this.mb0 = this.mappedFile.bytes(index * this.blockSize, this.blockSize, index);
+ this.mb0.reserve();
+
+ bytes.add(this.mb0);
+
+ for (int i = bytes.size() - 1; i >= 0; i--) {
+ if (bytes.get(i).unmapped()) {
+ bytes.remove(i);
+ }
+ }
+
+ return this.mb0;
+ }
+
+ @Override
+ public String path() {
+ return this.mappedFile.path();
+ }
+
+ @Override
+ public synchronized long size() {
+ return this.mappedFile.size();
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ if (this.mb0 != null && !this.mb0.unmapped()) {
+ this.mb0.release();
+ this.mb0 = null;
+ }
+
+ if (this.mb1 != null && !this.mb1.unmapped()) {
+ this.mb1.release();
+ this.mb1 = null;
+ }
+
+ for (int i = bytes.size() - 1; i >= 0; i--) {
+ bytes.get(i).cleanup();
+ }
+
+ this.bytes.clear();
+ this.mappedFile.close();
+ }
+
+ public static VanillaMappedBlocks readWrite(final File path, long size) throws IOException {
+ return readWrite(path, size, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public static VanillaMappedBlocks readOnly(final File path, long size) throws IOException {
+ return readOnly(path, size, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public static VanillaMappedBlocks readWrite(final File path, long size,
+ FileLifecycleListener listener) throws IOException {
+ return new VanillaMappedBlocks(path, VanillaMappedMode.RW, size, listener);
+ }
+
+ public static VanillaMappedBlocks readOnly(final File path, long size,
+ FileLifecycleListener listener) throws IOException {
+ return new VanillaMappedBlocks(path, VanillaMappedMode.RO, size, listener);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java
new file mode 100644
index 0000000..6f8191f
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedBytes.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import sun.misc.Cleaner;
+import sun.nio.ch.DirectBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class VanillaMappedBytes extends NativeBytes {
+ private final File path;
+ private final MappedByteBuffer buffer;
+ private final FileChannel channel;
+ private final FileLifecycleListener fileLifecycleListener;
+ private final long index;
+ private boolean unmapped;
+
+ public VanillaMappedBytes(final File path, final MappedByteBuffer buffer) {
+ this(path, buffer, -1, null, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public VanillaMappedBytes(final File path, final MappedByteBuffer buffer, FileLifecycleListener fileLifecycleListener) {
+ this(path, buffer, -1, null, fileLifecycleListener);
+ }
+
+ public VanillaMappedBytes(final File path, final MappedByteBuffer buffer, long index) {
+ this(path, buffer, index, null, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public VanillaMappedBytes(final File path, final MappedByteBuffer buffer, long index, FileLifecycleListener fileLifecycleListener) {
+ this(path, buffer, index, null, fileLifecycleListener);
+ }
+
+ protected VanillaMappedBytes(final File path, final MappedByteBuffer buffer, long index, final FileChannel channel) {
+ this(path, buffer, index, channel, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ protected VanillaMappedBytes(
+ final File path,
+ final MappedByteBuffer buffer,
+ long index,
+ final FileChannel channel,
+ final FileLifecycleListener fileLifecycleListener) {
+
+ super(
+ buffer.capacity() == 0 ? NO_PAGE : ((DirectBuffer) buffer).address(),
+ buffer.capacity() == 0 ? NO_PAGE : ((DirectBuffer) buffer).address() + buffer.capacity()
+ );
+
+ this.buffer = buffer;
+ this.path = path;
+ this.channel = channel;
+ this.unmapped = false;
+ this.index = index;
+ this.fileLifecycleListener = fileLifecycleListener;
+ }
+
+ public long index() {
+ return this.index;
+ }
+
+ public synchronized boolean unmapped() {
+ return this.unmapped;
+ }
+
+ @Override
+ public boolean release() {
+ if(!unmapped()) {
+ return super.release();
+ }
+
+ return false;
+ }
+
+ @Override
+ protected synchronized void cleanup() {
+ if(!this.unmapped) {
+ Cleaner cl = ((DirectBuffer)this.buffer).cleaner();
+ if (cl != null) {
+ long start = System.nanoTime();
+ cl.clean();
+
+ fileLifecycleListener.onEvent(
+ FileLifecycleListener.EventType.UNMAP,
+ this.path,
+ System.nanoTime() - start
+ );
+
+ }
+
+ try {
+ if (this.channel != null && this.channel.isOpen()) {
+ this.channel.close();
+ }
+ } catch(IOException e) {
+ throw new AssertionError(e);
+ }
+
+ this.unmapped = true;
+ }
+
+ super.cleanup();
+ }
+
+ public void force() {
+ long start = System.nanoTime();
+ this.buffer.force();
+
+ fileLifecycleListener.onEvent(
+ FileLifecycleListener.EventType.SYNC,
+ this.path,
+ System.nanoTime() - start
+ );
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer(ByteBuffer toReuse) {
+ return sliceAsByteBuffer(toReuse, buffer);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java
new file mode 100755
index 0000000..4aa0511
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedCache.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class VanillaMappedCache<T> implements Closeable {
+ private final boolean cleanOnClose;
+ private final Map<T,VanillaMappedBytes> cache;
+ private final FileLifecycleListener fileLifecycleListener;
+
+ public VanillaMappedCache() {
+ this(new LinkedHashMap<T, VanillaMappedBytes>(), false, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public VanillaMappedCache(final boolean cleanOnClose) {
+ this(new LinkedHashMap<T, VanillaMappedBytes>(), cleanOnClose, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public VanillaMappedCache(final int maximumCacheSize, boolean releaseOnRemove) {
+ this(maximumCacheSize, releaseOnRemove, false);
+ }
+ public VanillaMappedCache(final int maximumCacheSize, final boolean releaseOnRemove, final boolean cleanOnClose) {
+ this(maximumCacheSize, releaseOnRemove, cleanOnClose, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ /**
+ *
+ * @param maximumCacheSize the maximum number of VanillaMappedBytes to cache
+ * @param releaseOnRemove release the VanillaMappedBytes when evicted from cache
+ * @param cleanOnClose clean the VanillaMappedBytes when evicted from cache
+ * @param fileLifecycleListener the file lifecycle
+ */
+ public VanillaMappedCache(
+ final int maximumCacheSize,
+ final boolean releaseOnRemove,
+ final boolean cleanOnClose,
+ FileLifecycleListener fileLifecycleListener) {
+
+ this(new LinkedHashMap<T, VanillaMappedBytes>(maximumCacheSize,1.0f,true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<T, VanillaMappedBytes> eldest) {
+ boolean removed = size() >= maximumCacheSize;
+ if (removed && releaseOnRemove) {
+ eldest.getValue().release();
+ }
+
+ return removed;
+ }
+ },
+ cleanOnClose,
+ fileLifecycleListener);
+ }
+
+ private VanillaMappedCache(final Map<T, VanillaMappedBytes> cache, final boolean cleanOnClose, FileLifecycleListener fileLifecycleListener) {
+ this.cache = cache;
+ this.cleanOnClose = cleanOnClose;
+ this.fileLifecycleListener = fileLifecycleListener;
+ }
+
+ public VanillaMappedBytes get(T key) {
+ return this.cache.get(key);
+ }
+
+ public VanillaMappedBytes put(T key, File path, long size) throws IOException {
+ return put(key,path,size,-1);
+ }
+
+ public VanillaMappedBytes put(T key, File path, long size, long index) throws IOException {
+ VanillaMappedBytes data = this.cache.get(key);
+
+ if(data != null) {
+ if (!data.unmapped()) {
+ data.cleanup();
+
+ throw new IllegalStateException(
+ "Buffer at " + data.index() + " has a count of " + + data.refCount()
+ );
+ }
+ }
+
+ data = VanillaMappedFile.readWriteBytes(path, size, index, fileLifecycleListener);
+ this.cache.put(key,data);
+
+ return data;
+ }
+
+ public int size() {
+ return this.cache.size();
+ }
+
+ @Override
+ public void close() {
+ final Iterator<Map.Entry<T,VanillaMappedBytes>> it = this.cache.entrySet().iterator();
+ while(it.hasNext()) {
+ Map.Entry<T,VanillaMappedBytes> entry = it.next();
+ entry.getValue().release();
+
+ if(this.cleanOnClose && !entry.getValue().unmapped()) {
+ entry.getValue().cleanup();
+ entry.getValue().close();
+ it.remove();
+
+ } else if(entry.getValue().unmapped()) {
+ entry.getValue().close();
+ it.remove();
+ }
+ }
+
+ this.cache.clear();
+ }
+
+ public synchronized void checkCounts(int min, int max) {
+ for(VanillaMappedBytes data : this.cache.values()) {
+ if (data.refCount() < min || data.refCount() > max) {
+ throw new IllegalStateException(
+ "Buffer at " + data.index() + " has a count of " + + data.refCount()
+ );
+ }
+ }
+ }
+}
+
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java
new file mode 100755
index 0000000..5562311
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedFile.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.FileLifecycleListener.EventType;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+/*
+ * Merge memory mapped files:
+ * - net.openhft.lang.io.MappedFile
+ * - net.openhft.lang.io.MappedStore
+ * - net.openhft.chronicle.VanillaFile
+ */
+public class VanillaMappedFile implements VanillaMappedResource {
+
+ private final File path;
+ private final FileChannel fileChannel;
+ private final VanillaMappedMode mode;
+ private final long size;
+ private final FileLifecycleListener fileLifecycleListener;
+
+ public VanillaMappedFile(final File path, VanillaMappedMode mode) throws IOException {
+ this(path, mode, -1, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public VanillaMappedFile(final File path, VanillaMappedMode mode, long size,
+ FileLifecycleListener fileLifecycleListener) throws IOException {
+ this.path = path;
+ this.mode = mode;
+ this.size = size;
+ this.fileChannel = fileChannel(path, mode, this.size, fileLifecycleListener);
+ this.fileLifecycleListener = fileLifecycleListener;
+ }
+
+ public VanillaMappedBytes bytes(long address, long size) throws IOException {
+ return new VanillaMappedBytes(this.path, map(address, size), -1, null, this.fileLifecycleListener);
+ }
+
+ public VanillaMappedBytes bytes(long address, long size, long index) throws IOException {
+ return new VanillaMappedBytes(this.path, map(address,size), index, null, this.fileLifecycleListener);
+ }
+
+ @Override
+ public String path() {
+ return this.path.getAbsolutePath();
+ }
+
+ @Override
+ public long size() {
+ try {
+ return this.fileChannel.size();
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ if(this.fileChannel.isOpen()) {
+ long start = System.nanoTime();
+ this.fileChannel.close();
+ this.fileLifecycleListener.onEvent(EventType.CLOSE, this.path, System.nanoTime() - start);
+ }
+ }
+
+ // *************************************************************************
+ // Helpers
+ // *************************************************************************
+
+ private synchronized MappedByteBuffer map(long address, long size) throws IOException {
+ long start = System.nanoTime();
+ MappedByteBuffer buffer = this.fileChannel.map(this.mode.mapValue(),address,size);
+ buffer.order(ByteOrder.nativeOrder());
+ fileLifecycleListener.onEvent(EventType.MMAP, path, System.nanoTime() - start);
+ return buffer;
+ }
+
+ private static FileChannel fileChannel(final File path, VanillaMappedMode mapMode, long size, FileLifecycleListener fileLifecycleListener) throws IOException {
+ long start = System.nanoTime();
+ FileChannel fileChannel = null;
+ try {
+ final RandomAccessFile raf = new RandomAccessFile(path, mapMode.stringValue());
+ if (size > 0 && raf.length() != size) {
+ if (mapMode.mapValue() != FileChannel.MapMode.READ_WRITE) {
+ throw new IOException("Cannot resize file to " + size + " as mode is not READ_WRITE");
+ }
+
+ raf.setLength(size);
+ }
+
+ fileChannel = raf.getChannel();
+ } catch (Exception e) {
+ throw wrap(e);
+ }
+
+ fileLifecycleListener.onEvent(EventType.NEW, path, System.nanoTime() - start);
+ return fileChannel;
+ }
+
+ private static IOException wrap(Throwable throwable) {
+ if(throwable instanceof InvocationTargetException) {
+ throwable = throwable.getCause();
+
+ } else if(throwable instanceof IOException) {
+ return (IOException)throwable;
+ }
+
+ return new IOException(throwable);
+ }
+
+ public static VanillaMappedFile readWrite(final File path) throws IOException {
+ return new VanillaMappedFile(path,VanillaMappedMode.RW);
+ }
+
+ public static VanillaMappedFile readWrite(final File path, long size) throws IOException {
+ return new VanillaMappedFile(path, VanillaMappedMode.RW, size,
+ FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public static VanillaMappedFile readOnly(final File path) throws IOException {
+ return new VanillaMappedFile(path,VanillaMappedMode.RO);
+ }
+
+ public static VanillaMappedFile readOnly(final File path, long size) throws IOException {
+ return new VanillaMappedFile(path, VanillaMappedMode.RO, size,
+ FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public static VanillaMappedBytes readWriteBytes(final File path, long size) throws IOException {
+ return readWriteBytes(path, size, -1);
+ }
+
+ public static VanillaMappedBytes readWriteBytes(final File path, long size, long index) throws IOException {
+ return readWriteBytes(path, size, index, FileLifecycleListener.FileLifecycleListeners.IGNORE);
+ }
+
+ public static VanillaMappedBytes readWriteBytes(final File path, long size, long index, FileLifecycleListener fileLifecycleListener) throws IOException {
+ VanillaMappedFile vmf = new VanillaMappedFile(path, VanillaMappedMode.RW, -1, fileLifecycleListener);
+ return new VanillaMappedBytes(path, vmf.map(0,size), index, vmf.fileChannel, fileLifecycleListener);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java
new file mode 100644
index 0000000..2fb6926
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedMode.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import java.nio.channels.FileChannel;
+
+/**
+ * Helper wrapper for mapeed access mode
+ */
+public enum VanillaMappedMode {
+ RO("r" ,0, FileChannel.MapMode.READ_ONLY),
+ RW("rw",1,FileChannel.MapMode.READ_WRITE)
+ ;
+
+ private static final VanillaMappedMode[] VALUES = values();
+
+ private String stringValue;
+ private int intValue;
+ private FileChannel.MapMode mapValue;
+
+ VanillaMappedMode(String stringValue, int intValue, FileChannel.MapMode mapValue) {
+ this.stringValue = stringValue;
+ this.intValue = intValue;
+ this.mapValue = mapValue;
+ }
+
+ public int intValue() {
+ return this.intValue;
+ }
+
+ public String stringValue() {
+ return this.stringValue;
+ }
+
+ public FileChannel.MapMode mapValue() {
+ return this.mapValue;
+ }
+
+ public static VanillaMappedMode defaultMode() {
+ return VanillaMappedMode.RO;
+ }
+
+ public static VanillaMappedMode fromValue(int value) {
+ for(VanillaMappedMode mode : VALUES) {
+ if(mode.intValue() == value) {
+ return mode;
+ }
+ }
+
+ return defaultMode();
+ }
+
+ public static VanillaMappedMode fromValue(String value) {
+ for(VanillaMappedMode mode : VALUES) {
+ if(mode.stringValue().equalsIgnoreCase(value)) {
+ return mode;
+ }
+ }
+
+ return defaultMode();
+ }
+
+ public static VanillaMappedMode fromValue(FileChannel.MapMode value) {
+ for(VanillaMappedMode mode : VALUES) {
+ if(mode.mapValue() == value) {
+ return mode;
+ }
+ }
+
+ return defaultMode();
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java b/lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java
new file mode 100644
index 0000000..e0a584a
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/VanillaMappedResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package net.openhft.lang.io;
+
+import java.io.IOException;
+
+public interface VanillaMappedResource {
+ String path();
+
+ long size();
+
+ void close() throws IOException;
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/WrappedBytes.java b/lang/src/main/java/net/openhft/lang/io/WrappedBytes.java
new file mode 100755
index 0000000..1176cea
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/WrappedBytes.java
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io;
+
+import net.openhft.lang.io.serialization.ObjectSerializer;
+import net.openhft.lang.model.Byteable;
+import net.openhft.lang.model.constraints.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A super class for those which wrap bytes.
+ */
+public abstract class WrappedBytes<B extends Bytes> implements Bytes {
+ protected B wrapped;
+
+ protected WrappedBytes(B wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public void clearThreadAssociation() {
+ wrapped.clearThreadAssociation();
+ }
+
+ public ByteBuffer sliceAsByteBuffer(@org.jetbrains.annotations.Nullable ByteBuffer toReuse) {
+ return wrapped.sliceAsByteBuffer(toReuse);
+ }
+
+ public void readMarshallable(@NotNull Bytes in) throws IllegalStateException {
+ wrapped.readMarshallable(in);
+ }
+
+ @Override
+ public String toDebugString(long limit) {
+ return wrapped.toDebugString(limit);
+ }
+
+ @Override
+ public String toHexString(long limit) {
+ return wrapped.toHexString(limit);
+ }
+
+ @Override
+ public boolean compare(long offset, RandomDataInput input, long inputOffset, long len) {
+ return wrapped.compare(offset, input, inputOffset, len);
+ }
+
+ public long readCompactLong() {
+ return wrapped.readCompactLong();
+ }
+
+ public boolean tryLockNanosInt(long offset, long nanos) {
+ return wrapped.tryLockNanosInt(offset, nanos);
+ }
+
+ public void writeMarshallable(@NotNull Bytes out) {
+ wrapped.writeMarshallable(out);
+ }
+
+ public int readInt24() {
+ return wrapped.readInt24();
+ }
+
+ public void flush() {
+ wrapped.flush();
+ }
+
+ public void writeDouble(long offset, double v) {
+ wrapped.writeDouble(offset, v);
+ }
+
+ public long limit() {
+ return wrapped.limit();
+ }
+
+ @NotNull
+ public ByteStringAppender appendTimeMillis(long timeInMS) {
+ return wrapped.appendTimeMillis(timeInMS);
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public <E extends Enum<E>> E parseEnum(@NotNull Class<E> eClass, @NotNull StopCharTester tester) throws BufferUnderflowException {
+ return wrapped.parseEnum(eClass, tester);
+ }
+
+ public int refCount() {
+ return wrapped.refCount();
+ }
+
+ public void writeShort(long offset, int v) {
+ wrapped.writeShort(offset, v);
+ }
+
+ public <E> void writeEnum(@org.jetbrains.annotations.Nullable E e) {
+ wrapped.writeEnum(e);
+ }
+
+ @NotNull
+ public <E> ByteStringAppender append(@NotNull Iterable<E> list, @NotNull CharSequence separator) {
+ return wrapped.append(list, separator);
+ }
+
+ public void writeCompactUnsignedShort(int v) {
+ wrapped.writeCompactUnsignedShort(v);
+ }
+
+ public long readVolatileLong() {
+ return wrapped.readVolatileLong();
+ }
+
+ public void write(RandomDataInput in, long position, long length) {
+ wrapped.write(in, position, length);
+ }
+
+ public void write(Byteable byteable) {
+ wrapped.write(byteable);
+ }
+
+ public void writeOrderedInt(int v) {
+ wrapped.writeOrderedInt(v);
+ }
+
+ public boolean readUTFΔ(@NotNull StringBuilder stringBuilder) {
+ return wrapped.readUTFΔ(stringBuilder);
+ }
+
+ public void writeInt48(long offset, long v) {
+ wrapped.writeInt48(offset, v);
+ }
+
+ public long readLong() {
+ return wrapped.readLong();
+ }
+
+ public long readIncompleteLong(long offset) {
+ return wrapped.readIncompleteLong(offset);
+ }
+
+ public void writeLong(long v) {
+ wrapped.writeLong(v);
+ }
+
+ @NotNull
+ public ByteStringAppender appendDateTimeMillis(long timeInMS) {
+ return wrapped.appendDateTimeMillis(timeInMS);
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public <E> E readEnum(@NotNull Class<E> eClass) {
+ return wrapped.readEnum(eClass);
+ }
+
+ public void write(RandomDataInput in) {
+ wrapped.write(in);
+ }
+
+ @NotNull
+ public ByteStringAppender append(double d) {
+ return wrapped.append(d);
+ }
+
+ @NotNull
+ public String toDebugString() {
+ return wrapped.toDebugString();
+ }
+
+ public boolean isFinished() {
+ return wrapped.isFinished();
+ }
+
+ public void writeCompactUnsignedInt(long v) {
+ wrapped.writeCompactUnsignedInt(v);
+ }
+
+ @NotNull
+ public ByteStringAppender append(double d, int precision) {
+ return wrapped.append(d, precision);
+ }
+
+ public int readUnsignedByteOrThrow() throws BufferUnderflowException {
+ return wrapped.readUnsignedByteOrThrow();
+ }
+
+ public Bytes zeroOut(long start, long end) {
+ wrapped.zeroOut(start, end);
+ return this;
+ }
+
+ @Override
+ public Bytes zeroOut(long start, long end, boolean ifNotZero) {
+ wrapped.zeroOut(start, end, ifNotZero);
+ return this;
+ }
+
+ public void writeShort(int v) {
+ wrapped.writeShort(v);
+ }
+
+ public short addShort(long offset, short s) {
+ return wrapped.addShort(offset, s);
+ }
+
+ public void writeUnsignedInt(long v) {
+ wrapped.writeUnsignedInt(v);
+ }
+
+ public void free() {
+ wrapped.free();
+ }
+
+ public int readUnsignedShort() {
+ return wrapped.readUnsignedShort();
+ }
+
+ public void writeStopBit(long n) {
+ wrapped.writeStopBit(n);
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public <T> T readObject(Class<T> tClass) throws IllegalStateException {
+ return wrapped.readObject(tClass);
+ }
+
+ public void writeCompactInt(int v) {
+ wrapped.writeCompactInt(v);
+ }
+
+ public void writeOrderedLong(long v) {
+ wrapped.writeOrderedLong(v);
+ }
+
+ public byte addByte(long offset, byte b) {
+ return wrapped.addByte(offset, b);
+ }
+
+ public int readVolatileInt() {
+ return wrapped.readVolatileInt();
+ }
+
+ public void close() {
+ wrapped.close();
+ }
+
+ public void read(@NotNull ByteBuffer bb) {
+ wrapped.read(bb);
+ }
+
+ public void read(@NotNull ByteBuffer bb, int length) {
+ wrapped.read(bb, length);
+ }
+
+ @NotNull
+ public ByteStringAppender append(long l, int base) {
+ return wrapped.append(l, base);
+ }
+
+ public long skip(long n) {
+ return wrapped.skip(n);
+ }
+
+ public boolean selfTerminating() {
+ return wrapped.selfTerminating();
+ }
+
+ public void writeBytes(@NotNull String s) {
+ wrapped.writeBytes(s);
+ }
+
+ public long size() {
+ return wrapped.size();
+ }
+
+ public int readCompactUnsignedShort() {
+ return wrapped.readCompactUnsignedShort();
+ }
+
+ @NotNull
+ public ByteStringAppender append(@NotNull CharSequence s, int start, int end) {
+ return wrapped.append(s, start, end);
+ }
+
+ public void writeCompactLong(long v) {
+ wrapped.writeCompactLong(v);
+ }
+
+ public double readCompactDouble() {
+ return wrapped.readCompactDouble();
+ }
+
+ public void writeOrderedInt(long offset, int v) {
+ wrapped.writeOrderedInt(offset, v);
+ }
+
+ public void writeObject(Object object, int start, int end) {
+ wrapped.writeObject(object, start, end);
+ }
+
+ public CharSequence asString() {
+ return wrapped.asString();
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public String readUTFΔ() {
+ return wrapped.readUTFΔ();
+ }
+
+ public Bytes flip() {
+ return wrapped.flip();
+ }
+
+ public int addInt(long offset, int i) {
+ return wrapped.addInt(offset, i);
+ }
+
+ public long readUnsignedInt(long offset) {
+ return wrapped.readUnsignedInt(offset);
+ }
+
+ public void writeByte(int v) {
+ wrapped.writeByte(v);
+ }
+
+ public void writeUnsignedInt(long offset, long v) {
+ wrapped.writeUnsignedInt(offset, v);
+ }
+
+ public void writeInt(int v) {
+ wrapped.writeInt(v);
+ }
+
+ public short readShort() {
+ return wrapped.readShort();
+ }
+
+ public void writeUnsignedByte(long offset, int v) {
+ wrapped.writeUnsignedByte(offset, v);
+ }
+
+ public void asString(Appendable appendable) {
+ wrapped.asString(appendable);
+ }
+
+ public long readInt48(long offset) {
+ return wrapped.readInt48(offset);
+ }
+
+ public void unlockRWReadLock(long offset) throws IllegalStateException {
+ wrapped.unlockRWReadLock(offset);
+ }
+
+ @NotNull
+ public String readUTF() {
+ return wrapped.readUTF();
+ }
+
+ public void writeUnsignedShort(long offset, int v) {
+ wrapped.writeUnsignedShort(offset, v);
+ }
+
+ public void readFully(@NotNull char[] data) {
+ wrapped.readFully(data);
+ }
+
+ public void writeInt24(long offset, int v) {
+ wrapped.writeInt24(offset, v);
+ }
+
+ public void writeChars(@NotNull CharSequence cs) {
+ wrapped.writeChars(cs);
+ }
+
+ public float readFloat(long offset) {
+ return wrapped.readFloat(offset);
+ }
+
+ public long capacity() {
+ return wrapped.capacity();
+ }
+
+ public CharSequence subSequence(int start, int end) {
+ return wrapped.subSequence(start, end);
+ }
+
+ public Bytes clear() {
+ return wrapped.clear();
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public String readUTFΔ(long offset) throws IllegalStateException {
+ return wrapped.readUTFΔ(offset);
+ }
+
+ @NotNull
+ public ObjectSerializer objectSerializer() {
+ return wrapped.objectSerializer();
+ }
+
+ public void writeOrderedLong(long offset, long v) {
+ wrapped.writeOrderedLong(offset, v);
+ }
+
+ public long addAtomicLong(long offset, long l) {
+ return wrapped.addAtomicLong(offset, l);
+ }
+
+ @NotNull
+ public ByteStringAppender append(char c) {
+ return wrapped.append(c);
+ }
+
+ public void busyLockInt(long offset) throws InterruptedException, IllegalStateException {
+ wrapped.busyLockInt(offset);
+ }
+
+ public void resetLockInt(long offset) {
+ wrapped.resetLockInt(offset);
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public String readLine() {
+ return wrapped.readLine();
+ }
+
+ public char readChar(long offset) {
+ return wrapped.readChar(offset);
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public <T> T readInstance(@NotNull Class<T> objClass, T obj) {
+ return wrapped.readInstance(objClass, obj);
+ }
+
+ @NotNull
+ public ByteStringAppender append(boolean b) {
+ return wrapped.append(b);
+ }
+
+ public int addUnsignedByte(long offset, int i) {
+ return wrapped.addUnsignedByte(offset, i);
+ }
+
+ public void readFully(@NotNull byte[] byteArray, int off, int len) {
+ wrapped.readFully(byteArray, off, len);
+ }
+
+ public void readFully(@NotNull char[] data, int off, int len) {
+ wrapped.readFully(data, off, len);
+ }
+
+ public int addAndGetInt(long offset, int delta) {
+ return wrapped.addAndGetInt(offset, delta);
+ }
+
+ public long addUnsignedInt(long offset, long i) {
+ return wrapped.addUnsignedInt(offset, i);
+ }
+
+ public void writeInt48(long v) {
+ wrapped.writeInt48(v);
+ }
+
+ @NotNull
+ public ByteStringAppender append(@NotNull MutableDecimal md) {
+ return wrapped.append(md);
+ }
+
+ public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) {
+ return wrapped.readMap(map, kClass, vClass);
+ }
+
+ public char charAt(int index) {
+ return wrapped.charAt(index);
+ }
+
+ public void writeOrderedFloat(long offset, float v) {
+ wrapped.writeOrderedFloat(offset, v);
+ }
+
+ public void unlockRWWriteLock(long offset) throws IllegalStateException {
+ wrapped.unlockRWWriteLock(offset);
+ }
+
+ public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) throws BufferUnderflowException {
+ wrapped.parseUtf8(builder, tester);
+ }
+
+ @NotNull
+ public InputStream inputStream() {
+ return wrapped.inputStream();
+ }
+
+ public long remaining() {
+ return wrapped.remaining();
+ }
+
+ public void writeByte(long offset, int b) {
+ wrapped.writeByte(offset, b);
+ }
+
+ public double readDouble() {
+ return wrapped.readDouble();
+ }
+
+ public int readCompactInt() {
+ return wrapped.readCompactInt();
+ }
+
+ public boolean release() {
+ return wrapped.release();
+ }
+
+ public boolean readBoolean(long offset) {
+ return wrapped.readBoolean(offset);
+ }
+
+ public void writeBoolean(boolean v) {
+ wrapped.writeBoolean(v);
+ }
+
+ public int read(@NotNull byte[] byteArray) {
+ return wrapped.read(byteArray);
+ }
+
+ public void writeChars(@NotNull String s) {
+ wrapped.writeChars(s);
+ }
+
+ public Bytes slice() {
+ return wrapped.slice();
+ }
+
+ public Bytes zeroOut() {
+ return wrapped.zeroOut();
+ }
+
+ public void toString(Appendable sb, long start, long position, long end) {
+ wrapped.toString(sb, start, position, end);
+ }
+
+ public void writeOrderedDouble(long offset, double v) {
+ wrapped.writeOrderedDouble(offset, v);
+ }
+
+ public long readStopBit() {
+ return wrapped.readStopBit();
+ }
+
+ public void busyLockLong(long offset) throws InterruptedException, IllegalStateException {
+ wrapped.busyLockLong(offset);
+ }
+
+ public void writeDouble(double v) {
+ wrapped.writeDouble(v);
+ }
+
+ public double readDouble(long offset) {
+ return wrapped.readDouble(offset);
+ }
+
+ public float addFloat(long offset, float f) {
+ return wrapped.addFloat(offset, f);
+ }
+
+ public boolean skipTo(@NotNull StopCharTester tester) {
+ return wrapped.skipTo(tester);
+ }
+
+ public void writeChar(int v) {
+ wrapped.writeChar(v);
+ }
+
+ public void writeInt(long offset, int v) {
+ wrapped.writeInt(offset, v);
+ }
+
+ @NotNull
+ public OutputStream outputStream() {
+ return wrapped.outputStream();
+ }
+
+ public boolean compareAndSwapDouble(long offset, double expected, double x) {
+ return wrapped.compareAndSwapDouble(offset, expected, x);
+ }
+
+ public File file() {
+ return wrapped.file();
+ }
+
+ public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) {
+ wrapped.readList(list, eClass);
+ }
+
+ public void writeUnsignedByte(int v) {
+ wrapped.writeUnsignedByte(v);
+ }
+
+ public int readInt24(long offset) {
+ return wrapped.readInt24(offset);
+ }
+
+ public long readInt48() {
+ return wrapped.readInt48();
+ }
+
+ public void write(@NotNull char[] data) {
+ wrapped.write(data);
+ }
+
+ @org.jetbrains.annotations.Nullable
+ public Object readObject() throws IllegalStateException {
+ return wrapped.readObject();
+ }
+
+ @NotNull
+ public ByteStringAppender append(@net.openhft.lang.model.constraints.Nullable Enum value) {
+ return wrapped.append(value);
+ }
+
+ @NotNull
+ public String parseUtf8(@NotNull StopCharTester tester) throws BufferUnderflowException {
+ return wrapped.parseUtf8(tester);
+ }
+
+ public int readInt() {
+ return wrapped.readInt();
+ }
+
+ public void write(@NotNull char[] data, int off, int len) {
+ wrapped.write(data, off, len);
+ }
+
+ public int addUnsignedShort(long offset, int i) {
+ return wrapped.addUnsignedShort(offset, i);
+ }
+
+ public float readFloat() {
+ return wrapped.readFloat();
+ }
+
+ public int available() {
+ return wrapped.available();
+ }
+
+ public long position() {
+ return wrapped.position();
+ }
+
+ public double addDouble(long offset, double d) {
+ return wrapped.addDouble(offset, d);
+ }
+
+ public void write(int b) {
+ wrapped.write(b);
+ }
+
+ public int skipBytes(int n) {
+ return wrapped.skipBytes(n);
+ }
+
+ public short readCompactShort() {
+ return wrapped.readCompactShort();
+ }
+
+ public void write(long offset, byte[] byteArray) {
+ wrapped.write(offset, byteArray);
+ }
+
+ public <E> void writeList(@NotNull Collection<E> list) {
+ wrapped.writeList(list);
+ }
+
+ public int read(@NotNull byte[] byteArray, int off, int len) {
+ return wrapped.read(byteArray, off, len);
+ }
+
+ public int readInt(long offset) {
+ return wrapped.readInt(offset);
+ }
+
+ public void writeFloat(long offset, float v) {
+ wrapped.writeFloat(offset, v);
+ }
+
+ public long parseLong() throws BufferUnderflowException {
+ return wrapped.parseLong();
+ }
+
+ public int readUnsignedByte(long offset) {
+ return wrapped.readUnsignedByte(offset);
+ }
+
+ public Bytes slice(long offset, long length) {
+ return wrapped.slice(offset, length);
+ }
+
+ public void writeObject(@org.jetbrains.annotations.Nullable Object object) {
+ wrapped.writeObject(object);
+ }
+
+ public int length() {
+ return wrapped.length();
+ }
+
+ public char readChar() {
+ return wrapped.readChar();
+ }
+
+ public int read() {
+ return wrapped.read();
+ }
+
+ public void writeBoolean(long offset, boolean v) {
+ wrapped.writeBoolean(offset, v);
+ }
+
+ public double parseDouble() throws BufferUnderflowException {
+ return wrapped.parseDouble();
+ }
+
+ public void writeCompactDouble(double v) {
+ wrapped.writeCompactDouble(v);
+ }
+
+ public float addAtomicFloat(long offset, float f) {
+ return wrapped.addAtomicFloat(offset, f);
+ }
+
+ public void selfTerminating(boolean selfTerminate) {
+ wrapped.selfTerminating(selfTerminate);
+ }
+
+ public long readCompactUnsignedInt() {
+ return wrapped.readCompactUnsignedInt();
+ }
+
+ public double readVolatileDouble(long offset) {
+ return wrapped.readVolatileDouble(offset);
+ }
+
+ public long addLong(long offset, long i) {
+ return wrapped.addLong(offset, i);
+ }
+
+ public long readLong(long offset) {
+ return wrapped.readLong(offset);
+ }
+
+ public boolean compareAndSwapInt(long offset, int expected, int x) {
+ return wrapped.compareAndSwapInt(offset, expected, x);
+ }
+
+ @NotNull
+ public ByteStringAppender append(@NotNull CharSequence s) {
+ return wrapped.append(s);
+ }
+
+ @NotNull
+ public ByteStringAppender append(int i) {
+ return wrapped.append(i);
+ }
+
+ public <K, V> void writeMap(@NotNull Map<K, V> map) {
+ wrapped.writeMap(map);
+ }
+
+ public Boolean parseBoolean(@NotNull StopCharTester tester) throws BufferUnderflowException {
+ return wrapped.parseBoolean(tester);
+ }
+
+ public boolean tryRWReadLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ return wrapped.tryRWReadLock(offset, timeOutNS);
+ }
+
+ public int readUnsignedShort(long offset) {
+ return wrapped.readUnsignedShort(offset);
+ }
+
+ public void writeUTFΔ(long offset, int maxSize, @org.jetbrains.annotations.Nullable CharSequence s) throws IllegalStateException {
+ wrapped.writeUTFΔ(offset, maxSize, s);
+ }
+
+ public byte readByte(long offset) {
+ return wrapped.readByte(offset);
+ }
+
+ @NotNull
+ public ByteStringAppender append(long l) {
+ return wrapped.append(l);
+ }
+
+ public void writeUTFΔ(@org.jetbrains.annotations.Nullable CharSequence s) {
+ wrapped.writeUTFΔ(s);
+ }
+
+ public boolean compareAndSwapLong(long offset, long expected, long x) {
+ return wrapped.compareAndSwapLong(offset, expected, x);
+ }
+
+ public void writeCompactShort(int v) {
+ wrapped.writeCompactShort(v);
+ }
+
+ public Bytes bytes() {
+ return wrapped.bytes();
+ }
+
+ public void write(byte[] byteArray) {
+ wrapped.write(byteArray);
+ }
+
+ public void unlockInt(long offset) throws IllegalMonitorStateException {
+ wrapped.unlockInt(offset);
+ }
+
+ public boolean tryLockLong(long offset) {
+ return wrapped.tryLockLong(offset);
+ }
+
+ public byte readByte() {
+ return wrapped.readByte();
+ }
+
+ public boolean tryRWWriteLock(long offset, long timeOutNS) throws IllegalStateException, InterruptedException {
+ return wrapped.tryRWWriteLock(offset, timeOutNS);
+ }
+
+ public void write(byte[] byteArray, int off, int len) {
+ wrapped.write(byteArray, off, len);
+ }
+
+ public void writeUTF(@NotNull String s) {
+ wrapped.writeUTF(s);
+ }
+
+ public Bytes load() {
+ return wrapped.load();
+ }
+
+ public int getAndAdd(long offset, int delta) {
+ return wrapped.getAndAdd(offset, delta);
+ }
+
+ public short readShort(long offset) {
+ return wrapped.readShort(offset);
+ }
+
+ public boolean stepBackAndSkipTo(@NotNull StopCharTester tester) {
+ return wrapped.stepBackAndSkipTo(tester);
+ }
+
+ public void resetLockLong(long offset) {
+ wrapped.resetLockLong(offset);
+ }
+
+ public int readVolatileInt(long offset) {
+ return wrapped.readVolatileInt(offset);
+ }
+
+ @NotNull
+ public ByteOrder byteOrder() {
+ return wrapped.byteOrder();
+ }
+
+ public Bytes bytes(long offset, long length) {
+ return wrapped.bytes(offset, length);
+ }
+
+ public void alignPositionAddr(int alignment) {
+ wrapped.alignPositionAddr(alignment);
+ }
+
+ public void writeUnsignedShort(int v) {
+ wrapped.writeUnsignedShort(v);
+ }
+
+ public long parseLong(int base) throws BufferUnderflowException {
+ return wrapped.parseLong(base);
+ }
+
+ public boolean readBoolean() {
+ return wrapped.readBoolean();
+ }
+
+ public void checkEndOfBuffer() throws IndexOutOfBoundsException {
+ wrapped.checkEndOfBuffer();
+ }
+
+ public float readVolatileFloat(long offset) {
+ return wrapped.readVolatileFloat(offset);
+ }
+
+ @NotNull
+ public MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) throws BufferUnderflowException {
+ return wrapped.parseDecimal(decimal);
+ }
+
+ public double addAtomicDouble(long offset, double d) {
+ return wrapped.addAtomicDouble(offset, d);
+ }
+
+ public void unlockLong(long offset) throws IllegalMonitorStateException {
+ wrapped.unlockLong(offset);
+ }
+
+ public void writeFloat(float v) {
+ wrapped.writeFloat(v);
+ }
+
+ public void reserve() {
+ wrapped.reserve();
+ }
+
+ public void write(@NotNull ByteBuffer bb) {
+ wrapped.write(bb);
+ }
+
+ public long threadIdForLockLong(long offset) {
+ return wrapped.threadIdForLockLong(offset);
+ }
+
+ public void writeChar(long offset, int v) {
+ wrapped.writeChar(offset, v);
+ }
+
+ public boolean tryLockNanosLong(long offset, long nanos) {
+ return wrapped.tryLockNanosLong(offset, nanos);
+ }
+
+ public int addAtomicInt(long offset, int i) {
+ return wrapped.addAtomicInt(offset, i);
+ }
+
+ public <OBJ> void writeInstance(@NotNull Class<OBJ> objClass, @NotNull OBJ obj) {
+ wrapped.writeInstance(objClass, obj);
+ }
+
+ public void readFully(@NotNull byte[] byteArray) {
+ wrapped.readFully(byteArray);
+ }
+
+ public Bytes position(long position) {
+ return wrapped.position(position);
+ }
+
+ public void writeLong(long offset, long v) {
+ wrapped.writeLong(offset, v);
+ }
+
+ public void readObject(Object object, int start, int end) {
+ wrapped.readObject(object, start, end);
+ }
+
+ public int threadIdForLockInt(long offset) {
+ return wrapped.threadIdForLockInt(offset);
+ }
+
+ @NotNull
+ public ByteStringAppender appendDateMillis(long timeInMS) {
+ return wrapped.appendDateMillis(timeInMS);
+ }
+
+ public void writeInt24(int v) {
+ wrapped.writeInt24(v);
+ }
+
+ public boolean startsWith(RandomDataInput keyBytes) {
+ return wrapped.startsWith(keyBytes);
+ }
+
+ public long readUnsignedInt() {
+ return wrapped.readUnsignedInt();
+ }
+
+ public Bytes limit(long limit) {
+ return wrapped.limit(limit);
+ }
+
+ public void finish() {
+ wrapped.finish();
+ }
+
+ public long address() {
+ return wrapped.address();
+ }
+
+ public boolean tryLockInt(long offset) {
+ return wrapped.tryLockInt(offset);
+ }
+
+ public long readVolatileLong(long offset) {
+ return wrapped.readVolatileLong(offset);
+ }
+
+ public int readUnsignedByte() {
+ return wrapped.readUnsignedByte();
+ }
+
+ @Override
+ public void readFully(long offset, @org.jetbrains.annotations.NotNull byte[] byteArray, int off, int len) {
+ wrapped.readFully(offset, byteArray, off, len);
+ }
+
+ @Override
+ public void write(long offset, byte[] byteArray, int off, int len) {
+ wrapped.write(offset, byteArray, off, len);
+ }
+
+ @Override
+ public void write(long offset, Bytes bytes) {
+ wrapped.write(offset, bytes);
+ }
+
+ @Override
+ public boolean read8bitText(@org.jetbrains.annotations.NotNull StringBuilder stringBuilder) throws StreamCorruptedException {
+ return wrapped.read8bitText(stringBuilder);
+ }
+
+ @Override
+ public <E> E readEnum(long offset, int maxSize, Class<E> eClass) {
+ return wrapped.readEnum(offset, maxSize, eClass);
+ }
+
+ @Override
+ public void write8bitText(@Nullable CharSequence s) {
+ wrapped.write8bitText(s);
+ }
+
+ @Override
+ public void writeEnum(long offset, int len, Object object) {
+ wrapped.writeEnum(offset, len, object);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java
index a92b316..5ac643e 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallable.java
@@ -1,23 +1,23 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization;
import net.openhft.lang.io.Bytes;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
/**
* Integrated marshaller for objects.
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java
new file mode 100644
index 0000000..d68e355
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallableSerializer.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization;
+
+import net.openhft.lang.io.AbstractBytes;
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.NativeBytes;
+import net.openhft.lang.io.serialization.impl.NoMarshaller;
+import net.openhft.lang.io.serialization.impl.StringBuilderPool;
+import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.pool.EnumInterner;
+
+import java.io.Externalizable;
+import java.io.IOException;
+
+/**
+ * An extension of built-in Java serialization, featuring special treatment of {@link
+ * BytesMarshallable} objects, compact {@link String} encoding and support of pluggable custom
+ * serializers for arbitrary classes.
+ *
+ * <p>{@code BytesMarshallableSerializer} could benefit if objects (either top-level serialized or
+ * nested fields) implement {@link BytesMarshallable} interface the same way as built-in
+ * serialization benefit if objects implement {@link Externalizable} (of cause, {@code
+ * BytesMarshallableSerializer} supports {@code Externalizable} too).
+ *
+ * <p>{@link CharSequence}s, including {@code String}s (either top-level serialized or nested
+ * fields) are serialized in UTF-8 encoding.
+ *
+ * <p>Custom per-class serializers are held by {@link BytesMarshallerFactory}, which could be
+ * passed via constructor or static factory {@link #create create()} method.
+ *
+ * @see #create(BytesMarshallerFactory, ObjectSerializer)
+ */
+public class BytesMarshallableSerializer implements ObjectSerializer {
+ private static final long serialVersionUID = 0L;
+
+ private static final byte NULL = 'N';
+ private static final byte ENUMED = 'E';
+ private static final byte SERIALIZED = 'S';
+ private static final StringBuilderPool SBP = new StringBuilderPool();
+
+ private final BytesMarshallerFactory bytesMarshallerFactory;
+ private final ObjectSerializer objectSerializer;
+
+ protected BytesMarshallableSerializer(BytesMarshallerFactory bytesMarshallerFactory, ObjectSerializer objectSerializer) {
+ this.bytesMarshallerFactory = bytesMarshallerFactory;
+ this.objectSerializer = objectSerializer;
+ }
+
+ @Override
+ public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException {
+ if (object == null) {
+ bytes.writeByte(NULL);
+ return;
+ }
+ if (expectedClass != null) {
+ if (BytesMarshallable.class.isAssignableFrom(expectedClass)) {
+ ((BytesMarshallable) object).writeMarshallable(bytes);
+ return;
+
+ } else if (Externalizable.class.isAssignableFrom(expectedClass)) {
+ ((Externalizable) object).writeExternal(bytes);
+ return;
+
+ } else if (CharSequence.class.isAssignableFrom(expectedClass)) {
+ bytes.writeUTFΔ((CharSequence) object);
+ return;
+
+ } else if (Enum.class.isAssignableFrom(expectedClass)) {
+ bytes.write8bitText(object.toString());
+ return;
+ }
+ }
+ writeSerializable2(bytes, object);
+ }
+
+ private void writeSerializable2(Bytes bytes, Object object) throws IOException {
+ Class<?> clazz = object.getClass();
+ BytesMarshaller em = bytesMarshallerFactory.acquireMarshaller(clazz, false);
+ if (em == NoMarshaller.INSTANCE && autoGenerateMarshaller(object))
+ em = bytesMarshallerFactory.acquireMarshaller(clazz, true);
+
+ if (em != NoMarshaller.INSTANCE) {
+ if (em instanceof CompactBytesMarshaller) {
+ bytes.writeByte(((CompactBytesMarshaller) em).code());
+ em.write(bytes, object);
+ return;
+ }
+ bytes.writeByte(ENUMED);
+ this.writeSerializable(bytes, clazz, Class.class);
+ em.write(bytes, object);
+ return;
+ }
+ bytes.writeByte(SERIALIZED);
+ // TODO this is the lame implementation, but it works.
+ objectSerializer.writeSerializable(bytes, object, null);
+ }
+
+ static boolean autoGenerateMarshaller(Object obj) {
+ return (obj instanceof Comparable && obj.getClass().getPackage().getName().startsWith("java"))
+ || obj instanceof Externalizable
+ || obj instanceof BytesMarshallable;
+ }
+
+ @Override
+ public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException {
+ if (expectedClass != null) {
+ try {
+ if (BytesMarshallable.class.isAssignableFrom(expectedClass)) {
+ return readBytesMarshallable(bytes, expectedClass, object);
+
+ } else if (Externalizable.class.isAssignableFrom(expectedClass)) {
+ return readExternalizable(bytes, expectedClass, object);
+
+ } else if (CharSequence.class.isAssignableFrom(expectedClass)) {
+ return readCharSequence(bytes, object);
+
+ } else if (Enum.class.isAssignableFrom(expectedClass)) {
+ StringBuilder sb = SBP.acquireStringBuilder();
+ bytes.read8bitText(sb);
+ return (T) EnumInterner.intern((Class<Enum>) expectedClass, sb);
+ }
+ } catch (InstantiationException e) {
+ throw new IOException("Unable to create " + expectedClass, e);
+ }
+ }
+ int type = bytes.readUnsignedByteOrThrow();
+ switch (type) {
+ case AbstractBytes.END_OF_BUFFER:
+ case NULL:
+ return null;
+ case ENUMED: {
+ Class clazz = this.readSerializable(bytes, Class.class, null);
+ assert clazz != null;
+ return (T) bytesMarshallerFactory.acquireMarshaller(clazz, true).read(bytes, object);
+ }
+
+ case SERIALIZED: {
+ return objectSerializer.readSerializable(bytes, expectedClass, object);
+ }
+
+ default:
+ BytesMarshaller<Object> m = bytesMarshallerFactory.getMarshaller((byte) type);
+ if (m == null)
+ throw new IllegalStateException("Unknown type " + (char) type);
+ return (T) m.read(bytes);
+ }
+ }
+
+ private <T> T readCharSequence(Bytes bytes, T object) {
+ if (object instanceof StringBuilder) {
+ bytes.readUTFΔ(((StringBuilder) object));
+ return object;
+
+ } else {
+ return (T) bytes.readUTFΔ();
+ }
+ }
+
+ private <T> T readExternalizable(Bytes bytes, Class<T> expectedClass, T object) throws InstantiationException, IOException, ClassNotFoundException {
+ if (object == null)
+ object = (T) NativeBytes.UNSAFE.allocateInstance(expectedClass);
+ ((Externalizable) object).readExternal(bytes);
+ return object;
+ }
+
+ private <T> T readBytesMarshallable(Bytes bytes, Class<T> expectedClass, T object) throws InstantiationException {
+ if (object == null)
+ object = (T) NativeBytes.UNSAFE.allocateInstance(expectedClass);
+ ((BytesMarshallable) object).readMarshallable(bytes);
+ return object;
+ }
+
+ public static ObjectSerializer create() {
+ return create(new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE);
+ }
+
+ public static ObjectSerializer create(BytesMarshallerFactory bytesMarshallerFactory, ObjectSerializer instance) {
+ return bytesMarshallerFactory == null ? instance : new BytesMarshallableSerializer(bytesMarshallerFactory, instance);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java
index d9a4fa2..d2593aa 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshaller.java
@@ -1,45 +1,60 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization;
import net.openhft.lang.io.Bytes;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.io.Serializable;
/**
- * External marshaller for classes.
+ * External marshaller for classes. From design patterns point of view, this interface
+ * is marshalling <i>strategy</i>.
*
* @author peter.lawrey
* @see BytesMarshallable
*/
-public interface BytesMarshaller<E> {
+public interface BytesMarshaller<E> extends Serializable {
+
/**
- * write the object out as bytes.
+ * Write the object out to the {@code bytes}.
*
* @param bytes to write to
- * @param e to write
+ * @param e the object to write
*/
void write(Bytes bytes, E e);
/**
- * Read bytes and obtain an object
+ * Reads and returns an object from {@code bytes}.
*
* @param bytes to read
- * @return the object
+ * @return the read object
*/
@Nullable
E read(Bytes bytes);
+
+ /**
+ * Reads and returns an object from {@code bytes}, reusing the given object, if possible.
+ *
+ * @param bytes to read
+ * @param e an object to reuse, if possible. {@code null} could be passed, in this case
+ * a new object should be allocated anyway.
+ * @return the read object
+ */
+ @Nullable
+ E read(Bytes bytes, @Nullable E e);
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java
index 87218b7..6814fe8 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/BytesMarshallerFactory.java
@@ -1,27 +1,29 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
+
+import java.io.Serializable;
/**
* @author peter.lawrey
*/
-public interface BytesMarshallerFactory {
+public interface BytesMarshallerFactory extends Serializable {
@NotNull
<E> BytesMarshaller<E> acquireMarshaller(@NotNull Class<E> eClass, boolean create);
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java
index 6ee1f87..5b8ef29 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/CompactBytesMarshaller.java
@@ -1,17 +1,17 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization;
@@ -20,8 +20,21 @@ package net.openhft.lang.io.serialization;
* A BytesMarshaller with a byte code for the class.
*/
public interface CompactBytesMarshaller<E> extends BytesMarshaller<E> {
+ byte BYTE_BUFFER_CODE = 'B' & 31;
+ byte CLASS_CODE = 'C' & 31;
+ byte INT_CODE = 'I' & 31;
+ byte LONG_CODE = 'L' & 31;
+ byte DOUBLE_CODE = 'D' & 31;
+ byte DATE_CODE = 'T' & 31;
+ byte STRING_CODE = 'S' & 31;
+ byte STRINGZ_MAP_CODE = 'Y' & 31; // compressed string.
+ byte STRINGZ_CODE = 'Z' & 31; // compressed string.
+ byte LIST_CODE = '[';
+ byte SET_CODE = '[' & 31;
+ byte MAP_CODE = '{';
+
/**
- * the code for this marshaller
+ * @return the code for this marshaller
*/
byte code();
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java
new file mode 100644
index 0000000..30557fe
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/JDKObjectSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.model.constraints.NotNull;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+public enum JDKObjectSerializer implements ObjectSerializer {
+ INSTANCE;
+
+ @Override
+ public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException {
+ new ObjectOutputStream(bytes.outputStream()).writeObject(object);
+ }
+
+ @Override
+ public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException {
+ return (T) new ObjectInputStream(bytes.inputStream()).readObject();
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java
new file mode 100644
index 0000000..784e7a9
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/JDKZObjectSerializer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.model.constraints.NotNull;
+
+import java.io.*;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+public enum JDKZObjectSerializer implements ObjectSerializer {
+ INSTANCE;
+
+ @Override
+ public void writeSerializable(Bytes bytes, Object object, Class expectedClass) throws IOException {
+ // reset the finished flag and append
+ long position = bytes.position();
+ bytes.clear();
+ bytes.position(position + 4);
+ OutputStream out = bytes.outputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(new DeflaterOutputStream(out));
+ oos.writeObject(object);
+ oos.close();
+ long length = bytes.position() - position - 4;
+ bytes.writeUnsignedInt(position, length);
+ }
+
+ @Override
+ public <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException {
+ long length = bytes.readUnsignedInt();
+ if (length < 8 || length > Integer.MAX_VALUE)
+ throw new StreamCorruptedException("length = " + Long.toHexString(length));
+ long end = bytes.position() + length;
+ long lim = bytes.limit();
+ bytes.limit(end);
+ int magic = bytes.readUnsignedShort(bytes.position());
+ InputStream in = bytes.inputStream();
+ switch (magic) {
+ case 0xEDAC:
+ break;
+
+ case 0x9c78:
+ in = new InflaterInputStream(in);
+ break;
+ default:
+ throw new StreamCorruptedException("Unknown magic number " + Integer.toHexString(magic));
+ }
+ T t = (T) new ObjectInputStream(in).readObject();
+ bytes.limit(lim);
+ if (end != bytes.position()) {
+ System.out.println("diff: " + (end - bytes.position()));
+ bytes.position(end);
+ }
+ return t;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java
new file mode 100644
index 0000000..40dea33
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectFactory.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization;
+
+import java.io.Serializable;
+
+public interface ObjectFactory<E> extends Serializable {
+ E create() throws InstantiationException, IllegalAccessException;
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java
new file mode 100644
index 0000000..7683862
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/ObjectSerializer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.model.constraints.NotNull;
+
+import java.io.*;
+
+/**
+ * Abstracts serialization implementation, which at least should be able to serialize objects that
+ * Java built-in serialization is able serialize. In other words, {@code ObjectSerializer} abstracts
+ * Java serialization re-implementations and extensions. {@link Bytes} is used as core IO interface
+ * instead of {@link InputStream} + {@link OutputStream} pair, which Java built-in serialization
+ * use. However, note that {@code Bytes} could always be converted to these old interfaces by
+ * {@link Bytes#inputStream()} and {@link Bytes#outputStream()}, if needed.
+ *
+ * <p>The default fallback implementation is Java built-in serialization itself:
+ * {@link JDKObjectSerializer}.
+ *
+ * <p>Another provided implementation is {@link BytesMarshallableSerializer}, which basically
+ * extends built-in serialization with some improvements. For example, it could benefit if objects
+ * implement {@link BytesMarshallable} interface the same way as built-in serialization benefit
+ * if objects implement {@link Externalizable}. See {@link BytesMarshallableSerializer} docs for
+ * more information.
+ *
+ * <p>This interface is supposed to be implemented to plug such third-party serialization
+ * re-implementations, as Kryo, fast-serialization, etc.
+ */
+public interface ObjectSerializer extends Serializable {
+ /**
+ * write an object
+ *
+ * @param bytes to write to
+ * @param object object to write
+ * @param expectedClass which will be provided on read, can be null
+ */
+ void writeSerializable(@NotNull Bytes bytes, Object object, Class expectedClass) throws IOException;
+
+ /**
+ * Read an object
+ *
+ * @param bytes to read
+ * @param expectedClass proved when writing, can be null
+ * @param object to populate, can be null
+ * @return object read.
+ * @throws IOException if it not possible to serialize the object
+ * @throws ClassNotFoundException if the expectedClass can not be created
+ */
+ <T> T readSerializable(@NotNull Bytes bytes, Class<T> expectedClass, T object) throws IOException, ClassNotFoundException;
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java b/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java
deleted file mode 100644
index eaf5760..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/RawCopier.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2013 Peter Lawrey
- *
- * 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.lang.io.serialization;
-
-import net.openhft.lang.io.Bytes;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import static net.openhft.lang.io.NativeBytes.UNSAFE;
-
-/**
- * User: peter.lawrey Date: 22/09/13 Time: 16:51
- */
-public class RawCopier<T> {
- final int start, end;
- private final Class<T> tClass;
-
- public RawCopier(Class<T> tClass) {
- this.tClass = tClass;
- List<Field> fields = new ArrayList<Field>();
- addAllFields(fields, tClass);
- Collections.sort(fields, new Comparator<Field>() {
- @Override
- public int compare(Field o1, Field o2) {
- long off1 = UNSAFE.objectFieldOffset(o1);
- long off2 = UNSAFE.objectFieldOffset(o2);
- return Double.compare(off1, off2);
- }
- });
- start = (int) UNSAFE.objectFieldOffset(fields.get(0));
- Field lastField = null;
- for (Field field : fields) {
- if (Modifier.isTransient(field.getModifiers()) || !field.getType().isPrimitive())
- break;
- lastField = field;
- }
- end = (int) UNSAFE.objectFieldOffset(lastField) + sizeOf(lastField.getType());
-
- assert end > start : "end <= start, start: " + start + ", end: " + end;
- }
-
- public static <T> RawCopier<T> copies(Class<T> tClass) {
- return new RawCopier<T>(tClass);
- }
-
- private static int sizeOf(Class<?> type) {
- return UNSAFE.arrayIndexScale(Array.newInstance(type, 0).getClass());
- }
-
- public int start() {
- return start;
- }
-
- public int end() {
- return end;
- }
-
- public void toBytes(Object obj, Bytes bytes) {
- bytes.writeObject(obj, start, end);
- }
-
- public void fromBytes(Bytes bytes, Object obj) {
- bytes.readObject(obj, start, end);
- }
-
- public void copy(T from, T to) {
- long i;
- for (i = start; i < end - 7; i += 8) {
- UNSAFE.putLong(to, i, UNSAFE.getLong(from, i));
- }
- for (; i < end; i++) {
- UNSAFE.putByte(to, i, UNSAFE.getByte(from, i));
- }
- }
-
- private void addAllFields(List<Field> fields, Class tClass) {
- if (tClass != null && tClass != Object.class)
- addAllFields(fields, tClass.getSuperclass());
- for (Field field : tClass.getDeclaredFields()) {
- if (!Modifier.isStatic(field.getModifiers()))
- fields.add(field);
- }
- }
-}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java
deleted file mode 100644
index c6b5b2e..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationFilter.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.openhft.lang.io.serialization.direct;
-
-import java.lang.reflect.Field;
-import java.util.*;
-
-import static net.openhft.lang.io.serialization.direct.FieldMetadata.*;
-
-public final class DirectSerializationFilter {
- public static List<Field> stopAtFirstIneligibleField(List<Field> fields) {
- ArrayList<Field> eligibleFields = new ArrayList<Field>();
- for (Field f : fields) {
- if (checkEligible(f)) {
- eligibleFields.add(f);
- } else {
- break;
- }
- }
-
- return eligibleFields.isEmpty() ?
- Collections.<Field>emptyList() :
- eligibleFields;
- }
-
- private static boolean checkEligible(Field f) {
- return isPrimitive(f) &&
- !isStatic(f) &&
- !isTransient(f);
- }
-} \ No newline at end of file
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java
deleted file mode 100644
index 2fcd50c..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/DirectSerializationMetadata.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package net.openhft.lang.io.serialization.direct;
-
-import net.openhft.lang.Jvm;
-
-import java.lang.reflect.Field;
-import java.util.List;
-
-import static net.openhft.lang.io.NativeBytes.UNSAFE;
-
-public class DirectSerializationMetadata {
- private static final int OBJECT_ALIGNMENT = 8;
- private static final int OBJECT_ALIGNMENT_MASK = OBJECT_ALIGNMENT - 1;
-
- static final long NATIVE_WORD_SIZE = Jvm.is64Bit() ? 8 : 4;
- static final long OOP_SIZE = UNSAFE.arrayIndexScale(Object[].class);
- static final long OBJECT_HEADER_SIZE = NATIVE_WORD_SIZE + OOP_SIZE; // Object header has a native sized mark word + variable sized oop to klass meta object
-
- public static final SerializationMetadata EmptyObjectMetadata = new SerializationMetadata(0, 0);
-
- public static final class SerializationMetadata {
- final long start;
- final long length;
-
- SerializationMetadata(long start, long length) {
- this.start = start;
- this.length = length;
- }
-
- @Override
- public String toString() {
- return String.format("SerializationMetadata: Start %s Length %s", start, length);
- }
- }
-
- public static SerializationMetadata extractMetadata(List<Field> fields) {
- if (fields.isEmpty()) return EmptyObjectMetadata;
-
- Offsets offsets = minMaxOffsets(fields);
-
- long totalSize = OBJECT_HEADER_SIZE + offsets.max - offsets.min + 1;
- return new SerializationMetadata(offsets.min, padToObjectAlignment(totalSize) - OBJECT_HEADER_SIZE);
- }
-
- public static SerializationMetadata extractMetadataForPartialCopy(List<Field> fields) {
- if (fields.isEmpty()) return EmptyObjectMetadata;
-
- Offsets offsets = minMaxOffsets(fields);
-
- Field lastField = fields.get(fields.size() - 1);
-
- return new SerializationMetadata(offsets.min, offsets.max + sizeOf(lastField) - OBJECT_HEADER_SIZE);
- }
-
- private static Offsets minMaxOffsets(List<Field> fields) {
- long minOffset = UNSAFE.objectFieldOffset(fields.get(0));
- long maxOffset = UNSAFE.objectFieldOffset(fields.get(fields.size() - 1));
-
- return new Offsets(minOffset, maxOffset);
- }
-
- static long padToObjectAlignment(long length) {
- if ((length & OBJECT_ALIGNMENT_MASK) != 0) {
- long padding = OBJECT_ALIGNMENT - (length & OBJECT_ALIGNMENT_MASK);
- length += padding;
- }
-
- return length;
- }
-
- private static long sizeOf(Field field) {
- if (boolean.class.equals(field.getType())) return 1;
- else if (byte.class.equals(field.getType())) return 1;
- else if (short.class.equals(field.getType())) return 2;
- else if (char.class.equals(field.getType())) return 2;
- else if (int.class.equals(field.getType())) return 4;
- else if (float.class.equals(field.getType())) return 4;
- else return 8;
- }
-
- private static final class Offsets {
- public final long min;
- public final long max;
-
- private Offsets(long min, long max) {
- this.min = min;
- this.max = max;
- }
- }
-} \ No newline at end of file
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java
deleted file mode 100644
index d186c77..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/FieldMetadata.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package net.openhft.lang.io.serialization.direct;
-
-import java.lang.reflect.*;
-
-public final class FieldMetadata {
- public static boolean isPrimitive(Field f) {
- return f.getType().isPrimitive();
- }
-
- public static boolean isPrimitiveArray(Field f) {
- Class<?> clazz = f.getType();
- return clazz.isArray() && clazz.getComponentType().isPrimitive();
- }
-
- public static boolean isStatic(Field f) {
- return Modifier.isStatic(f.getModifiers());
- }
-
- public static boolean isTransient(Field f) {
- return Modifier.isTransient(f.getModifiers());
- }
-}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java
deleted file mode 100644
index 28ff782..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/Introspect.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2013 Peter Lawrey
- *
- * 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.lang.io.serialization.direct;
-
-import net.openhft.lang.Maths;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import static net.openhft.lang.io.NativeBytes.UNSAFE;
-import static net.openhft.lang.io.serialization.direct.FieldMetadata.isStatic;
-
-public class Introspect {
- public static List<Field> fields(Class<?> clazz) {
- ArrayList<Field> fields = new ArrayList<Field>();
-
- addToFields(clazz, fields);
- Collections.sort(fields, FieldOffsetComparator.Instance);
-
- return fields;
- }
-
- private static List<Field> addToFields(Class<?> clazz, ArrayList<Field> accumulator) {
- Collections.addAll(accumulator, clazz.getDeclaredFields());
- Class<?> maybeSuper = clazz.getSuperclass();
-
- return maybeSuper != null ?
- addToFields(maybeSuper, accumulator) :
- accumulator;
- }
-
- private static final class FieldOffsetComparator implements Comparator<Field> {
- public static final FieldOffsetComparator Instance = new FieldOffsetComparator();
-
- @Override
- public int compare(Field first, Field second) {
- return Maths.compare(offset(first), offset(second));
- }
-
- private static long offset(Field field) {
- return isStatic(field) ?
- UNSAFE.staticFieldOffset(field) :
- UNSAFE.objectFieldOffset(field);
- }
- }
-} \ No newline at end of file
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java
deleted file mode 100644
index 24f8a48..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshaller.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package net.openhft.lang.io.serialization.direct;
-
-import net.openhft.lang.io.Bytes;
-
-import static net.openhft.lang.io.NativeBytes.UNSAFE;
-import static net.openhft.lang.io.serialization.direct.DirectSerializationMetadata.SerializationMetadata;
-
-public final class ObjectMarshaller<T> {
- private final SerializationMetadata metadata;
-
- public ObjectMarshaller(SerializationMetadata metadata) {
- this.metadata = metadata;
- }
-
- public void write(Bytes bytes, T tObject) {
- long i = metadata.start;
- long end = metadata.start + metadata.length;
-
- while (i < end - 7) {
- bytes.writeLong(UNSAFE.getLong(tObject, i));
- i += 8;
- }
-
- while (i < end) {
- bytes.writeByte(UNSAFE.getByte(tObject, i));
- ++i;
- }
- }
-
- public T read(Bytes bytes, T tObject) {
- long i = metadata.start;
- long end = metadata.start + metadata.length;
-
- while (i < end - 7) {
- UNSAFE.putLong(tObject, i, bytes.readLong());
- i += 8;
- }
-
- while (i < end) {
- UNSAFE.putByte(tObject, i, bytes.readByte());
- ++i;
- }
-
- return tObject;
- }
-
- public long length() {
- return metadata.length;
- }
-} \ No newline at end of file
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java b/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java
deleted file mode 100644
index bb9e35d..0000000
--- a/lang/src/main/java/net/openhft/lang/io/serialization/direct/ObjectMarshallers.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package net.openhft.lang.io.serialization.direct;
-
-import net.openhft.lang.io.NativeBytes;
-
-import java.lang.reflect.Field;
-import java.util.*;
-import java.util.logging.Logger;
-
-import static java.util.logging.Level.WARNING;
-import static net.openhft.lang.io.serialization.direct.DirectSerializationMetadata.SerializationMetadata;
-
-public final class ObjectMarshallers {
- private static final Logger Log = Logger.getLogger(ObjectMarshallers.class.getName());
-
- private static final Map<Class, ObjectMarshaller> metadata = new HashMap<Class, ObjectMarshaller>();
-
- @SuppressWarnings("unchecked")
- public static <T> ObjectMarshaller<T> forClass(Class<T> clazz) {
- ObjectMarshaller om = metadata.get(clazz);
- if (om == null) {
- List<Field> fields = Introspect.fields(clazz);
- List<Field> eligibleFields = DirectSerializationFilter.stopAtFirstIneligibleField(fields);
-
- SerializationMetadata serializationMetadata;
-
- if (hasIneligibleFields(fields, eligibleFields)) {
- WarnAboutIneligibleFields.apply(clazz, fields, eligibleFields);
- serializationMetadata = DirectSerializationMetadata.extractMetadataForPartialCopy(eligibleFields);
- } else {
- serializationMetadata = DirectSerializationMetadata.extractMetadata(eligibleFields);
- }
-
- om = new ObjectMarshaller<T>(serializationMetadata);
- Log.log(WARNING, String.format("Class %s has metadata %s", clazz.getName(), serializationMetadata));
- metadata.put(clazz, om);
- }
-
- return (ObjectMarshaller<T>) om;
- }
-
- private static boolean hasIneligibleFields(List<Field> allFields, List<Field> eligibleFields) {
- return allFields.size() != eligibleFields.size();
- }
-
- private static class WarnAboutIneligibleFields {
- static void apply(Class clazz, List<Field> allFields, List<Field> eligibleFields) {
- List<Field> ineligibleFields = allFields.subList(eligibleFields.size(), allFields.size());
- Log.log(WARNING, String.format(
- "The following fields in Class %s will not be copied by ObjectMarshaller:\n%s",
- clazz.getName(),
- commaSeparate(ineligibleFields)
- ));
- }
-
- private static String commaSeparate(Collection<Field> fields) {
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (Field field : fields) {
- if (first) {
- sb.append("\t");
- sb.append(field.getName());
- first = false;
- } else {
- sb.append("\n\t");
- sb.append(field.getName());
- }
- }
-
- return sb.toString();
- }
- }
-}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java
new file mode 100644
index 0000000..70ac6f7
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/AllocateInstanceObjectFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.NativeBytes;
+import net.openhft.lang.io.serialization.ObjectFactory;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Object factory which creates an object by means of {@code Unsafe.allocateInstance()} call,
+ * i. e. without calling constructor.
+ *
+ * @param <E> type of created objects
+ */
+public final class AllocateInstanceObjectFactory<E> implements ObjectFactory<E> {
+ private static final long serialVersionUID = 0L;
+
+ private final Class<E> eClass;
+
+ public AllocateInstanceObjectFactory(Class<E> eClass) {
+ if (eClass.isInterface() || Modifier.isAbstract(eClass.getModifiers()) ||
+ eClass.isEnum()) {
+ throw new IllegalArgumentException(eClass + " should be a non-abstract non-enum class");
+ }
+ this.eClass = eClass;
+ }
+
+ public Class<E> allocatedClass() {
+ return eClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public E create() throws InstantiationException {
+ return (E) NativeBytes.UNSAFE.allocateInstance(eClass);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null && obj.getClass() == getClass() &&
+ ((AllocateInstanceObjectFactory) obj).eClass == eClass;
+ }
+
+ @Override
+ public int hashCode() {
+ return eClass.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{eClass=" + eClass + "}";
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java
new file mode 100644
index 0000000..ac4051f
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferMarshaller.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.nio.ByteBuffer;
+
+public enum ByteBufferMarshaller implements CompactBytesMarshaller<ByteBuffer> {
+ INSTANCE;
+
+ @Override
+ public byte code() {
+ return BYTE_BUFFER_CODE;
+ }
+
+ @Override
+ public void write(Bytes bytes, ByteBuffer byteBuffer) {
+ int position = byteBuffer.position();
+ bytes.writeStopBit(byteBuffer.remaining());
+ bytes.write(byteBuffer);
+
+ // reset the position back as we found it
+ byteBuffer.position(position);
+ }
+
+ @Override
+ public ByteBuffer read(Bytes bytes) {
+ return read(bytes, null);
+ }
+
+ @Override
+ public ByteBuffer read(Bytes bytes, @Nullable ByteBuffer byteBuffer) {
+ long length = bytes.readStopBit();
+ assert length <= Integer.MAX_VALUE;
+ if (length < 0 || length > Integer.MAX_VALUE) {
+ throw new IllegalStateException("Invalid length: " + length);
+ }
+ if (byteBuffer == null || byteBuffer.capacity() < length) {
+ byteBuffer = newByteBuffer((int) length);
+
+ } else {
+ byteBuffer.position(0);
+ byteBuffer.limit((int) length);
+ }
+
+ bytes.read(byteBuffer);
+ byteBuffer.flip();
+ return byteBuffer;
+ }
+
+ protected ByteBuffer newByteBuffer(int length) {
+ return ByteBuffer.allocate(length);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java
new file mode 100644
index 0000000..fe648e9
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ByteBufferZMarshaller.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+public enum ByteBufferZMarshaller implements CompactBytesMarshaller<ByteBuffer> {
+ INSTANCE;
+
+ @Override
+ public byte code() {
+ return BYTE_BUFFER_CODE;
+ }
+
+ @Override
+ public void write(Bytes bytes, ByteBuffer byteBuffer) {
+ bytes.writeStopBit(byteBuffer.remaining());
+ long position = bytes.position();
+ bytes.clear();
+ bytes.position(position + 4);
+ DataOutputStream dos = new DataOutputStream(new DeflaterOutputStream(bytes.outputStream()));
+ try {
+ while (byteBuffer.remaining() >= 8)
+ dos.writeLong(byteBuffer.getLong());
+ while (byteBuffer.remaining() > 0)
+ dos.write(byteBuffer.get());
+ dos.close();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ bytes.writeUnsignedInt(position, bytes.position() - position - 4);
+
+ bytes.write(byteBuffer);
+ }
+
+ @Override
+ public ByteBuffer read(Bytes bytes) {
+ return read(bytes, null);
+ }
+
+ @Override
+ public ByteBuffer read(Bytes bytes, @Nullable ByteBuffer byteBuffer) {
+ long length = bytes.readStopBit();
+ if (length < 0 || length > Integer.MAX_VALUE) {
+ throw new IllegalStateException("Invalid length: " + length);
+ }
+ if (byteBuffer == null || byteBuffer.capacity() < length) {
+ byteBuffer = newByteBuffer((int) length);
+
+ } else {
+ byteBuffer.clear();
+ }
+ byteBuffer.limit((int) length);
+
+ long position = bytes.position();
+ long end = position + length;
+
+ long limit = bytes.limit();
+ bytes.limit(end);
+
+ DataInputStream dis = new DataInputStream(new InflaterInputStream(bytes.inputStream()));
+ try {
+ while (byteBuffer.remaining() >= 8)
+ byteBuffer.putLong(dis.readLong());
+ while (byteBuffer.remaining() >= 0)
+ byteBuffer.put(dis.readByte());
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ bytes.position(end);
+ bytes.limit(limit);
+
+ byteBuffer.flip();
+ return byteBuffer;
+ }
+
+ protected ByteBuffer newByteBuffer(int length) {
+ return ByteBuffer.allocate(length);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java
index ebcb95f..59ea161 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/BytesMarshallableMarshaller.java
@@ -1,31 +1,34 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.NativeBytes;
import net.openhft.lang.io.serialization.BytesMarshallable;
import net.openhft.lang.io.serialization.BytesMarshaller;
-import net.openhft.lang.io.NativeBytes;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
/**
* @author peter.lawrey
*/
-public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements BytesMarshaller<E> {
+public class BytesMarshallableMarshaller<E extends BytesMarshallable>
+ implements BytesMarshaller<E> {
+ private static final long serialVersionUID = 0L;
@NotNull
private final Class<E> classMarshaled;
@@ -33,6 +36,10 @@ public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements
this.classMarshaled = classMarshaled;
}
+ public final Class<E> marshaledClass() {
+ return classMarshaled;
+ }
+
@Override
public void write(@NotNull Bytes bytes, @NotNull E e) {
e.writeMarshallable(bytes);
@@ -40,13 +47,42 @@ public class BytesMarshallableMarshaller<E extends BytesMarshallable> implements
@Override
public E read(@NotNull Bytes bytes) {
- E e;
- try {
- e = (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled);
- } catch (Exception e2) {
- throw new IllegalStateException(e2);
+ return read(bytes, null);
+ }
+
+ @Nullable
+ @Override
+ public E read(Bytes bytes, @Nullable E e) {
+ if (e == null) {
+ try {
+ e = getInstance();
+ } catch (Exception e2) {
+ throw new IllegalStateException(e2);
+ }
}
e.readMarshallable(bytes);
return e;
}
+
+ @SuppressWarnings("unchecked")
+ @NotNull
+ protected E getInstance() throws Exception {
+ return (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null && obj.getClass() == getClass() &&
+ ((BytesMarshallableMarshaller) obj).classMarshaled == classMarshaled;
+ }
+
+ @Override
+ public int hashCode() {
+ return classMarshaled.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{classMarshaled=" + classMarshaled + "}";
+ }
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java
index db36c37..92be9bd 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ClassMarshaller.java
@@ -1,17 +1,17 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
@@ -19,8 +19,9 @@ package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.Compare;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.CompactBytesMarshaller;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
+import net.openhft.lang.pool.StringInterner;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;
@@ -32,7 +33,8 @@ import java.util.Map;
/**
* @author peter.lawrey
*/
-public class ClassMarshaller implements CompactBytesMarshaller<Class> {
+public class ClassMarshaller extends ImmutableMarshaller<Class>
+ implements CompactBytesMarshaller<Class> {
private static final int CACHE_SIZE = 1019;
private static final Map<String, Class> SC_SHORT_NAME = new LinkedHashMap<String, Class>();
private static final Map<Class, String> CS_SHORT_NAME = new LinkedHashMap<Class, String>();
@@ -48,7 +50,7 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> {
}
private final ClassLoader classLoader;
- private final StringBuilder className = new StringBuilder(40);
+ private static final StringBuilderPool sbp = new StringBuilderPool();
@Nullable
@SuppressWarnings("unchecked")
private WeakReference<Class>[] classWeakReference = null;
@@ -68,9 +70,9 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> {
@Nullable
@Override
public Class read(@NotNull Bytes bytes) {
- className.setLength(0);
- bytes.readUTFΔ(className);
- return load(className);
+ StringBuilder sb = sbp.acquireStringBuilder();
+ bytes.readUTFΔ(sb);
+ return load(sb);
}
@Nullable
@@ -82,15 +84,16 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> {
WeakReference<Class> ref = classWeakReference[hash];
if (ref != null) {
Class clazz = ref.get();
- if (clazz != null && clazz.getName().equals(name))
+ if (clazz != null && StringInterner.isEqual(clazz.getName(), name))
return clazz;
}
try {
- Class<?> clazz = SC_SHORT_NAME.get(name.toString());
+ String className = name.toString();
+ Class<?> clazz = SC_SHORT_NAME.get(className);
if (clazz != null)
return clazz;
- clazz = classLoader.loadClass(name.toString());
+ clazz = classLoader.loadClass(className);
classWeakReference[hash] = new WeakReference<Class>(clazz);
return clazz;
} catch (ClassNotFoundException e) {
@@ -100,6 +103,7 @@ public class ClassMarshaller implements CompactBytesMarshaller<Class> {
@Override
public byte code() {
- return 'C' & 31; // control C
+ return CLASS_CODE; // control C
}
}
+ \ No newline at end of file
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java
new file mode 100644
index 0000000..dbf8f53
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CollectionMarshaller.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.util.Collection;
+
+abstract class CollectionMarshaller<E, C extends Collection<E>> {
+
+ public static final int NULL_LENGTH = -1;
+ final BytesMarshaller<E> eBytesMarshaller;
+
+ protected CollectionMarshaller(BytesMarshaller<E> eBytesMarshaller) {
+ this.eBytesMarshaller = eBytesMarshaller;
+ }
+
+ public void write(Bytes bytes, C c) {
+ if (c == null) {
+ bytes.writeStopBit(NULL_LENGTH);
+ return;
+ }
+ bytes.writeStopBit(c.size());
+ for (E e : c) {
+ eBytesMarshaller.write(bytes, e);
+ }
+ }
+
+ public C read(Bytes bytes) {
+ return read(bytes, null);
+ }
+
+ abstract C newCollection();
+
+ public C read(Bytes bytes, @Nullable C c) {
+ long length = bytes.readStopBit();
+
+ if (length == 0 && c != null) {
+ c.clear();
+ return c;
+ }
+
+ if (length < NULL_LENGTH || length > Integer.MAX_VALUE)
+ throw new IllegalStateException("Invalid length: " + length);
+
+ if (length == NULL_LENGTH)
+ return null;
+
+ if (c == null)
+ c = newCollection();
+
+ return readCollection(bytes, c, (int) length);
+ }
+
+ abstract C readCollection(Bytes bytes, C c, int length);
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java
index 08f66e4..676c86c 100644..100755
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/CompactEnumBytesMarshaller.java
@@ -1,26 +1,26 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.serialization.CompactBytesMarshaller;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
/**
- * Created with IntelliJ IDEA. User: peter Date: 09/12/13 Time: 17:05 To change this template use File | Settings | File
+ * Created with IntelliJ IDEA. User: peter.lawrey Date: 09/12/13 Time: 17:05
* Templates.
*/
public class CompactEnumBytesMarshaller<E> extends GenericEnumMarshaller<E> implements CompactBytesMarshaller<E> {
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java
index 03f6bb1..1e949c5 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/DateMarshaller.java
@@ -1,25 +1,25 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.CompactBytesMarshaller;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
import java.util.Date;
@@ -27,8 +27,8 @@ import java.util.Date;
* @author peter.lawrey
*/
public class DateMarshaller implements CompactBytesMarshaller<Date> {
- final int size1;
- private final StringBuilder sb = new StringBuilder();
+ private final int size1;
+ private static final StringBuilderPool sbp = new StringBuilderPool();
@Nullable
private Date[] interner = null;
@@ -65,12 +65,25 @@ public class DateMarshaller implements CompactBytesMarshaller<Date> {
@Nullable
@Override
public Date read(@NotNull Bytes bytes) {
+ StringBuilder sb = sbp.acquireStringBuilder();
bytes.readUTFΔ(sb);
long time = parseLong(sb);
return lookupDate(time);
}
@Nullable
+ @Override
+ public Date read(Bytes bytes, @Nullable Date date) {
+ if (date == null)
+ return read(bytes);
+ StringBuilder sb = sbp.acquireStringBuilder();
+ bytes.readUTFΔ(sb);
+ long time = parseLong(sb);
+ date.setTime(time);
+ return date;
+ }
+
+ @Nullable
private Date lookupDate(long time) {
int idx = hashFor(time);
if (interner == null)
@@ -90,6 +103,6 @@ public class DateMarshaller implements CompactBytesMarshaller<Date> {
@Override
public byte code() {
- return 'T' & 31; // Control T.
+ return DATE_CODE; // Control T.
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java
index 11f060b..189f7f5 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/EnumBytesMarshaller.java
@@ -1,25 +1,25 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.BytesMarshaller;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
import java.util.BitSet;
import java.util.LinkedHashMap;
@@ -28,19 +28,17 @@ import java.util.Map;
/**
* @author peter.lawrey
*/
-public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E> {
- @NotNull
- private final Class<E> classMarshaled;
+public class EnumBytesMarshaller<E extends Enum<E>> extends ImmutableMarshaller<E>
+ implements BytesMarshaller<E> {
@SuppressWarnings("unchecked")
private final E[] interner = (E[]) new Enum[1024];
private final BitSet internerDup = new BitSet(1024);
- private final Map<String, E> map = new LinkedHashMap<String, E>();
+ private final Map<String, E> map = new LinkedHashMap<String, E>(64);
private final E defaultValue;
private final int mask;
- private final StringBuilder reader = new StringBuilder();
+ private static final StringBuilderPool sbp = new StringBuilderPool();
public EnumBytesMarshaller(@NotNull Class<E> classMarshaled, E defaultValue) {
- this.classMarshaled = classMarshaled;
this.defaultValue = defaultValue;
mask = interner.length - 1;
@@ -52,6 +50,7 @@ public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E
//noinspection UnqualifiedFieldAccess,AssignmentToNull
interner[idx] = null;
internerDup.set(idx);
+
} else {
interner[idx] = e;
}
@@ -77,17 +76,18 @@ public class EnumBytesMarshaller<E extends Enum<E>> implements BytesMarshaller<E
@Override
public E read(@NotNull Bytes bytes) {
- bytes.readUTFΔ(reader);
- return builderToEnum();
+ StringBuilder sb = sbp.acquireStringBuilder();
+ bytes.readUTFΔ(sb);
+ return builderToEnum(sb);
}
- private E builderToEnum() {
- int num = hashFor(reader);
+ private E builderToEnum(StringBuilder sb) {
+ int num = hashFor(sb);
int idx = num & mask;
E e = interner[idx];
if (e != null) return e;
if (!internerDup.get(idx)) return defaultValue;
- e = map.get(reader.toString());
+ e = map.get(sb.toString());
return e == null ? defaultValue : e;
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java
index 05f5390..4e9b82c 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ExternalizableMarshaller.java
@@ -1,25 +1,26 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
-import net.openhft.lang.io.serialization.BytesMarshaller;
import net.openhft.lang.io.NativeBytes;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
import java.io.Externalizable;
import java.io.IOException;
@@ -28,6 +29,8 @@ import java.io.IOException;
* @author peter.lawrey
*/
public class ExternalizableMarshaller<E extends Externalizable> implements BytesMarshaller<E> {
+ private static final long serialVersionUID = 0L;
+
@NotNull
private final Class<E> classMarshaled;
@@ -35,6 +38,10 @@ public class ExternalizableMarshaller<E extends Externalizable> implements Bytes
this.classMarshaled = classMarshaled;
}
+ public final Class<E> marshaledClass() {
+ return classMarshaled;
+ }
+
@Override
public void write(Bytes bytes, @NotNull E e) {
try {
@@ -46,13 +53,41 @@ public class ExternalizableMarshaller<E extends Externalizable> implements Bytes
@Override
public E read(Bytes bytes) {
- E e;
+ return read(bytes, null);
+ }
+
+ @Nullable
+ @Override
+ public E read(Bytes bytes, @Nullable E e) {
try {
- e = (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled);
+ if (e == null)
+ e = getInstance();
e.readExternal(bytes);
+ return e;
} catch (Exception e2) {
throw new IllegalStateException(e2);
}
- return e;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NotNull
+ protected E getInstance() throws Exception {
+ return (E) NativeBytes.UNSAFE.allocateInstance(classMarshaled);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null && obj.getClass() == getClass() &&
+ ((ExternalizableMarshaller) obj).classMarshaled == classMarshaled;
+ }
+
+ @Override
+ public int hashCode() {
+ return classMarshaled.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{marshaledClass=" + classMarshaled + "}";
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java
index 8a1b52d..8ed4590 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/GenericEnumMarshaller.java
@@ -1,25 +1,25 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.BytesMarshaller;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -30,17 +30,20 @@ import java.util.Map;
* @author peter.lawrey
*/
public class GenericEnumMarshaller<E> implements BytesMarshaller<E> {
- @NotNull
- private final Class<E> classMarshaled;
+ private final int capacity;
@Nullable
- private final Constructor<E> constructor;
+ private transient final Constructor<E> constructor;
@Nullable
- private final Method valueOf;
+ private transient final Method valueOf;
@NotNull
private final Map<String, E> map;
+ //used by the read resolve method
+ private final Class<E> classMarshaled;
+
public GenericEnumMarshaller(@NotNull Class<E> classMarshaled, final int capacity) {
this.classMarshaled = classMarshaled;
+ this.capacity = capacity;
Constructor<E> constructor = null;
Method valueOf = null;
try {
@@ -48,6 +51,7 @@ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> {
} catch (NoSuchMethodException e) {
try {
constructor = classMarshaled.getConstructor(String.class);
+ constructor.setAccessible(true);
} catch (NoSuchMethodException e1) {
throw new IllegalArgumentException(classMarshaled + " doesn't have a valueOf(String) or a Constructor(String)");
}
@@ -62,6 +66,10 @@ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> {
};
}
+ private Object readResolve() {
+ return new GenericEnumMarshaller(classMarshaled, capacity);
+ }
+
@Override
public void write(@NotNull Bytes bytes, @Nullable E e) {
bytes.writeUTFΔ(e == null ? null : e.toString());
@@ -74,19 +82,26 @@ public class GenericEnumMarshaller<E> implements BytesMarshaller<E> {
return s == null ? null : valueOf(s);
}
+ @Nullable
+ @Override
+ public E read(Bytes bytes, @Nullable E e) {
+ return read(bytes);
+ }
+
private E valueOf(String s) {
E e = map.get(s);
if (e == null)
try {
if (constructor != null) {
map.put(s, e = constructor.newInstance(s));
+
} else {
@SuppressWarnings("unchecked")
E invoke = (E) valueOf.invoke(null, s);
map.put(s, e = invoke);
}
} catch (Exception t) {
- throw new AssertionError(t.getCause());
+ throw new AssertionError(t);
}
return e;
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java
new file mode 100644
index 0000000..de55a9c
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ImmutableMarshaller.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+
+abstract class ImmutableMarshaller<E> implements BytesMarshaller<E> {
+ @Nullable
+ @Override
+ public final E read(Bytes bytes, @Nullable E e) {
+ return read(bytes);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java
new file mode 100644
index 0000000..cc9b592
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/ListMarshaller.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListMarshaller<E> extends CollectionMarshaller<E, List<E>> implements CompactBytesMarshaller<List<E>> {
+
+ ListMarshaller(BytesMarshaller<E> eBytesMarshaller) {
+ super(eBytesMarshaller);
+ }
+
+ public static <E> BytesMarshaller<List<E>> of(BytesMarshaller<E> eBytesMarshaller) {
+ return new ListMarshaller<E>(eBytesMarshaller);
+ }
+
+ @Override
+ public byte code() {
+ return LIST_CODE;
+ }
+
+ @Override
+ List<E> newCollection() {
+ return new ArrayList<E>();
+ }
+
+ @Override
+ List<E> readCollection(Bytes bytes, List<E> es, int length) {
+ List<E> ret = es;
+ ret.clear();
+
+ for (int i = 0; i < length; i++) {
+ ret.add(eBytesMarshaller.read(bytes));
+ }
+
+ return ret;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java
new file mode 100755
index 0000000..195d96d
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/MapMarshaller.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Created by peter.lawrey on 24/10/14.
+ */
+public class MapMarshaller<K, V> implements CompactBytesMarshaller<Map<K, V>> {
+ private final BytesMarshaller<K> kBytesMarshaller;
+ private final BytesMarshaller<V> vBytesMarshaller;
+
+ MapMarshaller(BytesMarshaller<K> kBytesMarshaller, BytesMarshaller<V> vBytesMarshaller) {
+ this.kBytesMarshaller = kBytesMarshaller;
+ this.vBytesMarshaller = vBytesMarshaller;
+ }
+
+ @Override
+ public byte code() {
+ return MAP_CODE;
+ }
+
+ @Override
+ public void write(Bytes bytes, Map<K, V> kvMap) {
+ bytes.writeInt(kvMap.size());
+ for (Map.Entry<K, V> entry : kvMap.entrySet()) {
+ kBytesMarshaller.write(bytes, entry.getKey());
+ vBytesMarshaller.write(bytes, entry.getValue());
+ }
+ }
+
+ @Override
+ public Map<K, V> read(Bytes bytes) {
+ return read(bytes, null);
+ }
+
+ @Override
+ public Map<K, V> read(Bytes bytes, @Nullable Map<K, V> kvMap) {
+ if (kvMap == null) {
+ kvMap = new LinkedHashMap<K, V>();
+
+ } else {
+ kvMap.clear();
+ }
+ int size = bytes.readInt();
+ for (int i = 0; i < size; i++)
+ kvMap.put(kBytesMarshaller.read(bytes), vBytesMarshaller.read(bytes));
+ return kvMap;
+ }
+
+ public static <K, V> BytesMarshaller<Map<K, V>> of(BytesMarshaller<K> keyMarshaller, BytesMarshaller<V> valueMarshaller) {
+ return new MapMarshaller<K, V>(keyMarshaller, valueMarshaller);
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java
new file mode 100644
index 0000000..0fba720
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NewInstanceObjectFactory.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.serialization.ObjectFactory;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Object factory which creates an object by means of {@link Class#newInstance()} call,
+ * i. e. with calling the default no-arg constructor of the class.
+ *
+ * @param <E> type of created objects
+ */
+public final class NewInstanceObjectFactory<E> implements ObjectFactory<E> {
+ private static final long serialVersionUID = 0L;
+
+ private final Class<E> eClass;
+
+ public NewInstanceObjectFactory(Class<E> eClass) {
+ if (eClass.isInterface() || Modifier.isAbstract(eClass.getModifiers()) ||
+ eClass.isEnum()) {
+ throw new IllegalArgumentException(eClass + " should be a non-abstract non-enum class");
+ }
+ this.eClass = eClass;
+ }
+
+ @Override
+ public E create() throws IllegalAccessException, InstantiationException {
+ return eClass.newInstance();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null && obj.getClass() == getClass() &&
+ ((NewInstanceObjectFactory) obj).eClass == eClass;
+ }
+
+ @Override
+ public int hashCode() {
+ return eClass.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{eClass=" + eClass + "}";
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java
index 1369c4a..c76054c 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoMarshaller.java
@@ -1,23 +1,24 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
/**
* Created with IntelliJ IDEA. User: peter.lawrey Date: 19/09/13 Time: 18:26 To change this template use File | Settings | File
@@ -35,4 +36,10 @@ public enum NoMarshaller implements BytesMarshaller<Void> {
public Void read(Bytes bytes) {
throw new UnsupportedOperationException();
}
+
+ @Nullable
+ @Override
+ public Void read(Bytes bytes, @Nullable Void aVoid) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java
new file mode 100644
index 0000000..c3e061f
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NoObjectFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.serialization.ObjectFactory;
+
+/**
+ * Placeholder object factory which always throws {@code UnsupportedOperationException}.
+ */
+public enum NoObjectFactory implements ObjectFactory {
+ INSTANCE;
+
+ /**
+ * Always throws {@code UnsupportedOperationException}.
+ *
+ * @return nothing
+ * @throws UnsupportedOperationException always
+ */
+ @Override
+ public Object create() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java
new file mode 100644
index 0000000..af76bc7
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/NullObjectFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.serialization.ObjectFactory;
+
+/**
+ * Object factory which always returns {@code null}.
+ */
+public enum NullObjectFactory implements ObjectFactory {
+ INSTANCE;
+
+ public static <E> ObjectFactory<E> of() {
+ return INSTANCE;
+ }
+
+ /**
+ * Always returns {@code null}.
+ *
+ * @return {@code null}
+ */
+ @Override
+ public Object create() {
+ return null;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java
new file mode 100644
index 0000000..e401722
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SetMarshaller.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.BytesMarshaller;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class SetMarshaller<E> extends CollectionMarshaller<E, Set<E>> implements CompactBytesMarshaller<Set<E>> {
+ SetMarshaller(BytesMarshaller<E> eBytesMarshaller) {
+ super(eBytesMarshaller);
+ }
+
+ public static <E> BytesMarshaller<Set<E>> of(BytesMarshaller<E> eBytesMarshaller) {
+ return new SetMarshaller<E>(eBytesMarshaller);
+ }
+
+ @Override
+ public byte code() {
+ return SET_CODE;
+ }
+
+ @Override
+ Set<E> newCollection() {
+ return new LinkedHashSet<E>();
+ }
+
+ @Override
+ Set<E> readCollection(Bytes bytes, Set<E> es, int length) {
+ es.clear();
+
+ for (int i = 0; i < length; i++) {
+ es.add(eBytesMarshaller.read(bytes));
+ }
+
+ return es;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java
new file mode 100755
index 0000000..14ff64f
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/SnappyStringMarshaller.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.ByteBufferBytes;
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+import org.xerial.snappy.Snappy;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+
+/**
+ * Created by peter.lawrey on 24/10/14.
+ */
+public enum SnappyStringMarshaller implements CompactBytesMarshaller<CharSequence> {
+ INSTANCE;
+ private static final StringFactory STRING_FACTORY = getStringFactory();
+
+ private static final int NULL_LENGTH = -1;
+
+ private static StringFactory getStringFactory() {
+ try {
+ return new StringFactory17();
+ } catch (Exception e) {
+ // do nothing
+ }
+
+ try {
+ return new StringFactory16();
+ } catch (Exception e) {
+ // no more alternatives
+ throw new AssertionError(e);
+ }
+ }
+
+ private static final ThreadLocal<ThreadLocals> THREAD_LOCALS = new ThreadLocal<ThreadLocals>();
+
+ static class ThreadLocals {
+ ByteBuffer decompressedByteBuffer = ByteBuffer.allocateDirect(32 * 1024);
+ Bytes decompressedBytes = ByteBufferBytes.wrap(decompressedByteBuffer);
+ ByteBuffer compressedByteBuffer = ByteBuffer.allocateDirect(0);
+
+ public void clear() {
+ decompressedByteBuffer.clear();
+ decompressedBytes.clear();
+ compressedByteBuffer.clear();
+ }
+ }
+
+ @Override
+ public byte code() {
+ return STRINGZ_CODE;
+ }
+
+ public ThreadLocals acquireThreadLocals() {
+ ThreadLocals threadLocals = THREAD_LOCALS.get();
+ if (threadLocals == null)
+ THREAD_LOCALS.set(threadLocals = new ThreadLocals());
+ threadLocals.clear();
+ return threadLocals;
+ }
+
+ @Override
+ public void write(Bytes bytes, CharSequence s) {
+ if (s == null) {
+ bytes.writeStopBit(NULL_LENGTH);
+ return;
+
+ } else if (s.length() == 0) {
+ bytes.writeStopBit(0);
+ return;
+ }
+ // write the total length.
+ int length = s.length();
+ bytes.writeStopBit(length);
+
+ ThreadLocals threadLocals = acquireThreadLocals();
+ // stream the portions of the string.
+ Bytes db = threadLocals.decompressedBytes;
+ ByteBuffer dbb = threadLocals.decompressedByteBuffer;
+ ByteBuffer cbb = bytes.sliceAsByteBuffer(threadLocals.compressedByteBuffer);
+
+ int position = 0;
+ while (position < length) {
+ // 3 is the longest encoding.
+ while (position < length && db.remaining() >= 3)
+ db.writeStopBit(s.charAt(position++));
+ dbb.position(0);
+ dbb.limit((int) db.position());
+ // portion copied now compress it.
+ int portionLengthPos = cbb.position();
+ cbb.putShort((short) 0);
+ int compressedLength;
+ try {
+ Snappy.compress(dbb, cbb);
+ compressedLength = cbb.remaining();
+ if (compressedLength >= 1 << 16)
+ throw new AssertionError();
+ // unflip.
+ cbb.position(cbb.limit());
+ cbb.limit(cbb.capacity());
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ cbb.putShort(portionLengthPos, (short) compressedLength);
+ db.clear();
+ }
+ // the end.
+ cbb.putShort((short) 0);
+ bytes.position(bytes.position() + cbb.position());
+ }
+
+ @Override
+ public String read(Bytes bytes) {
+ return read(bytes, null);
+ }
+
+ @Override
+ public String read(Bytes bytes, @Nullable CharSequence ignored) {
+ long size = bytes.readStopBit();
+ if (size == NULL_LENGTH)
+ return null;
+ if (size < 0 || size > Integer.MAX_VALUE)
+ throw new IllegalStateException("Invalid length: " + size);
+ if (size == 0)
+ return "";
+ ThreadLocals threadLocals = acquireThreadLocals();
+ // stream the portions of the string.
+ Bytes db = threadLocals.decompressedBytes;
+ ByteBuffer dbb = threadLocals.decompressedByteBuffer;
+ ByteBuffer cbb = bytes.sliceAsByteBuffer(threadLocals.compressedByteBuffer);
+
+ char[] chars = new char[(int) size];
+ int pos = 0;
+ for (int chunkLen; (chunkLen = cbb.getShort() & 0xFFFF) > 0; ) {
+ cbb.limit(cbb.position() + chunkLen);
+ dbb.clear();
+ try {
+ Snappy.uncompress(cbb, dbb);
+ cbb.position(cbb.limit());
+ cbb.limit(cbb.capacity());
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ db.position(0);
+ db.limit(dbb.limit());
+ while (db.remaining() > 0)
+ chars[pos++] = (char) db.readStopBit();
+ }
+ bytes.position(bytes.position() + cbb.position());
+ try {
+ return STRING_FACTORY.fromChars(chars);
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static abstract class StringFactory {
+ abstract String fromChars(char[] chars) throws IllegalAccessException, InvocationTargetException, InstantiationException;
+ }
+
+ private static final class StringFactory16 extends StringFactory {
+ private final Constructor<String> constructor;
+
+ private StringFactory16() throws NoSuchMethodException {
+ constructor = String.class.getDeclaredConstructor(int.class,
+ int.class, char[].class);
+ constructor.setAccessible(true);
+ }
+
+ @Override
+ String fromChars(char[] chars) throws IllegalAccessException, InvocationTargetException, InstantiationException {
+ return constructor.newInstance(0, chars.length, chars);
+ }
+ }
+
+ private static final class StringFactory17 extends StringFactory {
+ private final Constructor<String> constructor;
+
+ private StringFactory17() throws NoSuchMethodException {
+ constructor = String.class.getDeclaredConstructor(char[].class, boolean.class);
+ constructor.setAccessible(true);
+ }
+
+ @Override
+ String fromChars(char[] chars) throws IllegalAccessException, InvocationTargetException, InstantiationException {
+ return constructor.newInstance(chars, true);
+ }
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java
new file mode 100755
index 0000000..d7ca469
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringBuilderPool.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+/**
+ * Created by peter.lawrey on 29/10/14.
+ */
+public class StringBuilderPool {
+ final ThreadLocal<StringBuilder> sbtl = new ThreadLocal<StringBuilder>();
+
+ public StringBuilder acquireStringBuilder() {
+ StringBuilder sb = sbtl.get();
+ if (sb == null) {
+ sbtl.set(sb = new StringBuilder(128));
+ }
+ sb.setLength(0);
+ return sb;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java
index 2fb2ee5..67843ae 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringMarshaller.java
@@ -1,34 +1,35 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
import net.openhft.lang.io.Bytes;
import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+import net.openhft.lang.model.constraints.NotNull;
+import net.openhft.lang.model.constraints.Nullable;
import net.openhft.lang.pool.StringInterner;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/**
* @author peter.lawrey
*/
-public class StringMarshaller implements CompactBytesMarshaller<String> {
+public class StringMarshaller extends ImmutableMarshaller<String>
+ implements CompactBytesMarshaller<String> {
private final int size;
- private final StringBuilder reader = new StringBuilder();
- private StringInterner interner;
+ private static final StringBuilderPool sbp = new StringBuilderPool();
+ private transient StringInterner interner;
public StringMarshaller(int size) {
this.size = size;
@@ -42,19 +43,22 @@ public class StringMarshaller implements CompactBytesMarshaller<String> {
@Nullable
@Override
public String read(@NotNull Bytes bytes) {
- if (bytes.readUTFΔ(reader))
- return builderToString();
+ StringBuilder sb = sbp.acquireStringBuilder();
+ if (bytes.readUTFΔ(sb))
+ return builderToString(sb);
return null;
}
-
- private String builderToString() {
- if (interner == null)
+ private String builderToString(StringBuilder reader) {
+ if (interner == null) {
+ if (size == 0)
+ return reader.toString();
interner = new StringInterner(size);
+ }
return interner.intern(reader);
}
public byte code() {
- return 'S' & 31;
+ return STRING_CODE;
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java
new file mode 100755
index 0000000..efec05e
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/StringZMapMarshaller.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.serialization.impl;
+
+import net.openhft.lang.io.Bytes;
+import net.openhft.lang.io.serialization.CompactBytesMarshaller;
+import net.openhft.lang.model.constraints.Nullable;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+/**
+ * Created by peter.lawrey on 24/10/14.
+ */
+public enum StringZMapMarshaller implements CompactBytesMarshaller<Map<String, String>> {
+ FAST(Deflater.BEST_SPEED),
+ COMPACT(Deflater.BEST_COMPRESSION),
+ INSTANCE(Deflater.DEFAULT_STRATEGY);
+
+ private final int level;
+
+ private static final long NULL_SIZE = -1;
+
+ StringZMapMarshaller(int level) {
+ this.level = level;
+ }
+
+ @Override
+ public byte code() {
+ return STRINGZ_MAP_CODE;
+ }
+
+ @Override
+ public void write(Bytes bytes, Map<String, String> kvMap) {
+ if (kvMap == null) {
+ bytes.writeStopBit(NULL_SIZE);
+ return;
+ }
+
+ bytes.writeStopBit(kvMap.size());
+ long position = bytes.position();
+ bytes.clear();
+ bytes.position(position + 4);
+ DataOutputStream dos = new DataOutputStream(
+ new DeflaterOutputStream(bytes.outputStream(), new Deflater(level)));
+ try {
+ for (Map.Entry<String, String> entry : kvMap.entrySet()) {
+ dos.writeUTF(entry.getKey());
+ dos.writeUTF(entry.getValue());
+ }
+ dos.close();
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ bytes.writeUnsignedInt(position, bytes.position() - position - 4);
+ }
+
+ @Override
+ public Map<String, String> read(Bytes bytes) {
+ return read(bytes, null);
+ }
+
+ @Override
+ public Map<String, String> read(Bytes bytes, @Nullable Map<String, String> kvMap) {
+ long size = bytes.readStopBit();
+ if (size == NULL_SIZE)
+ return null;
+ if (size < 0 || size > Integer.MAX_VALUE)
+ throw new IllegalStateException("Invalid length: " + size);
+
+ long length = bytes.readUnsignedInt();
+ if (length < 0 || length > Integer.MAX_VALUE)
+ throw new IllegalStateException(new StreamCorruptedException());
+ long position = bytes.position();
+ long end = position + length;
+
+ long limit = bytes.limit();
+ bytes.limit(end);
+
+ DataInputStream dis = new DataInputStream(new InflaterInputStream(bytes.inputStream()));
+ if (kvMap == null) {
+ kvMap = new LinkedHashMap<String, String>();
+
+ } else {
+ kvMap.clear();
+ }
+ try {
+ for (int i = 0; i < size; i++) {
+ String key = dis.readUTF();
+ String value = dis.readUTF();
+ kvMap.put(key, value);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ bytes.position(end);
+ bytes.limit(limit);
+ return kvMap;
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java b/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java
index 6fc66ee..3e8d94a 100644
--- a/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java
+++ b/lang/src/main/java/net/openhft/lang/io/serialization/impl/VanillaBytesMarshallerFactory.java
@@ -1,17 +1,17 @@
/*
- * Copyright 2013 Peter Lawrey
+ * Copyright (C) 2015 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
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.lang.io.serialization.impl;
@@ -20,63 +20,92 @@ import net.openhft.lang.io.serialization.BytesMarshallable;
import net.openhft.lang.io.serialization.BytesMarshaller;
import net.openhft.lang.io.serialization.BytesMarshallerFactory;
import net.openhft.lang.io.serialization.CompactBytesMarshaller;
-import org.jetbrains.annotations.NotNull;
+import net.openhft.lang.model.constraints.NotNull;
import java.io.Externalizable;
+import java.nio.ByteBuffer;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
+import static net.openhft.lang.io.serialization.CompactBytesMarshaller.*;
+
/**
* @author peter.lawrey
*/
-public class VanillaBytesMarshallerFactory implements BytesMarshallerFactory {
+public final class VanillaBytesMarshallerFactory implements BytesMarshallerFactory {
+ private static final long serialVersionUID = 1L;
+
+ private transient Map<Class<?>, BytesMarshaller<?>> marshallerMap;
+ private transient BytesMarshaller<?>[] compactMarshallerMap;
- private final Map<Class, BytesMarshaller> marshallerMap = new LinkedHashMap<Class, BytesMarshaller>();
- private final BytesMarshaller[] compactMarshallerMap = new BytesMarshaller[256];
+ private void init() {
+ marshallerMap = new LinkedHashMap<Class<?>, BytesMarshaller<?>>();
+ compactMarshallerMap = new BytesMarshaller[256];
- // private final Map<Class, BytesMarshaller> marshallerTextMap = new LinkedHashMap<Class, BytesMarshaller>();
- {
- BytesMarshaller stringMarshaller = new StringMarshaller(16 * 1024);
+ BytesMarshaller<String> stringMarshaller = new StringMarshaller(16 * 1024);
addMarshaller(String.class, stringMarshaller);
- addMarshaller(CharSequence.class, stringMarshaller);
+ addMarshaller(CharSequence.class, (BytesMarshaller)stringMarshaller);
addMarshaller(Class.class, new ClassMarshaller(Thread.currentThread().getContextClassLoader()));
addMarshaller(Date.class, new DateMarshaller(10191));
- addMarshaller(Integer.class, new CompactEnumBytesMarshaller<Integer>(Integer.class, 10191, (byte) ('I' & 31)));
- addMarshaller(Long.class, new CompactEnumBytesMarshaller<Long>(Long.class, 10191, (byte) ('L' & 31)));
- addMarshaller(Double.class, new CompactEnumBytesMarshaller<Double>(Double.class, 10191, (byte) ('D' & 31)));
+ addMarshaller(Integer.class, new CompactEnumBytesMarshaller<Integer>(Integer.class, 10191, INT_CODE));
+ addMarshaller(Long.class, new CompactEnumBytesMarshaller<Long>(Long.class, 10191, LONG_CODE));
+ addMarshaller(Double.class, new CompactEnumBytesMarshaller<Double>(Double.class, 10191, DOUBLE_CODE));
+ addMarshaller(ByteBuffer.class, ByteBufferMarshaller.INSTANCE);
}
@NotNull
@SuppressWarnings("unchecked")
@Override
public <E> BytesMarshaller<E> acquireMarshaller(@NotNull Class<E> eClass, boolean create) {
+ if (marshallerMap == null) {
+ init();
+ }
+
BytesMarshaller em = marshallerMap.get(eClass);
- if (em == null)
- if (eClass.isEnum())
+ if (em == null) {
+ if (eClass.isEnum()) {
marshallerMap.put(eClass, em = new EnumBytesMarshaller(eClass, null));
- else if (BytesMarshallable.class.isAssignableFrom(eClass))
+
+ } else if (BytesMarshallable.class.isAssignableFrom(eClass)) {
marshallerMap.put(eClass, em = new BytesMarshallableMarshaller((Class) eClass));
- else if (Externalizable.class.isAssignableFrom(eClass))
+
+ } else if (Externalizable.class.isAssignableFrom(eClass)) {
marshallerMap.put(eClass, em = new ExternalizableMarshaller((Class) eClass));
- else {
+
+ } else if (Throwable.class.isAssignableFrom(eClass)) {
+ marshallerMap.put(eClass, em = NoMarshaller.INSTANCE);
+
+ } else {
try {
marshallerMap.put(eClass, em = new GenericEnumMarshaller<E>(eClass, 1000));
} catch (Exception e) {
marshallerMap.put(eClass, em = NoMarshaller.INSTANCE);
}
}
+ }
+
return em;
}
@Override
+ @SuppressWarnings("unchecked")
public <E> BytesMarshaller<E> getMarshaller(byte code) {
- return compactMarshallerMap[code & 0xFF];
+ if (marshallerMap == null) {
+ init();
+ }
+
+ return (BytesMarshaller<E>) compactMarshallerMap[code & 0xFF];
}
public <E> void addMarshaller(Class<E> eClass, BytesMarshaller<E> marshaller) {
+ if (marshallerMap == null) {
+ init();
+ }
+
marshallerMap.put(eClass, marshaller);
- if (marshaller instanceof CompactBytesMarshaller)
+ if (marshaller instanceof CompactBytesMarshaller) {
compactMarshallerMap[((CompactBytesMarshaller) marshaller).code()] = marshaller;
+ }
}
}
diff --git a/lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java b/lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java
new file mode 100644
index 0000000..4896026
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/view/BytesInputStream.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.view;
+
+import net.openhft.lang.io.Bytes;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.InputStream;
+
+/**
+ * {@code InputStream} view of {@link Bytes}. Reading bytes from this stream pushes {@linkplain
+ * Bytes#position() position} of the underlying {@code Bytes}. When {@linkplain Bytes#limit() limit}
+ * is reached, {@code BytesInputStream} behaves as there is no more input.
+ *
+ * <p>This {@code InputStream} implementation supports {@link #mark(int)} and {@link #reset()}
+ * methods.
+ *
+ * <p>{@code BytesInputStream} objects are reusable, see {@link #bytes(Bytes)} method.
+ *
+ * @see Bytes#inputStream()
+ * @see BytesOutputStream
+ */
+public class BytesInputStream extends InputStream {
+ private Bytes bytes;
+ private long mark = 0;
+
+ /**
+ * Constructs a {@code BytesInputStream} backed by the given {@code bytes}.
+ *
+ * @param bytes the {@code Bytes} backing the constructed {@code BytesInputStream}
+ */
+ public BytesInputStream(Bytes bytes) {
+ this.bytes = bytes;
+ }
+
+ /**
+ * Constructs a {@code BytesInputStream} without backing {@code Bytes}, {@link #bytes(Bytes)}
+ * method must be called before first actual use of the constructed {@code BytesInputStream}
+ * instance.
+ */
+ public BytesInputStream() {}
+
+ /**
+ * Reassigns the underlying {@code Bytes} of this input stream.
+ *
+ * @param bytes new {@code Bytes} backing this {@code BytesInputStream}
+ * @return this {@code BytesInputStream} object back
+ */
+ public BytesInputStream bytes(Bytes bytes) {
+ this.bytes = bytes;
+ mark = 0;
+ return this;
+ }
+
+ @Override
+ public int available() {
+ return bytes.available();
+ }
+
+ @Override
+ public void close() {
+ bytes.finish();
+ }
+
+ @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod")
+ @Override
+ public void mark(int readLimit) {
+ mark = bytes.position();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public int read(@NotNull byte[] b, int off, int len) {
+ if (b == null) {
+ throw new NullPointerException();
+
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+
+ } else if (len == 0) {
+ return 0;
+ }
+ return bytes.read(b, off, len);
+ }
+
+ @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod")
+ @Override
+ public void reset() {
+ bytes.position(mark);
+ }
+
+ @Override
+ public long skip(long n) {
+ return bytes.skip(n);
+ }
+
+ @Override
+ public int read() {
+ return bytes.read();
+ }
+}
diff --git a/lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java b/lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java
new file mode 100644
index 0000000..a05402f
--- /dev/null
+++ b/lang/src/main/java/net/openhft/lang/io/view/BytesOutputStream.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 higherfrequencytrading.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.openhft.lang.io.view;
+
+import net.openhft.lang.io.Bytes;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * {@code OutputStream} view of {@link Bytes}. Writing data to this stream pushes {@linkplain
+ * Bytes#position() position} of the underlying {@code Bytes}. On attempt of writing bytes beyond
+ * backing {@code Bytes}' {@linkplain Bytes#limit() limit} {@link IOException} is thrown.
+ *
+ * <p>{@code BytesOutputStream} are reusable, see {@link #bytes(Bytes)} method.
+ *
+ * @see Bytes#outputStream()
+ * @see BytesInputStream
+ */
+public class BytesOutputStream extends OutputStream {
+ private Bytes bytes;
+
+ /**
+ * Constructs a {@code BytesOutputStream} backed by the given {@code bytes}.
+ *
+ * @param bytes the {@code Bytes} backing the constructed {@code BytesOutputStream}
+ */
+ public BytesOutputStream(Bytes bytes) {
+ this.bytes = bytes;
+ }
+
+ /**
+ * Constructs a {@code BytesOutputStream} without backing {@code Bytes}, {@link #bytes(Bytes)}
+ * method must be called before first actual use of the constructed {@code BytesOutputStream}
+ * instance.
+ */
+ public BytesOutputStream() {}
+
+ /**
+ * Reassigns the underlying {@code Bytes} of this output stream.
+ *
+ * @param bytes new {@code Bytes} backing this {@code BytesOutputStream}
+ * @return this {@code BytesOutputStream} object back
+ */
+ public BytesOutputStream bytes(Bytes bytes) {
+ this.bytes = bytes;
+ return this;
+ }
+
+ @Override
+ public void close() {
+ try {
+ super.close();
+ } catch (IOException e) {
+ // never happens.
+ throw new AssertionError(e);
+ }
+ // Don't close as we may want to continue'
+ }
+
+ private void checkNotClosed() throws IOException {
+ if (bytes.isFinished())
+ throw new IOException("Underlying bytes is closed");
+ }
+
+ private void checkAvailable(int n) throws IOException {
+ if (n > bytes.remaining())
+ throw new IOException("Not enough available space for writing " + n + " bytes");
+ }
+
+ @Override
+ public void write(@NotNull byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+
+ } else if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+
+ } else if (len == 0) {
+ return;
+ }
+ checkNotClosed();
+ checkAvailable(len);
+ bytes.write(b, off, len);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ checkNotClosed();
+ checkAvailable(1);
+ bytes.writeUnsignedByte(b);
+ }
+}