ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From voze...@apache.org
Subject ignite git commit: IGNITE-1838: Added fast-path deserialization for common field types (String, Date, Timestamp, UUID, BigDecimal, null).
Date Tue, 03 Nov 2015 14:41:17 GMT
Repository: ignite
Updated Branches:
  refs/heads/ignite-1282 433806d5e -> 7e399e842


IGNITE-1838: Added fast-path deserialization for common field types (String, Date, Timestamp, UUID, BigDecimal, null).


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/7e399e84
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/7e399e84
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/7e399e84

Branch: refs/heads/ignite-1282
Commit: 7e399e842322a3b6d2dad3974255a1629507e29b
Parents: 433806d
Author: vozerov-gridgain <vozerov@gridgain.com>
Authored: Tue Nov 3 17:41:51 2015 +0300
Committer: vozerov-gridgain <vozerov@gridgain.com>
Committed: Tue Nov 3 17:41:51 2015 +0300

----------------------------------------------------------------------
 .../internal/portable/PortableFieldImpl.java    |  28 +-
 .../internal/portable/PortableObjectImpl.java   | 100 ++-
 .../portable/PortableObjectOffheapImpl.java     |  95 ++-
 .../internal/portable/PortablePrimitives.java   |  44 ++
 .../internal/portable/PortableReaderExImpl.java |   2 +-
 .../internal/portable/PortableSchema.java       |   9 +-
 .../apache/ignite/portable/PortableField.java   |   7 +
 .../PortableCompactOffsetsAbstractSelfTest.java | 201 ++++++
 .../PortableCompactOffsetsHeapSelfTest.java     |  32 +
 .../PortableCompactOffsetsOffheapSelfTest.java  |  61 ++
 .../PortableFieldsAbstractSelfTest.java         | 688 +++++++++++++++++++
 .../portable/PortableFieldsHeapSelfTest.java    |  32 +
 .../portable/PortableFieldsOffheapSelfTest.java |  61 ++
 .../IgnitePortableObjectsTestSuite.java         |   9 +
 14 files changed, 1349 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableFieldImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableFieldImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableFieldImpl.java
index 12be55c..80b0a6d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableFieldImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableFieldImpl.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.portable;
 
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.portable.PortableField;
 import org.apache.ignite.portable.PortableObject;
 
