All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectfabric.ImmutableReader Maven / Gradle / Ivy

There is a newer version: 0.9.1
Show newest version
/**
 * This file is part of ObjectFabric (http://objectfabric.org).
 *
 * ObjectFabric is licensed under the Apache License, Version 2.0, the terms
 * of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
 * 
 * Copyright ObjectFabric Inc.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

package org.objectfabric;

import org.objectfabric.ThreadAssert.SingleThreaded;

@SingleThreaded
class ImmutableReader extends Continuation {

    private Buff _buff;

    private byte _serializationVersion;

    private char[] _chars = new char[16];

    private int _charCount;

    protected ImmutableReader(List interruptionStack) {
        super(interruptionStack);
    }

    final Buff getBuff() {
        return _buff;
    }

    final void setBuff(Buff buff) {
        _buff = buff;
    }

    //

    void reset() {
        if (Debug.COMMUNICATIONS)
            ThreadAssert.getOrCreateCurrent().resetReaderDebugCounter(this);
    }

    void clean() {
        _chars = null;
        _charCount = 0;
    }

    private final int remaining() {
        /*
         * TODO: always say 0 the first time to test all interruption sites.
         */
        return _buff.limit() - _buff.position();
    }

    void startRead() {
        _serializationVersion = _buff.getByte();
    }

    private final boolean getBoolean() {
        return _buff.getByte() != 0;
    }

    /*
     * Fixed lengths.
     */

    private static final int BYTE_LENGTH = 1;

    public final boolean canReadByte() {
        int needed = BYTE_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.BYTE_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final byte readByte() {
        return readByte(0);
    }

    public final byte readByte(int tag) {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.BYTE_INDEX, tag);

        return _buff.getByte();
    }

    public final boolean canReadByteBoxed() {
        int needed = BOOLEAN_LENGTH + BYTE_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.BYTE_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Byte readByteBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.BYTE_BOXED_INDEX, 0);

        if (getBoolean()) {
            byte value = _buff.getByte();
            return new Byte(value);
        }

        return null; // Marker Byte
    }

    //

    private static final int BOOLEAN_LENGTH = 1;

    public final boolean canReadBoolean() {
        int needed = BOOLEAN_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.BOOLEAN_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final boolean readBoolean() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.BOOLEAN_INDEX, 0);

        return getBoolean();
    }

    public final boolean canReadBooleanBoxed() {
        int needed = BOOLEAN_LENGTH + BOOLEAN_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.BOOLEAN_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Boolean readBooleanBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.BOOLEAN_BOXED_INDEX, 0);

        if (getBoolean())
            return new Boolean(getBoolean());

        return null; // Marker Boolean
    }

    //

    private static final int SHORT_LENGTH = Short.SIZE / 8;

    public final boolean canReadShort() {
        int needed = SHORT_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.SHORT_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final short readShort() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.SHORT_INDEX, 0);

        return _buff.getShort();
    }

    public final boolean canReadShortBoxed() {
        int needed = BOOLEAN_LENGTH + SHORT_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.SHORT_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Short readShortBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.SHORT_BOXED_INDEX, 0);

        if (getBoolean())
            return new Short(_buff.getShort());

        return null; // Marker Short
    }

    //

    private static final int CHARACTER_LENGTH = Character.SIZE / 8;

    public final boolean canReadCharacter() {
        int needed = CHARACTER_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.CHARACTER_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final char readCharacter() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.CHARACTER_INDEX, 0);

        return _buff.getChar();
    }

    public final boolean canReadCharacterBoxed() {
        int needed = BOOLEAN_LENGTH + CHARACTER_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.CHARACTER_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Character readCharacterBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.CHARACTER_BOXED_INDEX, 0);

        if (getBoolean())
            return new Character(_buff.getChar());

        return null; // Marker Character
    }

    //

    private static final int INTEGER_LENGTH = Integer.SIZE / 8;

    public final boolean canReadInteger() {
        int needed = INTEGER_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.INTEGER_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final int readInteger() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.INTEGER_INDEX, 0);

        return _buff.getInt();
    }

    public final boolean canReadIntegerBoxed() {
        int needed = BOOLEAN_LENGTH + INTEGER_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.INTEGER_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Integer readIntegerBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.INTEGER_BOXED_INDEX, 0);

        if (getBoolean())
            return new Integer(_buff.getInt());

        return null; // Marker Integer
    }

    //

    private static final int LONG_LENGTH = Long.SIZE / 8;

    public final boolean canReadLong() {
        int needed = LONG_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.LONG_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final long readLong() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.LONG_INDEX, 0);

        return _buff.getLong();
    }

    public final boolean canReadLongBoxed() {
        int needed = BOOLEAN_LENGTH + LONG_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.LONG_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Long readLongBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.LONG_BOXED_INDEX, 0);

        if (getBoolean())
            return new Long(_buff.getLong());

        return null; // Marker Long
    }

    //

    private static final int FLOAT_LENGTH = INTEGER_LENGTH;

    public final boolean canReadFloat() {
        int needed = FLOAT_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.FLOAT_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final float readFloat() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.FLOAT_INDEX, 0);

        int value = _buff.getInt();
        return Platform.get().intToFloat(value);
    }

    public final boolean canReadFloatBoxed() {
        int needed = BOOLEAN_LENGTH + FLOAT_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.FLOAT_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Float readFloatBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.FLOAT_BOXED_INDEX, 0);

        if (getBoolean()) {
            int bits = _buff.getInt();
            float value = Platform.get().intToFloat(bits);
            return new Float(value); // Converter
        }

        return null; // Marker Float
    }

    //

    private static final int DOUBLE_LENGTH = LONG_LENGTH;

    public final boolean canReadDouble() {
        int needed = DOUBLE_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.DOUBLE_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final double readDouble() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.DOUBLE_INDEX, 0);

        long value = _buff.getLong();
        return Platform.get().longToDouble(value);
    }

    public final boolean canReadDoubleBoxed() {
        int needed = BOOLEAN_LENGTH + DOUBLE_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.DOUBLE_BOXED_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final Double readDoubleBoxed() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.DOUBLE_BOXED_INDEX, 0);

        if (getBoolean()) {
            long bits = _buff.getLong();
            double value = Platform.get().longToDouble(bits);
            return new Double(value); // Converter
        }

        return null; // Marker Double
    }

    //

    private static final int DATE_LENGTH = LONG_LENGTH;

    public final boolean canReadDate() {
        int needed = DATE_LENGTH;

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            Helper.instance().setExpectedClass(Immutable.DATE_INDEX);
            needed += ImmutableWriter.DEBUG_OVERHEAD;
        }

        return remaining() >= needed;
    }

    public final java.util.Date readDate() {
        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            assertDebugInfo(Immutable.DATE_INDEX, 0);

        long ticks = _buff.getLong();

        if (ticks >= 0)
            return new java.util.Date((ticks - 621355968000000000L) / 10000);

        return null; // Marker Date
    }

    /*
     * Non fixed lengths.
     */

    public final String readString() {
        return readString(true, 0);
    }

    public final String readString(boolean debug, int tag) {
        int value;

        if (interrupted())
            value = resumeInt();
        else {
            value = Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications() && debug ? -2 : -1;
            _charCount = 0;
        }

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications() && debug) {
            if (value == -2) {
                if (!canReadDebugInfo()) {
                    interruptInt(-2);
                    return null;
                }

                Helper.instance().setExpectedClass(Immutable.STRING_INDEX);
                assertDebugInfo(Immutable.STRING_INDEX, tag);
            }
        }

        for (;;) {
            if (value < 0) {
                if (remaining() == 0) {
                    interruptInt(-1);
                    return null;
                }

                value = _buff.getByte() & 0xff;
            }

            if ((value & ImmutableWriter.STRING_ENC_DOES_NOT_FIT_ON_1_BYTE_MASK) == 0) {
                append((char) value);
            } else if ((value & ImmutableWriter.STRING_ENC_DOES_NOT_FIT_ON_2_BYTES_MASK) == 0) {
                if (remaining() == 0) {
                    interruptInt(value);
                    return null;
                }

                value &= ~ImmutableWriter.STRING_ENC_DOES_NOT_FIT_ON_1_BYTE_MASK;
                value = value << 8 | _buff.getByte() & 0xff;
                append((char) value);
            } else if ((value & ImmutableWriter.STRING_ENC_EOF_MASK) == 0) {
                if (remaining() < CHARACTER_LENGTH) {
                    interruptInt(value);
                    return null;
                }

                char c = _buff.getChar();
                append(c);
            } else if ((value & ImmutableWriter.STRING_ENC_NULL_MASK) == 0)
                return new String(_chars, 0, _charCount);
            else
                return null;

            value = -1;
        }
    }

    private final void append(char value) {
        if (_charCount >= _chars.length) {
            char[] temp = new char[_chars.length << 1];
            Platform.arraycopy(_chars, 0, temp, 0, _chars.length);
            _chars = temp;
        }

        _chars[_charCount++] = value;
    }

    //

    public final byte[] readBinary() {
        return readBinary(Immutable.BINARY_INDEX);
    }

    @SuppressWarnings("null")
    private final byte[] readBinary(int classId) {
        int index = Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications() ? -2 : -1;
        byte[] array = null;

        if (interrupted()) {
            index = resumeInt();
            array = (byte[]) resume();
        }

        if (Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications()) {
            if (index == -2) {
                if (!canReadDebugInfo()) {
                    interrupt(null);
                    interruptInt(-2);
                    return null;
                }

                Helper.instance().setExpectedClass(classId);
                assertDebugInfo(classId, 0);
            }
        }

        if (index < 0) {
            if (remaining() < INTEGER_LENGTH) {
                interrupt(null);
                interruptInt(-1);
                return null;
            }

            int length = _buff.getInt();

            if (length < 0)
                return null;

            array = new byte[length];
            index = 0;
        }

        // TODO: use get(byte[])

        for (; index < array.length; index++) {
            if (remaining() == 0) {
                interrupt(array);
                interruptInt(index);
                return null;
            }

            array[index] = _buff.getByte();
        }

        return array;
    }

    //

    public final java.math.BigInteger readBigInteger() {
        byte[] data = readBinary(Immutable.BIG_INTEGER_INDEX);

        if (data != null)
            return new java.math.BigInteger(data);

        return null; // Marker BigInteger
    }

    //

    public final java.math.BigDecimal readDecimal() {
        byte[] a = readBinary(Immutable.DECIMAL_INDEX);

        if (a != null) {
            byte[] b = new byte[a.length - 4];
            Platform.arraycopy(a, 0, b, 0, b.length);
            int b0 = a[a.length - 4] & 0x000000ff;
            int b1 = (a[a.length - 3] << 8) & 0x0000ff00;
            int b2 = (a[a.length - 2] << 16) & 0x00ff0000;
            int b3 = (a[a.length - 1] << 24) & 0xff000000;
            int scale = b3 | b2 | b1 | b0;
            return new java.math.BigDecimal(new java.math.BigInteger(b), scale);
        }

        return null; // Marker BigDecimal
    }

    // Debug

    public final boolean canReadDebugInfo() {
        if (!Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            throw new IllegalStateException();

        return remaining() >= ImmutableWriter.DEBUG_OVERHEAD;
    }

    private final void assertDebugInfo(int classId, int tag) {
        if (!Debug.COMMUNICATIONS && ImmutableWriter.getCheckCommunications())
            throw new IllegalStateException();

        if (Debug.COMMUNICATIONS_LOG_ALL) {
            String name = Immutable.ALL.get(classId).toString();
            long counter = ThreadAssert.getOrCreateCurrent().getReaderDebugCounter(this);
            String c = Platform.get().simpleClassName(this);
            Log.write(c + ", " + counter + ", class: " + name + ", tag: " + tag);
        }

        Debug.assertion(_serializationVersion != 0);

        long readDebugCounter = _buff.getLong();
        byte readClassId = _buff.getByte();
        int readTag = _buff.getInt();
        long debugCounter = ThreadAssert.getOrCreateCurrent().getAndIncrementReaderDebugCounter(this);
        Debug.assertion(classId == Helper.instance().getExpectedClass());
        Debug.assertion(readTag == tag);
        Debug.assertion(readClassId == (byte) classId);
        Debug.assertion(readDebugCounter == debugCounter);
    }
}