/* * Copyright 2011 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 vanilla.java.chronicle.impl; import vanilla.java.chronicle.ByteString; import vanilla.java.chronicle.ByteStringAppender; import vanilla.java.chronicle.Chronicle; import vanilla.java.chronicle.Excerpt; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; /** * @author peter.lawrey */ public abstract class AbstractExcerpt<C extends Chronicle> implements Excerpt<C> { protected final DirectChronicle chronicle; protected long index; protected long start = 0; protected long position = 0; protected int capacity = 0; protected long limit = 0; protected long startPosition; protected ByteBuffer buffer; private boolean forWrite = false; 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 ExcerptInputStream inputStream = null; private ExcerptOutputStream outputStream = null; protected AbstractExcerpt(C chronicle) { this.chronicle = (DirectChronicle) chronicle; } @Override public C chronicle() { return (C) chronicle; } @Override public boolean index(long index) throws IndexOutOfBoundsException { readMemoryBarrier(); long endPosition = chronicle.getIndexData(index + 1); if (endPosition == 0) { capacity = 0; buffer = null; return false; } long startPosition = chronicle.getIndexData(index); capacity = (int) (endPosition - startPosition); index0(index, startPosition, endPosition); forWrite = false; // TODO Assumes the start of the record won't be all 0's // TODO Need to determine whether this is required as a safety check or not. return readLong(0) != 0L; } private boolean readMemoryBarrier() { return barrier.get(); } @Override public void startExcerpt(int capacity) { this.capacity = capacity; long startPosition = chronicle.startExcerpt(capacity); long endPosition = startPosition + capacity; index0(chronicle.size(), startPosition, endPosition); forWrite = true; } @Override public void finish() { if (position > limit) throw new IllegalStateException("Capacity allowed: " + capacity + " data read/written: " + (position - start)); if (forWrite) { final long endPosition = startPosition + (position - start); chronicle.setIndexData(index + 1, endPosition); chronicle.incrSize(); capacity = (int) (position - start); writeMemoryBarrier(); } } protected abstract void index0(long index, long startPosition, long endPosition); final AtomicBoolean barrier = new AtomicBoolean(); private void writeMemoryBarrier() { barrier.lazySet(true); } @Override public long index() { return index; } @Override public Excerpt<C> position(int position) { if (position < 0 || position > capacity()) throw new IndexOutOfBoundsException(); this.position = start + position; // start has to be added return this; } @Override public int position() { return (int) (position - start); } @Override public int capacity() { return (int) (limit - start); } @Override public int remaining() { return (int) (limit - position); } @Override public void readFully(byte[] b) { readFully(b, 0, b.length); } // RandomDataInput @Override public int skipBytes(int n) { int position = position(); int n2 = Math.min(n, capacity - position); position(position + n2); return n2; } @Override public void readFully(byte[] b, int off, int len) { while (len-- > 0) b[off++] = readByte(); } @Override public boolean readBoolean() { return readByte() != 0; } @Override public boolean readBoolean(int offset) { return readByte(offset) != 0; } @Override public int readUnsignedByte() { return readByte() & 0xFF; } @Override public int readUnsignedByte(int offset) { return readByte(offset) & 0xFF; } @Override public int readUnsignedShort() { return readShort() & 0xFFFF; } @Override public int readUnsignedShort(int offset) { return readShort(offset) & 0xFFFF; } @Override public String readLine() { StringBuilder input = new StringBuilder(); EOL: while (position() < capacity()) { int c = readUnsignedByte(); switch (c) { case '\n': break EOL; case '\r': int cur = position(); if (cur < capacity() && readByte(cur) == '\n') position(cur + 1); break EOL; default: input.append((char) c); break; } } return input.toString(); } @Override public String readUTF() { try { return DataInputStream.readUTF(this); } catch (IOException e) { throw new AssertionError(e); } } @Override public String readUTF(int offset) { long oldPosition = position; position = offset; try { return readUTF(); } finally { position = oldPosition; } } 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; @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() { if (chronicle.byteOrder() == ByteOrder.BIG_ENDIAN) return (readUnsignedByte() << 24 + readUnsignedShort() << 8) >> 8; // extra shifting to get sign extension. return (readUnsignedByte() << 8 + readUnsignedShort() << 16) >> 8; } @Override public int readInt24(int offset) { if (chronicle.byteOrder() == ByteOrder.BIG_ENDIAN) return (readUnsignedByte(offset) << 24 + readUnsignedShort(offset + 1) << 8) >> 8; // extra shifting to get sign extension. return (readUnsignedByte(offset) << 8 + readUnsignedShort(offset + 1) << 16) >> 8; } @Override public long readUnsignedInt() { return readInt() & 0xFFFFFFFFL; } @Override public long readUnsignedInt(int offset) { return readInt(offset) & 0xFFFFFFFFL; } 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 = 0xFFFF; @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 = readUnsignedByte(); if (b == USHORT_EXTENDED) return readUnsignedInt(); return b; } @Override public long readInt48() { if (chronicle.byteOrder() == ByteOrder.BIG_ENDIAN) return ((long) readUnsignedShort() << 48 + readUnsignedInt() << 16) >> 16; // extra shifting to get sign extension. return (readUnsignedShort() << 16 + readUnsignedInt() << 32) >> 8; } @Override public long readInt48(int offset) { if (chronicle.byteOrder() == ByteOrder.BIG_ENDIAN) return ((long) readUnsignedShort(offset) << 48 + readUnsignedInt(offset + 2) << 16) >> 16; // extra shifting to get sign extension. return (readUnsignedShort(offset) << 16 + readUnsignedInt(offset + 2) << 32) >> 16; } 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; @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; } } // RandomDataOutput @Override public double readCompactDouble() { float f = readFloat(); if (Float.isNaN(f)) return readDouble(); return f; } @Override public void readByteString(ByteString as) { as.clear(); int len = readByte() & 0xFF; for (int i = 0; i < len; i++) as.append(readByte()); } @Override public int readByteString(int offset, ByteString as) { as.clear(); int len = readByte(offset) & 0xFF; for (int i = 1; i <= len; i++) as.append(readByte(offset + i)); return offset + len + 1; } @Override public void readByteString(StringBuilder sb) { sb.setLength(0); int len = readByte() & 0xFF; for (int i = 0; i < len; i++) sb.append(readByte()); } @Override public int readByteString(int offset, StringBuilder sb) { sb.setLength(0); int len = readByte(offset) & 0xFF; for (int i = 1; i <= len; i++) sb.append(readByte(offset + i)); return offset + len + 1; } @Override public String readByteString() { int len = readByte() & 0xFF; if (len == 0) return ""; byte[] bytes = new byte[len]; for (int i = 0; i < len; i++) bytes[i] = readByte(); return new String(bytes, 0); } @Override public void readChars(StringBuilder sb) { int len = readChar(); sb.setLength(0); for (int i = 0; i < len; i++) sb.append(readChar()); } @Override public String readChars() { int len = readChar(); if (len == 0) return ""; char[] chars = new char[len]; for (int i = 0; i < len; i++) chars[i] = readChar(); return new String(chars); } @Override public ByteOrder order() { return buffer.order(); } @Override public void read(ByteBuffer bb) { int len = Math.min(bb.remaining(), length()); if (bb.order() == order()) { while (len >= 8) { bb.putLong(readLong()); len -= 8; } } while (len > 0) { bb.put(readByte()); len--; } } //// RandomOutputStream @Override public void write(byte[] b) { write(b, 0, b.length); } @Override public void writeBoolean(boolean v) { write(v ? -1 : 0); } @Override public void writeBoolean(int offset, boolean v) { write(offset, v ? -1 : 0); } @Override public void writeBytes(String s) { writeBytes((CharSequence) s); } @Override public void writeBytes(CharSequence s) { int len = s.length(); if (len > 255) throw new IllegalArgumentException("Len cannot be " + len + " > 255"); write(len); for (int i = 0; i < len; i++) write(s.charAt(i)); } @Override public void writeBytes(int offset, CharSequence s) { int len = s.length(); if (len > 255) throw new IllegalArgumentException("Len cannot be " + len + " > 255"); write(offset, len); for (int i = 0; i < len; i++) write(s.charAt(i)); for (int i = 0; i < len; i++) write(offset + 1 + i, s.charAt(i)); } @Override public void writeChars(String s) { writeChars((CharSequence) s); } @Override public void writeChars(CharSequence s) { int len = s.length(); if (len > 65535) throw new IllegalArgumentException("Len cannot be " + len + " > 65535"); writeChar(len); for (int i = 0; i < len; i++) writeChar(s.charAt(i)); } @Override public void writeChars(int offset, CharSequence s) { int len = s.length(); if (len > 65535) throw new IllegalArgumentException("Len cannot be " + len + " > 65535"); writeChar(offset + len); for (int i = 0; i < len; i++) writeChar(offset + 2 + i, s.charAt(i)); } static final Method writeUTFMethod; static { try { writeUTFMethod = DataOutputStream.class.getDeclaredMethod("writeUTF", String.class, DataOutput.class); writeUTFMethod.setAccessible(true); } catch (NoSuchMethodException e) { throw new AssertionError(e); } } @Override public void writeUTF(String s) { try { writeUTFMethod.invoke(null, s, this); } catch (IllegalAccessException e) { throw new AssertionError(e); } catch (InvocationTargetException e) { // rethrow the orginal exception. Thread.currentThread().stop(e.getCause()); } } @Override public void writeByte(int v) { write(v); } @Override public void writeUnsignedByte(int v) { writeByte(v); } @Override public void writeUnsignedByte(int offset, int v) { write(offset, v); } @Override public void write(int offset, byte[] b) { for (int i = 0; i < b.length; i++) write(offset + i, b[i]); } @Override public void write(byte[] b, int off, int len) { for (int i = 0; i < len; i++) write(b[off + i]); } @Override public void writeUnsignedShort(int v) { writeShort(v); } @Override public void writeUnsignedShort(int 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 (chronicle.byteOrder() == ByteOrder.BIG_ENDIAN) { writeUnsignedByte(v >>> 16); writeUnsignedShort(v); } else { writeUnsignedByte(v); writeUnsignedShort(v >>> 8); } } @Override public void writeInt24(int offset, int v) { if (chronicle.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(int 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(BYTE_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 (chronicle.byteOrder() == ByteOrder.BIG_ENDIAN) { writeUnsignedShort((int) (v >>> 32)); writeUnsignedInt(v); } else { writeUnsignedShort((int) v); writeUnsignedInt(v >>> 16); } } @Override public void writeInt48(int offset, long v) { if (chronicle.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(BYTE_MIN_VALUE); } else if (v == Long.MAX_VALUE) { writeInt(BYTE_MAX_VALUE); } else { writeInt(BYTE_EXTENDED); writeLong(v); } } @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(ByteBuffer bb) { if (bb.order() == order()) while (bb.remaining() >= 8) writeLong(bb.getLong()); while (bb.remaining() >= 1) writeByte(bb.get()); } //// ByteStringAppender @Override public int length() { return position(); } @Override public ByteStringAppender append(CharSequence s) { for (int i = 0, len = s.length(); i < len; i++) writeByte(s.charAt(i)); return this; } @Override public ByteStringAppender append(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; } @Override public ByteStringAppender append(byte[] str) { write(str); return this; } @Override public ByteStringAppender append(byte[] str, int offset, int len) { write(str, offset, len); return this; } @Override public ByteStringAppender append(boolean b) { append(b ? "true" : "false"); return this; } @Override public ByteStringAppender append(char c) { writeByte(c); return this; } @Override public ByteStringAppender append(int num) { return append((long) num); } @Override public ByteStringAppender append(long num) { if (num < 0) { if (num == Long.MIN_VALUE) { append(MIN_VALUE_TEXT); return this; } writeByte('-'); num = -num; } if (num == 0) { writeByte('0'); } else { appendLong0(num); } return this; } @Override public ByteStringAppender appendTime(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; } @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) { buffer.put(Infinity); } else { buffer.put(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; } 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; } value += modDiv * mod / 5; } final double d = Math.scalb((double) value, exp); return negative ? -d : d; } private void appendLong0(long num) { // find the number of digits long power10 = power10(num); // starting from the end, write each digit while (power10 > 0) { // write the lowest digit. writeByte((byte) (num / power10 % 10 + '0')); // remove that digit. power10 /= 10; } } @Override public ByteStringAppender append(double d, int precision) { if (precision < 0) precision = 0; if (precision >= TENS.length) precision = TENS.length - 1; long power10 = TENS[precision]; 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 > 0 && val % 10 == 0) { val /= 10; precision--; } if (precision > 0) appendDouble0(val, precision); else appendLong0(val); return this; } private void appendDouble0(long num, int precision) { // find the number of digits long power10 = Math.max(TENS[precision], power10(num)); // starting from the end, write each digit long decimalPoint = TENS[precision - 1]; while (power10 > 0) { if (decimalPoint == power10) writeByte('.'); // write the lowest digit. writeByte((byte) (num / power10 % 10 + '0')); // remove that digit. power10 /= 10; } } static final long[] TENS = new long[19]; static { TENS[0] = 1; for (int i = 1; i < TENS.length; i++) TENS[i] = TENS[i - 1] * 10; } public static long power10(long l) { int idx = Arrays.binarySearch(TENS, l); return idx >= 0 ? TENS[idx] : TENS[~idx - 1]; } @Override public InputStream inputStream() { if (inputStream == null) inputStream = new ExcerptInputStream(); return inputStream; } @Override public OutputStream outputStream() { if (outputStream == null) outputStream = new ExcerptOutputStream(); return outputStream; } protected class ExcerptInputStream extends InputStream { private int mark = 0; @Override public int available() throws IOException { return remaining(); } @Override public void close() throws IOException { finish(); } @Override public void mark(int readlimit) { mark = position(); } @Override public boolean markSupported() { return true; } @Override public int read(byte[] b, int off, int len) throws IOException { AbstractExcerpt.this.readFully(b, off, len); return len; } @Override public void reset() throws IOException { position(mark); } @Override public long skip(long n) throws IOException { if (n > Integer.MAX_VALUE) throw new IOException("Skip too large"); return skipBytes((int) n); } @Override public int read() throws IOException { if (remaining() > 0) return readUnsignedByte(); return -1; } } protected class ExcerptOutputStream extends OutputStream { @Override public void close() throws IOException { finish(); } @Override public void write(byte[] b) throws IOException { AbstractExcerpt.this.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { AbstractExcerpt.this.write(b, off, len); } @Override public void write(int b) throws IOException { writeUnsignedByte(b); } } }