@@ -25,8 +27,12 @@ import org.apache.ignite.portable.PortableObject;
  */
 public class PortableFieldImpl implements PortableField {
     /** Well-known object schemas. */
+    @GridToStringExclude
     private final PortableSchemaRegistry schemas;
 
+    /** Field name. */
+    private final String fieldName;
+
     /** Pre-calculated field ID. */
     private final int fieldId;
 
@@ -34,18 +40,29 @@ public class PortableFieldImpl implements PortableField {
      * Constructor.
      *
      * @param schemas Schemas.
+     * @param fieldName Field name.
      * @param fieldId Field ID.
      */
-    public PortableFieldImpl(PortableSchemaRegistry schemas, int fieldId) {
+    public PortableFieldImpl(PortableSchemaRegistry schemas, String fieldName, int fieldId) {
+        assert schemas != null;
+        assert fieldName != null;
+        assert fieldId != 0;
+
         this.schemas = schemas;
+        this.fieldName = fieldName;
         this.fieldId = fieldId;
     }
 
     /** {@inheritDoc} */
+    @Override public String name() {
+        return fieldName;
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean exists(PortableObject obj) {
         PortableObjectEx obj0 = (PortableObjectEx)obj;
 
-        return fieldOrder(obj0) != 0;
+        return fieldOrder(obj0) != PortableSchema.ORDER_NOT_FOUND;
     }
 
     /** {@inheritDoc} */
@@ -55,7 +72,7 @@ public class PortableFieldImpl implements PortableField {
 
         int order = fieldOrder(obj0);
 
-        return order != 0 ? (T)obj0.fieldByOrder(order) : null;
+        return order != PortableSchema.ORDER_NOT_FOUND ? (T)obj0.fieldByOrder(order) : null;
     }
 
     /**
@@ -79,4 +96,9 @@ public class PortableFieldImpl implements PortableField {
 
         return schema.order(fieldId);
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PortableFieldImpl.class, this);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectImpl.java
index c7e01d7..1d1be2b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectImpl.java
@@ -25,6 +25,7 @@ import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.portable.CacheObjectPortableProcessorImpl;
+import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
@@ -39,16 +40,28 @@ import java.io.Externalizable;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.nio.ByteBuffer;
+import java.sql.Timestamp;
+import java.util.Date;
+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.BYTE;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.CHAR;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.DATE;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.DECIMAL;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.DOUBLE;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.FLOAT;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.INT;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.LONG;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.NULL;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.SHORT;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.STRING;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.TIMESTAMP;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.UUID;
 
 /**
  * Portable object implementation.
@@ -178,13 +191,6 @@ public final class PortableObjectImpl extends PortableObjectEx implements Extern
     }
 
     /**
-     * @return {@code True} if detach is allowed.
-     */
-    public boolean detachAllowed() {
-        return true;
-    }
-
-    /**
      * @param detachAllowed Detach allowed flag.
      */
     public void detachAllowed(boolean detachAllowed) {
@@ -270,9 +276,9 @@ public final class PortableObjectImpl extends PortableObjectEx implements Extern
         int fieldPos;
 
         if (fieldOffsetSize == PortableUtils.OFFSET_1)
-            fieldPos = start + (int)PortablePrimitives.readByte(arr, fieldOffsetPos) & 0xFF;
+            fieldPos = start + ((int)PortablePrimitives.readByte(arr, fieldOffsetPos) & 0xFF);
         else if (fieldOffsetSize == PortableUtils.OFFSET_2)
-            fieldPos = start + (int)PortablePrimitives.readShort(arr, fieldOffsetPos) & 0xFFFF;
+            fieldPos = start + ((int)PortablePrimitives.readShort(arr, fieldOffsetPos) & 0xFFFF);
         else
             fieldPos = start + PortablePrimitives.readInt(arr, fieldOffsetPos);
 
@@ -320,6 +326,78 @@ public final class PortableObjectImpl extends PortableObjectEx implements Extern
 
                 break;
 
+            case STRING: {
+                boolean utf = PortablePrimitives.readBoolean(arr, fieldPos + 1);
+
+                if (utf) {
+                    int dataLen = PortablePrimitives.readInt(arr, fieldPos + 2);
+
+                    val = new String(arr, fieldPos + 6, dataLen, UTF_8);
+                }
+                else {
+                    int dataLen = PortablePrimitives.readInt(arr, fieldPos + 2);
+                    char[] data = PortablePrimitives.readCharArray(arr, fieldPos + 6, dataLen);
+
+                    val = String.valueOf(data);
+                }
+
+                break;
+            }
+
+            case DATE: {
+                long time = PortablePrimitives.readLong(arr, fieldPos + 1);
+
+                val = new Date(time);
+
+                break;
+            }
+
+            case TIMESTAMP: {
+                long time = PortablePrimitives.readLong(arr, fieldPos + 1);
+                int nanos = PortablePrimitives.readInt(arr, fieldPos + 1 + 8);
+
+                Timestamp ts = new Timestamp(time);
+
+                ts.setNanos(ts.getNanos() + nanos);
+
+                val = ts;
+
+                break;
+            }
+
+            case UUID: {
+                long most = PortablePrimitives.readLong(arr, fieldPos + 1);
+                long least = PortablePrimitives.readLong(arr, fieldPos + 1 + 8);
+
+                val = new UUID(most, least);
+
+                break;
+            }
+
+            case DECIMAL: {
+                int scale = PortablePrimitives.readInt(arr, fieldPos + 1);
+
+                int dataLen = PortablePrimitives.readInt(arr, fieldPos + 5);
+                byte[] data = PortablePrimitives.readByteArray(arr, fieldPos + 9, dataLen);
+
+                BigInteger intVal = new BigInteger(data);
+
+                if (scale < 0) {
+                    scale &= 0x7FFFFFFF;
+
+                    intVal = intVal.negate();
+                }
+
+                val = new BigDecimal(intVal, scale);
+
+                break;
+            }
+
+            case NULL:
+                val = null;
+
+                break;
+
             default: {
                 PortableReaderExImpl reader = new PortableReaderExImpl(ctx, arr, start, null);
 
@@ -395,13 +473,15 @@ public final class PortableObjectImpl extends PortableObjectEx implements Extern
 
     /** {@inheritDoc} */
     @Override public PortableField fieldDescriptor(String fieldName) throws PortableException {
+        A.notNull(fieldName, "fieldName");
+
         int typeId = typeId();
 
         PortableSchemaRegistry schemaReg = ctx.schemaRegistry(typeId);
 
         int fieldId = ctx.userTypeIdMapper(typeId).fieldId(typeId, fieldName);
 
-        return new PortableFieldImpl(schemaReg, fieldId);
+        return new PortableFieldImpl(schemaReg, fieldName, fieldId);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectOffheapImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectOffheapImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectOffheapImpl.java
index 7ba5553..3027f76 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectOffheapImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectOffheapImpl.java
@@ -21,12 +21,19 @@ import java.io.Externalizable;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.nio.ByteBuffer;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.UUID;
+
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.portable.streams.PortableOffheapInputStream;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
 import org.apache.ignite.plugin.extensions.communication.MessageWriter;
@@ -37,14 +44,21 @@ import org.apache.ignite.portable.PortableObject;
 import org.jetbrains.annotations.Nullable;
 import sun.misc.Unsafe;
 
+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.BYTE;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.CHAR;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.DATE;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.DECIMAL;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.DOUBLE;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.FLOAT;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.INT;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.LONG;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.NULL;
 import static org.apache.ignite.internal.portable.GridPortableMarshaller.SHORT;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.STRING;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.TIMESTAMP;
+import static org.apache.ignite.internal.portable.GridPortableMarshaller.UUID;
 
 /**
  *  Portable object implementation over offheap memory
@@ -127,13 +141,15 @@ public class PortableObjectOffheapImpl extends PortableObjectEx implements Exter
 
     /** {@inheritDoc} */
     @Override public PortableField fieldDescriptor(String fieldName) throws PortableException {
+        A.notNull(fieldName, "fieldName");
+
         int typeId = typeId();
 
         PortableSchemaRegistry schemaReg = ctx.schemaRegistry(typeId);
 
         int fieldId = ctx.userTypeIdMapper(typeId).fieldId(typeId, fieldName);
 
-        return new PortableFieldImpl(schemaReg, fieldId);
+        return new PortableFieldImpl(schemaReg, fieldName, fieldId);
     }
 
     /** {@inheritDoc} */
@@ -202,9 +218,9 @@ public class PortableObjectOffheapImpl extends PortableObjectEx implements Exter
         int fieldPos;
 
         if (fieldOffsetSize == PortableUtils.OFFSET_1)
-            fieldPos = start + (int)PortablePrimitives.readByte(ptr, fieldOffsetPos) & 0xFF;
+            fieldPos = start + ((int)PortablePrimitives.readByte(ptr, fieldOffsetPos) & 0xFF);
         else if (fieldOffsetSize == PortableUtils.OFFSET_2)
-            fieldPos = start + (int)PortablePrimitives.readShort(ptr, fieldOffsetPos) & 0xFFFF;
+            fieldPos = start + ((int)PortablePrimitives.readShort(ptr, fieldOffsetPos) & 0xFFFF);
         else
             fieldPos = start + PortablePrimitives.readInt(ptr, fieldOffsetPos);
 
@@ -252,6 +268,79 @@ public class PortableObjectOffheapImpl extends PortableObjectEx implements Exter
 
                 break;
 
+            case STRING: {
+                boolean utf = PortablePrimitives.readBoolean(ptr, fieldPos + 1);
+
+                if (utf) {
+                    int dataLen = PortablePrimitives.readInt(ptr, fieldPos + 2);
+                    byte[] data = PortablePrimitives.readByteArray(ptr, fieldPos + 6, dataLen);
+
+                    val = new String(data, UTF_8);
+                }
+                else {
+                    int dataLen = PortablePrimitives.readInt(ptr, fieldPos + 2);
+                    char[] data = PortablePrimitives.readCharArray(ptr, fieldPos + 6, dataLen);
+
+                    val = String.valueOf(data);
+                }
+
+                break;
+            }
+
+            case DATE: {
+                long time = PortablePrimitives.readLong(ptr, fieldPos + 1);
+
+                val = new Date(time);
+
+                break;
+            }
+
+            case TIMESTAMP: {
+                long time = PortablePrimitives.readLong(ptr, fieldPos + 1);
+                int nanos = PortablePrimitives.readInt(ptr, fieldPos + 1 + 8);
+
+                Timestamp ts = new Timestamp(time);
+
+                ts.setNanos(ts.getNanos() + nanos);
+
+                val = ts;
+
+                break;
+            }
+
+            case UUID: {
+                long most = PortablePrimitives.readLong(ptr, fieldPos + 1);
+                long least = PortablePrimitives.readLong(ptr, fieldPos + 1 + 8);
+
+                val = new UUID(most, least);
+
+                break;
+            }
+
+            case DECIMAL: {
+                int scale = PortablePrimitives.readInt(ptr, fieldPos + 1);
+
+                int dataLen = PortablePrimitives.readInt(ptr, fieldPos + 5);
+                byte[] data = PortablePrimitives.readByteArray(ptr, fieldPos + 9, dataLen);
+
+                BigInteger intVal = new BigInteger(data);
+
+                if (scale < 0) {
+                    scale &= 0x7FFFFFFF;
+
+                    intVal = intVal.negate();
+                }
+
+                val = new BigDecimal(intVal, scale);
+
+                break;
+            }
+
+            case NULL:
+                val = null;
+
+                break;
+
             default: {
                 PortableReaderExImpl reader = new PortableReaderExImpl(ctx,
                     new PortableOffheapInputStream(ptr, size, false),

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/internal/portable/PortablePrimitives.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortablePrimitives.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortablePrimitives.java
index d5a78bc..02f552a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortablePrimitives.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortablePrimitives.java
@@ -68,6 +68,32 @@ public abstract class PortablePrimitives {
     /**
      * @param arr Array.
      * @param off Offset.
+     * @return Value.
+     */
+    public static byte[] readByteArray(byte[] arr, int off, int len) {
+        byte[] arr0 = new byte[len];
+
+        UNSAFE.copyMemory(arr, BYTE_ARR_OFF + off, arr0, BYTE_ARR_OFF, len);
+
+        return arr0;
+    }
+
+    /**
+     * @param ptr Pointer.
+     * @param off Offset.
+     * @return Value.
+     */
+    public static byte[] readByteArray(long ptr, int off, int len) {
+        byte[] arr0 = new byte[len];
+
+        UNSAFE.copyMemory(null, ptr + off, arr0, BYTE_ARR_OFF, len);
+
+        return arr0;
+    }
+
+    /**
+     * @param arr Array.
+     * @param off Offset.
      * @param val Value.
      */
     public static void writeBoolean(byte[] arr, int off, boolean val) {
@@ -191,6 +217,24 @@ public abstract class PortablePrimitives {
     }
 
     /**
+     * @param ptr Pointer.
+     * @param off Offset.
+     * @return Value.
+     */
+    public static char[] readCharArray(long ptr, int off, int len) {
+        char[] arr0 = new char[len];
+
+        UNSAFE.copyMemory(null, ptr + off, arr0, CHAR_ARR_OFF, len << 1);
+
+        if (BIG_ENDIAN) {
+            for (int i = 0; i < len; i++)
+                arr0[i] = Character.reverseBytes(arr0[i]);
+        }
+
+        return arr0;
+    }
+
+    /**
      * @param arr Array.
      * @param off Offset.
      * @param val Value.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableReaderExImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableReaderExImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableReaderExImpl.java
index 9a088f4..b5decba 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableReaderExImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableReaderExImpl.java
@@ -2633,7 +2633,7 @@ public class PortableReaderExImpl implements PortableReader, PortableRawReaderEx
 
             int order = schema.order(id);
 
-            if (order != 0) {
+            if (order != PortableSchema.ORDER_NOT_FOUND) {
                 int offsetPos = footerStart + order * (4 + offsetSize) + 4;
 
                 int pos = start + PortableUtils.fieldOffsetRelative(in, offsetPos, offsetSize);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java
index 03be319..96a93f4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java
@@ -29,6 +29,9 @@ import java.util.Map;
  * - When there are more fields, we store them inside a hash map.
  */
 public class PortableSchema {
+    /** Order returned if field is not found. */
+    public static final int ORDER_NOT_FOUND = -1;
+
     /** Inline flag. */
     private final boolean inline;
 
@@ -179,12 +182,12 @@ public class PortableSchema {
             if (id == id7)
                 return 7;
 
-            return 0;
+            return ORDER_NOT_FOUND;
         }
         else {
-            Integer off = map.get(id);
+            Integer order = map.get(id);
 
-            return off != null ? off : 0;
+            return order != null ? order : ORDER_NOT_FOUND;
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/main/java/org/apache/ignite/portable/PortableField.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/portable/PortableField.java b/modules/core/src/main/java/org/apache/ignite/portable/PortableField.java
index f8851ee..81a7424 100644
--- a/modules/core/src/main/java/org/apache/ignite/portable/PortableField.java
+++ b/modules/core/src/main/java/org/apache/ignite/portable/PortableField.java
@@ -22,6 +22,13 @@ package org.apache.ignite.portable;
  */
 public interface PortableField {
     /**
+     * Get field's name.
+     *
+     * @return Name.
+     */
+    public String name();
+
+    /**
      * Check whether field exists in the object.
      *
      * @param obj Object.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsAbstractSelfTest.java
new file mode 100644
index 0000000..28058de
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsAbstractSelfTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.internal.util.IgniteUtils;
+import org.apache.ignite.marshaller.MarshallerContextTestImpl;
+import org.apache.ignite.marshaller.portable.PortableMarshaller;
+import org.apache.ignite.portable.PortableField;
+import org.apache.ignite.portable.PortableMetadata;
+import org.apache.ignite.portable.PortableTypeConfiguration;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import java.util.Arrays;
+
+/**
+ * Contains tests for compact offsets.
+ */
+public abstract class PortableCompactOffsetsAbstractSelfTest extends GridCommonAbstractTest {
+    /** 2 pow 8. */
+    private static int POW_8 = 1 << 8;
+
+    /** 2 pow 16. */
+    private static int POW_16 = 1 << 16;
+
+    /** Dummy metadata handler. */
+    protected static final PortableMetaDataHandler META_HND = new PortableMetaDataHandler() {
+        @Override public void addMeta(int typeId, PortableMetadata meta) {
+            // No-op.
+        }
+
+        @Override public PortableMetadata metadata(int typeId) {
+            return null;
+        }
+    };
+
+    /** Marshaller. */
+    protected PortableMarshaller marsh;
+
+    /** Portable context. */
+    protected PortableContext ctx;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        ctx = new PortableContext(META_HND, null);
+
+        marsh = new PortableMarshaller();
+
+        marsh.setTypeConfigurations(Arrays.asList(new PortableTypeConfiguration(TestObject.class.getName())));
+        marsh.setContext(new MarshallerContextTestImpl(null));
+
+        IgniteUtils.invoke(PortableMarshaller.class, marsh, "setPortableContext", ctx);
+    }
+
+    /**
+     * Test 1 byte.
+     *
+     * @throws Exception If failed.
+     */
+    public void test1Byte() throws Exception {
+        check(POW_8 >> 2);
+    }
+
+    /**
+     * Test 1 byte with sign altering.
+     *
+     * @throws Exception If failed.
+     */
+    public void test1ByteSign() throws Exception {
+        check(POW_8 >> 1);
+    }
+
+    /**
+     * Test 2 bytes.
+     *
+     * @throws Exception If failed.
+     */
+    public void test2Bytes() throws Exception {
+        check(POW_16 >> 2);
+    }
+
+    /**
+     * Test 2 bytes with sign altering.
+     *
+     * @throws Exception If failed.
+     */
+    public void test2BytesSign() throws Exception {
+        check(POW_16 >> 1);
+    }
+
+    /**
+     * Test 4 bytes.
+     *
+     * @throws Exception If failed.
+     */
+    public void test4Bytes() throws Exception {
+        check(POW_16 << 2);
+    }
+
+    /**
+     * Main check routine.
+     *
+     * @param len Length of the first field.
+     *
+     * @throws Exception If failed.
+     */
+    private void check(int len) throws Exception {
+        TestObject obj = new TestObject(len);
+
+        PortableObjectEx portObj = toPortable(marsh, obj);
+
+        // 1. Test portable object content.
+        assert portObj.hasField("field1");
+        assert portObj.hasField("field2");
+
+        byte[] field1 = portObj.field("field1");
+        Integer field2 = portObj.field("field2");
+
+        assert field1 != null;
+        assert field2 != null;
+
+        assert Arrays.equals(obj.field1, field1);
+        assert obj.field2 == field2;
+
+        // 2. Test fields API.
+        PortableField field1Desc = portObj.fieldDescriptor("field1");
+        PortableField field2Desc = portObj.fieldDescriptor("field2");
+
+        assert field1Desc.exists(portObj);
+        assert field2Desc.exists(portObj);
+
+        assert Arrays.equals(obj.field1, (byte[])field1Desc.value(portObj));
+        assert obj.field2 == (Integer)field2Desc.value(portObj);
+
+        // 3. Test deserialize.
+        TestObject objRestored = portObj.deserialize();
+
+        assert objRestored != null;
+
+        assert Arrays.equals(obj.field1, objRestored.field1);
+        assert obj.field2 == objRestored.field2;
+    }
+
+    /**
+     * Convert object to portable object.
+     *
+     * @param marsh Marshaller.
+     * @param obj Object.
+     * @return Portable object.
+     * @throws Exception If failed.
+     */
+    protected abstract PortableObjectEx toPortable(PortableMarshaller marsh, Object obj) throws Exception;
+
+    /**
+     * Test object.
+     */
+    public static class TestObject {
+        /** First field with variable length. */
+        public byte[] field1;
+
+        /** Second field. */
+        public int field2;
+
+        /**
+         * Default constructor.
+         */
+        public TestObject() {
+            // No-op.
+        }
+
+        /**
+         * Constructor.
+         *
+         * @param len Array length.
+         */
+        public TestObject(int len) {
+            field1 = new byte[len];
+
+            field1[0] = 1;
+            field1[len - 1] = 2;
+
+            field2 = len;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsHeapSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsHeapSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsHeapSelfTest.java
new file mode 100644
index 0000000..826f972
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsHeapSelfTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.marshaller.portable.PortableMarshaller;
+
+/**
+ * Compact offsets tests for heap portable objects.
+ */
+public class PortableCompactOffsetsHeapSelfTest extends PortableCompactOffsetsAbstractSelfTest {
+    /** {@inheritDoc} */
+    @Override protected PortableObjectEx toPortable(PortableMarshaller marsh, Object obj) throws Exception {
+        byte[] bytes = marsh.marshal(obj);
+
+        return new PortableObjectImpl(ctx, bytes, 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsOffheapSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsOffheapSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsOffheapSelfTest.java
new file mode 100644
index 0000000..9ad1c67
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableCompactOffsetsOffheapSelfTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.internal.util.GridUnsafe;
+import org.apache.ignite.marshaller.portable.PortableMarshaller;
+import org.eclipse.jetty.util.ConcurrentHashSet;
+import sun.misc.Unsafe;
+
+/**
+ * Compact offsets tests for offheap portable objects.
+ */
+public class PortableCompactOffsetsOffheapSelfTest extends PortableCompactOffsetsAbstractSelfTest {
+    /** Unsafe instance. */
+    private static final Unsafe UNSAFE = GridUnsafe.unsafe();
+
+    /** Byte array offset for unsafe mechanics. */
+    protected static final long BYTE_ARR_OFF = UNSAFE.arrayBaseOffset(byte[].class);
+
+    /** Allocated unsafe pointer. */
+    private final ConcurrentHashSet<Long> ptrs = new ConcurrentHashSet<>();
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        // Cleanup allocated objects.
+        for (Long ptr : ptrs)
+            UNSAFE.freeMemory(ptr);
+
+        ptrs.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected PortableObjectEx toPortable(PortableMarshaller marsh, Object obj) throws Exception {
+        byte[] arr = marsh.marshal(obj);
+
+        long ptr = UNSAFE.allocateMemory(arr.length);
+
+        ptrs.add(ptr);
+
+        UNSAFE.copyMemory(arr, BYTE_ARR_OFF, null, ptr, arr.length);
+
+        return new PortableObjectOffheapImpl(ctx, ptr, 0, arr.length);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsAbstractSelfTest.java
new file mode 100644
index 0000000..2acc1f5
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsAbstractSelfTest.java
@@ -0,0 +1,688 @@
+/*
+ * 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.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.marshaller.MarshallerContextTestImpl;
+import org.apache.ignite.marshaller.portable.PortableMarshaller;
+import org.apache.ignite.portable.PortableField;
+import org.apache.ignite.portable.PortableMetadata;
+import org.apache.ignite.portable.PortableObject;
+import org.apache.ignite.portable.PortableTypeConfiguration;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * Contains tests for portable object fields.
+ */
+public abstract class PortableFieldsAbstractSelfTest extends GridCommonAbstractTest {
+    /** Dummy metadata handler. */
+    protected static final PortableMetaDataHandler META_HND = new PortableMetaDataHandler() {
+        @Override public void addMeta(int typeId, PortableMetadata meta) {
+            // No-op.
+        }
+
+        @Override public PortableMetadata metadata(int typeId) {
+            return null;
+        }
+    };
+
+    /** Marshaller. */
+    protected PortableMarshaller marsh;
+
+    /** Portable context. */
+    protected PortableContext ctx;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        ctx = new PortableContext(META_HND, null);
+
+        marsh = new PortableMarshaller();
+
+        marsh.setTypeConfigurations(Arrays.asList(
+            new PortableTypeConfiguration(TestObject.class.getName()),
+            new PortableTypeConfiguration(TestOuterObject.class.getName()),
+            new PortableTypeConfiguration(TestInnerObject.class.getName())
+        ));
+
+        marsh.setContext(new MarshallerContextTestImpl(null));
+
+        IgniteUtils.invoke(PortableMarshaller.class, marsh, "setPortableContext", ctx);
+    }
+
+    /**
+     * Test byte field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testByte() throws Exception {
+        check("fByte");
+    }
+
+    /**
+     * Test byte array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testByteArray() throws Exception {
+        check("fByteArr");
+    }
+
+    /**
+     * Test boolean field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testBoolean() throws Exception {
+        check("fBool");
+    }
+
+    /**
+     * Test boolean array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testBooleanArray() throws Exception {
+        check("fBoolArr");
+    }
+
+    /**
+     * Test short field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testShort() throws Exception {
+        check("fShort");
+    }
+
+    /**
+     * Test short array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testShortArray() throws Exception {
+        check("fShortArr");
+    }
+
+    /**
+     * Test char field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testChar() throws Exception {
+        check("fChar");
+    }
+
+    /**
+     * Test char array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testCharArray() throws Exception {
+        check("fCharArr");
+    }
+
+    /**
+     * Test int field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testInt() throws Exception {
+        check("fInt");
+    }
+
+    /**
+     * Test int array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testIntArray() throws Exception {
+        check("fIntArr");
+    }
+
+    /**
+     * Test long field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testLong() throws Exception {
+        check("fLong");
+    }
+
+    /**
+     * Test long array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testLongArray() throws Exception {
+        check("fLongArr");
+    }
+
+    /**
+     * Test float field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testFloat() throws Exception {
+        check("fFloat");
+    }
+
+    /**
+     * Test float array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testFloatArray() throws Exception {
+        check("fFloatArr");
+    }
+
+    /**
+     * Test double field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDouble() throws Exception {
+        check("fDouble");
+    }
+
+    /**
+     * Test double array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDoubleArray() throws Exception {
+        check("fDoubleArr");
+    }
+
+    /**
+     * Test string field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testString() throws Exception {
+        check("fString");
+    }
+
+    /**
+     * Test string array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testStringArray() throws Exception {
+        check("fStringArr");
+    }
+
+    /**
+     * Test date field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDate() throws Exception {
+        check("fDate");
+    }
+
+    /**
+     * Test date array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDateArray() throws Exception {
+        check("fDateArr");
+    }
+
+    /**
+     * Test timestamp field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTimestamp() throws Exception {
+        check("fTimestamp");
+    }
+
+    /**
+     * Test timestamp array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testTimestampArray() throws Exception {
+        check("fTimestampArr");
+    }
+
+    /**
+     * Test UUID field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testUuid() throws Exception {
+        check("fUuid");
+    }
+
+    /**
+     * Test UUID array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testUuidArray() throws Exception {
+        check("fUuidArr");
+    }
+
+    /**
+     * Test decimal field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDecimal() throws Exception {
+        check("fDecimal");
+    }
+
+    /**
+     * Test decimal array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testDecimalArray() throws Exception {
+        check("fDecimalArr");
+    }
+
+    /**
+     * Test object field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testObject() throws Exception {
+        check("fObj");
+    }
+
+    /**
+     * Test object array field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testObjectArray() throws Exception {
+        check("fObjArr");
+    }
+
+    /**
+     * Test null field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testNull() throws Exception {
+        check("fNull");
+    }
+
+    /**
+     * Test missing field.
+     *
+     * @throws Exception If failed.
+     */
+    public void testMissing() throws Exception {
+        String fieldName = "fMissing";
+
+        checkNormal(fieldName, false);
+        checkNested(fieldName, false);
+    }
+
+    /**
+     * Check field resolution in both normal and nested modes.
+     *
+     * @param fieldName Field name.
+     * @throws Exception If failed.
+     */
+    public void check(String fieldName) throws Exception {
+        checkNormal(fieldName, true);
+        checkNested(fieldName, true);
+    }
+
+    /**
+     * Check field.
+     *
+     * @param fieldName Field name.
+     * @param exists Whether field should exist.
+     * @throws Exception If failed.
+     */
+    private void checkNormal(String fieldName, boolean exists) throws Exception {
+        TestContext ctx = context(fieldName);
+
+        check0(fieldName, ctx, exists);
+    }
+
+    /**
+     * Check nested field.
+     *
+     * @param fieldName Field name.
+     * @param exists Whether field should exist.
+     * @throws Exception If failed.
+     */
+    private void checkNested(String fieldName, boolean exists) throws Exception {
+        TestContext ctx = nestedContext(fieldName);
+
+        check0(fieldName, ctx, exists);
+    }
+
+    /**
+     * Internal check routine.
+     *
+     * @param fieldName Field name.
+     * @param ctx Context.
+     * @param exists Whether field should exist.
+     * @throws Exception If failed.
+     */
+    private void check0(String fieldName, TestContext ctx, boolean exists) throws Exception {
+        Object val = ctx.field.value(ctx.portObj);
+
+        if (exists) {
+            assertTrue(ctx.field.exists(ctx.portObj));
+
+            Object expVal = U.field(ctx.obj, fieldName);
+
+            if (val instanceof PortableObject)
+                val = ((PortableObject) val).deserialize();
+
+            if (val != null && val.getClass().isArray()) {
+                assertNotNull(expVal);
+
+                if (val instanceof byte[])
+                    assertTrue(Arrays.equals((byte[]) expVal, (byte[]) val));
+                else if (val instanceof boolean[])
+                    assertTrue(Arrays.equals((boolean[]) expVal, (boolean[]) val));
+                else if (val instanceof short[])
+                    assertTrue(Arrays.equals((short[]) expVal, (short[]) val));
+                else if (val instanceof char[])
+                    assertTrue(Arrays.equals((char[]) expVal, (char[]) val));
+                else if (val instanceof int[])
+                    assertTrue(Arrays.equals((int[]) expVal, (int[]) val));
+                else if (val instanceof long[])
+                    assertTrue(Arrays.equals((long[]) expVal, (long[]) val));
+                else if (val instanceof float[])
+                    assertTrue(Arrays.equals((float[]) expVal, (float[]) val));
+                else if (val instanceof double[])
+                    assertTrue(Arrays.equals((double[]) expVal, (double[]) val));
+                else {
+                    Object[] expVal0 = (Object[])expVal;
+                    Object[] val0 = (Object[])val;
+
+                    assertEquals(expVal0.length, val0.length);
+
+                    for (int i = 0; i < expVal0.length; i++) {
+                        Object expItem = expVal0[i];
+                        Object item = val0[i];
+
+                        if (item instanceof PortableObject)
+                            item = ((PortableObject)item).deserialize();
+
+                        assertEquals(expItem, item);
+                    }
+                }
+            }
+            else
+                assertEquals(expVal, val);
+        }
+        else {
+            assertFalse(ctx.field.exists(ctx.portObj));
+
+            assert val == null;
+        }
+    }
+
+    /**
+     * Get test context.
+     *
+     * @param fieldName Field name.
+     * @return Test context.
+     * @throws Exception If failed.
+     */
+    private TestContext context(String fieldName) throws Exception {
+        TestObject obj = createObject();
+
+        PortableObjectEx portObj = toPortable(marsh, obj);
+
+        PortableField field = portObj.fieldDescriptor(fieldName);
+
+        return new TestContext(obj, portObj, field);
+    }
+
+    /**
+     * Get test context with nested test object.
+     *
+     * @param fieldName Field name.
+     * @return Test context.
+     * @throws Exception If failed.
+     */
+    private TestContext nestedContext(String fieldName) throws Exception {
+        TestObject obj = createObject();
+        TestOuterObject outObj = new TestOuterObject(obj);
+
+        PortableObjectEx portOutObj = toPortable(marsh, outObj);
+        PortableObjectEx portObj = portOutObj.field("fInner");
+
+        assert portObj != null;
+
+        PortableField field = portObj.fieldDescriptor(fieldName);
+
+        return new TestContext(obj, portObj, field);
+    }
+
+    /**
+     * Create test object.
+     *
+     * @return Test object.
+     */
+    private TestObject createObject() {
+        return new TestObject(0);
+    }
+
+    /**
+     * Convert object to portable object.
+     *
+     * @param marsh Marshaller.
+     * @param obj Object.
+     * @return Portable object.
+     * @throws Exception If failed.
+     */
+    protected abstract PortableObjectEx toPortable(PortableMarshaller marsh, Object obj) throws Exception;
+
+    /**
+     * Outer test object.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public static class TestOuterObject {
+        /** Inner object. */
+        public TestObject fInner;
+
+        /**
+         * Default constructor.
+         */
+        public TestOuterObject() {
+            // No-op.
+        }
+
+        /**
+         * Constructor.
+         *
+         * @param fInner Inner object.
+         */
+        public TestOuterObject(TestObject fInner) {
+            this.fInner = fInner;
+        }
+    }
+
+    /**
+     * Test object class, c
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public static class TestObject {
+        /** Primitive fields. */
+        public byte fByte;
+        public boolean fBool;
+        public short fShort;
+        public char fChar;
+        public int fInt;
+        public long fLong;
+        public float fFloat;
+        public double fDouble;
+
+        public byte[] fByteArr;
+        public boolean[] fBoolArr;
+        public short[] fShortArr;
+        public char[] fCharArr;
+        public int[] fIntArr;
+        public long[] fLongArr;
+        public float[] fFloatArr;
+        public double[] fDoubleArr;
+
+        /** Special fields. */
+        public String fString;
+        public Date fDate;
+        public Timestamp fTimestamp;
+        public UUID fUuid;
+        public BigDecimal fDecimal;
+
+        public String[] fStringArr;
+        public Date[] fDateArr;
+        public Timestamp[] fTimestampArr;
+        public UUID[] fUuidArr;
+        public BigDecimal[] fDecimalArr;
+
+        /** Nested object. */
+        public TestInnerObject fObj;
+
+        public TestInnerObject[] fObjArr;
+
+        /** Field which is always set to null. */
+        public Object fNull;
+
+        /**
+         * Default constructor.
+         */
+        public TestObject() {
+            // No-op.
+        }
+
+        /**
+         * Non-default constructor.
+         *
+         * @param ignore Ignored.
+         */
+        public TestObject(int ignore) {
+            fByte = 1;
+            fBool = true;
+            fShort = 2;
+            fChar = 3;
+            fInt = 4;
+            fLong = 5;
+            fFloat = 6.6f;
+            fDouble = 7.7;
+
+            fByteArr = new byte[] { 1, 2 };
+            fBoolArr = new boolean[] { true, false };
+            fShortArr = new short[] { 2, 3 };
+            fCharArr = new char[] { 3, 4 };
+            fIntArr = new int[] { 4, 5 };
+            fLongArr = new long[] { 5, 6 };
+            fFloatArr = new float[] { 6.6f, 7.7f };
+            fDoubleArr = new double[] { 7.7, 8.8 };
+
+            fString = "8";
+            fDate = new Date();
+            fTimestamp = new Timestamp(new Date().getTime() + 1);
+            fUuid = UUID.randomUUID();
+            fDecimal = new BigDecimal(9);
+
+            fStringArr = new String[] { "8", "9" };
+            fDateArr = new Date[] { new Date(), new Date(new Date().getTime() + 1) };
+            fTimestampArr =
+                new Timestamp[] { new Timestamp(new Date().getTime() + 1), new Timestamp(new Date().getTime() + 2) };
+            fUuidArr = new UUID[] { UUID.randomUUID(), UUID.randomUUID() };
+            fDecimalArr = new BigDecimal[] { new BigDecimal(9), new BigDecimal(10) };
+
+            fObj = new TestInnerObject(10);
+            fObjArr = new TestInnerObject[] { new TestInnerObject(10), new TestInnerObject(11) };
+        }
+    }
+
+    /**
+     * Inner test object.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public static class TestInnerObject {
+        /** Value. */
+        private int val;
+
+        /**
+         * Default constructor.
+         */
+        public TestInnerObject() {
+            // No-op.
+        }
+
+        /**
+         * Constructor.
+         *
+         * @param val Value.
+         */
+        public TestInnerObject(int val) {
+            this.val = val;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return val;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object other) {
+            return other != null && other instanceof TestInnerObject && val == ((TestInnerObject)(other)).val;
+        }
+    }
+
+    /**
+     * Test context.
+     */
+    public static class TestContext {
+        /** Object. */
+        public final TestObject obj;
+
+        /** Portable object. */
+        public final PortableObjectEx portObj;
+
+        /** Field. */
+        public final PortableField field;
+
+        /**
+         * Constructor.
+         *
+         * @param obj Object.
+         * @param portObj Portable object.
+         * @param field Field.
+         */
+        public TestContext(TestObject obj, PortableObjectEx portObj, PortableField field) {
+            this.obj = obj;
+            this.portObj = portObj;
+            this.field = field;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsHeapSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsHeapSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsHeapSelfTest.java
new file mode 100644
index 0000000..f7ccd1d
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsHeapSelfTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.marshaller.portable.PortableMarshaller;
+
+/**
+ * Field tests for heap-based portables.
+ */
+public class PortableFieldsHeapSelfTest extends PortableFieldsAbstractSelfTest {
+    /** {@inheritDoc} */
+    @Override protected PortableObjectEx toPortable(PortableMarshaller marsh, Object obj) throws Exception {
+        byte[] bytes = marsh.marshal(obj);
+
+        return new PortableObjectImpl(ctx, bytes, 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsOffheapSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsOffheapSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsOffheapSelfTest.java
new file mode 100644
index 0000000..e421375
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/portable/PortableFieldsOffheapSelfTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.internal.util.GridUnsafe;
+import org.apache.ignite.marshaller.portable.PortableMarshaller;
+import org.eclipse.jetty.util.ConcurrentHashSet;
+import sun.misc.Unsafe;
+
+/**
+ * Field tests for heap-based portables.
+ */
+public class PortableFieldsOffheapSelfTest extends PortableFieldsAbstractSelfTest {
+    /** Unsafe instance. */
+    private static final Unsafe UNSAFE = GridUnsafe.unsafe();
+
+    /** Byte array offset for unsafe mechanics. */
+    protected static final long BYTE_ARR_OFF = UNSAFE.arrayBaseOffset(byte[].class);
+
+    /** Allocated unsafe pointer. */
+    private final ConcurrentHashSet<Long> ptrs = new ConcurrentHashSet<>();
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        // Cleanup allocated objects.
+        for (Long ptr : ptrs)
+            UNSAFE.freeMemory(ptr);
+
+        ptrs.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected PortableObjectEx toPortable(PortableMarshaller marsh, Object obj) throws Exception {
+        byte[] arr = marsh.marshal(obj);
+
+        long ptr = UNSAFE.allocateMemory(arr.length);
+
+        ptrs.add(ptr);
+
+        UNSAFE.copyMemory(arr, BYTE_ARR_OFF, null, ptr, arr.length);
+
+        return new PortableObjectOffheapImpl(ctx, ptr, 0, arr.length);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e399e84/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePortableObjectsTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePortableObjectsTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePortableObjectsTestSuite.java
index ecd25e1..3cfd530 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePortableObjectsTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePortableObjectsTestSuite.java
@@ -28,6 +28,11 @@ import org.apache.ignite.internal.portable.GridPortableMarshallerSelfTest;
 import org.apache.ignite.internal.portable.GridPortableMetaDataDisabledSelfTest;
 import org.apache.ignite.internal.portable.GridPortableMetaDataSelfTest;
 import org.apache.ignite.internal.portable.GridPortableWildcardsSelfTest;
+import org.apache.ignite.internal.portable.PortableCompactOffsetsAbstractSelfTest;
+import org.apache.ignite.internal.portable.PortableCompactOffsetsHeapSelfTest;
+import org.apache.ignite.internal.portable.PortableCompactOffsetsOffheapSelfTest;
+import org.apache.ignite.internal.portable.PortableFieldsHeapSelfTest;
+import org.apache.ignite.internal.portable.PortableFieldsOffheapSelfTest;
 import org.apache.ignite.internal.processors.cache.portable.GridCacheClientNodePortableMetadataMultinodeTest;
 import org.apache.ignite.internal.processors.cache.portable.GridCacheClientNodePortableMetadataTest;
 import org.apache.ignite.internal.processors.cache.portable.GridCachePortableStoreObjectsSelfTest;
@@ -60,6 +65,10 @@ public class IgnitePortableObjectsTestSuite extends TestSuite {
         suite.addTestSuite(GridPortableMarshallerCtxDisabledSelfTest.class);
         suite.addTestSuite(GridPortableBuilderSelfTest.class);
         suite.addTestSuite(GridPortableBuilderStringAsCharsSelfTest.class);
+        suite.addTestSuite(PortableFieldsHeapSelfTest.class);
+        suite.addTestSuite(PortableFieldsOffheapSelfTest.class);
+        suite.addTestSuite(PortableCompactOffsetsHeapSelfTest.class);
+        suite.addTestSuite(PortableCompactOffsetsOffheapSelfTest.class);
         suite.addTestSuite(GridPortableMetaDataSelfTest.class);
         suite.addTestSuite(GridPortableMetaDataDisabledSelfTest.class);
         suite.addTestSuite(GridPortableAffinityKeySelfTest.class);


Mime
View raw message