diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 10:17:09 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 10:17:09 +0200 |
commit | 3d9b009255ffd897a9bd84ca3977cd3f553da8ef (patch) | |
tree | 13a8296e2989ff8da3fc50d335fd48b2dd0c6064 /lang/src/main/java/net/openhft/lang/io | |
parent | b2ec1a2d459cfef3ff13133c1f7f5972e3740258 (diff) |
Imported Upstream version 6.7.6
Diffstat (limited to 'lang/src/main/java/net/openhft/lang/io')
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 >= limit() */ long position(); /** * @param position to skip to + * @return this bytes object back + * @throws java.lang.IllegalArgumentException if positions < 0 || position >= limit */ - void position(long position); + Bytes position(long position) throws IllegalArgumentException; + + /** + * @return the current limit which must be >= capacity() + */ + long limit(); + + /** + * @param limit the new limit which must be >= 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 << 8) | (b & 0xff)) + * <pre><code>(short)((a << 8) | (b & 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 << 8) | (b & 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 & 0xff) << 8) | (b & 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 & 0xff) << 8) | (b & 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 => Short.MIN_VALUE, Byte.MAX_VALUE => Short.MAX_VALUE, Byte.MIN_VALUE+2 to * Byte.MAX_VALUE-1 => same as short value, Byte.MIN_VALUE+1 => 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 => readUnsignedShort(), default => (a & 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 << 8) | (b & 0xff)) + * <pre><code>(char)((a << 8) | (b & 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 << 8) | (b & 0xff)) + * <pre><code>(char)((a << 8) | (b & 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8))) >> 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8))) >> 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & * 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 & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & * 0xff)) @@ -387,8 +396,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * with readShort(). This mapped as follows; Short.MIN_VALUE => Integer.MIN_VALUE, Short.MAX_VALUE => * Integer.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 => same as short value, Short.MIN_VALUE+1 => * 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 => readUnsignedInt(), default => (a & 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 & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -419,8 +428,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 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 & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -441,8 +458,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 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 & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -465,8 +482,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 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 & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -488,8 +505,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(g & 0xff) << 8) | * ((long)(h & 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 & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -508,8 +525,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(e & 0xff) << 24) | * ((long)(f & 0xff) << 16)) >> 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 & 0xff) << 56) | * ((long)(b & 0xff) << 48) | * ((long)(c & 0xff) << 40) | @@ -527,8 +544,8 @@ public interface RandomDataInput extends ObjectInput, RandomAccess, BytesCommon * ((long)(e & 0xff) << 24) | * ((long)(f & 0xff) << 16)) >> 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 => Long.MIN_VALUE, Integer.MAX_VALUE => Long.MAX_VALUE, * Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 => same as short value, Integer.MIN_VALUE+1 => 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()) < 0) { * l |= (b & 0x7FL) << count; * count += 7; * } - * if (b == 0 && count > 0) + * if (b == 0 && count > 0) * return ~l; * return l | (b << 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  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& 0x1F) << 6) | (b & 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 & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 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> <tt>></tt> <tt>remaining()</tt>, + * then no bytes are transferred and a {@link + * java.nio.BufferOverflowException} is thrown. + * + * <p> Otherwise, this method copies + * <i>n</i> = <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 & (v >> 8)) * (byte)(0xff & 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 & (v >> 8)) * (byte)(0xff & 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 & (v >> 8)) * (byte)(0xff & 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 & (v >> 8)) * (byte)(0xff & v) @@ -216,8 +260,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Writes one or three bytes as follows; Short.MIN_VALUE => Byte.MIN_VALUE, Short.MAX_VALUE => Byte.MAX_VALUE, * Short.MIN_VALUE+2 to Short.MAX_VALUE-1 => writeByte(x), default => 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 => 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 & 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 & (v >> 8)) * (byte)(0xff & 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 & (v >> 8)) * (byte)(0xff & 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 & (v >> 16)) * (byte)(0xff & (v >>    8)) * (byte)(0xff & 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 & (v >> 16)) * (byte)(0xff & (v >>    8)) * (byte)(0xff & 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 & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    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 & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    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 & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    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 & (v >> 24)) * (byte)(0xff & (v >> 16)) * (byte)(0xff & (v >>    8)) @@ -368,8 +412,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Writes two or six bytes as follows; Integer.MIN_VALUE => Short.MIN_VALUE, Integer.MAX_VALUE => * Short.MAX_VALUE, Short.MIN_VALUE+2 to Short.MAX_VALUE-1 => writeShort(x), default => * 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 << 16) - 2 => 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 & * 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 & (v >> 40)) * (byte)(0xff & (v >> 32)) * (byte)(0xff & (v >> 24)) @@ -454,7 +498,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * (byte)(0xff & 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 & ((1L << 48) + * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v & ((1L << 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 & (v >> 40)) * (byte)(0xff & (v >> 32)) * (byte)(0xff & (v >> 24)) @@ -473,7 +517,7 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * (byte)(0xff & 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 & ((1L << 48) + * <code>RandomDataInput</code> , which will then return a <code>long</code> equal to <code>v & ((1L << 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 & (v >> 56)) * (byte)(0xff & (v >> 48)) * (byte)(0xff & (v >> 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 & (v >> 56)) * (byte)(0xff & (v >> 48)) * (byte)(0xff & (v >> 40)) @@ -527,8 +571,8 @@ public interface RandomDataOutput extends ObjectOutput, RandomAccess, BytesCommo * Writes four or twelve bytes as follows Long.MIN_VALUE => Integer.MIN_VALUE, Long.MAX_VALUE => * Integer.MAX_VALUE, Integer.MIN_VALUE+2 to Integer.MAX_VALUE-1 => writeInt(x), default => * 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>\u0001</code> through - * <code>\u007f</code>, it is represented by one byte:<p> + * <code>\u007f</code>, it is represented by one byte: * <pre>(byte)c </pre> <p> * If a character <code>c</code> is <code>\u0000</code> or is in the range <code>\u0080</code> through * <code>\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 & (c >> 6))) * (byte)(0x80 | (0x3f & c)) * </code></pre> <p> If a character * <code>c</code> is in the range <code>\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 & (c >> 12))) * (byte)(0x80 | (0x3f & (c >> 6))) * (byte)(0x80 | (0x3f & 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> <tt>></tt> <tt>remaining()</tt>, + * then no bytes are transferred and a {@link + * java.nio.BufferOverflowException} is thrown. + * + * <p> Otherwise, this method copies + * <i>n</i> = <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); + } +} |