You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2015/11/05 04:28:20 UTC

[28/66] [abbrv] ignite git commit: Merge branch ignite-1282 into ignite-950-new

http://git-wip-us.apache.org/repos/asf/ignite/blob/c050b4ce/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryWriterExImpl.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryWriterExImpl.java
index 75c44b5,0000000..1681e4b
mode 100644,000000..100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryWriterExImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/BinaryWriterExImpl.java
@@@ -1,1855 -1,0 +1,1915 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one or more
 + * contributor license agreements.  See the NOTICE file distributed with
 + * this work for additional information regarding copyright ownership.
 + * The ASF licenses this file to You 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 org.apache.ignite.internal.portable;
 +
 +import org.apache.ignite.IgniteCheckedException;
 +import org.apache.ignite.binary.BinaryTypeIdMapper;
 +import org.apache.ignite.internal.portable.streams.PortableHeapOutputStream;
 +import org.apache.ignite.internal.portable.streams.PortableOutputStream;
 +import org.apache.ignite.internal.util.typedef.internal.A;
 +import org.apache.ignite.binary.BinaryObjectException;
 +import org.apache.ignite.binary.BinaryRawWriter;
 +import org.apache.ignite.binary.BinaryWriter;
 +import org.jetbrains.annotations.Nullable;
 +
 +import java.io.IOException;
 +import java.io.ObjectOutput;
 +import java.lang.reflect.InvocationTargetException;
 +import java.math.BigDecimal;
 +import java.math.BigInteger;
 +import java.sql.Timestamp;
 +import java.util.Collection;
 +import java.util.Date;
 +import java.util.IdentityHashMap;
 +import java.util.Map;
 +import java.util.UUID;
 +
 +import static java.nio.charset.StandardCharsets.UTF_8;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.BOOLEAN;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.BOOLEAN_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.BYTE;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.BYTE_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.CHAR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.CHAR_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.CLASS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.COL;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DATE;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DATE_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DECIMAL;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DECIMAL_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DOUBLE;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DOUBLE_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.ENUM;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.ENUM_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.FLAGS_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.FLOAT;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.FLOAT_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.INT;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.INT_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.LONG;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.LONG_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.MAP;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.MAP_ENTRY;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.NULL;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.OBJ;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.OBJ_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.OPTM_MARSH;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.PORTABLE_OBJ;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.SCHEMA_ID_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.SCHEMA_OR_RAW_OFF_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.SHORT;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.SHORT_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.STRING;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.STRING_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.TIMESTAMP;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.TIMESTAMP_ARR;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.TOTAL_LEN_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.UNREGISTERED_TYPE_ID;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.UUID;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.UUID_ARR;
 +
 +/**
 + * Portable writer implementation.
 + */
 +public class BinaryWriterExImpl implements BinaryWriter, BinaryRawWriterEx, ObjectOutput {
 +    /** Length: integer. */
 +    private static final int LEN_INT = 4;
 +
 +    /** */
 +    private static final int INIT_CAP = 1024;
 +
 +    /** FNV1 hash offset basis. */
 +    private static final int FNV1_OFFSET_BASIS = 0x811C9DC5;
 +
 +    /** FNV1 hash prime. */
 +    private static final int FNV1_PRIME = 0x01000193;
 +
++    /** Maximum offset which fits in 1 byte. */
++    private static final int MAX_OFFSET_1 = 1 << 8;
++
++    /** Maximum offset which fits in 2 bytes. */
++    private static final int MAX_OFFSET_2 = 1 << 16;
++
 +    /** Thread-local schema. */
 +    private static final ThreadLocal<SchemaHolder> SCHEMA = new ThreadLocal<>();
 +
 +    /** */
 +    private final PortableContext ctx;
 +
 +    /** */
 +    private final int start;
 +
 +    /** */
 +    private Class<?> cls;
 +
 +    /** */
 +    private int typeId;
 +
 +    /** Raw offset position. */
 +    private int rawOffPos;
 +
 +    /** */
 +    private boolean metaEnabled;
 +
 +    /** */
 +    private int metaHashSum;
 +
 +    /** Handles. */
 +    private Map<Object, Integer> handles;
 +
 +    /** Output stream. */
 +    private PortableOutputStream out;
 +
 +    /** Schema. */
 +    private SchemaHolder schema;
 +
 +    /** Schema ID. */
 +    private int schemaId;
 +
 +    /** Amount of written fields. */
 +    private int fieldCnt;
 +
 +    /** ID mapper. */
 +    private BinaryTypeIdMapper idMapper;
 +
 +    /**
 +     * @param ctx Context.
 +     */
 +    BinaryWriterExImpl(PortableContext ctx) {
 +        this(ctx, new PortableHeapOutputStream(INIT_CAP));
 +    }
 +
 +    /**
 +     * @param ctx Context.
 +     * @param out Output stream.
 +     */
 +    BinaryWriterExImpl(PortableContext ctx, PortableOutputStream out) {
 +        this(ctx, out, new IdentityHashMap<Object, Integer>());
 +    }
 +
 +     /**
 +      * @param ctx Context.
 +      * @param out Output stream.
 +      * @param handles Handles.
 +      */
 +     private BinaryWriterExImpl(PortableContext ctx, PortableOutputStream out, Map<Object, Integer> handles) {
 +         this.ctx = ctx;
 +         this.out = out;
 +         this.handles = handles;
 +
 +         start = out.position();
 +     }
 +
 +    /**
 +     * @param ctx Context.
 +     * @param typeId Type ID.
 +     */
 +    public BinaryWriterExImpl(PortableContext ctx, int typeId, boolean metaEnabled) {
 +        this(ctx);
 +
 +        this.typeId = typeId;
 +        this.metaEnabled = metaEnabled;
 +    }
 +
 +    /**
 +     * Close the writer releasing resources if necessary.
 +     */
 +    @Override public void close() {
 +        out.close();
 +    }
 +
 +    /**
 +     * @return Meta data hash sum or {@code null} if meta data is disabled.
 +     */
 +    @Nullable Integer metaDataHashSum() {
 +        return metaEnabled ? metaHashSum : null;
 +    }
 +
 +    /**
 +     * @param obj Object.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void marshal(Object obj) throws BinaryObjectException {
 +        marshal(obj, true);
 +    }
 +
 +    /**
 +     * @param obj Object.
 +     * @param enableReplace Object replacing enabled flag.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void marshal(Object obj, boolean enableReplace) throws BinaryObjectException {
 +        assert obj != null;
 +
 +        cls = obj.getClass();
 +
 +        PortableClassDescriptor desc = ctx.descriptorForClass(cls);
 +
 +        if (desc == null)
 +            throw new BinaryObjectException("Object is not portable: [class=" + cls + ']');
 +
 +        if (desc.excluded()) {
 +            doWriteByte(NULL);
 +            return;
 +        }
 +
 +        if (desc.useOptimizedMarshaller()) {
 +            writeByte(OPTM_MARSH);
 +
 +            try {
 +                byte[] arr = ctx.optimizedMarsh().marshal(obj);
 +
 +                writeInt(arr.length);
 +
 +                write(arr);
 +            }
 +            catch (IgniteCheckedException e) {
 +                throw new BinaryObjectException("Failed to marshal object with optimized marshaller: " + obj, e);
 +            }
 +
 +            return;
 +        }
 +
 +        if (enableReplace && desc.getWriteReplaceMethod() != null) {
 +            Object replacedObj;
 +
 +            try {
 +                replacedObj = desc.getWriteReplaceMethod().invoke(obj);
 +            }
 +            catch (IllegalAccessException e) {
 +                throw new RuntimeException(e);
 +            }
 +            catch (InvocationTargetException e) {
 +                if (e.getTargetException() instanceof BinaryObjectException)
 +                    throw (BinaryObjectException)e.getTargetException();
 +
 +                throw new BinaryObjectException("Failed to execute writeReplace() method on " + obj, e);
 +            }
 +
 +            if (replacedObj == null) {
 +                doWriteByte(NULL);
 +                return;
 +            }
 +
 +            marshal(replacedObj, false);
 +
 +            return;
 +        }
 +
 +        typeId = desc.typeId();
 +
 +        metaEnabled = ctx.isMetaDataEnabled(typeId);
 +
 +        desc.write(obj, this);
 +    }
 +
 +    /**
 +     * @param obj Object.
 +     * @return Handle.
 +     */
 +    int handle(Object obj) {
 +        assert obj != null;
 +
 +        Integer h = handles.get(obj);
 +
 +        if (h != null)
 +            return out.position() - h;
 +        else {
 +            handles.put(obj, out.position());
 +
 +            return -1;
 +        }
 +    }
 +
 +    /**
 +     * @return Array.
 +     */
 +    public byte[] array() {
 +        return out.arrayCopy();
 +    }
 +
 +    /**
 +     * @return Stream current position.
 +     */
 +    int position() {
 +        return out.position();
 +    }
 +
 +    /**
 +     * Sets new position.
 +     *
 +     * @param pos Position.
 +     */
 +    void position(int pos) {
 +        out.position(pos);
 +    }
 +
 +    /**
 +     * @param bytes Number of bytes to reserve.
 +     * @return Offset.
 +     */
 +    public int reserve(int bytes) {
 +        int pos = out.position();
 +
 +        out.position(pos + bytes);
 +
 +        return pos;
 +    }
 +
 +    /**
 +     * Perform post-write activity. This includes:
 +     * - writing object length;
 +     * - writing schema offset;
 +     * - writing schema to the tail.
 +     *
 +     * @param userType User type flag.
 +     */
 +    public void postWrite(boolean userType) {
 +        if (schema != null) {
 +            // Write schema ID.
 +            out.writeInt(start + SCHEMA_ID_POS, schemaId);
 +
 +            // Write schema offset.
 +            out.writeInt(start + SCHEMA_OR_RAW_OFF_POS, out.position() - start);
 +
 +            // Write the schema.
-             schema.writeAndPop(this, fieldCnt);
++            int offsetByteCnt = schema.write(this, fieldCnt);
 +
 +            // Write raw offset if needed.
 +            if (rawOffPos != 0)
 +                out.writeInt(rawOffPos - start);
++
++            if (offsetByteCnt == PortableUtils.OFFSET_1) {
++                int flags = (userType ? PortableUtils.FLAG_USR_TYP : 0) | PortableUtils.FLAG_OFFSET_ONE_BYTE;
++
++                out.writeShort(start + FLAGS_POS, (short)flags);
++            }
++            else if (offsetByteCnt == PortableUtils.OFFSET_2) {
++                int flags = (userType ? PortableUtils.FLAG_USR_TYP : 0) | PortableUtils.FLAG_OFFSET_TWO_BYTES;
++
++                out.writeShort(start + FLAGS_POS, (short)flags);
++            }
 +        }
 +        else {
 +            // Write raw-only flag is needed.
 +            int flags = (userType ? PortableUtils.FLAG_USR_TYP : 0) | PortableUtils.FLAG_RAW_ONLY;
 +
 +            out.writeShort(start + FLAGS_POS, (short)flags);
 +
 +            // If there are no schema, we are free to write raw offset to schema offset.
 +            out.writeInt(start + SCHEMA_OR_RAW_OFF_POS, (rawOffPos == 0 ? out.position() : rawOffPos) - start);
 +        }
 +
 +        // 5. Write length.
 +        out.writeInt(start + TOTAL_LEN_POS, out.position() - start);
 +    }
 +
 +    /**
++     * Pop schema.
++     */
++    public void popSchema() {
++        if (schema != null) {
++            assert fieldCnt > 0;
++
++            schema.pop(fieldCnt);
++        }
++    }
++
++    /**
 +     * @param val Byte array.
 +     */
 +    public void write(byte[] val) {
 +        assert val != null;
 +
 +        out.writeByteArray(val);
 +    }
 +
 +    /**
 +     * @param val Byte array.
 +     * @param off Offset.
 +     * @param len Length.
 +     */
 +    public void write(byte[] val, int off, int len) {
 +        assert val != null;
 +
 +        out.write(val, off, len);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteByte(byte val) {
 +        out.writeByte(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteShort(short val) {
 +        out.writeShort(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteInt(int val) {
 +        out.writeInt(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteLong(long val) {
 +        out.writeLong(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteFloat(float val) {
 +        out.writeFloat(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteDouble(double val) {
 +        out.writeDouble(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteChar(char val) {
 +        out.writeChar(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    public void doWriteBoolean(boolean val) {
 +        out.writeBoolean(val);
 +    }
 +
 +    /**
 +     * @param val String value.
 +     */
 +    public void doWriteDecimal(@Nullable BigDecimal val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(DECIMAL);
 +
 +            BigInteger intVal = val.unscaledValue();
 +
 +            if (intVal.signum() == -1) {
 +                intVal = intVal.negate();
 +
 +                out.writeInt(val.scale() | 0x80000000);
 +            }
 +            else
 +                out.writeInt(val.scale());
 +
 +            byte[] vals = intVal.toByteArray();
 +
 +            out.writeInt(vals.length);
 +            out.writeByteArray(vals);
 +        }
 +    }
 +
 +    /**
 +     * @param val String value.
 +     */
 +    public void doWriteString(@Nullable String val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(STRING);
 +
 +            if (ctx.isConvertString()) {
 +                doWriteBoolean(true);
 +
 +                byte[] strArr = val.getBytes(UTF_8);
 +
 +                doWriteInt(strArr.length);
 +
 +                out.writeByteArray(strArr);
 +            }
 +            else {
 +                doWriteBoolean(false);
 +
 +                char[] strArr = val.toCharArray();
 +
 +                doWriteInt(strArr.length);
 +
 +                out.writeCharArray(strArr);
 +            }
 +        }
 +    }
 +
 +    /**
 +     * @param uuid UUID.
 +     */
 +    public void doWriteUuid(@Nullable UUID uuid) {
 +        if (uuid == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(UUID);
 +            doWriteLong(uuid.getMostSignificantBits());
 +            doWriteLong(uuid.getLeastSignificantBits());
 +        }
 +    }
 +
 +    /**
 +     * @param date Date.
 +     */
 +    public void doWriteDate(@Nullable Date date) {
 +        if (date == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(DATE);
 +            doWriteLong(date.getTime());
 +        }
 +    }
 +
 +    /**
 +     * @param ts Timestamp.
 +     */
 +    public void doWriteTimestamp(@Nullable Timestamp ts) {
 +        if (ts== null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(TIMESTAMP);
 +            doWriteLong(ts.getTime());
 +            doWriteInt(ts.getNanos() % 1000000);
 +        }
 +    }
 +
 +    /**
 +     * Write object.
 +     *
 +     * @param obj Object.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    public void doWriteObject(@Nullable Object obj) throws BinaryObjectException {
 +        if (obj == null)
 +            doWriteByte(NULL);
 +        else {
 +            BinaryWriterExImpl writer = new BinaryWriterExImpl(ctx, out, handles);
 +
 +            writer.marshal(obj);
 +        }
 +    }
 +
 +    /**
 +     * @param val Byte array.
 +     */
 +    void doWriteByteArray(@Nullable byte[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(BYTE_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeByteArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Short array.
 +     */
 +    void doWriteShortArray(@Nullable short[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(SHORT_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeShortArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Integer array.
 +     */
 +    void doWriteIntArray(@Nullable int[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(INT_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeIntArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Long array.
 +     */
 +    void doWriteLongArray(@Nullable long[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(LONG_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeLongArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Float array.
 +     */
 +    void doWriteFloatArray(@Nullable float[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(FLOAT_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeFloatArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Double array.
 +     */
 +    void doWriteDoubleArray(@Nullable double[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(DOUBLE_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeDoubleArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Char array.
 +     */
 +    void doWriteCharArray(@Nullable char[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(CHAR_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeCharArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Boolean array.
 +     */
 +    void doWriteBooleanArray(@Nullable boolean[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(BOOLEAN_ARR);
 +            doWriteInt(val.length);
 +
 +            out.writeBooleanArray(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Array of strings.
 +     */
 +    void doWriteDecimalArray(@Nullable BigDecimal[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(DECIMAL_ARR);
 +            doWriteInt(val.length);
 +
 +            for (BigDecimal str : val)
 +                doWriteDecimal(str);
 +        }
 +    }
 +
 +    /**
 +     * @param val Array of strings.
 +     */
 +    void doWriteStringArray(@Nullable String[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(STRING_ARR);
 +            doWriteInt(val.length);
 +
 +            for (String str : val)
 +                doWriteString(str);
 +        }
 +    }
 +
 +    /**
 +     * @param val Array of UUIDs.
 +     */
 +    void doWriteUuidArray(@Nullable UUID[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(UUID_ARR);
 +            doWriteInt(val.length);
 +
 +            for (UUID uuid : val)
 +                doWriteUuid(uuid);
 +        }
 +    }
 +
 +    /**
 +     * @param val Array of dates.
 +     */
 +    void doWriteDateArray(@Nullable Date[] val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            doWriteByte(DATE_ARR);
 +            doWriteInt(val.length);
 +
 +            for (Date date : val)
 +                doWriteDate(date);
 +        }
 +    }
 +
 +     /**
 +      * @param val Array of timestamps.
 +      */
 +     void doWriteTimestampArray(@Nullable Timestamp[] val) {
 +         if (val == null)
 +             doWriteByte(NULL);
 +         else {
 +             if (tryWriteAsHandle(val))
 +                 return;
 +
 +             doWriteByte(TIMESTAMP_ARR);
 +             doWriteInt(val.length);
 +
 +             for (Timestamp ts : val)
 +                 doWriteTimestamp(ts);
 +         }
 +     }
 +
 +    /**
 +     * @param val Array of objects.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void doWriteObjectArray(@Nullable Object[] val) throws BinaryObjectException {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(val))
 +                return;
 +
 +            PortableClassDescriptor desc = ctx.descriptorForClass(val.getClass().getComponentType());
 +
 +            doWriteByte(OBJ_ARR);
 +
 +            if (desc.registered())
 +                doWriteInt(desc.typeId());
 +            else {
 +                doWriteInt(UNREGISTERED_TYPE_ID);
 +                doWriteString(val.getClass().getComponentType().getName());
 +            }
 +
 +            doWriteInt(val.length);
 +
 +            for (Object obj : val)
 +                doWriteObject(obj);
 +        }
 +    }
 +
 +    /**
 +     * @param col Collection.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void doWriteCollection(@Nullable Collection<?> col) throws BinaryObjectException {
 +        if (col == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(col))
 +                return;
 +
 +            doWriteByte(COL);
 +            doWriteInt(col.size());
 +            doWriteByte(ctx.collectionType(col.getClass()));
 +
 +            for (Object obj : col)
 +                doWriteObject(obj);
 +        }
 +    }
 +
 +    /**
 +     * @param map Map.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void doWriteMap(@Nullable Map<?, ?> map) throws BinaryObjectException {
 +        if (map == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(map))
 +                return;
 +
 +            doWriteByte(MAP);
 +            doWriteInt(map.size());
 +            doWriteByte(ctx.mapType(map.getClass()));
 +
 +            for (Map.Entry<?, ?> e : map.entrySet()) {
 +                doWriteObject(e.getKey());
 +                doWriteObject(e.getValue());
 +            }
 +        }
 +    }
 +
 +    /**
 +     * @param e Map entry.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void doWriteMapEntry(@Nullable Map.Entry<?, ?> e) throws BinaryObjectException {
 +        if (e == null)
 +            doWriteByte(NULL);
 +        else {
 +            if (tryWriteAsHandle(e))
 +                return;
 +
 +            doWriteByte(MAP_ENTRY);
 +            doWriteObject(e.getKey());
 +            doWriteObject(e.getValue());
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void doWriteEnum(@Nullable Enum<?> val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            PortableClassDescriptor desc = ctx.descriptorForClass(val.getClass());
 +
 +            doWriteByte(ENUM);
 +
 +            if (desc.registered())
 +                doWriteInt(desc.typeId());
 +            else {
 +                doWriteInt(UNREGISTERED_TYPE_ID);
 +                doWriteString(val.getClass().getName());
 +            }
 +
 +            doWriteInt(val.ordinal());
 +        }
 +    }
 +
 +    /**
 +     * @param val Array.
 +     */
 +    void doWriteEnumArray(@Nullable Object[] val) {
 +        assert val == null || val.getClass().getComponentType().isEnum();
 +
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            PortableClassDescriptor desc = ctx.descriptorForClass(val.getClass().getComponentType());
 +            doWriteByte(ENUM_ARR);
 +
 +            if (desc.registered())
 +                doWriteInt(desc.typeId());
 +            else {
 +                doWriteInt(UNREGISTERED_TYPE_ID);
 +                doWriteString(val.getClass().getComponentType().getName());
 +            }
 +
 +            doWriteInt(val.length);
 +
 +            // TODO: Denis: Redundant data for each element of the array.
 +            for (Object o : val)
 +                doWriteEnum((Enum<?>)o);
 +        }
 +    }
 +
 +    /**
 +     * @param val Class.
 +     */
 +    void doWriteClass(@Nullable Class val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            PortableClassDescriptor desc = ctx.descriptorForClass(val);
 +
 +            doWriteByte(CLASS);
 +
 +            if (desc.registered())
 +                doWriteInt(desc.typeId());
 +            else {
 +                doWriteInt(UNREGISTERED_TYPE_ID);
 +                doWriteString(val.getClass().getName());
 +            }
 +        }
 +    }
 +
 +    /**
 +     * @param po Portable object.
 +     */
 +    public void doWritePortableObject(@Nullable BinaryObjectImpl po) {
 +        if (po == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(PORTABLE_OBJ);
 +
 +            byte[] poArr = po.array();
 +
 +            doWriteInt(poArr.length);
 +
 +            out.writeByteArray(poArr);
 +
 +            doWriteInt(po.start());
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeByteField(@Nullable Byte val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(BYTE);
 +            doWriteByte(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Class.
 +     */
 +    void writeClassField(@Nullable Class val) {
 +        doWriteClass(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeShortField(@Nullable Short val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(SHORT);
 +            doWriteShort(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeIntField(@Nullable Integer val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(INT);
 +            doWriteInt(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeLongField(@Nullable Long val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(LONG);
 +            doWriteLong(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeFloatField(@Nullable Float val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(FLOAT);
 +            doWriteFloat(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeDoubleField(@Nullable Double val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(DOUBLE);
 +            doWriteDouble(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeCharField(@Nullable Character val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(CHAR);
 +            doWriteChar(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeBooleanField(@Nullable Boolean val) {
 +        if (val == null)
 +            doWriteByte(NULL);
 +        else {
 +            doWriteByte(BOOLEAN);
 +            doWriteBoolean(val);
 +        }
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeDecimalField(@Nullable BigDecimal val) {
 +        doWriteDecimal(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeStringField(@Nullable String val) {
 +        doWriteString(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeUuidField(@Nullable UUID val) {
 +        doWriteUuid(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeDateField(@Nullable Date val) {
 +        doWriteDate(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeTimestampField(@Nullable Timestamp val) {
 +        doWriteTimestamp(val);
 +    }
 +
 +    /**
 +     * @param obj Object.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void writeObjectField(@Nullable Object obj) throws BinaryObjectException {
 +        doWriteObject(obj);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeByteArrayField(@Nullable byte[] val) {
 +        doWriteByteArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeShortArrayField(@Nullable short[] val) {
 +        doWriteShortArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeIntArrayField(@Nullable int[] val) {
 +        doWriteIntArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeLongArrayField(@Nullable long[] val) {
 +        doWriteLongArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeFloatArrayField(@Nullable float[] val) {
 +        doWriteFloatArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeDoubleArrayField(@Nullable double[] val) {
 +        doWriteDoubleArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeCharArrayField(@Nullable char[] val) {
 +        doWriteCharArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeBooleanArrayField(@Nullable boolean[] val) {
 +        doWriteBooleanArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeDecimalArrayField(@Nullable BigDecimal[] val) {
 +        doWriteDecimalArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeStringArrayField(@Nullable String[] val) {
 +        doWriteStringArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeUuidArrayField(@Nullable UUID[] val) {
 +        doWriteUuidArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeDateArrayField(@Nullable Date[] val) {
 +        doWriteDateArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeTimestampArrayField(@Nullable Timestamp[] val) {
 +        doWriteTimestampArray(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void writeObjectArrayField(@Nullable Object[] val) throws BinaryObjectException {
 +        doWriteObjectArray(val);
 +    }
 +
 +    /**
 +     * @param col Collection.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void writeCollectionField(@Nullable Collection<?> col) throws BinaryObjectException {
 +        doWriteCollection(col);
 +    }
 +
 +    /**
 +     * @param map Map.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void writeMapField(@Nullable Map<?, ?> map) throws BinaryObjectException {
 +        doWriteMap(map);
 +    }
 +
 +    /**
 +     * @param e Map entry.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void writeMapEntryField(@Nullable Map.Entry<?, ?> e) throws BinaryObjectException {
 +        doWriteMapEntry(e);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeEnumField(@Nullable Enum<?> val) {
 +        doWriteEnum(val);
 +    }
 +
 +    /**
 +     * @param val Value.
 +     */
 +    void writeEnumArrayField(@Nullable Object[] val) {
 +        doWriteEnumArray(val);
 +    }
 +
 +    /**
 +     * @param po Portable object.
 +     * @throws org.apache.ignite.binary.BinaryObjectException In case of error.
 +     */
 +    void writePortableObjectField(@Nullable BinaryObjectImpl po) throws BinaryObjectException {
 +        doWritePortableObject(po);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeByte(String fieldName, byte val) throws BinaryObjectException {
 +        writeFieldId(fieldName, BYTE);
 +        writeByteField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeByte(byte val) throws BinaryObjectException {
 +        doWriteByte(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeShort(String fieldName, short val) throws BinaryObjectException {
 +        writeFieldId(fieldName, SHORT);
 +        writeShortField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeShort(short val) throws BinaryObjectException {
 +        doWriteShort(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeInt(String fieldName, int val) throws BinaryObjectException {
 +        writeFieldId(fieldName, INT);
 +        writeIntField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeInt(int val) throws BinaryObjectException {
 +        doWriteInt(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeLong(String fieldName, long val) throws BinaryObjectException {
 +        writeFieldId(fieldName, LONG);
 +        writeLongField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeLong(long val) throws BinaryObjectException {
 +        doWriteLong(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeFloat(String fieldName, float val) throws BinaryObjectException {
 +        writeFieldId(fieldName, FLOAT);
 +        writeFloatField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeFloat(float val) throws BinaryObjectException {
 +        doWriteFloat(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDouble(String fieldName, double val) throws BinaryObjectException {
 +        writeFieldId(fieldName, DOUBLE);
 +        writeDoubleField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDouble(double val) throws BinaryObjectException {
 +        doWriteDouble(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeChar(String fieldName, char val) throws BinaryObjectException {
 +        writeFieldId(fieldName, CHAR);
 +        writeCharField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeChar(char val) throws BinaryObjectException {
 +        doWriteChar(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeBoolean(String fieldName, boolean val) throws BinaryObjectException {
 +        writeFieldId(fieldName, BOOLEAN);
 +        writeBooleanField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeBoolean(boolean val) throws BinaryObjectException {
 +        doWriteBoolean(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDecimal(String fieldName, @Nullable BigDecimal val) throws BinaryObjectException {
 +        writeFieldId(fieldName, DECIMAL);
 +        writeDecimalField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDecimal(@Nullable BigDecimal val) throws BinaryObjectException {
 +        doWriteDecimal(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeString(String fieldName, @Nullable String val) throws BinaryObjectException {
 +        writeFieldId(fieldName, STRING);
 +        writeStringField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeString(@Nullable String val) throws BinaryObjectException {
 +        doWriteString(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeUuid(String fieldName, @Nullable UUID val) throws BinaryObjectException {
 +        writeFieldId(fieldName, UUID);
 +        writeUuidField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeUuid(@Nullable UUID val) throws BinaryObjectException {
 +        doWriteUuid(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDate(String fieldName, @Nullable Date val) throws BinaryObjectException {
 +        writeFieldId(fieldName, DATE);
 +        writeDateField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDate(@Nullable Date val) throws BinaryObjectException {
 +        doWriteDate(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeTimestamp(String fieldName, @Nullable Timestamp val) throws BinaryObjectException {
 +        writeFieldId(fieldName, TIMESTAMP);
 +        writeTimestampField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeTimestamp(@Nullable Timestamp val) throws BinaryObjectException {
 +        doWriteTimestamp(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeObject(String fieldName, @Nullable Object obj) throws BinaryObjectException {
 +        writeFieldId(fieldName, OBJ);
 +        writeObjectField(obj);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeObject(@Nullable Object obj) throws BinaryObjectException {
 +        doWriteObject(obj);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeObjectDetached(@Nullable Object obj) throws BinaryObjectException {
 +        if (obj == null)
 +            doWriteByte(NULL);
 +        else {
 +            BinaryWriterExImpl writer = new BinaryWriterExImpl(ctx, out, new IdentityHashMap<Object, Integer>());
 +
 +            writer.marshal(obj);
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeByteArray(String fieldName, @Nullable byte[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, BYTE_ARR);
 +        writeByteArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeByteArray(@Nullable byte[] val) throws BinaryObjectException {
 +        doWriteByteArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeShortArray(String fieldName, @Nullable short[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, SHORT_ARR);
 +        writeShortArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeShortArray(@Nullable short[] val) throws BinaryObjectException {
 +        doWriteShortArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeIntArray(String fieldName, @Nullable int[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, INT_ARR);
 +        writeIntArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeIntArray(@Nullable int[] val) throws BinaryObjectException {
 +        doWriteIntArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeLongArray(String fieldName, @Nullable long[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, LONG_ARR);
 +        writeLongArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeLongArray(@Nullable long[] val) throws BinaryObjectException {
 +        doWriteLongArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeFloatArray(String fieldName, @Nullable float[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, FLOAT_ARR);
 +        writeFloatArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeFloatArray(@Nullable float[] val) throws BinaryObjectException {
 +        doWriteFloatArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDoubleArray(String fieldName, @Nullable double[] val)
 +        throws BinaryObjectException {
 +        writeFieldId(fieldName, DOUBLE_ARR);
 +        writeDoubleArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDoubleArray(@Nullable double[] val) throws BinaryObjectException {
 +        doWriteDoubleArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeCharArray(String fieldName, @Nullable char[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, CHAR_ARR);
 +        writeCharArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeCharArray(@Nullable char[] val) throws BinaryObjectException {
 +        doWriteCharArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeBooleanArray(String fieldName, @Nullable boolean[] val)
 +        throws BinaryObjectException {
 +        writeFieldId(fieldName, BOOLEAN_ARR);
 +        writeBooleanArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeBooleanArray(@Nullable boolean[] val) throws BinaryObjectException {
 +        doWriteBooleanArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDecimalArray(String fieldName, @Nullable BigDecimal[] val)
 +        throws BinaryObjectException {
 +        writeFieldId(fieldName, DECIMAL_ARR);
 +        writeDecimalArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDecimalArray(@Nullable BigDecimal[] val) throws BinaryObjectException {
 +        doWriteDecimalArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeStringArray(String fieldName, @Nullable String[] val)
 +        throws BinaryObjectException {
 +        writeFieldId(fieldName, STRING_ARR);
 +        writeStringArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeStringArray(@Nullable String[] val) throws BinaryObjectException {
 +        doWriteStringArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeUuidArray(String fieldName, @Nullable UUID[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, UUID_ARR);
 +        writeUuidArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeUuidArray(@Nullable UUID[] val) throws BinaryObjectException {
 +        doWriteUuidArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDateArray(String fieldName, @Nullable Date[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, DATE_ARR);
 +        writeDateArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeDateArray(@Nullable Date[] val) throws BinaryObjectException {
 +        doWriteDateArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeTimestampArray(String fieldName, @Nullable Timestamp[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, TIMESTAMP_ARR);
 +        writeTimestampArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeTimestampArray(@Nullable Timestamp[] val) throws BinaryObjectException {
 +        doWriteTimestampArray(val);
 +    }
 +
 +     /** {@inheritDoc} */
 +    @Override public void writeObjectArray(String fieldName, @Nullable Object[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, OBJ_ARR);
 +        writeObjectArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeObjectArray(@Nullable Object[] val) throws BinaryObjectException {
 +        doWriteObjectArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T> void writeCollection(String fieldName, @Nullable Collection<T> col)
 +        throws BinaryObjectException {
 +        writeFieldId(fieldName, COL);
 +        writeCollectionField(col);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T> void writeCollection(@Nullable Collection<T> col) throws BinaryObjectException {
 +        doWriteCollection(col);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <K, V> void writeMap(String fieldName, @Nullable Map<K, V> map)
 +        throws BinaryObjectException {
 +        writeFieldId(fieldName, MAP);
 +        writeMapField(map);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <K, V> void writeMap(@Nullable Map<K, V> map) throws BinaryObjectException {
 +        doWriteMap(map);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T extends Enum<?>> void writeEnum(String fieldName, T val) throws BinaryObjectException {
 +        writeFieldId(fieldName, ENUM);
 +        writeEnumField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T extends Enum<?>> void writeEnum(T val) throws BinaryObjectException {
 +        doWriteEnum(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T extends Enum<?>> void writeEnumArray(String fieldName, T[] val) throws BinaryObjectException {
 +        writeFieldId(fieldName, ENUM_ARR);
 +        writeEnumArrayField(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T extends Enum<?>> void writeEnumArray(T[] val) throws BinaryObjectException {
 +        doWriteEnumArray(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public BinaryRawWriter rawWriter() {
 +        if (rawOffPos == 0)
 +            rawOffPos = out.position();
 +
 +        return this;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public PortableOutputStream out() {
 +        return out;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @SuppressWarnings("NullableProblems")
 +    @Override public void writeBytes(String s) throws IOException {
 +        int len = s.length();
 +
 +        writeInt(len);
 +
 +        for (int i = 0; i < len; i++)
 +            writeByte(s.charAt(i));
 +    }
 +
 +    /** {@inheritDoc} */
 +    @SuppressWarnings("NullableProblems")
 +    @Override public void writeChars(String s) throws IOException {
 +        int len = s.length();
 +
 +        writeInt(len);
 +
 +        for (int i = 0; i < len; i++)
 +            writeChar(s.charAt(i));
 +    }
 +
 +    /** {@inheritDoc} */
 +    @SuppressWarnings("NullableProblems")
 +    @Override public void writeUTF(String s) throws IOException {
 +        writeString(s);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeByte(int v) throws IOException {
 +        doWriteByte((byte) v);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeShort(int v) throws IOException {
 +        doWriteShort((short) v);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeChar(int v) throws IOException {
 +        doWriteChar((char) v);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void write(int b) throws IOException {
 +        doWriteByte((byte) b);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void flush() throws IOException {
 +        // No-op.
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public int reserveInt() {
 +        return reserve(LEN_INT);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void writeInt(int pos, int val) throws BinaryObjectException {
 +        out.writeInt(pos, val);
 +    }
 +
 +    /**
 +     * @param fieldName Field name.
 +     * @throws org.apache.ignite.binary.BinaryObjectException If fields are not allowed.
 +     */
 +    private void writeFieldId(String fieldName, byte fieldType) throws BinaryObjectException {
 +        A.notNull(fieldName, "fieldName");
 +
 +        if (rawOffPos != 0)
 +            throw new BinaryObjectException("Individual field can't be written after raw writer is acquired " +
 +                "via rawWriter() method. Consider fixing serialization logic for class: " + cls.getName());
 +
 +        if (idMapper == null)
 +            idMapper = ctx.userTypeIdMapper(typeId);
 +
 +        int id = idMapper.fieldId(typeId, fieldName);
 +
 +        writeFieldId(id);
 +
 +        if (metaEnabled)
 +            metaHashSum = 31 * metaHashSum + (id + fieldType);
 +    }
 +
 +    /**
 +     * Write field ID.
 +     * @param fieldId Field ID.
 +     */
 +    public void writeFieldId(int fieldId) {
 +        int fieldOff = out.position() - start;
 +
 +        if (schema == null) {
 +            schema = SCHEMA.get();
 +
 +            if (schema == null) {
 +                schema = new SchemaHolder();
 +
 +                SCHEMA.set(schema);
 +            }
 +
 +            // Initialize offset when the first field is written.
 +            schemaId = FNV1_OFFSET_BASIS;
 +        }
 +
 +        // Advance schema hash.
 +        int schemaId0 = schemaId ^ (fieldId & 0xFF);
 +        schemaId0 = schemaId0 * FNV1_PRIME;
 +        schemaId0 = schemaId0 ^ ((fieldId >> 8) & 0xFF);
 +        schemaId0 = schemaId0 * FNV1_PRIME;
 +        schemaId0 = schemaId0 ^ ((fieldId >> 16) & 0xFF);
 +        schemaId0 = schemaId0 * FNV1_PRIME;
 +        schemaId0 = schemaId0 ^ ((fieldId >> 24) & 0xFF);
 +        schemaId0 = schemaId0 * FNV1_PRIME;
 +
 +        schemaId = schemaId0;
 +
 +        schema.push(fieldId, fieldOff);
 +
 +        fieldCnt++;
 +    }
 +
 +    /**
 +     * Attempts to write the object as a handle.
 +     *
 +     * @param obj Object to write.
 +     * @return {@code true} if the object has been written as a handle.
 +     */
 +    boolean tryWriteAsHandle(Object obj) {
 +        int handle = handle(obj);
 +
 +        if (handle >= 0) {
 +            doWriteByte(GridPortableMarshaller.HANDLE);
 +            doWriteInt(handle);
 +
 +            return true;
 +        }
 +
 +        return false;
 +    }
 +
 +    /**
 +     * Create new writer with same context.
 +     *
 +     * @param typeId type
 +     * @return New writer.
 +     */
 +    public BinaryWriterExImpl newWriter(int typeId) {
 +        BinaryWriterExImpl res = new BinaryWriterExImpl(ctx, out, handles);
 +
 +        res.typeId = typeId;
 +
 +        return res;
 +    }
 +
 +    /**
 +     * @return Portable context.
 +     */
 +    public PortableContext context() {
 +        return ctx;
 +    }
 +
 +    /**
 +     * Schema holder.
 +     */
 +    private static class SchemaHolder {
 +        /** Grow step. */
 +        private static final int GROW_STEP = 16;
 +
 +        /** Maximum stable size. */
 +        private static final int MAX_SIZE = 256;
 +
 +        /** Data. */
 +        private int[] data;
 +
 +        /** Index. */
 +        private int idx;
 +
 +        /**
 +         * Constructor.
 +         */
 +        public SchemaHolder() {
 +            data = new int[GROW_STEP];
 +        }
 +
 +        /**
 +         * Push another frame.
 +         *
 +         * @param id Field ID.
 +         * @param off Field offset.
 +         */
 +        public void push(int id, int off) {
 +            if (idx == data.length) {
 +                int[] data0 = new int[data.length + GROW_STEP];
 +
 +                System.arraycopy(data, 0, data0, 0, data.length);
 +
 +                data = data0;
 +            }
 +
 +            data[idx] = id;
 +            data[idx + 1] = off;
 +
 +            idx += 2;
 +        }
 +
 +        /**
 +         * Write collected frames and pop them.
 +         *
 +         * @param writer Writer.
-          * @param cnt Count.
++         * @param fieldCnt Count.
++         * @return Amount of bytes dedicated to
 +         */
-         public void writeAndPop(BinaryWriterExImpl writer, int cnt) {
-             int startIdx = idx - cnt * 2;
++        public int write(BinaryWriterExImpl writer, int fieldCnt) {
++            int startIdx = idx - fieldCnt * 2;
 +
 +            assert startIdx >= 0;
 +
-             for (int idx0 = startIdx; idx0 < idx;) {
-                 writer.writeInt(data[idx0++]);
-                 writer.writeInt(data[idx0++]);
++            int lastOffset = data[idx - 1];
++
++            int res;
++
++            if (lastOffset < MAX_OFFSET_1) {
++                for (int idx0 = startIdx; idx0 < idx; ) {
++                    writer.writeInt(data[idx0++]);
++                    writer.writeByte((byte) data[idx0++]);
++                }
++
++                res = PortableUtils.OFFSET_1;
 +            }
++            else if (lastOffset < MAX_OFFSET_2) {
++                for (int idx0 = startIdx; idx0 < idx; ) {
++                    writer.writeInt(data[idx0++]);
++                    writer.writeShort((short)data[idx0++]);
++                }
++
++                res = PortableUtils.OFFSET_2;
++            }
++            else {
++                for (int idx0 = startIdx; idx0 < idx; ) {
++                    writer.writeInt(data[idx0++]);
++                    writer.writeInt(data[idx0++]);
++                }
 +
-             idx = startIdx;
++                res = PortableUtils.OFFSET_4;
++            }
++
++            return res;
++        }
++
++        /**
++         * Pop current object's frame.
++         */
++        public void pop(int fieldCnt) {
++            idx = idx - fieldCnt * 2;
 +
 +            // Shrink data array if needed.
 +            if (idx == 0 && data.length > MAX_SIZE)
 +                data = new int[MAX_SIZE];
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/ignite/blob/c050b4ce/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
index bbd6ad9,aa72017..b95dc3d
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableClassDescriptor.java
@@@ -524,21 -524,26 +524,26 @@@ public class PortableClassDescriptor 
  
              case PORTABLE:
                  if (writeHeader(obj, writer)) {
-                     if (serializer != null)
-                         serializer.writeBinary(obj, writer);
-                     else
-                         ((Binarylizable)obj).writeBinary(writer);
+                     try {
+                         if (serializer != null)
 -                            serializer.writePortable(obj, writer);
++                            serializer.writeBinary(obj, writer);
+                         else
 -                            ((PortableMarshalAware) obj).writePortable(writer);
++                            ((Binarylizable)obj).writeBinary(writer);
  
-                     writer.postWrite(userType);
+                         writer.postWrite(userType);
+                     }
+                     finally {
+                         writer.popSchema();
+                     }
  
 -                    if (obj.getClass() != PortableMetaDataImpl.class
 +                    if (obj.getClass() != BinaryMetaDataImpl.class
                          && ctx.isMetaDataChanged(typeId, writer.metaDataHashSum())) {
 -                        PortableMetaDataCollector metaCollector = new PortableMetaDataCollector(typeName);
 +                        BinaryMetaDataCollector metaCollector = new BinaryMetaDataCollector(typeName);
  
                          if (serializer != null)
 -                            serializer.writePortable(obj, metaCollector);
 +                            serializer.writeBinary(obj, metaCollector);
                          else
 -                            ((PortableMarshalAware)obj).writePortable(metaCollector);
 +                            ((Binarylizable)obj).writeBinary(metaCollector);
  
                          ctx.updateMetaData(typeId, typeName, metaCollector.meta());
                      }
@@@ -552,12 -557,15 +557,15 @@@
  
                      try {
                          ((Externalizable)obj).writeExternal(writer);
+ 
+                         writer.postWrite(userType);
                      }
                      catch (IOException e) {
 -                        throw new PortableException("Failed to write Externalizable object: " + obj, e);
 +                        throw new BinaryObjectException("Failed to write Externalizable object: " + obj, e);
                      }
- 
-                     writer.postWrite(userType);
+                     finally {
+                         writer.popSchema();
+                     }
                  }
  
                  break;

http://git-wip-us.apache.org/repos/asf/ignite/blob/c050b4ce/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableUtils.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/ignite/blob/c050b4ce/modules/core/src/main/java/org/apache/ignite/internal/portable/builder/BinaryObjectBuilderImpl.java
----------------------------------------------------------------------
diff --cc modules/core/src/main/java/org/apache/ignite/internal/portable/builder/BinaryObjectBuilderImpl.java
index e6a68c1,0000000..a809d3c
mode 100644,000000..100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/builder/BinaryObjectBuilderImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/builder/BinaryObjectBuilderImpl.java
@@@ -1,551 -1,0 +1,566 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one or more
 + * contributor license agreements.  See the NOTICE file distributed with
 + * this work for additional information regarding copyright ownership.
 + * The ASF licenses this file to You 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 org.apache.ignite.internal.portable.builder;
 +
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.LinkedHashMap;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.ignite.binary.BinaryObject;
 +import org.apache.ignite.binary.BinaryObjectBuilder;
 +import org.apache.ignite.binary.BinaryObjectException;
 +import org.apache.ignite.binary.BinaryInvalidTypeException;
 +import org.apache.ignite.binary.BinaryType;
 +import org.apache.ignite.internal.portable.BinaryObjectImpl;
 +import org.apache.ignite.internal.portable.BinaryObjectOffheapImpl;
 +import org.apache.ignite.internal.portable.BinaryWriterExImpl;
 +import org.apache.ignite.internal.portable.PortableContext;
 +import org.apache.ignite.internal.portable.PortableUtils;
 +import org.apache.ignite.internal.processors.cache.portable.CacheObjectPortableProcessorImpl;
 +import org.apache.ignite.internal.util.GridArgumentCheck;
 +import org.apache.ignite.internal.util.typedef.F;
 +import org.apache.ignite.internal.util.typedef.internal.U;
 +import org.apache.ignite.lang.IgniteBiTuple;
 +import org.jetbrains.annotations.Nullable;
 +
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.DFLT_HDR_LEN;
++import static org.apache.ignite.internal.portable.GridPortableMarshaller.FLAGS_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.HASH_CODE_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.PROTO_VER_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.TYPE_ID_POS;
 +import static org.apache.ignite.internal.portable.GridPortableMarshaller.UNREGISTERED_TYPE_ID;
 +
 +/**
 + *
 + */
 +public class BinaryObjectBuilderImpl implements BinaryObjectBuilder {
 +    /** */
 +    private static final Object REMOVED_FIELD_MARKER = new Object();
 +
 +    /** */
 +    private final PortableContext ctx;
 +
 +    /** */
 +    private final int typeId;
 +
 +    /** May be null. */
 +    private String typeName;
 +
 +    /** May be null. */
 +    private String clsNameToWrite;
 +
 +    /** */
 +    private boolean registeredType = true;
 +
 +    /** */
 +    private Map<String, Object> assignedVals;
 +
 +    /** */
 +    private Map<Integer, Object> readCache;
 +
 +    /** Position of object in source array, or -1 if object is not created from PortableObject. */
 +    private final int start;
 +
++    /** Flags. */
++    private final short flags;
++
 +    /** Total header length */
 +    private final int hdrLen;
 +
-     /**
-      * Context of PortableObject reading process. Or {@code null} if object is not created from PortableObject.
-      */
++    /** Context of PortableObject reading process. Or {@code null} if object is not created from PortableObject. */
 +    private final PortableBuilderReader reader;
 +
 +    /** */
 +    private int hashCode;
 +
 +    /**
 +     * @param clsName Class name.
 +     * @param ctx Portable context.
 +     */
 +    public BinaryObjectBuilderImpl(PortableContext ctx, String clsName) {
 +        this(ctx, ctx.typeId(clsName), PortableContext.typeName(clsName));
 +    }
 +
 +    /**
 +     * @param typeId Type ID.
 +     * @param ctx Portable context.
 +     */
 +    public BinaryObjectBuilderImpl(PortableContext ctx, int typeId) {
 +        this(ctx, typeId, null);
 +    }
 +
 +    /**
 +     * @param typeName Type name.
 +     * @param ctx Context.
 +     * @param typeId Type id.
 +     */
 +    public BinaryObjectBuilderImpl(PortableContext ctx, int typeId, String typeName) {
 +        this.typeId = typeId;
 +        this.typeName = typeName;
 +        this.ctx = ctx;
 +
 +        start = -1;
++        flags = -1;
 +        reader = null;
 +        hdrLen = DFLT_HDR_LEN;
 +
 +        readCache = Collections.emptyMap();
 +    }
 +
 +    /**
 +     * @param obj Object to wrap.
 +     */
 +    public BinaryObjectBuilderImpl(BinaryObjectImpl obj) {
 +        this(new PortableBuilderReader(obj), obj.start());
 +
 +        reader.registerObject(this);
 +    }
 +
 +    /**
 +     * @param reader ctx
 +     * @param start Start.
 +     */
 +    BinaryObjectBuilderImpl(PortableBuilderReader reader, int start) {
 +        this.reader = reader;
 +        this.start = start;
++        this.flags = reader.readShortPositioned(start + FLAGS_POS);
 +
 +        byte ver = reader.readBytePositioned(start + PROTO_VER_POS);
 +
 +        PortableUtils.checkProtocolVersion(ver);
 +
 +        int typeId = reader.readIntPositioned(start + TYPE_ID_POS);
 +        ctx = reader.portableContext();
 +        hashCode = reader.readIntPositioned(start + HASH_CODE_POS);
 +
 +        if (typeId == UNREGISTERED_TYPE_ID) {
 +            int mark = reader.position();
 +
 +            reader.position(start + DFLT_HDR_LEN);
 +
 +            clsNameToWrite = reader.readString();
 +
 +            Class cls;
 +
 +            try {
 +                // TODO: IGNITE-1272 - Is class loader needed here?
 +                cls = U.forName(clsNameToWrite, null);
 +            }
 +            catch (ClassNotFoundException e) {
 +                throw new BinaryInvalidTypeException("Failed to load the class: " + clsNameToWrite, e);
 +            }
 +
 +            this.typeId = ctx.descriptorForClass(cls).typeId();
 +
 +            registeredType = false;
 +
 +            hdrLen = reader.position() - mark;
 +
 +            reader.position(mark);
 +        }
 +        else {
 +            this.typeId = typeId;
 +            hdrLen = DFLT_HDR_LEN;
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public BinaryObject build() {
 +        try (BinaryWriterExImpl writer = new BinaryWriterExImpl(ctx, typeId, false)) {
 +
 +            PortableBuilderSerializer serializationCtx = new PortableBuilderSerializer();
 +
 +            serializationCtx.registerObjectWriting(this, 0);
 +
 +            serializeTo(writer, serializationCtx);
 +
 +            byte[] arr = writer.array();
 +
 +            return new BinaryObjectImpl(ctx, arr, 0);
 +        }
 +    }
 +
 +    /**
 +     * @param writer Writer.
 +     * @param serializer Serializer.
 +     */
 +    void serializeTo(BinaryWriterExImpl writer, PortableBuilderSerializer serializer) {
-         PortableUtils.writeHeader(writer,
-             true,
-             registeredType ? typeId : UNREGISTERED_TYPE_ID,
-             hashCode,
-             registeredType ? null : clsNameToWrite);
++        try {
++            PortableUtils.writeHeader(writer,
++                true,
++                registeredType ? typeId : UNREGISTERED_TYPE_ID,
++                hashCode,
++                registeredType ? null : clsNameToWrite);
 +
-         Set<Integer> remainsFlds = null;
++            Set<Integer> remainsFlds = null;
 +
-         if (reader != null) {
-             Map<Integer, Object> assignedFldsById;
++            if (reader != null) {
++                Map<Integer, Object> assignedFldsById;
 +
-             if (assignedVals != null) {
-                 assignedFldsById = U.newHashMap(assignedVals.size());
++                if (assignedVals != null) {
++                    assignedFldsById = U.newHashMap(assignedVals.size());
 +
-                 for (Map.Entry<String, Object> entry : assignedVals.entrySet()) {
-                     int fldId = ctx.fieldId(typeId, entry.getKey());
++                    for (Map.Entry<String, Object> entry : assignedVals.entrySet()) {
++                        int fldId = ctx.fieldId(typeId, entry.getKey());
 +
-                     assignedFldsById.put(fldId, entry.getValue());
-                 }
++                        assignedFldsById.put(fldId, entry.getValue());
++                    }
 +
-                 remainsFlds = assignedFldsById.keySet();
-             }
-             else
-                 assignedFldsById = Collections.emptyMap();
++                    remainsFlds = assignedFldsById.keySet();
++                } else
++                    assignedFldsById = Collections.emptyMap();
 +
-             // Get footer details.
-             IgniteBiTuple<Integer, Integer> footer = PortableUtils.footerAbsolute(reader, start);
++                // Get footer details.
++                int fieldOffsetSize = PortableUtils.fieldOffsetSize(flags);
 +
-             int footerPos = footer.get1();
-             int footerEnd = footer.get2();
++                IgniteBiTuple<Integer, Integer> footer = PortableUtils.footerAbsolute(reader, start, fieldOffsetSize);
 +
-             // Get raw position.
-             int rawPos = PortableUtils.rawOffsetAbsolute(reader, start);
++                int footerPos = footer.get1();
++                int footerEnd = footer.get2();
 +
-             // Position reader on data.
-             reader.position(start + hdrLen);
++                // Get raw position.
++                int rawPos = PortableUtils.rawOffsetAbsolute(reader, start, fieldOffsetSize);
 +
-             while (reader.position() < rawPos) {
-                 int fieldId = reader.readIntPositioned(footerPos);
-                 int fieldLen = fieldPositionAndLength(footerPos, footerEnd, rawPos).get2();
++                // Position reader on data.
++                reader.position(start + hdrLen);
 +
-                 footerPos += 8;
++                while (reader.position() + 4 < rawPos) {
++                    int fieldId = reader.readIntPositioned(footerPos);
++                    int fieldLen = fieldPositionAndLength(footerPos, footerEnd, rawPos, fieldOffsetSize).get2();
 +
-                 if (assignedFldsById.containsKey(fieldId)) {
-                     Object assignedVal = assignedFldsById.remove(fieldId);
++                    footerPos += 4 + fieldOffsetSize;
 +
-                     reader.skip(fieldLen);
++                    if (assignedFldsById.containsKey(fieldId)) {
++                        Object assignedVal = assignedFldsById.remove(fieldId);
 +
-                     if (assignedVal != REMOVED_FIELD_MARKER) {
-                         writer.writeFieldId(fieldId);
++                        reader.skip(fieldLen);
 +
-                         serializer.writeValue(writer, assignedVal);
-                     }
-                 }
-                 else {
-                     int type = fieldLen != 0 ? reader.readByte(0) : 0;
++                        if (assignedVal != REMOVED_FIELD_MARKER) {
++                            writer.writeFieldId(fieldId);
 +
-                     if (fieldLen != 0 && !PortableUtils.isPlainArrayType(type) && PortableUtils.isPlainType(type)) {
-                         writer.writeFieldId(fieldId);
-                         writer.write(reader.array(), reader.position(), fieldLen);
++                            serializer.writeValue(writer, assignedVal);
++                        }
++                    } else {
++                        int type = fieldLen != 0 ? reader.readByte(0) : 0;
 +
-                         reader.skip(fieldLen);
-                     }
-                     else {
-                         writer.writeFieldId(fieldId);
++                        if (fieldLen != 0 && !PortableUtils.isPlainArrayType(type) && PortableUtils.isPlainType(type)) {
++                            writer.writeFieldId(fieldId);
++                            writer.write(reader.array(), reader.position(), fieldLen);
 +
-                         Object val;
++                            reader.skip(fieldLen);
++                        } else {
++                            writer.writeFieldId(fieldId);
 +
-                         if (fieldLen == 0)
-                             val = null;
-                         else if (readCache == null) {
-                             int savedPos = reader.position();
++                            Object val;
 +
-                             val = reader.parseValue();
++                            if (fieldLen == 0)
++                                val = null;
++                            else if (readCache == null) {
++                                int savedPos = reader.position();
 +
-                             assert reader.position() == savedPos + fieldLen;
-                         }
-                         else {
-                             val = readCache.get(fieldId);
++                                val = reader.parseValue();
 +
-                             reader.skip(fieldLen);
-                         }
++                                assert reader.position() == savedPos + fieldLen;
++                            } else {
++                                val = readCache.get(fieldId);
++
++                                reader.skip(fieldLen);
++                            }
 +
-                         serializer.writeValue(writer, val);
++                            serializer.writeValue(writer, val);
++                        }
 +                    }
 +                }
 +            }
-         }
- 
-         if (assignedVals != null && (remainsFlds == null || !remainsFlds.isEmpty())) {
-             boolean metadataEnabled = ctx.isMetaDataEnabled(typeId);
 +
-             BinaryType metadata = null;
++            if (assignedVals != null && (remainsFlds == null || !remainsFlds.isEmpty())) {
++                boolean metadataEnabled = ctx.isMetaDataEnabled(typeId);
 +
-             if (metadataEnabled)
-                 metadata = ctx.metaData(typeId);
++                BinaryType metadata = null;
 +
-             Map<String, String> newFldsMetadata = null;
++                if (metadataEnabled)
++                    metadata = ctx.metaData(typeId);
 +
-             for (Map.Entry<String, Object> entry : assignedVals.entrySet()) {
-                 Object val = entry.getValue();
++                Map<String, String> newFldsMetadata = null;
 +
-                 if (val == REMOVED_FIELD_MARKER)
-                     continue;
- 
-                 String name = entry.getKey();
++                for (Map.Entry<String, Object> entry : assignedVals.entrySet()) {
++                    Object val = entry.getValue();
 +
-                 int fldId = ctx.fieldId(typeId, name);
++                    if (val == REMOVED_FIELD_MARKER)
++                        continue;
 +
-                 if (remainsFlds != null && !remainsFlds.contains(fldId))
-                     continue;
++                    String name = entry.getKey();
 +
-                 writer.writeFieldId(fldId);
++                    int fldId = ctx.fieldId(typeId, name);
 +
-                 serializer.writeValue(writer, val);
++                    if (remainsFlds != null && !remainsFlds.contains(fldId))
++                        continue;
 +
-                 if (metadataEnabled) {
-                     String oldFldTypeName = metadata == null ? null : metadata.fieldTypeName(name);
++                    writer.writeFieldId(fldId);
 +
-                     String newFldTypeName;
++                    serializer.writeValue(writer, val);
 +
-                     if (val instanceof PortableValueWithType)
-                         newFldTypeName = ((PortableValueWithType)val).typeName();
-                     else {
-                         byte type = PortableUtils.typeByClass(val.getClass());
++                    if (metadataEnabled) {
++                        String oldFldTypeName = metadata == null ? null : metadata.fieldTypeName(name);
 +
-                         newFldTypeName = CacheObjectPortableProcessorImpl.fieldTypeName(type);
-                     }
++                        String newFldTypeName;
 +
-                     if (oldFldTypeName == null) {
-                         // It's a new field, we have to add it to metadata.
++                        if (val instanceof PortableValueWithType)
++                            newFldTypeName = ((PortableValueWithType) val).typeName();
++                        else {
++                            byte type = PortableUtils.typeByClass(val.getClass());
 +
-                         if (newFldsMetadata == null)
-                             newFldsMetadata = new HashMap<>();
++                            newFldTypeName = CacheObjectPortableProcessorImpl.fieldTypeName(type);
++                        }
 +
-                         newFldsMetadata.put(name, newFldTypeName);
-                     }
-                     else {
-                         if (!"Object".equals(oldFldTypeName) && !oldFldTypeName.equals(newFldTypeName)) {
-                             throw new BinaryObjectException(
-                                 "Wrong value has been set [" +
-                                     "typeName=" + (typeName == null ? metadata.typeName() : typeName) +
-                                     ", fieldName=" + name +
-                                     ", fieldType=" + oldFldTypeName +
-                                     ", assignedValueType=" + newFldTypeName +
-                                     ", assignedValue=" + (((PortableValueWithType)val).value()) + ']'
-                             );
++                        if (oldFldTypeName == null) {
++                            // It's a new field, we have to add it to metadata.
++
++                            if (newFldsMetadata == null)
++                                newFldsMetadata = new HashMap<>();
++
++                            newFldsMetadata.put(name, newFldTypeName);
++                        } else {
++                            if (!"Object".equals(oldFldTypeName) && !oldFldTypeName.equals(newFldTypeName)) {
++                                throw new BinaryObjectException(
++                                    "Wrong value has been set [" +
++                                        "typeName=" + (typeName == null ? metadata.typeName() : typeName) +
++                                        ", fieldName=" + name +
++                                        ", fieldType=" + oldFldTypeName +
++                                        ", assignedValueType=" + newFldTypeName +
++                                        ", assignedValue=" + (((PortableValueWithType) val).value()) + ']'
++                                );
++                            }
 +                        }
 +                    }
 +                }
-             }
 +
-             if (newFldsMetadata != null) {
-                 String typeName = this.typeName;
++                if (newFldsMetadata != null) {
++                    String typeName = this.typeName;
 +
-                 if (typeName == null)
-                     typeName = metadata.typeName();
++                    if (typeName == null)
++                        typeName = metadata.typeName();
 +
-                 ctx.updateMetaData(typeId, typeName, newFldsMetadata);
++                    ctx.updateMetaData(typeId, typeName, newFldsMetadata);
++                }
 +            }
-         }
 +
-         if (reader != null) {
-             // Write raw data if any.
-             int rawOff = PortableUtils.rawOffsetAbsolute(reader, start);
-             int footerStart = PortableUtils.footerStartAbsolute(reader, start);
++            if (reader != null) {
++                // Write raw data if any.
++                int fieldOffsetSize = PortableUtils.fieldOffsetSize(flags);
++
++                int rawOff = PortableUtils.rawOffsetAbsolute(reader, start, fieldOffsetSize);
++                int footerStart = PortableUtils.footerStartAbsolute(reader, start);
 +
-             if (rawOff < footerStart) {
-                 writer.rawWriter();
++                if (rawOff < footerStart) {
++                    writer.rawWriter();
 +
-                 writer.write(reader.array(), rawOff, footerStart - rawOff);
++                    writer.write(reader.array(), rawOff, footerStart - rawOff);
++                }
++
++                // Shift reader to the end of the object.
++                reader.position(start + PortableUtils.length(reader, start));
 +            }
 +
-             // Shift reader to the end of the object.
-             reader.position(start + PortableUtils.length(reader, start));
++            writer.postWrite(true);
++        }
++        finally {
++            writer.popSchema();
 +        }
- 
-         writer.postWrite(true);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public BinaryObjectBuilderImpl hashCode(int hashCode) {
 +        this.hashCode = hashCode;
 +
 +        return this;
 +    }
 +
 +    /**
 +     * Get field position and length.
 +     *
 +     * @param footerPos Field position inside the footer (absolute).
 +     * @param footerEnd Footer end (absolute).
 +     * @param rawPos Raw data position (absolute).
++     * @param fieldOffsetSize Size of field's offset.
 +     * @return Tuple with field position and length.
 +     */
-     private IgniteBiTuple<Integer, Integer> fieldPositionAndLength(int footerPos, int footerEnd, int rawPos) {
-         int fieldOffset = reader.readIntPositioned(footerPos + 4);
++    private IgniteBiTuple<Integer, Integer> fieldPositionAndLength(int footerPos, int footerEnd, int rawPos,
++        int fieldOffsetSize) {
++        // Get field offset first.
++        int fieldOffset = PortableUtils.fieldOffsetRelative(reader, footerPos + 4, fieldOffsetSize);
 +        int fieldPos = start + fieldOffset;
 +
 +        // Get field length.
 +        int fieldLen;
 +
-         if (footerPos + 8 == footerEnd)
++        if (footerPos + 4 + fieldOffsetSize == footerEnd)
 +            // This is the last field, compare to raw offset.
 +            fieldLen = rawPos - fieldPos;
 +        else {
 +            // Field is somewhere in the middle, get difference with the next offset.
-             int nextFieldOffset = reader.readIntPositioned(footerPos + 8 + 4);
++            int nextFieldOffset = PortableUtils.fieldOffsetRelative(reader, footerPos + 4 + fieldOffsetSize + 4,
++                fieldOffsetSize);
 +
 +            fieldLen = nextFieldOffset - fieldOffset;
 +        }
 +
 +        return F.t(fieldPos, fieldLen);
 +    }
 +
 +    /**
 +     * Initialize read cache if needed.
 +     */
 +    private void ensureReadCacheInit() {
 +        if (readCache == null) {
++            int fieldOffsetSize = PortableUtils.fieldOffsetSize(flags);
++
 +            Map<Integer, Object> readCache = new HashMap<>();
 +
-             IgniteBiTuple<Integer, Integer> footer = PortableUtils.footerAbsolute(reader, start);
++            IgniteBiTuple<Integer, Integer> footer = PortableUtils.footerAbsolute(reader, start, fieldOffsetSize);
 +
 +            int footerPos = footer.get1();
 +            int footerEnd = footer.get2();
 +
-             int rawPos = PortableUtils.rawOffsetAbsolute(reader, start);
++            int rawPos = PortableUtils.rawOffsetAbsolute(reader, start, fieldOffsetSize);
 +
-             while (footerPos < footerEnd) {
++            while (footerPos + 4 < footerEnd) {
 +                int fieldId = reader.readIntPositioned(footerPos);
 +
-                 IgniteBiTuple<Integer, Integer> posAndLen = fieldPositionAndLength(footerPos, footerEnd, rawPos);
++                IgniteBiTuple<Integer, Integer> posAndLen =
++                    fieldPositionAndLength(footerPos, footerEnd, rawPos, fieldOffsetSize);
 +
 +                Object val = reader.getValueQuickly(posAndLen.get1(), posAndLen.get2());
 +
 +                readCache.put(fieldId, val);
 +
 +                // Shift current footer position.
-                 footerPos += 8;
++                footerPos += 4 + fieldOffsetSize;
 +            }
 +
 +            this.readCache = readCache;
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @SuppressWarnings("unchecked")
 +    @Override public <T> T getField(String name) {
 +        Object val;
 +
 +        if (assignedVals != null && assignedVals.containsKey(name)) {
 +            val = assignedVals.get(name);
 +
 +            if (val == REMOVED_FIELD_MARKER)
 +                return null;
 +        }
 +        else {
 +            ensureReadCacheInit();
 +
 +            int fldId = ctx.fieldId(typeId, name);
 +
 +            val = readCache.get(fldId);
 +        }
 +
 +        return (T)PortableUtils.unwrapLazy(val);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public BinaryObjectBuilder setField(String name, Object val) {
 +        GridArgumentCheck.notNull(val, name);
 +
 +        if (assignedVals == null)
 +            assignedVals = new LinkedHashMap<>();
 +
 +        Object oldVal = assignedVals.put(name, val);
 +
 +        if (oldVal instanceof PortableValueWithType) {
 +            ((PortableValueWithType)oldVal).value(val);
 +
 +            assignedVals.put(name, oldVal);
 +        }
 +
 +        return this;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public <T> BinaryObjectBuilder setField(String name, @Nullable T val, Class<? super T> type) {
 +        if (assignedVals == null)
 +            assignedVals = new LinkedHashMap<>();
 +
 +        //int fldId = ctx.fieldId(typeId, fldName);
 +
 +        assignedVals.put(name, new PortableValueWithType(PortableUtils.typeByClass(type), val));
 +
 +        return this;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public BinaryObjectBuilder setField(String name, @Nullable BinaryObjectBuilder builder) {
 +        if (builder == null)
 +            return setField(name, null, Object.class);
 +        else
 +            return setField(name, (Object)builder);
 +    }
 +
 +    /**
 +     * Removes field from portable object.
 +     *
 +     * @param name Field name.
 +     * @return {@code this} instance for chaining.
 +     */
 +    @Override public BinaryObjectBuilderImpl removeField(String name) {
 +        if (assignedVals == null)
 +            assignedVals = new LinkedHashMap<>();
 +
 +        assignedVals.put(name, REMOVED_FIELD_MARKER);
 +
 +        return this;
 +    }
 +
 +    /**
 +     * Creates builder initialized by specified portable object.
 +     *
 +     * @param obj Portable object to initialize builder.
 +     * @return New builder.
 +     */
 +    public static BinaryObjectBuilderImpl wrap(BinaryObject obj) {
 +        BinaryObjectImpl heapObj;
 +
 +        if (obj instanceof BinaryObjectOffheapImpl)
 +            heapObj = (BinaryObjectImpl)((BinaryObjectOffheapImpl)obj).heapCopy();
 +        else
 +            heapObj = (BinaryObjectImpl)obj;
 +
 +        return new BinaryObjectBuilderImpl(heapObj);
 +    }
 +
 +    /**
 +     * @return Object start position in source array.
 +     */
 +    int start() {
 +        return start;
 +    }
 +
 +    /**
 +     * @return Object type id.
 +     */
 +    public int typeId() {
 +        return typeId;
 +    }
 +}