/*
* Copyright 2016 higherfrequencytrading.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.lang.io;
import net.openhft.lang.Jvm;
import net.openhft.lang.Maths;
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.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 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.
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();
private static final byte[] NaN = "NaN".getBytes();
private static final long MAX_VALUE_DIVIDE_5 = Long.MAX_VALUE / 5;
private static final byte BYTE_MIN_VALUE = Byte.MIN_VALUE;
private static final byte BYTE_EXTENDED = Byte.MIN_VALUE + 1;
private static final byte BYTE_MAX_VALUE = Byte.MIN_VALUE + 2;
private static final short UBYTE_EXTENDED = 0xff;
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;
// 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[] RADIX = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();
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 final StringInterner SI = new StringInterner(4096);
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;
volatile Thread singleThread = null;
private ObjectSerializer objectSerializer;
private boolean selfTerminating = false;
AbstractBytes() {
this(new VanillaBytesMarshallerFactory(), new AtomicInteger(1));
}
AbstractBytes(BytesMarshallerFactory bytesMarshallerFactory, AtomicInteger refCount) {
this(BytesMarshallableSerializer.create(bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE), refCount);
}
AbstractBytes(ObjectSerializer objectSerializer, AtomicInteger refCount) {
this.finished = false;
this.refCount = refCount;
setObjectSerializer(objectSerializer);
}
private static boolean equalsCaseIgnore(StringBuilder sb, String s) {
if (sb.length() != s.length())
return false;
for (int i = 0; i < s.length(); i++)
if (Character.toLowerCase(sb.charAt(i)) != s.charAt(i))
return false;
return true;
}
private static double asDouble(long value, int exp, boolean negative, int decimalPlaces) {
if (decimalPlaces > 0 && value < Long.MAX_VALUE / 2) {
if (value < Long.MAX_VALUE / (1L << 32)) {
exp -= 32;
value <<= 32;
}
if (value < Long.MAX_VALUE / (1L << 16)) {
exp -= 16;
value <<= 16;
}
if (value < Long.MAX_VALUE / (1L << 8)) {
exp -= 8;
value <<= 8;
}
if (value < Long.MAX_VALUE / (1L << 4)) {
exp -= 4;
value <<= 4;
}
if (value < Long.MAX_VALUE / (1L << 2)) {
exp -= 2;
value <<= 2;
}
if (value < Long.MAX_VALUE / (1L << 1)) {
exp -= 1;
value <<= 1;
}
}
for (; decimalPlaces > 0; decimalPlaces--) {
exp--;
long mod = value % 5;
value /= 5;
int modDiv = 1;
if (value < Long.MAX_VALUE / (1L << 4)) {
exp -= 4;
value <<= 4;
modDiv <<= 4;
}
if (value < Long.MAX_VALUE / (1L << 2)) {
exp -= 2;
value <<= 2;
modDiv <<= 2;
}
if (value < Long.MAX_VALUE / (1L << 1)) {
exp -= 1;
value <<= 1;
modDiv <<= 1;
}
if (decimalPlaces > 1)
value += modDiv * mod / 5;
else
value += (modDiv * mod + 4) / 5;
}
final double d = Math.scalb((double) value, exp);
return negative ? -d : d;
}
private static void warnIdLimit(long id) {
LoggerHolder.LOGGER.log(Level.WARNING, "High thread id may result in collisions id: " + id);
ID_LIMIT_WARNED = true;
}
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 i = 0;
ascii:
{
for (; i < strlen - 3; i += 4) {
char c0 = str.charAt(i);
char c1 = str.charAt(i + 1);
char c2 = str.charAt(i + 2);
char c3 = str.charAt(i + 3);
if ((c0 | c1 | c2 | c3) > 0x007F)
break ascii;
NativeBytes.UNSAFE.putInt(bytes.positionAddr + i, c0 | (c1 << 8) | (c2 << 16) | (c3 << 24));
}
for (; i < strlen; i++) {
char c = str.charAt(i);
if (c > 0x007F)
// if (c + Integer.MIN_VALUE - 1 <= Integer.MIN_VALUE + 0x007F-1)
break ascii;
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 i;
ascii:
{
for (i = 0; i < strlen; i++) {
char c = chars[i];
if (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();
}
@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 = acquireStringBuilder();
parseUtf8(sb, tester);
if (sb.length() == 0)
return null;
switch (sb.charAt(0)) {
case 't':
case 'T':
return sb.length() == 1 || equalsCaseIgnore(sb, "true") ? true : null;
case 'y':
case 'Y':
return sb.length() == 1 || equalsCaseIgnore(sb, "yes") ? true : null;
case '0':
return sb.length() == 1 ? false : null;
case '1':
return sb.length() == 1 ? true : null;
case 'f':
case 'F':
return sb.length() == 1 || equalsCaseIgnore(sb, "false") ? false : null;
case 'n':
case 'N':
return sb.length() == 1 || equalsCaseIgnore(sb, "no") ? false : null;
}
return null;
}
@Override
public void readFully(@NotNull byte[] bytes) {
readFully(bytes, 0, bytes.length);
}
@Override
public void readFully(@NotNull char[] data) {
readFully(data, 0, data.length);
}
@Override
public int skipBytes(int n) {
return (int) skip(n);
}
@Override
public boolean readBoolean() {
return readByteOrThrow(false) != 0;
}
@Override
public boolean readBoolean(long offset) {
return readByte(offset) != 0;
}
@Override
public int readUnsignedByte() {
return readByte() & UNSIGNED_BYTE_MASK;
}
@Override
public int readUnsignedByte(long offset) {
return readByte(offset) & UNSIGNED_BYTE_MASK;
}
@Override
public int readUnsignedShort() {
return readShort() & UNSIGNED_SHORT_MASK;
}
@Override
public int readUnsignedShort(long offset) {
return readShort(offset) & UNSIGNED_SHORT_MASK;
}
@NotNull
@Override
public String readLine() {
StringBuilder input = acquireStringBuilder();
EOL:
while (position() < capacity()) {
int c = readUnsignedByteOrThrow();
switch (c) {
case END_OF_BUFFER:
case '\n':
break EOL;
case '\r':
long cur = position();
if (cur < capacity() && readByte(cur) == '\n')
position(cur + 1);
break EOL;
default:
input.append((char) c);
break;
}
}
return SI.intern(input);
}
@Nullable
@Override
public String readUTFΔ() {
StringBuilder utfReader = acquireStringBuilder();
if (readUTFΔ(utfReader))
return utfReader.length() == 0 ? "" : SI.intern(utfReader);
return null;
}
// locking at it temporarily changes position.
// todo write a version without changing the position.
@Nullable
@Override
public synchronized String readUTFΔ(long offset) throws IllegalStateException {
long position = position();
try {
position(offset);
return readUTFΔ();
} finally {
position(position);
}
}
@NotNull
private StringBuilder acquireStringBuilder() {
return sbp.acquireStringBuilder();
}
@Override
public boolean readUTFΔ(@NotNull StringBuilder stringBuilder) {
try {
stringBuilder.setLength(0);
return appendUTF0(stringBuilder);
} catch (IOException unexpected) {
throw new IllegalStateException(unexpected);
}
}
@SuppressWarnings("MagicNumber")
private boolean appendUTF0(@NotNull Appendable appendable) throws IOException {
long len = readStopBit();
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;
}
@NotNull
@Override
public String parseUtf8(@NotNull StopCharTester tester) {
StringBuilder utfReader = acquireStringBuilder();
parseUtf8(utfReader, tester);
return SI.intern(utfReader);
}
@Override
public void parseUtf8(@NotNull StringBuilder builder, @NotNull StopCharTester tester) {
builder.setLength(0);
try {
readUTF0(builder, tester);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private void readUTF0(@NotNull Appendable appendable, @NotNull StopCharTester tester) throws IOException {
while (true) {
int c = readUnsignedByteOrThrow();
if (c >= 128) {
position(position() - 1);
readUTF2(appendable, tester);
break;
}
if (tester.isStopChar(c))
return;
appendable.append((char) c);
}
}
// 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:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
/* 0xxxxxxx */
if (tester.isStopChar(c))
return;
appendable.append((char) c);
break;
case 12:
case 13: {
/* 110x xxxx 10xx xxxx */
int char2 = readUnsignedByte();
if ((char2 & 0xC0) != 0x80)
throw new UTFDataFormatException(
"malformed input around byte");
int c2 = (char) (((c & 0x1F) << 6) |
(char2 & 0x3F));
if (tester.isStopChar(c2))
return;
appendable.append((char) c2);
break;
}
case 14: {
/* 1110 xxxx 10xx xxxx 10xx xxxx */
int char2 = readUnsignedByte();
int char3 = readUnsignedByte();
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
throw new UTFDataFormatException(
"malformed input around byte ");
int c3 = (char) (((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
(char3 & 0x3F));
if (tester.isStopChar(c3))
return;
appendable.append((char) c3);
break;
}
default:
/* 10xx xxxx, 1111 xxxx */
throw new UTFDataFormatException(
"malformed input around byte ");
}
}
}
@Override
public boolean stepBackAndSkipTo(@NotNull StopCharTester tester) {
if (position() > 0)
position(position() - 1);
return skipTo(tester);
}
@Override
public boolean skipTo(@NotNull StopCharTester tester) {
while (remaining() > 0) {
int ch = readUnsignedByteOrThrow();
if (tester.isStopChar(ch))
return true;
}
return false;
}
@NotNull
@Override
public String readUTF() {
try {
int len = readUnsignedShort();
StringBuilder utfReader = acquireStringBuilder();
readUTF0(utfReader, len);
return utfReader.length() == 0 ? "" : SI.intern(utfReader);
} catch (IOException unexpected) {
throw new AssertionError(unexpected);
}
}
@Override
public short readCompactShort() {
byte b = readByte();
switch (b) {
case BYTE_MIN_VALUE:
return Short.MIN_VALUE;
case BYTE_MAX_VALUE:
return Short.MAX_VALUE;
case BYTE_EXTENDED:
return readShort();
default:
return b;
}
}
@Override
public int readCompactUnsignedShort() {
int b = readUnsignedByte();
if (b == UBYTE_EXTENDED)
return readUnsignedShort();
return b;
}
@Override
public int readInt24() {
int b = readUnsignedByte();
int s = readUnsignedShort();
if (byteOrder() == ByteOrder.BIG_ENDIAN)
return ((b << 24) + (s << 8)) >> 8;
// extra shifting to get sign extension.
return ((b << 8) + (s << 16)) >> 8;
}
@Override
public int readInt24(long offset) {
int b = readUnsignedByte(offset);
int s = readUnsignedShort(offset + 1);
if (byteOrder() == ByteOrder.BIG_ENDIAN)
return ((b << 24) + (s << 8)) >> 8;
// extra shifting to get sign extension.
return ((b << 8) + (s << 16)) >> 8;
}
@Override
public long readUnsignedInt() {
return readInt() & UNSIGNED_INT_MASK;
}
@Override
public long readUnsignedInt(long offset) {
return readInt(offset) & UNSIGNED_INT_MASK;
}
@Override
public int readCompactInt() {
short b = readShort();
switch (b) {
case SHORT_MIN_VALUE:
return Integer.MIN_VALUE;
case SHORT_MAX_VALUE:
return Integer.MAX_VALUE;
case SHORT_EXTENDED:
return readInt();
default:
return b;
}
}
@Override
public long readCompactUnsignedInt() {
int b = readUnsignedShort();
if (b == USHORT_EXTENDED)
return readUnsignedInt();
return b;
}
@Override
public long readInt48() {
long s = readUnsignedShort();
long l = readUnsignedInt();
if (byteOrder() == ByteOrder.BIG_ENDIAN)
return ((s << 48) + (l << 16)) >> 16;
// extra shifting to get sign extension.
return ((s << 16) + (l << 32)) >> 16;
}
@Override
public long readInt48(long offset) {
long s = readUnsignedShort(offset);
long l = readUnsignedInt(offset + 2);
if (byteOrder() == ByteOrder.BIG_ENDIAN)
return ((s << 48) + (l << 16)) >> 16;
// extra shifting to get sign extension.
return ((s << 16) + (l << 32)) >> 16;
}
@Override
public long readCompactLong() {
int b = readInt();
switch (b) {
case INT_MIN_VALUE:
return Long.MIN_VALUE;
case INT_MAX_VALUE:
return Long.MAX_VALUE;
case INT_EXTENDED:
return readLong();
default:
return b;
}
}
@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;
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) {
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;
}
}
@Override
public double readCompactDouble() {
float f = readFloat();
if (Float.isNaN(f))
return readDouble();
return f;
}
@Override
public void read(@NotNull ByteBuffer bb) {
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());
len -= 8;
}
}
while (len > 0) {
bb.put(readByte());
len--;
}
}
// // RandomOutputStream
@Override
public void write(@NotNull byte[] bytes) {
write(bytes, 0, bytes.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 ? 'Y' : 0);
}
@Override
public void writeBoolean(long offset, boolean v) {
writeByte(offset, v ? 'Y' : 0);
}
@Override
public void writeBytes(@NotNull String s) {
int len = s.length();
for (int i = 0; i < len; i++)
write(s.charAt(i));
}
@Override
public void writeChars(@NotNull String s) {
writeChars((CharSequence) s);
}
@Override
public void writeChars(@NotNull CharSequence cs) {
int len = cs.length();
for (int i = 0; i < len; i++)
writeChar(cs.charAt(i));
}
@Override
public void writeUTF(@NotNull String str) {
long utflen = findUTFLength(str);
if (utflen > 65535)
throw new IllegalStateException("String too long " + utflen + " when encoded, max: 65535");
writeUnsignedShort((int) utflen);
checkUFTLength(utflen);
writeUTF0(this, str, str.length());
}
@Override
public void writeUTFΔ(@Nullable CharSequence str) throws IllegalArgumentException {
if (str == null) {
writeStopBit(-1);
return;
}
long utflen = findUTFLength(str);
writeStopBit(utflen);
checkUFTLength(utflen);
writeUTF0(this, str, str.length());
}
// locking at it temporarily changes position.
// todo write a version without changing the position.
@Override
public synchronized void writeUTFΔ(long offset, int maxSize, @Nullable CharSequence s) throws IllegalStateException {
assert maxSize > 1;
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(this, s, s.length());
zeroOut(position(), offset + maxSize);
} finally {
position(position);
}
}
@NotNull
public ByteStringAppender append(@NotNull CharSequence str) {
int strlen = str.length();
writeUTF0(this, str, strlen);
return this;
}
private void checkUFTLength(long utflen) throws IllegalArgumentException {
if (utflen > remaining())
throw new IllegalArgumentException(
"encoded string too long: " + utflen + " bytes, remaining=" + remaining());
}
@Override
public void writeByte(int v) {
write(v);
}
@Override
public void writeUnsignedByte(int v) {
writeByte(v);
}
@Override
public void writeUnsignedByte(long offset, int v) {
writeByte(offset, v);
}
@Override
public void write(long offset, @NotNull byte[] bytes) {
checkWrite(bytes.length);
for (int i = 0; i < bytes.length; i++)
writeByte(offset + i, bytes[i]);
}
@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) {
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);
}
@Override
public void writeUnsignedShort(long offset, int v) {
writeShort(offset, v);
}
@Override
public void writeCompactShort(int v) {
if (v > BYTE_MAX_VALUE && v <= Byte.MAX_VALUE)
writeByte(v);
else
switch (v) {
case Short.MIN_VALUE:
writeByte(BYTE_MIN_VALUE);
break;
case Short.MAX_VALUE:
writeByte(BYTE_MAX_VALUE);
break;
default:
writeByte(BYTE_EXTENDED);
writeShort(v);
break;
}
}
@Override
public void writeCompactUnsignedShort(int v) {
if (v >= 0 && v < USHORT_EXTENDED) {
writeByte(v);
} else {
writeUnsignedShort(USHORT_EXTENDED);
writeUnsignedShort(v);
}
}
@Override
public void writeInt24(int v) {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedByte(v >>> 16);
writeUnsignedShort(v);
} else {
writeUnsignedByte(v);
writeUnsignedShort(v >>> 8);
}
}
@Override
public void writeInt24(long offset, int v) {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedByte(offset, v >>> 16);
writeUnsignedShort(offset + 1, v);
} else {
writeUnsignedByte(offset, v);
writeUnsignedShort(offset + 1, v >>> 8);
}
}
@Override
public void writeUnsignedInt(long v) {
writeInt((int) v);
}
@Override
public void writeUnsignedInt(long offset, long v) {
writeInt(offset, (int) v);
}
@Override
public void writeCompactInt(int v) {
if (v > SHORT_MAX_VALUE && v <= Short.MAX_VALUE)
writeShort(v);
else
switch (v) {
case Integer.MIN_VALUE:
writeShort(SHORT_MIN_VALUE);
break;
case Integer.MAX_VALUE:
writeShort(SHORT_MAX_VALUE);
break;
default:
writeShort(SHORT_EXTENDED);
writeInt(v);
break;
}
}
@Override
public void writeCompactUnsignedInt(long v) {
if (v >= 0 && v < USHORT_EXTENDED) {
writeShort((int) v);
} else {
writeShort(USHORT_EXTENDED);
writeUnsignedInt(v);
}
}
@Override
public void writeInt48(long v) {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedShort((int) (v >>> 32));
writeUnsignedInt(v);
} else {
writeUnsignedShort((int) v);
writeUnsignedInt(v >>> 16);
}
}
@Override
public void writeInt48(long offset, long v) {
if (byteOrder() == ByteOrder.BIG_ENDIAN) {
writeUnsignedShort(offset, (int) (v >>> 32));
writeUnsignedInt(offset + 2, v);
} else {
writeUnsignedShort(offset, (int) v);
writeUnsignedInt(offset + 2, v >>> 16);
}
}
@Override
public void writeCompactLong(long v) {
if (v > INT_MAX_VALUE && v <= Integer.MAX_VALUE) {
writeInt((int) v);
} else if (v == Long.MIN_VALUE) {
writeInt(INT_MIN_VALUE);
} else if (v == Long.MAX_VALUE) {
writeInt(INT_MAX_VALUE);
} 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;
}
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);
}
}
@Override
public void writeCompactDouble(double v) {
float f = (float) v;
if (f == v) {
writeFloat(f);
} else {
writeFloat(Float.NaN);
writeDouble(v);
}
}
@Override
public void write(@NotNull ByteBuffer bb) {
if (bb.order() == byteOrder())
while (bb.remaining() >= 8)
writeLong(bb.getLong());
while (bb.remaining() >= 1)
writeByte(bb.get());
}
// ByteStringAppender
@NotNull
@Override
public ByteStringAppender append(@NotNull CharSequence s, int start, int end) {
for (int i = start, len = Math.min(end, s.length()); i < len; i++)
writeByte(s.charAt(i));
return this;
}
@NotNull
@Override
public ByteStringAppender append(@Nullable Enum value) {
return value == null ? this : append(value.toString());
}
@NotNull
@Override
public ByteStringAppender append(boolean b) {
append(b ? "true" : "false");
return this;
}
@NotNull
@Override
public ByteStringAppender append(char c) {
writeByte(c);
return this;
}
@NotNull
@Override
public ByteStringAppender append(int num) {
return append((long) num);
}
@NotNull
@Override
public ByteStringAppender append(long num) {
if (num < 0) {
if (num == Long.MIN_VALUE) {
write(MIN_VALUE_TEXT);
return this;
}
writeByte('-');
num = -num;
}
if (num == 0) {
writeByte('0');
} else {
appendLong0(num);
}
return this;
}
@NotNull
@Override
public ByteStringAppender append(long num, int base) {
if (base < 2 || base > Character.MAX_RADIX)
throw new IllegalArgumentException("Invalid base: " + base);
if (num < 0) {
if (num == Long.MIN_VALUE) {
writeBytes(BigInteger.valueOf(num).toString(base));
return this;
}
writeByte('-');
num = -num;
}
if (num == 0) {
writeByte('0');
} else {
while (num > 0) {
writeByte(RADIX[((int) (num % base))]);
num /= base;
}
}
return this;
}
@NotNull
@Override
public ByteStringAppender appendDateMillis(long timeInMS) {
DateCache dateCache = dateCacheTL.get();
if (dateCache == null) {
dateCacheTL.set(dateCache = new DateCache());
}
long date = timeInMS / 86400000;
if (dateCache.lastDay != date) {
dateCache.lastDateStr = dateCache.dateFormat.format(new Date(timeInMS)).getBytes(ISO_8859_1);
dateCache.lastDay = date;
} else {
assert dateCache.lastDateStr != null;
}
write(dateCache.lastDateStr);
return this;
}
@NotNull
@Override
public ByteStringAppender appendDateTimeMillis(long timeInMS) {
appendDateMillis(timeInMS);
writeByte('T');
appendTimeMillis(timeInMS % 86400000L);
return this;
}
@NotNull
@Override
public ByteStringAppender appendTimeMillis(long timeInMS) {
int hours = (int) (timeInMS / (60 * 60 * 1000));
if (hours > 99) {
appendLong0(hours); // can have over 24 hours.
} else {
writeByte((char) (hours / 10 + '0'));
writeByte((char) (hours % 10 + '0'));
}
writeByte(':');
int minutes = (int) ((timeInMS / (60 * 1000)) % 60);
writeByte((char) (minutes / 10 + '0'));
writeByte((char) (minutes % 10 + '0'));
writeByte(':');
int seconds = (int) ((timeInMS / 1000) % 60);
writeByte((char) (seconds / 10 + '0'));
writeByte((char) (seconds % 10 + '0'));
writeByte('.');
int millis = (int) (timeInMS % 1000);
writeByte((char) (millis / 100 + '0'));
writeByte((char) (millis / 10 % 10 + '0'));
writeByte((char) (millis % 10 + '0'));
return this;
}
@NotNull
@Override
public ByteStringAppender append(double d) {
long val = Double.doubleToRawLongBits(d);
int sign = (int) (val >>> 63);
int exp = (int) ((val >>> 52) & 2047);
long mantissa = val & ((1L << 52) - 1);
if (sign != 0) {
writeByte('-');
}
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;
}
final int shift = (1023 + 52) - exp;
if (shift > 0) {
// integer and faction
if (shift < 53) {
long intValue = mantissa >> shift;
appendLong0(intValue);
mantissa -= intValue << shift;
if (mantissa > 0) {
writeByte('.');
mantissa <<= 1;
mantissa++;
int precision = shift + 1;
long error = 1;
long value = intValue;
int decimalPlaces = 0;
while (mantissa > error) {
// times 5*2 = 10
mantissa *= 5;
error *= 5;
precision--;
long num = (mantissa >> precision);
value = value * 10 + num;
writeByte((char) ('0' + num));
mantissa -= num << precision;
final double parsedValue = asDouble(value, 0, sign != 0, ++decimalPlaces);
if (parsedValue == d)
break;
}
}
return this;
} else {
// faction.
writeByte('0');
writeByte('.');
mantissa <<= 6;
mantissa += (1 << 5);
int precision = shift + 6;
long error = (1 << 5);
long value = 0;
int decimalPlaces = 0;
while (mantissa > error) {
while (mantissa > MAX_VALUE_DIVIDE_5) {
mantissa >>>= 1;
error = (error + 1) >>> 1;
precision--;
}
// times 5*2 = 10
mantissa *= 5;
error *= 5;
precision--;
if (precision >= 64) {
decimalPlaces++;
writeByte('0');
continue;
}
long num = (mantissa >>> precision);
value = value * 10 + num;
final char c = (char) ('0' + num);
assert !(c < '0' || c > '9');
writeByte(c);
mantissa -= num << precision;
final double parsedValue = asDouble(value, 0, sign != 0, ++decimalPlaces);
if (parsedValue == d)
break;
}
return this;
}
}
// large number
mantissa <<= 10;
int precision = -10 - shift;
int digits = 0;
while ((precision > 53 || mantissa > Long.MAX_VALUE >> precision) && precision > 0) {
digits++;
precision--;
long mod = mantissa % 5;
mantissa /= 5;
int modDiv = 1;
while (mantissa < MAX_VALUE_DIVIDE_5 && precision > 1) {
precision -= 1;
mantissa <<= 1;
modDiv <<= 1;
}
mantissa += modDiv * mod / 5;
}
long val2 = precision > 0 ? mantissa << precision : mantissa >>> -precision;
appendLong0(val2);
for (int i = 0; i < digits; i++)
writeByte('0');
return this;
}
@Override
public double parseDouble() {
long value = 0;
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) {
if (ch >= '0' && ch <= '9') {
while (value >= MAX_VALUE_DIVIDE_10) {
value >>>= 1;
exp++;
}
value = value * 10 + (ch - '0');
decimalPlaces++;
} 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) {
if (list instanceof RandomAccess && list instanceof List) {
return append((List<E>) list, separator);
}
int i = 0;
for (E e : list) {
if (i++ > 0)
append(separator);
if (e != null)
append(e.toString());
}
return this;
}
@NotNull
<E> ByteStringAppender append(@NotNull List<E> list, @NotNull CharSequence separator) {
for (int i = 0; i < list.size(); i++) {
if (i > 0)
append(separator);
E e = list.get(i);
if (e != null)
append(e.toString());
}
return this;
}
@NotNull
@Override
public MutableDecimal parseDecimal(@NotNull MutableDecimal decimal) {
long num = 0, scale = Long.MIN_VALUE;
boolean negative = false;
while (true) {
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;
}
}
if (negative)
num = -num;
decimal.set(num, scale > 0 ? (int) scale : 0);
return decimal;
}
@Override
public long parseLong() {
long num = 0;
boolean negative = false;
while (true) {
int b = readUnsignedByteOrThrow();
// if (b >= '0' && b <= '9')
if ((b - ('0' + Integer.MIN_VALUE)) <= 9 + Integer.MIN_VALUE)
num = num * 10 + b - '0';
else if (b == '-')
negative = true;
else
break;
}
return negative ? -num : num;
}
@Override
public long parseLong(int base) {
if (base < 2 || base > Character.MAX_RADIX)
throw new IllegalArgumentException("Invalid base:" + base);
long num = 0;
boolean negative = false;
while (true) {
int b = readUnsignedByteOrThrow();
byte rp = RADIX_PARSE[b];
if (rp >= 0 && rp < base) {
num = num * base + rp;
} else if (b == '-')
negative = true;
else
break;
}
return negative ? -num : num;
}
private void appendLong0(long num) {
// Extract digits into the end of the numberBuffer
int endIndex = appendLong1(num);
// Bulk copy the digits into the front of the buffer
write(numberBuffer, endIndex, MAX_NUMBER_LENGTH - endIndex);
}
private int appendLong1(long num) {
numberBuffer[19] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 19;
numberBuffer[18] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 18;
numberBuffer[17] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 17;
numberBuffer[16] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 16;
numberBuffer[15] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 15;
numberBuffer[14] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 14;
numberBuffer[13] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 13;
numberBuffer[12] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 12;
numberBuffer[11] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 11;
numberBuffer[10] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 10;
numberBuffer[9] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 9;
numberBuffer[8] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 8;
numberBuffer[7] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 7;
numberBuffer[6] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 6;
numberBuffer[5] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 5;
numberBuffer[4] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 4;
numberBuffer[3] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 3;
numberBuffer[2] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0)
return 2;
numberBuffer[1] = (byte) (num % 10L + '0');
return 1;
}
@NotNull
@Override
public ByteStringAppender append(double d, int precision) {
if (precision < 0)
precision = 0;
long power10 = Maths.power10(precision);
if (power10 < 0)
power10 = 100000000000000000L;
if (d < 0) {
d = -d;
writeByte('-');
}
double d2 = d * power10;
if (d2 > Long.MAX_VALUE || d2 < Long.MIN_VALUE + 1)
return append(d);
long val = (long) (d2 + 0.5);
while (precision > 1 && val % 10 == 0) {
val /= 10;
precision--;
}
if (precision > 0 && val % 10 == 0) {
val = (val + 5) / 10;
precision--;
}
if (precision > 0)
appendDouble0(val, precision);
else
appendLong0(val);
return this;
}
private void appendDouble0(long num, int precision) {
// Extract digits into the end of the numberBuffer
// Once desired precision is reached, write the '.'
int endIndex = appendDouble1(num, precision);
// Bulk copy the digits into the front of the buffer
// TODO: Can this be avoided with use of correctly offset bulk appends on Excerpt?
// Uses (numberBufferIdx - 1) because index was advanced one too many times
write(numberBuffer, endIndex, MAX_NUMBER_LENGTH - endIndex);
}
private int appendDouble1(long num, int precision) {
int endIndex = MAX_NUMBER_LENGTH;
int maxEnd = MAX_NUMBER_LENGTH - precision - 2;
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 1)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 2)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 3)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 4)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 5)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 6)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 7)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 8)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 9)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 10)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 11)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 12)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 13)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 14)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 15)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 16)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 17)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
num /= 10;
if (num <= 0 && endIndex <= maxEnd)
return endIndex;
if (precision == 18)
numberBuffer[--endIndex] = (byte) '.';
numberBuffer[--endIndex] = (byte) (num % 10L + '0');
return endIndex;
}
@NotNull
@Override
public ByteStringAppender append(@NotNull MutableDecimal md) {
StringBuilder sb = acquireStringBuilder();
md.toString(sb);
append(sb);
return this;
}
@NotNull
@Override
public InputStream inputStream() {
return new BytesInputStream(this);
}
@NotNull
@Override
public OutputStream outputStream() {
return new BytesOutputStream(this);
}
@NotNull
@Override
public ObjectSerializer objectSerializer() {
return objectSerializer;
}
@SuppressWarnings("unchecked")
@Override
public <E> void writeEnum(@Nullable E e) {
if (e == null) {
write8bitText(null);
return;
}
if (e instanceof CharSequence) {
write8bitText((CharSequence) e);
return;
}
if (e instanceof Enum) {
write8bitText(((Enum) e).name());
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) {
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 = parseUtf8(tester);
if (text.isEmpty())
return null;
return EnumInterner.intern(eClass, text);
}
@Override
public <E> void writeList(@NotNull Collection<E> list) {
writeStopBit(list.size());
for (E e : list)
writeEnum(e);
}
@Override
public <K, V> void writeMap(@NotNull Map<K, V> map) {
writeStopBit(map.size());
for (Map.Entry<K, V> entry : map.entrySet()) {
writeEnum(entry.getKey());
writeEnum(entry.getValue());
}
}
@Override
public <E> void readList(@NotNull Collection<E> list, @NotNull Class<E> eClass) {
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 = readEnum(eClass);
list.add(e);
}
}
@Override
@NotNull
public <K, V> Map<K, V> readMap(@NotNull Map<K, V> map, @NotNull Class<K> kClass, @NotNull Class<V> vClass) {
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));
return map;
}
@Override
public int available() {
return (int) Math.min(Integer.MAX_VALUE, remaining());
}
@Override
public int read() {
return remaining() > 0 ? readUnsignedByte() : -1;
}
@Override
public int read(@NotNull byte[] bytes) {
return read(bytes, 0, bytes.length);
}
@Override
public abstract int read(@NotNull byte[] bytes, int off, int len);
@Override
public long skip(long n) {
if (n < -position())
throw new IllegalArgumentException("Skip bytes out of range, was " + n);
if (n > remaining())
n = remaining();
position(position() + n);
return n;
}
@Override
public void close() {
if (!isFinished())
finish();
}
@Override
public void finish() throws IndexOutOfBoundsException {
if (remaining() < 0)
throwOverflow();
finished = true;
}
private void throwOverflow() throws IndexOutOfBoundsException {
throw new IndexOutOfBoundsException("Buffer overflow, capacity: " + capacity() + " position: " + position());
}
@Override
public boolean isFinished() {
return finished;
}
@Override
public AbstractBytes clear() {
finished = false;
position(0L);
limit(capacity());
return this;
}
@Override
public Bytes flip() {
limit(position());
position(0);
return this;
}
@Override
public void flush() {
checkEndOfBuffer();
}
@Nullable
@Override
public Object readObject() {
try {
return objectSerializer.readSerializable(this, null, null);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Nullable
@Override
public <T> T readObject(Class<T> tClass) throws IllegalStateException {
Object o = readObject();
if (o == null || tClass.isInstance(o))
return (T) o;
throw new ClassCastException("Cannot convert " + o.getClass().getName() + " to " + tClass.getName() + " was " + o);
}
@Nullable
@Override
@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);
}
}
@SuppressWarnings("unchecked")
@Override
public void writeObject(@Nullable Object obj) {
try {
objectSerializer.writeSerializable(this, obj, null);
} catch (Exception e) {
throw new IllegalStateException(e);
}
checkEndOfBuffer();
}
@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) {
return tryLockNanos4a(offset);
}
@Override
public boolean tryLockNanosInt(long offset, long nanos) {
int limit = nanos <= 10000 ? (int) nanos / 10 : 1000;
for (int i = 0; i < limit; i++)
if (tryLockNanos4a(offset))
return true;
if (nanos <= 10000)
return false;
long end = System.nanoTime() + nanos - 10000;
do {
if (tryLockNanos4a(offset))
return true;
} while (end > System.nanoTime() && !currentThread().isInterrupted());
return false;
}
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;
//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) {
//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;
//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;
}
@Override
public void busyLockInt(long offset) throws InterruptedException, IllegalStateException {
boolean success = tryLockNanosInt(offset, BUSY_LOCK_LIMIT);
if (!success)
if (currentThread().isInterrupted())
throw new InterruptedException();
else
throw new IllegalStateException("Failed to acquire lock after " + BUSY_LOCK_LIMIT / 1e9 + " seconds.");
}
@Override
public void unlockInt(long offset) throws IllegalMonitorStateException {
int lowId = shortThreadId();
int firstValue = ((1 << 24) | lowId);
if (compareAndSwapInt(offset, firstValue, 0))
return;
// try to cheek the lowId and the count.
unlockFailedInt(offset, lowId);
}
@Override
public void resetLockInt(long offset) {
writeOrderedInt(offset, 0);
}
@Override
public int threadIdForLockInt(long offset) {
return readVolatileInt(offset) & INT_LOCK_MASK;
}
int shortThreadId() {
return shortThreadId0();
}
int shortThreadId0() {
final int tid = (int) getId() & INT_LOCK_MASK;
if (!ID_LIMIT_WARNED && tid > 1 << 24) {
warnIdLimit(tid);
}
return tid;
}
Thread currentThread() {
return Thread.currentThread();
}
@Override
public boolean tryLockLong(long offset) {
long id = uniqueTid();
return tryLockNanos8a(offset, id);
}
long uniqueTid() {
return Jvm.getUniqueTid(currentThread());
}
@Override
public boolean tryLockNanosLong(long offset, long nanos) {
long id = uniqueTid();
int limit = nanos <= 10000 ? (int) nanos / 10 : 1000;
for (int i = 0; i < limit; i++)
if (tryLockNanos8a(offset, id))
return true;
if (nanos <= 10000)
return false;
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 (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;
}
private boolean tryLockNanos8a(long offset, long id) {
long firstValue = (1L << 48) | id;
if (compareAndSwapLong(offset, 0, firstValue))
return true;
long currentValue = readLong(offset);
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("Reentered 65535 times without an unlock");
currentValue += 1L << 48;
writeOrderedLong(offset, currentValue);
return true;
}
return false;
}
@Override
public void busyLockLong(long offset) throws InterruptedException, IllegalStateException {
boolean success = tryLockNanosLong(offset, BUSY_LOCK_LIMIT);
if (!success)
if (currentThread().isInterrupted())
throw new InterruptedException();
else
throw new IllegalStateException("Failed to acquire lock after " + BUSY_LOCK_LIMIT / 1e9 + " seconds.");
}
@Override
public void unlockLong(long offset) throws IllegalMonitorStateException {
long id = Jvm.getUniqueTid();
long firstValue = (1L << 48) | id;
if (compareAndSwapLong(offset, firstValue, 0))
return;
// try to check the lowId and the count.
unlockFailedLong(offset, id);
}
@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 = readInt(offset);
long holderId = currentValue & INT_LOCK_MASK;
if (holderId == lowId) {
currentValue -= 1 << 24;
writeOrderedInt(offset, (int) currentValue);
} else if (currentValue == 0) {
LoggerHolder.LOGGER.log(Level.WARNING, "No thread holds this lock, threadId: " + shortThreadId());
} else {
throw new IllegalMonitorStateException("Thread " + holderId + " holds this lock, " + (currentValue >>> 24) + " times");
}
}
private void unlockFailedLong(long offset, long id) throws IllegalMonitorStateException {
long currentValue = readLong(offset);
long holderId = currentValue & (-1L >>> 16);
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))
+ " holds this lock, " + (currentValue >>> 48)
+ " times, unlock from " + Jvm.getProcessId()
+ " thread " + currentThread().getId());
}
}
@Override
public int getAndAdd(long offset, int delta) {
for (; ; ) {
int current = readVolatileInt(offset);
int next = current + delta;
if (compareAndSwapInt(offset, current, next))
return current;
}
}
@Override
public int addAndGetInt(long offset, int delta) {
for (; ; ) {
int current = readVolatileInt(offset);
int next = current + delta;
if (compareAndSwapInt(offset, current, next))
return next;
}
}
@Override
public byte addByte(long offset, byte b) {
byte b2 = readByte(offset);
b2 += b;
writeByte(offset, b2);
return b2;
}
@Override
public int addUnsignedByte(long offset, int i) {
int b2 = readUnsignedByte(offset);
b2 += i;
writeUnsignedByte(offset, b2);
return b2 & 0xFF;
}
@Override
public short addShort(long offset, short s) {
short s2 = readShort(offset);
s2 += s;
writeShort(offset, s2);
return s2;
}
@Override
public int addUnsignedShort(long offset, int i) {
int b2 = readUnsignedShort(offset);
b2 += i;
writeUnsignedShort(offset, b2);
return b2 & 0xFFFF;
}
@Override
public int addInt(long offset, int i) {
int b2 = readInt(offset);
b2 += i;
writeInt(offset, b2);
return b2;
}
@Override
public long addUnsignedInt(long offset, long i) {
long b2 = readUnsignedInt(offset);
b2 += i;
writeUnsignedInt(offset, b2);
return b2 & 0xFFFFFFFFL;
}
@Override
public long addLong(long offset, long i) {
long b2 = readLong(offset);
b2 += i;
writeLong(offset, b2);
return b2;
}
@Override
public float addFloat(long offset, float f) {
float b2 = readFloat(offset);
b2 += f;
writeFloat(offset, b2);
return b2;
}
@Override
public double addDouble(long offset, double d) {
double b2 = readDouble(offset);
b2 += d;
writeDouble(offset, b2);
return b2;
}
@Override
public int addAtomicInt(long offset, int i) {
return addAndGetInt(offset, i);
}
@Override
public long addAtomicLong(long offset, long delta) {
for (; ; ) {
long current = readVolatileLong(offset);
long next = current + delta;
if (compareAndSwapLong(offset, current, next))
return next;
}
}
@Override
public float addAtomicFloat(long offset, float delta) {
for (; ; ) {
int current = readVolatileInt(offset);
int next = Float.floatToRawIntBits(Float.intBitsToFloat(current) + delta);
if (compareAndSwapInt(offset, current, next))
return next;
}
}
@Override
public double addAtomicDouble(long offset, double delta) {
for (; ; ) {
long current = readVolatileLong(offset);
long next = Double.doubleToRawLongBits(Double.longBitsToDouble(current) + delta);
if (compareAndSwapLong(offset, current, next))
return next;
}
}
@Override
public float readVolatileFloat(long offset) {
return Float.intBitsToFloat(readVolatileInt(offset));
}
@Override
public double readVolatileDouble(long offset) {
return Double.longBitsToDouble(readVolatileLong(offset));
}
@Override
public void writeOrderedFloat(long offset, float v) {
writeOrderedInt(offset, Float.floatToRawIntBits(v));
}
@Override
public void writeOrderedDouble(long offset, double v) {
writeOrderedLong(offset, Double.doubleToRawLongBits(v));
}
@Override
public int length() {
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) {
if (index < 0 || index >= length())
throw new IndexOutOfBoundsException();
return (char) readUnsignedByte(position() + index);
}
@Override
public void readMarshallable(@NotNull Bytes in) throws IllegalStateException {
long len = Math.min(remaining(), in.remaining());
long inPosition = in.position();
write(in, inPosition, len);
in.position(inPosition + len);
}
@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(RandomDataInput bytes, long position, long length) {
if (length > remaining())
throw new IllegalArgumentException("Attempt to write " + length + " bytes with " + remaining() + " remaining");
if (bytes.byteOrder() == byteOrder()) {
while (length >= 8) {
writeLong(bytes.readLong(position));
position += 8;
length -= 8;
}
}
while (length >= 1) {
writeByte(bytes.readByte(position));
position++;
length--;
}
}
@Override
public void write(@NotNull Byteable byteable) {
if (byteable.bytes() == null) {
throw new IllegalArgumentException("Attempt to write an unitialized Byteable object");
}
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;
}
@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);
}
@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 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 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);
}
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");
}
}
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);
}
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;
}
}
}
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;
}
}
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
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
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;
}
}
}