/**
   * @param obj Object.
   * @return Bytes.
   * @throws org.apache.ignite.binary.BinaryObjectException If failed.
   */
  public byte[] marshal(@Nullable Object obj) throws BinaryObjectException {
    byte[] arr = portableMarsh.marshal(obj);

    assert arr.length > 0;

    return arr;
  }
  /** {@inheritDoc} */
  @Override
  public Object unmarshal(CacheObjectContext ctx, byte[] bytes, ClassLoader clsLdr)
      throws IgniteCheckedException {
    if (!((CacheObjectPortableContext) ctx).portableEnabled() || portableMarsh == null)
      return super.unmarshal(ctx, bytes, clsLdr);

    return portableMarsh.unmarshal(bytes, clsLdr);
  }
  /** {@inheritDoc} */
  @Override
  public byte[] marshal(CacheObjectContext ctx, Object val) throws IgniteCheckedException {
    if (!((CacheObjectPortableContext) ctx).portableEnabled() || portableMarsh == null)
      return super.marshal(ctx, val);

    byte[] arr = portableMarsh.marshal(val);

    assert arr.length > 0;

    return arr;
  }
  /**
   * @param ptr Off-heap pointer.
   * @param forceHeap If {@code true} creates heap-based object.
   * @return Object.
   * @throws org.apache.ignite.binary.BinaryObjectException If failed.
   */
  public Object unmarshal(long ptr, boolean forceHeap) throws BinaryObjectException {
    assert ptr > 0 : ptr;

    int size = UNSAFE.getInt(ptr);

    ptr += 4;

    byte type = UNSAFE.getByte(ptr++);

    if (type != CacheObject.TYPE_BYTE_ARR) {
      assert size > 0 : size;

      PortableInputStream in = new PortableOffheapInputStream(ptr, size, forceHeap);

      return portableMarsh.unmarshal(in);
    } else return U.copyMemory(ptr, size);
  }
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  @Override
  public Object marshalToPortable(@Nullable Object obj) throws BinaryObjectException {
    if (obj == null) return null;

    if (PortableUtils.isPortableType(obj.getClass())) return obj;

    if (obj instanceof Object[]) {
      Object[] arr = (Object[]) obj;

      Object[] pArr = new Object[arr.length];

      for (int i = 0; i < arr.length; i++) pArr[i] = marshalToPortable(arr[i]);

      return pArr;
    }

    if (obj instanceof IgniteBiTuple) {
      IgniteBiTuple tup = (IgniteBiTuple) obj;

      if (obj instanceof T2)
        return new T2<>(marshalToPortable(tup.get1()), marshalToPortable(tup.get2()));

      return new IgniteBiTuple<>(marshalToPortable(tup.get1()), marshalToPortable(tup.get2()));
    }

    if (obj instanceof Collection) {
      Collection<Object> col = (Collection<Object>) obj;

      Collection<Object> pCol;

      if (col instanceof Set) pCol = (Collection<Object>) PortableUtils.newSet((Set<?>) col);
      else pCol = new ArrayList<>(col.size());

      for (Object item : col) pCol.add(marshalToPortable(item));

      return pCol;
    }

    if (obj instanceof Map) {
      Map<?, ?> map = (Map<?, ?>) obj;

      Map<Object, Object> pMap = PortableUtils.newMap((Map<Object, Object>) obj);

      for (Map.Entry<?, ?> e : map.entrySet())
        pMap.put(marshalToPortable(e.getKey()), marshalToPortable(e.getValue()));

      return pMap;
    }

    if (obj instanceof Map.Entry) {
      Map.Entry<?, ?> e = (Map.Entry<?, ?>) obj;

      return new GridMapEntry<>(marshalToPortable(e.getKey()), marshalToPortable(e.getValue()));
    }

    byte[] arr = portableMarsh.marshal(obj);

    assert arr.length > 0;

    Object obj0 = portableMarsh.unmarshal(arr, null);

    // Possible if a class has writeObject method.
    if (obj0 instanceof BinaryObject) ((BinaryObjectImpl) obj0).detachAllowed(true);

    return obj0;
  }