You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/12/14 15:10:26 UTC
[11/55] [abbrv] ignite git commit: ignite-2065: rename "portable"
classes to "binary"
http://git-wip-us.apache.org/repos/asf/ignite/blob/71ad9cea/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchema.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchema.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchema.java
new file mode 100644
index 0000000..99e642c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchema.java
@@ -0,0 +1,466 @@
+/*
+ * 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.binary;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Schema describing portable object content. We rely on the following assumptions:
+ * - When amount of fields in the object is low, it is better to inline these values into int fields thus allowing
+ * for quick comparisons performed within already fetched L1 cache line.
+ * - When there are more fields, we store them inside a hash map.
+ */
+public class BinarySchema implements Externalizable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Order returned if field is not found. */
+ public static final int ORDER_NOT_FOUND = -1;
+
+ /** Minimum sensible size. */
+ private static final int MAP_MIN_SIZE = 32;
+
+ /** Empty cell. */
+ private static final int MAP_EMPTY = 0;
+
+ /** Schema ID. */
+ private int schemaId;
+
+ /** IDs depending on order. */
+ private int[] ids;
+
+ /** Interned names of associated fields. */
+ private String[] names;
+
+ /** ID-to-order data. */
+ private int[] idToOrderData;
+
+ /** ID-to-order mask. */
+ private int idToOrderMask;
+
+ /** ID 1. */
+ private int id0;
+
+ /** ID 2. */
+ private int id1;
+
+ /** ID 3. */
+ private int id2;
+
+ /** ID 4. */
+ private int id3;
+
+ /**
+ * {@link Externalizable} support.
+ */
+ public BinarySchema() {
+ // No-op.
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param schemaId Schema ID.
+ * @param fieldIds Field IDs.
+ */
+ private BinarySchema(int schemaId, List<Integer> fieldIds) {
+ assert fieldIds != null;
+
+ this.schemaId = schemaId;
+
+ initialize(fieldIds);
+ }
+
+ /**
+ * @return Schema ID.
+ */
+ public int schemaId() {
+ return schemaId;
+ }
+
+ /**
+ * Try speculatively confirming order for the given field name.
+ *
+ * @param expOrder Expected order.
+ * @param expName Expected name.
+ * @return Field ID.
+ */
+ @SuppressWarnings("StringEquality")
+ public Confirmation confirmOrder(int expOrder, String expName) {
+ assert expName != null;
+
+ if (expOrder < names.length) {
+ String name = names[expOrder];
+
+ // Note that we use only reference equality assuming that field names are interned literals.
+ if (name == expName)
+ return Confirmation.CONFIRMED;
+
+ if (name == null)
+ return Confirmation.CLARIFY;
+ }
+
+ return Confirmation.REJECTED;
+ }
+
+ /**
+ * Add field name.
+ *
+ * @param order Order.
+ * @param name Name.
+ */
+ public void clarifyFieldName(int order, String name) {
+ assert name != null;
+ assert order < names.length;
+
+ names[order] = name.intern();
+ }
+
+ /**
+ * Get field ID by order in footer.
+ *
+ * @param order Order.
+ * @return Field ID.
+ */
+ public int fieldId(int order) {
+ return order < ids.length ? ids[order] : 0;
+ }
+
+ /**
+ * Get field order in footer by field ID.
+ *
+ * @param id Field ID.
+ * @return Offset or {@code 0} if there is no such field.
+ */
+ public int order(int id) {
+ if (idToOrderData == null) {
+ if (id == id0)
+ return 0;
+
+ if (id == id1)
+ return 1;
+
+ if (id == id2)
+ return 2;
+
+ if (id == id3)
+ return 3;
+
+ return ORDER_NOT_FOUND;
+ }
+ else {
+ int idx = (id & idToOrderMask) << 1;
+
+ int curId = idToOrderData[idx];
+
+ if (id == curId) // Hit!
+ return idToOrderData[idx + 1];
+ else if (curId == MAP_EMPTY) // No such ID!
+ return ORDER_NOT_FOUND;
+ else {
+ // Unlikely collision scenario.
+ for (int i = 2; i < idToOrderData.length; i += 2) {
+ int newIdx = (idx + i) % idToOrderData.length;
+
+ assert newIdx < idToOrderData.length - 1;
+
+ curId = idToOrderData[newIdx];
+
+ if (id == curId)
+ return idToOrderData[newIdx + 1];
+ else if (curId == MAP_EMPTY)
+ return ORDER_NOT_FOUND;
+ }
+
+ return ORDER_NOT_FOUND;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return schemaId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ return o != null && o instanceof BinarySchema && schemaId == ((BinarySchema)o).schemaId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeInt(schemaId);
+
+ out.writeInt(ids.length);
+
+ for (Integer id : ids)
+ out.writeInt(id);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ schemaId = in.readInt();
+
+ int idsCnt = in.readInt();
+
+ List<Integer> fieldIds = new ArrayList<>(idsCnt);
+
+ for (int i = 0; i < idsCnt; i++)
+ fieldIds.add(in.readInt());
+
+ initialize(fieldIds);
+ }
+
+ /**
+ * Parse values.
+ *
+ * @param vals Values.
+ * @param size Proposed result size.
+ * @return Parse result.
+ */
+ private static ParseResult parse(int[] vals, int size) {
+ int mask = maskForPowerOfTwo(size);
+
+ int totalSize = size * 2;
+
+ int[] data = new int[totalSize];
+ int collisions = 0;
+
+ for (int order = 0; order < vals.length; order++) {
+ int id = vals[order];
+
+ assert id != 0;
+
+ int idIdx = (id & mask) << 1;
+
+ if (data[idIdx] == 0) {
+ // Found empty slot.
+ data[idIdx] = id;
+ data[idIdx + 1] = order;
+ }
+ else {
+ // Collision!
+ collisions++;
+
+ boolean placeFound = false;
+
+ for (int i = 2; i < totalSize; i += 2) {
+ int newIdIdx = (idIdx + i) % totalSize;
+
+ if (data[newIdIdx] == 0) {
+ data[newIdIdx] = id;
+ data[newIdIdx + 1] = order;
+
+ placeFound = true;
+
+ break;
+ }
+ }
+
+ assert placeFound : "Should always have a place for entry!";
+ }
+ }
+
+ return new ParseResult(data, collisions);
+ }
+
+ /**
+ * Get next power of two which greater or equal to the given number.
+ * This implementation is not meant to be very efficient, so it is expected to be used relatively rare.
+ *
+ * @param val Number
+ * @return Nearest pow2.
+ */
+ private static int nextPowerOfTwo(int val) {
+ int res = 1;
+
+ while (res < val)
+ res = res << 1;
+
+ if (res < 0)
+ throw new IllegalArgumentException("Value is too big to find positive pow2: " + val);
+
+ return res;
+ }
+
+ /**
+ * Calculate mask for the given value which is a power of two.
+ *
+ * @param val Value.
+ * @return Mask.
+ */
+ private static int maskForPowerOfTwo(int val) {
+ int mask = 0;
+ int comparand = 1;
+
+ while (comparand < val) {
+ mask |= comparand;
+
+ comparand <<= 1;
+ }
+
+ return mask;
+ }
+
+ /**
+ * Initialization routine.
+ *
+ * @param fieldIds Field IDs.
+ */
+ private void initialize(List<Integer> fieldIds) {
+ ids = new int[fieldIds.size()];
+
+ for (int i = 0; i < fieldIds.size(); i++)
+ ids[i] = fieldIds.get(i);
+
+ names = new String[fieldIds.size()];
+
+ if (fieldIds.size() <= 4) {
+ Iterator<Integer> iter = fieldIds.iterator();
+
+ id0 = iter.hasNext() ? iter.next() : 0;
+ id1 = iter.hasNext() ? iter.next() : 0;
+ id2 = iter.hasNext() ? iter.next() : 0;
+ id3 = iter.hasNext() ? iter.next() : 0;
+ }
+ else {
+ id0 = id1 = id2 = id3 = 0;
+
+ initializeMap(ids);
+ }
+ }
+
+ /**
+ * Initialize the map.
+ *
+ * @param vals Values.
+ */
+ private void initializeMap(int[] vals) {
+ int size = Math.max(nextPowerOfTwo(vals.length) << 2, MAP_MIN_SIZE);
+
+ assert size > 0;
+
+ ParseResult finalRes;
+
+ ParseResult res1 = parse(vals, size);
+
+ if (res1.collisions == 0)
+ finalRes = res1;
+ else {
+ ParseResult res2 = parse(vals, size * 2);
+
+ // Failed to decrease aom
+ if (res2.collisions == 0)
+ finalRes = res2;
+ else
+ finalRes = parse(vals, size * 4);
+ }
+
+ idToOrderData = finalRes.data;
+ idToOrderMask = maskForPowerOfTwo(idToOrderData.length / 2);
+ }
+
+ /**
+ * Schema builder.
+ */
+ public static class Builder {
+ /** Schema ID. */
+ private int schemaId = BinaryUtils.schemaInitialId();
+
+ /** Fields. */
+ private final ArrayList<Integer> fields = new ArrayList<>();
+
+ /**
+ * Create new schema builder.
+ *
+ * @return Schema builder.
+ */
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Private constructor.
+ */
+ private Builder() {
+ // No-op.
+ }
+
+ /**
+ * Add field.
+ *
+ * @param fieldId Field ID.
+ */
+ public void addField(int fieldId) {
+ fields.add(fieldId);
+
+ schemaId = BinaryUtils.updateSchemaId(schemaId, fieldId);
+ }
+
+ /**
+ * Build schema.
+ *
+ * @return Schema.
+ */
+ public BinarySchema build() {
+ return new BinarySchema(schemaId, fields);
+ }
+ }
+
+ /**
+ * Order confirmation result.
+ */
+ public enum Confirmation {
+ /** Confirmed. */
+ CONFIRMED,
+
+ /** Denied. */
+ REJECTED,
+
+ /** Field name clarification is needed. */
+ CLARIFY
+ }
+
+ /**
+ * Result of map parsing.
+ */
+ private static class ParseResult {
+ /** Data. */
+ private int[] data;
+
+ /** Collisions. */
+ private int collisions;
+
+ /**
+ * Constructor.
+ *
+ * @param data Data.
+ * @param collisions Collisions.
+ */
+ private ParseResult(int[] data, int collisions) {
+ this.data = data;
+ this.collisions = collisions;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/71ad9cea/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchemaRegistry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchemaRegistry.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchemaRegistry.java
new file mode 100644
index 0000000..6920a34
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinarySchemaRegistry.java
@@ -0,0 +1,172 @@
+/*
+ * 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.binary;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+
+/**
+ * Portable schema registry. Contains all well-known object schemas.
+ * <p>
+ * We rely on the fact that usually object has only few different schemas. For this reason we inline several
+ * of them with optional fallback to normal hash map lookup.
+ *
+ */
+public class BinarySchemaRegistry {
+ /** Empty schema ID. */
+ private static final int EMPTY = 0;
+
+ /** Whether registry still works in inline mode. */
+ private volatile boolean inline = true;
+
+ /** First schema ID. */
+ private int schemaId1;
+
+ /** Second schema ID. */
+ private int schemaId2;
+
+ /** Third schema ID. */
+ private int schemaId3;
+
+ /** Fourth schema ID. */
+ private int schemaId4;
+
+ /** First schema. */
+ private BinarySchema schema1;
+
+ /** Second schema. */
+ private BinarySchema schema2;
+
+ /** Third schema. */
+ private BinarySchema schema3;
+
+ /** Fourth schema. */
+ private BinarySchema schema4;
+
+ /** Schemas with COW semantics. */
+ private volatile HashMap<Integer, BinarySchema> schemas;
+
+ /**
+ * Get schema for the given ID. We rely on very relaxed memory semantics here assuming that it is not critical
+ * to return false-positive {@code null} values.
+ *
+ * @param schemaId Schema ID.
+ * @return Schema or {@code null}.
+ */
+ @Nullable public BinarySchema schema(int schemaId) {
+ if (inline) {
+ if (schemaId == schemaId1)
+ return schema1;
+ else if (schemaId == schemaId2)
+ return schema2;
+ else if (schemaId == schemaId3)
+ return schema3;
+ else if (schemaId == schemaId4)
+ return schema4;
+ }
+ else {
+ HashMap<Integer, BinarySchema> schemas0 = schemas;
+
+ // Null can be observed here due to either data race or race condition when switching to non-inlined mode.
+ // Both of them are benign for us because they lead only to unnecessary schema re-calc.
+ if (schemas0 != null)
+ return schemas0.get(schemaId);
+ }
+
+ return null;
+ }
+
+ /**
+ * Add schema.
+ *
+ * @param schemaId Schema ID.
+ * @param schema Schema.
+ */
+ public void addSchema(int schemaId, BinarySchema schema) {
+ synchronized (this) {
+ if (inline) {
+ // Check if this is already known schema.
+ if (schemaId == schemaId1 || schemaId == schemaId2 || schemaId == schemaId3 || schemaId == schemaId4)
+ return;
+
+ // Try positioning new schema in inline mode.
+ if (schemaId1 == EMPTY) {
+ schemaId1 = schemaId;
+
+ schema1 = schema;
+
+ inline = true; // Forcing HB edge just in case.
+
+ return;
+ }
+
+ if (schemaId2 == EMPTY) {
+ schemaId2 = schemaId;
+
+ schema2 = schema;
+
+ inline = true; // Forcing HB edge just in case.
+
+ return;
+ }
+
+ if (schemaId3 == EMPTY) {
+ schemaId3 = schemaId;
+
+ schema3 = schema;
+
+ inline = true; // Forcing HB edge just in case.
+
+ return;
+ }
+
+ if (schemaId4 == EMPTY) {
+ schemaId4 = schemaId;
+
+ schema4 = schema;
+
+ inline = true; // Forcing HB edge just in case.
+
+ return;
+ }
+
+ // No luck, switching to hash map mode.
+ HashMap<Integer, BinarySchema> newSchemas = new HashMap<>();
+
+ newSchemas.put(schemaId1, schema1);
+ newSchemas.put(schemaId2, schema2);
+ newSchemas.put(schemaId3, schema3);
+ newSchemas.put(schemaId4, schema4);
+
+ newSchemas.put(schemaId, schema);
+
+ schemas = newSchemas;
+
+ inline = false;
+ }
+ else {
+ HashMap<Integer, BinarySchema> newSchemas = new HashMap<>(schemas);
+
+ newSchemas.put(schemaId, schema);
+
+ schemas = newSchemas;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/71ad9cea/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryThreadLocalContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryThreadLocalContext.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryThreadLocalContext.java
index 8fff80b..0ddd581 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryThreadLocalContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryThreadLocalContext.java
@@ -17,9 +17,8 @@
package org.apache.ignite.internal.binary;
-import org.apache.ignite.internal.binary.streams.PortableMemoryAllocatorChunk;
-import org.apache.ignite.internal.binary.streams.PortableMemoryAllocator;
-import org.apache.ignite.internal.binary.streams.PortableMemoryAllocatorChunk;
+import org.apache.ignite.internal.binary.streams.BinaryMemoryAllocatorChunk;
+import org.apache.ignite.internal.binary.streams.BinaryMemoryAllocator;
/**
* Contains thread-local data for binary marshalling.
@@ -33,7 +32,7 @@ public class BinaryThreadLocalContext {
};
/** Memory chunk. */
- private final PortableMemoryAllocatorChunk chunk = PortableMemoryAllocator.INSTANCE.chunk();
+ private final BinaryMemoryAllocatorChunk chunk = BinaryMemoryAllocator.INSTANCE.chunk();
/** Schema holder. */
private final BinaryWriterSchemaHolder schema = new BinaryWriterSchemaHolder();
@@ -57,7 +56,7 @@ public class BinaryThreadLocalContext {
/**
* @return Memory chunk.
*/
- public PortableMemoryAllocatorChunk chunk() {
+ public BinaryMemoryAllocatorChunk chunk() {
return chunk;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/71ad9cea/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTypeImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTypeImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTypeImpl.java
index d19076b..23aacb2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTypeImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryTypeImpl.java
@@ -26,7 +26,7 @@ import java.util.Collection;
*/
public class BinaryTypeImpl implements BinaryType {
/** Portable context. */
- private final PortableContext ctx;
+ private final BinaryContext ctx;
/** Type metadata. */
private final BinaryMetadata meta;
@@ -37,7 +37,7 @@ public class BinaryTypeImpl implements BinaryType {
* @param ctx Portable context.
* @param meta Type metadata.
*/
- public BinaryTypeImpl(PortableContext ctx, BinaryMetadata meta) {
+ public BinaryTypeImpl(BinaryContext ctx, BinaryMetadata meta) {
this.ctx = ctx;
this.meta = meta;
}
@@ -80,7 +80,7 @@ public class BinaryTypeImpl implements BinaryType {
/**
* @return Context.
*/
- public PortableContext context() {
+ public BinaryContext context() {
return ctx;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/71ad9cea/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
new file mode 100644
index 0000000..d285df5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryUtils.java
@@ -0,0 +1,1859 @@
+/*
+ * 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.binary;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.binary.BinaryCollectionFactory;
+import org.apache.ignite.binary.BinaryInvalidTypeException;
+import org.apache.ignite.binary.BinaryMapFactory;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.binary.Binarylizable;
+import org.apache.ignite.internal.binary.builder.BinaryLazyValue;
+import org.apache.ignite.internal.binary.streams.BinaryInputStream;
+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 org.jsr166.ConcurrentHashMap8;
+
+import java.io.ByteArrayInputStream;
+import java.io.Externalizable;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Portable utils.
+ */
+public class BinaryUtils {
+ /** */
+ public static final Map<Class<?>, Byte> PLAIN_CLASS_TO_FLAG = new HashMap<>();
+
+ /** */
+ public static final Map<Byte, Class<?>> FLAG_TO_CLASS = new HashMap<>();
+
+ /** {@code true} if serialized value of this type cannot contain references to objects. */
+ private static final boolean[] PLAIN_TYPE_FLAG = new boolean[102];
+
+ /** Portable classes. */
+ private static final Collection<Class<?>> PORTABLE_CLS = new HashSet<>();
+
+ /** Flag: user type. */
+ public static final short FLAG_USR_TYP = 0x0001;
+
+ /** Flag: only raw data exists. */
+ public static final short FLAG_HAS_SCHEMA = 0x0002;
+
+ /** Flag indicating that object has raw data. */
+ public static final short FLAG_HAS_RAW = 0x0004;
+
+ /** Flag: offsets take 1 byte. */
+ public static final short FLAG_OFFSET_ONE_BYTE = 0x0008;
+
+ /** Flag: offsets take 2 bytes. */
+ public static final short FLAG_OFFSET_TWO_BYTES = 0x0010;
+
+ /** Flag: compact footer, no field IDs. */
+ public static final short FLAG_COMPACT_FOOTER = 0x0020;
+
+ /** Offset which fits into 1 byte. */
+ public static final int OFFSET_1 = 1;
+
+ /** Offset which fits into 2 bytes. */
+ public static final int OFFSET_2 = 2;
+
+ /** Offset which fits into 4 bytes. */
+ public static final int OFFSET_4 = 4;
+
+ /** Field ID length. */
+ public static final int FIELD_ID_LEN = 4;
+
+ /** Field type names. */
+ private static final String[] FIELD_TYPE_NAMES;
+
+ /** FNV1 hash offset basis. */
+ private static final int FNV1_OFFSET_BASIS = 0x811C9DC5;
+
+ /** FNV1 hash prime. */
+ private static final int FNV1_PRIME = 0x01000193;
+
+ /**
+ * Static class initializer.
+ */
+ static {
+ PLAIN_CLASS_TO_FLAG.put(Byte.class, GridBinaryMarshaller.BYTE);
+ PLAIN_CLASS_TO_FLAG.put(Short.class, GridBinaryMarshaller.SHORT);
+ PLAIN_CLASS_TO_FLAG.put(Integer.class, GridBinaryMarshaller.INT);
+ PLAIN_CLASS_TO_FLAG.put(Long.class, GridBinaryMarshaller.LONG);
+ PLAIN_CLASS_TO_FLAG.put(Float.class, GridBinaryMarshaller.FLOAT);
+ PLAIN_CLASS_TO_FLAG.put(Double.class, GridBinaryMarshaller.DOUBLE);
+ PLAIN_CLASS_TO_FLAG.put(Character.class, GridBinaryMarshaller.CHAR);
+ PLAIN_CLASS_TO_FLAG.put(Boolean.class, GridBinaryMarshaller.BOOLEAN);
+ PLAIN_CLASS_TO_FLAG.put(BigDecimal.class, GridBinaryMarshaller.DECIMAL);
+ PLAIN_CLASS_TO_FLAG.put(String.class, GridBinaryMarshaller.STRING);
+ PLAIN_CLASS_TO_FLAG.put(UUID.class, GridBinaryMarshaller.UUID);
+ PLAIN_CLASS_TO_FLAG.put(Date.class, GridBinaryMarshaller.DATE);
+ PLAIN_CLASS_TO_FLAG.put(Timestamp.class, GridBinaryMarshaller.TIMESTAMP);
+
+ PLAIN_CLASS_TO_FLAG.put(byte[].class, GridBinaryMarshaller.BYTE_ARR);
+ PLAIN_CLASS_TO_FLAG.put(short[].class, GridBinaryMarshaller.SHORT_ARR);
+ PLAIN_CLASS_TO_FLAG.put(int[].class, GridBinaryMarshaller.INT_ARR);
+ PLAIN_CLASS_TO_FLAG.put(long[].class, GridBinaryMarshaller.LONG_ARR);
+ PLAIN_CLASS_TO_FLAG.put(float[].class, GridBinaryMarshaller.FLOAT_ARR);
+ PLAIN_CLASS_TO_FLAG.put(double[].class, GridBinaryMarshaller.DOUBLE_ARR);
+ PLAIN_CLASS_TO_FLAG.put(char[].class, GridBinaryMarshaller.CHAR_ARR);
+ PLAIN_CLASS_TO_FLAG.put(boolean[].class, GridBinaryMarshaller.BOOLEAN_ARR);
+ PLAIN_CLASS_TO_FLAG.put(BigDecimal[].class, GridBinaryMarshaller.DECIMAL_ARR);
+ PLAIN_CLASS_TO_FLAG.put(String[].class, GridBinaryMarshaller.STRING_ARR);
+ PLAIN_CLASS_TO_FLAG.put(UUID[].class, GridBinaryMarshaller.UUID_ARR);
+ PLAIN_CLASS_TO_FLAG.put(Date[].class, GridBinaryMarshaller.DATE_ARR);
+ PLAIN_CLASS_TO_FLAG.put(Timestamp[].class, GridBinaryMarshaller.TIMESTAMP_ARR);
+
+ for (Map.Entry<Class<?>, Byte> entry : PLAIN_CLASS_TO_FLAG.entrySet())
+ FLAG_TO_CLASS.put(entry.getValue(), entry.getKey());
+
+ PLAIN_CLASS_TO_FLAG.put(byte.class, GridBinaryMarshaller.BYTE);
+ PLAIN_CLASS_TO_FLAG.put(short.class, GridBinaryMarshaller.SHORT);
+ PLAIN_CLASS_TO_FLAG.put(int.class, GridBinaryMarshaller.INT);
+ PLAIN_CLASS_TO_FLAG.put(long.class, GridBinaryMarshaller.LONG);
+ PLAIN_CLASS_TO_FLAG.put(float.class, GridBinaryMarshaller.FLOAT);
+ PLAIN_CLASS_TO_FLAG.put(double.class, GridBinaryMarshaller.DOUBLE);
+ PLAIN_CLASS_TO_FLAG.put(char.class, GridBinaryMarshaller.CHAR);
+ PLAIN_CLASS_TO_FLAG.put(boolean.class, GridBinaryMarshaller.BOOLEAN);
+
+ for (byte b : new byte[] {
+ GridBinaryMarshaller.BYTE, GridBinaryMarshaller.SHORT, GridBinaryMarshaller.INT, GridBinaryMarshaller.LONG, GridBinaryMarshaller.FLOAT, GridBinaryMarshaller.DOUBLE,
+ GridBinaryMarshaller.CHAR, GridBinaryMarshaller.BOOLEAN, GridBinaryMarshaller.DECIMAL, GridBinaryMarshaller.STRING, GridBinaryMarshaller.UUID, GridBinaryMarshaller.DATE, GridBinaryMarshaller.TIMESTAMP,
+ GridBinaryMarshaller.BYTE_ARR, GridBinaryMarshaller.SHORT_ARR, GridBinaryMarshaller.INT_ARR, GridBinaryMarshaller.LONG_ARR, GridBinaryMarshaller.FLOAT_ARR, GridBinaryMarshaller.DOUBLE_ARR,
+ GridBinaryMarshaller.CHAR_ARR, GridBinaryMarshaller.BOOLEAN_ARR, GridBinaryMarshaller.DECIMAL_ARR, GridBinaryMarshaller.STRING_ARR, GridBinaryMarshaller.UUID_ARR, GridBinaryMarshaller.DATE_ARR, GridBinaryMarshaller.TIMESTAMP_ARR,
+ GridBinaryMarshaller.ENUM, GridBinaryMarshaller.ENUM_ARR, GridBinaryMarshaller.NULL}) {
+
+ PLAIN_TYPE_FLAG[b] = true;
+ }
+
+ PORTABLE_CLS.add(Byte.class);
+ PORTABLE_CLS.add(Short.class);
+ PORTABLE_CLS.add(Integer.class);
+ PORTABLE_CLS.add(Long.class);
+ PORTABLE_CLS.add(Float.class);
+ PORTABLE_CLS.add(Double.class);
+ PORTABLE_CLS.add(Character.class);
+ PORTABLE_CLS.add(Boolean.class);
+ PORTABLE_CLS.add(String.class);
+ PORTABLE_CLS.add(UUID.class);
+ PORTABLE_CLS.add(Date.class);
+ PORTABLE_CLS.add(Timestamp.class);
+ PORTABLE_CLS.add(BigDecimal.class);
+ PORTABLE_CLS.add(byte[].class);
+ PORTABLE_CLS.add(short[].class);
+ PORTABLE_CLS.add(int[].class);
+ PORTABLE_CLS.add(long[].class);
+ PORTABLE_CLS.add(float[].class);
+ PORTABLE_CLS.add(double[].class);
+ PORTABLE_CLS.add(char[].class);
+ PORTABLE_CLS.add(boolean[].class);
+ PORTABLE_CLS.add(String[].class);
+ PORTABLE_CLS.add(UUID[].class);
+ PORTABLE_CLS.add(Date[].class);
+ PORTABLE_CLS.add(Timestamp[].class);
+ PORTABLE_CLS.add(BigDecimal[].class);
+
+ FIELD_TYPE_NAMES = new String[104];
+
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.BYTE] = "byte";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.SHORT] = "short";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.INT] = "int";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.LONG] = "long";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.BOOLEAN] = "boolean";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.FLOAT] = "float";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.DOUBLE] = "double";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.CHAR] = "char";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.UUID] = "UUID";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.DECIMAL] = "decimal";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.STRING] = "String";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.DATE] = "Date";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.TIMESTAMP] = "Timestamp";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.ENUM] = "Enum";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.OBJ] = "Object";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.PORTABLE_OBJ] = "Object";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.COL] = "Collection";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.MAP] = "Map";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.CLASS] = "Class";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.BYTE_ARR] = "byte[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.SHORT_ARR] = "short[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.INT_ARR] = "int[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.LONG_ARR] = "long[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.BOOLEAN_ARR] = "boolean[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.FLOAT_ARR] = "float[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.DOUBLE_ARR] = "double[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.CHAR_ARR] = "char[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.UUID_ARR] = "UUID[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.DECIMAL_ARR] = "decimal[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.STRING_ARR] = "String[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.DATE_ARR] = "Date[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.TIMESTAMP_ARR] = "Timestamp[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.OBJ_ARR] = "Object[]";
+ FIELD_TYPE_NAMES[GridBinaryMarshaller.ENUM_ARR] = "Enum[]";
+ }
+
+ /**
+ * Check if user type flag is set.
+ *
+ * @param flags Flags.
+ * @return {@code True} if set.
+ */
+ public static boolean isUserType(short flags) {
+ return isFlagSet(flags, FLAG_USR_TYP);
+ }
+
+ /**
+ * Check if raw-only flag is set.
+ *
+ * @param flags Flags.
+ * @return {@code True} if set.
+ */
+ public static boolean hasSchema(short flags) {
+ return isFlagSet(flags, FLAG_HAS_SCHEMA);
+ }
+
+ /**
+ * Check if raw-only flag is set.
+ *
+ * @param flags Flags.
+ * @return {@code True} if set.
+ */
+ public static boolean hasRaw(short flags) {
+ return isFlagSet(flags, FLAG_HAS_RAW);
+ }
+
+ /**
+ * Check if "no-field-ids" flag is set.
+ *
+ * @param flags Flags.
+ * @return {@code True} if set.
+ */
+ public static boolean isCompactFooter(short flags) {
+ return isFlagSet(flags, FLAG_COMPACT_FOOTER);
+ }
+
+ /**
+ * Check whether particular flag is set.
+ *
+ * @param flags Flags.
+ * @param flag Flag.
+ * @return {@code True} if flag is set in flags.
+ */
+ private static boolean isFlagSet(short flags, short flag) {
+ return (flags & flag) == flag;
+ }
+
+ /**
+ * Schema initial ID.
+ *
+ * @return ID.
+ */
+ public static int schemaInitialId() {
+ return FNV1_OFFSET_BASIS;
+ }
+
+ /**
+ * Update schema ID when new field is added.
+ *
+ * @param schemaId Current schema ID.
+ * @param fieldId Field ID.
+ * @return New schema ID.
+ */
+ public static int updateSchemaId(int schemaId, int fieldId) {
+ schemaId = schemaId ^ (fieldId & 0xFF);
+ schemaId = schemaId * FNV1_PRIME;
+ schemaId = schemaId ^ ((fieldId >> 8) & 0xFF);
+ schemaId = schemaId * FNV1_PRIME;
+ schemaId = schemaId ^ ((fieldId >> 16) & 0xFF);
+ schemaId = schemaId * FNV1_PRIME;
+ schemaId = schemaId ^ ((fieldId >> 24) & 0xFF);
+ schemaId = schemaId * FNV1_PRIME;
+
+ return schemaId;
+ }
+
+ /**
+ * @param typeName Field type name.
+ * @return Field type ID;
+ */
+ @SuppressWarnings("StringEquality")
+ public static int fieldTypeId(String typeName) {
+ for (int i = 0; i < FIELD_TYPE_NAMES.length; i++) {
+ String typeName0 = FIELD_TYPE_NAMES[i];
+
+ if (typeName.equals(typeName0))
+ return i;
+ }
+
+ throw new IllegalArgumentException("Invalid metadata type name: " + typeName);
+ }
+
+ /**
+ * @param typeId Field type ID.
+ * @return Field type name.
+ */
+ public static String fieldTypeName(int typeId) {
+ assert typeId >= 0 && typeId < FIELD_TYPE_NAMES.length : typeId;
+
+ String typeName = FIELD_TYPE_NAMES[typeId];
+
+ assert typeName != null : typeId;
+
+ return typeName;
+ }
+
+ /**
+ * Write value with flag. e.g. writePlainObject(writer, (byte)77) will write two byte: {BYTE, 77}.
+ *
+ * @param writer W
+ * @param val Value.
+ */
+ public static void writePlainObject(BinaryWriterExImpl writer, Object val) {
+ Byte flag = PLAIN_CLASS_TO_FLAG.get(val.getClass());
+
+ if (flag == null)
+ throw new IllegalArgumentException("Can't write object with type: " + val.getClass());
+
+ switch (flag) {
+ case GridBinaryMarshaller.BYTE:
+ writer.writeByte(flag);
+ writer.writeByte((Byte)val);
+
+ break;
+
+ case GridBinaryMarshaller.SHORT:
+ writer.writeByte(flag);
+ writer.writeShort((Short)val);
+
+ break;
+
+ case GridBinaryMarshaller.INT:
+ writer.writeByte(flag);
+ writer.writeInt((Integer)val);
+
+ break;
+
+ case GridBinaryMarshaller.LONG:
+ writer.writeByte(flag);
+ writer.writeLong((Long)val);
+
+ break;
+
+ case GridBinaryMarshaller.FLOAT:
+ writer.writeByte(flag);
+ writer.writeFloat((Float)val);
+
+ break;
+
+ case GridBinaryMarshaller.DOUBLE:
+ writer.writeByte(flag);
+ writer.writeDouble((Double)val);
+
+ break;
+
+ case GridBinaryMarshaller.CHAR:
+ writer.writeByte(flag);
+ writer.writeChar((Character)val);
+
+ break;
+
+ case GridBinaryMarshaller.BOOLEAN:
+ writer.writeByte(flag);
+ writer.writeBoolean((Boolean)val);
+
+ break;
+
+ case GridBinaryMarshaller.DECIMAL:
+ writer.doWriteDecimal((BigDecimal)val);
+
+ break;
+
+ case GridBinaryMarshaller.STRING:
+ writer.doWriteString((String)val);
+
+ break;
+
+ case GridBinaryMarshaller.UUID:
+ writer.doWriteUuid((UUID)val);
+
+ break;
+
+ case GridBinaryMarshaller.DATE:
+ writer.doWriteDate((Date)val);
+
+ break;
+
+ case GridBinaryMarshaller.TIMESTAMP:
+ writer.doWriteTimestamp((Timestamp) val);
+
+ break;
+
+ case GridBinaryMarshaller.BYTE_ARR:
+ writer.doWriteByteArray((byte[])val);
+
+ break;
+
+ case GridBinaryMarshaller.SHORT_ARR:
+ writer.doWriteShortArray((short[])val);
+
+ break;
+
+ case GridBinaryMarshaller.INT_ARR:
+ writer.doWriteIntArray((int[])val);
+
+ break;
+
+ case GridBinaryMarshaller.LONG_ARR:
+ writer.doWriteLongArray((long[])val);
+
+ break;
+
+ case GridBinaryMarshaller.FLOAT_ARR:
+ writer.doWriteFloatArray((float[])val);
+
+ break;
+
+ case GridBinaryMarshaller.DOUBLE_ARR:
+ writer.doWriteDoubleArray((double[])val);
+
+ break;
+
+ case GridBinaryMarshaller.CHAR_ARR:
+ writer.doWriteCharArray((char[])val);
+
+ break;
+
+ case GridBinaryMarshaller.BOOLEAN_ARR:
+ writer.doWriteBooleanArray((boolean[])val);
+
+ break;
+
+ case GridBinaryMarshaller.DECIMAL_ARR:
+ writer.doWriteDecimalArray((BigDecimal[])val);
+
+ break;
+
+ case GridBinaryMarshaller.STRING_ARR:
+ writer.doWriteStringArray((String[])val);
+
+ break;
+
+ case GridBinaryMarshaller.UUID_ARR:
+ writer.doWriteUuidArray((UUID[])val);
+
+ break;
+
+ case GridBinaryMarshaller.DATE_ARR:
+ writer.doWriteDateArray((Date[])val);
+
+ break;
+
+ case GridBinaryMarshaller.TIMESTAMP_ARR:
+ writer.doWriteTimestampArray((Timestamp[])val);
+
+ break;
+
+ default:
+ throw new IllegalArgumentException("Can't write object with type: " + val.getClass());
+ }
+ }
+
+ /**
+ * @param obj Value to unwrap.
+ * @return Unwrapped value.
+ */
+ public static Object unwrapLazy(@Nullable Object obj) {
+ if (obj instanceof BinaryLazyValue)
+ return ((BinaryLazyValue)obj).value();
+
+ return obj;
+ }
+
+ /**
+ * @param delegate Iterator to delegate.
+ * @return New iterator.
+ */
+ public static Iterator<Object> unwrapLazyIterator(final Iterator<Object> delegate) {
+ return new Iterator<Object>() {
+ @Override public boolean hasNext() {
+ return delegate.hasNext();
+ }
+
+ @Override public Object next() {
+ return unwrapLazy(delegate.next());
+ }
+
+ @Override public void remove() {
+ delegate.remove();
+ }
+ };
+ }
+
+ /**
+ * @return {@code true} if content of serialized value cannot contain references to other object.
+ */
+ public static boolean isPlainType(int type) {
+ return type > 0 && type < PLAIN_TYPE_FLAG.length && PLAIN_TYPE_FLAG[type];
+ }
+
+ /**
+ * Checks whether an array type values can or can not contain references to other object.
+ *
+ * @param type Array type.
+ * @return {@code true} if content of serialized array value cannot contain references to other object.
+ */
+ public static boolean isPlainArrayType(int type) {
+ return (type >= GridBinaryMarshaller.BYTE_ARR && type <= GridBinaryMarshaller.DATE_ARR) || type == GridBinaryMarshaller.TIMESTAMP_ARR;
+ }
+
+ /**
+ * @param cls Class.
+ * @return Portable field type.
+ */
+ public static byte typeByClass(Class<?> cls) {
+ Byte type = PLAIN_CLASS_TO_FLAG.get(cls);
+
+ if (type != null)
+ return type;
+
+ if (cls.isEnum())
+ return GridBinaryMarshaller.ENUM;
+
+ if (cls.isArray())
+ return cls.getComponentType().isEnum() || cls.getComponentType() == Enum.class ? GridBinaryMarshaller.ENUM_ARR : GridBinaryMarshaller.OBJ_ARR;
+
+ if (isSpecialCollection(cls))
+ return GridBinaryMarshaller.COL;
+
+ if (isSpecialMap(cls))
+ return GridBinaryMarshaller.MAP;
+
+ return GridBinaryMarshaller.OBJ;
+ }
+
+ /**
+ * Tells whether provided type is portable.
+ *
+ * @param cls Class to check.
+ * @return Whether type is portable.
+ */
+ public static boolean isPortableType(Class<?> cls) {
+ assert cls != null;
+
+ return BinaryObject.class.isAssignableFrom(cls) ||
+ PORTABLE_CLS.contains(cls) ||
+ cls.isEnum() ||
+ (cls.isArray() && cls.getComponentType().isEnum());
+ }
+
+ /**
+ * Attempts to create a new map of the same type as {@code map} has. Otherwise returns new {@code HashMap} instance.
+ *
+ * @param map Original map.
+ * @return New map.
+ */
+ public static <K, V> Map<K, V> newMap(Map<K, V> map) {
+ if (map instanceof LinkedHashMap)
+ return U.newLinkedHashMap(map.size());
+ else if (map instanceof TreeMap)
+ return new TreeMap<>(((TreeMap<Object, Object>)map).comparator());
+ else if (map instanceof ConcurrentHashMap8)
+ return new ConcurrentHashMap8<>(U.capacity(map.size()));
+ else if (map instanceof ConcurrentHashMap)
+ return new ConcurrentHashMap<>(U.capacity(map.size()));
+
+ return U.newHashMap(map.size());
+ }
+
+ /**
+ * Attempts to create a new set of the same type as {@code set} has. Otherwise returns new {@code HashSet} instance.
+ *
+ * @param set Original set.
+ * @return New set.
+ */
+ public static <V> Set<V> newSet(Set<V> set) {
+ if (set instanceof LinkedHashSet)
+ return U.newLinkedHashSet(set.size());
+ else if (set instanceof TreeSet)
+ return new TreeSet<>(((TreeSet<Object>)set).comparator());
+ else if (set instanceof ConcurrentSkipListSet)
+ return new ConcurrentSkipListSet<>(((ConcurrentSkipListSet<Object>)set).comparator());
+
+ return U.newHashSet(set.size());
+ }
+
+ /**
+ * Check protocol version.
+ *
+ * @param protoVer Protocol version.
+ */
+ public static void checkProtocolVersion(byte protoVer) {
+ if (GridBinaryMarshaller.PROTO_VER != protoVer)
+ throw new BinaryObjectException("Unsupported protocol version: " + protoVer);
+ }
+
+ /**
+ * Get portable object length.
+ *
+ * @param in Input stream.
+ * @param start Start position.
+ * @return Length.
+ */
+ public static int length(BinaryPositionReadable in, int start) {
+ return in.readIntPositioned(start + GridBinaryMarshaller.TOTAL_LEN_POS);
+ }
+
+ /**
+ * Get footer start of the object.
+ *
+ * @param in Input stream.
+ * @param start Object start position inside the stream.
+ * @return Footer start.
+ */
+ public static int footerStartRelative(BinaryPositionReadable in, int start) {
+ short flags = in.readShortPositioned(start + GridBinaryMarshaller.FLAGS_POS);
+
+ if (hasSchema(flags))
+ // Schema exists, use offset.
+ return in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
+ else
+ // No schema, footer start equals to object end.
+ return length(in, start);
+ }
+
+ /**
+ * Get object's footer.
+ *
+ * @param in Input stream.
+ * @param start Start position.
+ * @return Footer start.
+ */
+ public static int footerStartAbsolute(BinaryPositionReadable in, int start) {
+ return footerStartRelative(in, start) + start;
+ }
+
+ /**
+ * Get object's footer.
+ *
+ * @param in Input stream.
+ * @param start Start position.
+ * @return Footer.
+ */
+ public static IgniteBiTuple<Integer, Integer> footerAbsolute(BinaryPositionReadable in, int start) {
+ short flags = in.readShortPositioned(start + GridBinaryMarshaller.FLAGS_POS);
+
+ int footerEnd = length(in, start);
+
+ if (hasSchema(flags)) {
+ // Schema exists.
+ int footerStart = in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
+
+ if (hasRaw(flags))
+ footerEnd -= 4;
+
+ assert footerStart <= footerEnd;
+
+ return F.t(start + footerStart, start + footerEnd);
+ }
+ else
+ // No schema.
+ return F.t(start + footerEnd, start + footerEnd);
+ }
+
+ /**
+ * Get relative raw offset of the object.
+ *
+ * @param in Input stream.
+ * @param start Object start position inside the stream.
+ * @return Raw offset.
+ */
+ public static int rawOffsetRelative(BinaryPositionReadable in, int start) {
+ short flags = in.readShortPositioned(start + GridBinaryMarshaller.FLAGS_POS);
+
+ int len = length(in, start);
+
+ if (hasSchema(flags)){
+ // Schema exists.
+ if (hasRaw(flags))
+ // Raw offset is set, it is at the very end of the object.
+ return in.readIntPositioned(start + len - 4);
+ else
+ // Raw offset is not set, so just return schema offset.
+ return in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
+ }
+ else
+ // No schema, raw offset is located on schema offset position.
+ return in.readIntPositioned(start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS);
+ }
+
+ /**
+ * Get absolute raw offset of the object.
+ *
+ * @param in Input stream.
+ * @param start Object start position inside the stream.
+ * @return Raw offset.
+ */
+ public static int rawOffsetAbsolute(BinaryPositionReadable in, int start) {
+ return start + rawOffsetRelative(in, start);
+ }
+
+ /**
+ * Get offset length for the given flags.
+ *
+ * @param flags Flags.
+ * @return Offset size.
+ */
+ public static int fieldOffsetLength(short flags) {
+ if ((flags & FLAG_OFFSET_ONE_BYTE) == FLAG_OFFSET_ONE_BYTE)
+ return OFFSET_1;
+ else if ((flags & FLAG_OFFSET_TWO_BYTES) == FLAG_OFFSET_TWO_BYTES)
+ return OFFSET_2;
+ else
+ return OFFSET_4;
+ }
+
+ /**
+ * Get field ID length.
+ *
+ * @param flags Flags.
+ * @return Field ID length.
+ */
+ public static int fieldIdLength(short flags) {
+ return isCompactFooter(flags) ? 0 : FIELD_ID_LEN;
+ }
+
+ /**
+ * Get relative field offset.
+ *
+ * @param stream Stream.
+ * @param pos Position.
+ * @param fieldOffsetSize Field offset size.
+ * @return Relative field offset.
+ */
+ public static int fieldOffsetRelative(BinaryPositionReadable stream, int pos, int fieldOffsetSize) {
+ int res;
+
+ if (fieldOffsetSize == OFFSET_1)
+ res = (int)stream.readBytePositioned(pos) & 0xFF;
+ else if (fieldOffsetSize == OFFSET_2)
+ res = (int)stream.readShortPositioned(pos) & 0xFFFF;
+ else
+ res = stream.readIntPositioned(pos);
+
+ return res;
+ }
+
+ /**
+ * Merge old and new metas.
+ *
+ * @param oldMeta Old meta.
+ * @param newMeta New meta.
+ * @return New meta if old meta was null, old meta if no changes detected, merged meta otherwise.
+ * @throws BinaryObjectException If merge failed due to metadata conflict.
+ */
+ public static BinaryMetadata mergeMetadata(@Nullable BinaryMetadata oldMeta, BinaryMetadata newMeta) {
+ assert newMeta != null;
+
+ if (oldMeta == null)
+ return newMeta;
+ else {
+ assert oldMeta.typeId() == newMeta.typeId();
+
+ // Check type name.
+ if (!F.eq(oldMeta.typeName(), newMeta.typeName())) {
+ throw new BinaryObjectException(
+ "Two portable types have duplicate type ID [" + "typeId=" + oldMeta.typeId() +
+ ", typeName1=" + oldMeta.typeName() + ", typeName2=" + newMeta.typeName() + ']'
+ );
+ }
+
+ // Check affinity field names.
+ if (!F.eq(oldMeta.affinityKeyFieldName(), newMeta.affinityKeyFieldName())) {
+ throw new BinaryObjectException(
+ "Binary type has different affinity key fields [" + "typeName=" + newMeta.typeName() +
+ ", affKeyFieldName1=" + oldMeta.affinityKeyFieldName() +
+ ", affKeyFieldName2=" + newMeta.affinityKeyFieldName() + ']'
+ );
+ }
+
+ // Check enum flag.
+ if (oldMeta.isEnum() != newMeta.isEnum()) {
+ if (oldMeta.isEnum())
+ throw new BinaryObjectException("Binary type already registered as enum: " +
+ newMeta.typeName());
+ else
+ throw new BinaryObjectException("Binary type already registered as non-enum: " +
+ newMeta.typeName());
+ }
+
+ // Check and merge fields.
+ boolean changed = false;
+
+ Map<String, Integer> mergedFields = new HashMap<>(oldMeta.fieldsMap());
+ Map<String, Integer> newFields = newMeta.fieldsMap();
+
+ for (Map.Entry<String, Integer> newField : newFields.entrySet()) {
+ Integer oldFieldType = mergedFields.put(newField.getKey(), newField.getValue());
+
+ if (oldFieldType == null)
+ changed = true;
+ else if (!F.eq(oldFieldType, newField.getValue())) {
+ throw new BinaryObjectException(
+ "Binary type has different field types [" + "typeName=" + oldMeta.typeName() +
+ ", fieldName=" + newField.getKey() +
+ ", fieldTypeName1=" + fieldTypeName(oldFieldType) +
+ ", fieldTypeName2=" + fieldTypeName(newField.getValue()) + ']'
+ );
+ }
+ }
+
+ // Check and merge schemas.
+ Collection<BinarySchema> mergedSchemas = new HashSet<>(oldMeta.schemas());
+
+ for (BinarySchema newSchema : newMeta.schemas()) {
+ if (mergedSchemas.add(newSchema))
+ changed = true;
+ }
+
+ // Return either old meta if no changes detected, or new merged meta.
+ return changed ? new BinaryMetadata(oldMeta.typeId(), oldMeta.typeName(), mergedFields,
+ oldMeta.affinityKeyFieldName(), mergedSchemas, oldMeta.isEnum()) : oldMeta;
+ }
+ }
+
+ /**
+ * @param cls Class.
+ * @return Mode.
+ */
+ @SuppressWarnings("IfMayBeConditional")
+ public static BinaryWriteMode mode(Class<?> cls) {
+ assert cls != null;
+
+ /** Primitives. */
+ if (cls == byte.class)
+ return BinaryWriteMode.P_BYTE;
+ else if (cls == boolean.class)
+ return BinaryWriteMode.P_BOOLEAN;
+ else if (cls == short.class)
+ return BinaryWriteMode.P_SHORT;
+ else if (cls == char.class)
+ return BinaryWriteMode.P_CHAR;
+ else if (cls == int.class)
+ return BinaryWriteMode.P_INT;
+ else if (cls == long.class)
+ return BinaryWriteMode.P_LONG;
+ else if (cls == float.class)
+ return BinaryWriteMode.P_FLOAT;
+ else if (cls == double.class)
+ return BinaryWriteMode.P_DOUBLE;
+
+ /** Boxed primitives. */
+ else if (cls == Byte.class)
+ return BinaryWriteMode.BYTE;
+ else if (cls == Boolean.class)
+ return BinaryWriteMode.BOOLEAN;
+ else if (cls == Short.class)
+ return BinaryWriteMode.SHORT;
+ else if (cls == Character.class)
+ return BinaryWriteMode.CHAR;
+ else if (cls == Integer.class)
+ return BinaryWriteMode.INT;
+ else if (cls == Long.class)
+ return BinaryWriteMode.LONG;
+ else if (cls == Float.class)
+ return BinaryWriteMode.FLOAT;
+ else if (cls == Double.class)
+ return BinaryWriteMode.DOUBLE;
+
+ /** The rest types. */
+ else if (cls == BigDecimal.class)
+ return BinaryWriteMode.DECIMAL;
+ else if (cls == String.class)
+ return BinaryWriteMode.STRING;
+ else if (cls == UUID.class)
+ return BinaryWriteMode.UUID;
+ else if (cls == Date.class)
+ return BinaryWriteMode.DATE;
+ else if (cls == Timestamp.class)
+ return BinaryWriteMode.TIMESTAMP;
+ else if (cls == byte[].class)
+ return BinaryWriteMode.BYTE_ARR;
+ else if (cls == short[].class)
+ return BinaryWriteMode.SHORT_ARR;
+ else if (cls == int[].class)
+ return BinaryWriteMode.INT_ARR;
+ else if (cls == long[].class)
+ return BinaryWriteMode.LONG_ARR;
+ else if (cls == float[].class)
+ return BinaryWriteMode.FLOAT_ARR;
+ else if (cls == double[].class)
+ return BinaryWriteMode.DOUBLE_ARR;
+ else if (cls == char[].class)
+ return BinaryWriteMode.CHAR_ARR;
+ else if (cls == boolean[].class)
+ return BinaryWriteMode.BOOLEAN_ARR;
+ else if (cls == BigDecimal[].class)
+ return BinaryWriteMode.DECIMAL_ARR;
+ else if (cls == String[].class)
+ return BinaryWriteMode.STRING_ARR;
+ else if (cls == UUID[].class)
+ return BinaryWriteMode.UUID_ARR;
+ else if (cls == Date[].class)
+ return BinaryWriteMode.DATE_ARR;
+ else if (cls == Timestamp[].class)
+ return BinaryWriteMode.TIMESTAMP_ARR;
+ else if (cls.isArray())
+ return cls.getComponentType().isEnum() ? BinaryWriteMode.ENUM_ARR : BinaryWriteMode.OBJECT_ARR;
+ else if (cls == BinaryObjectImpl.class)
+ return BinaryWriteMode.PORTABLE_OBJ;
+ else if (Binarylizable.class.isAssignableFrom(cls))
+ return BinaryWriteMode.PORTABLE;
+ else if (Externalizable.class.isAssignableFrom(cls))
+ return BinaryWriteMode.EXTERNALIZABLE;
+ else if (isSpecialCollection(cls))
+ return BinaryWriteMode.COL;
+ else if (isSpecialMap(cls))
+ return BinaryWriteMode.MAP;
+ else if (cls.isEnum())
+ return BinaryWriteMode.ENUM;
+ else if (cls == Class.class)
+ return BinaryWriteMode.CLASS;
+ else
+ return BinaryWriteMode.OBJECT;
+ }
+
+ /**
+ * Check if class represents a collection which must be treated specially.
+ *
+ * @param cls Class.
+ * @return {@code True} if this is a special collection class.
+ */
+ private static boolean isSpecialCollection(Class cls) {
+ return ArrayList.class.equals(cls) || LinkedList.class.equals(cls) ||
+ HashSet.class.equals(cls) || LinkedHashSet.class.equals(cls);
+ }
+
+ /**
+ * Check if class represents a map which must be treated specially.
+ *
+ * @param cls Class.
+ * @return {@code True} if this is a special map class.
+ */
+ private static boolean isSpecialMap(Class cls) {
+ return HashMap.class.equals(cls) || LinkedHashMap.class.equals(cls);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static byte[] doReadByteArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readByteArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static boolean[] doReadBooleanArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readBooleanArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static short[] doReadShortArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readShortArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static char[] doReadCharArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readCharArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static int[] doReadIntArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readIntArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static long[] doReadLongArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readLongArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static float[] doReadFloatArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readFloatArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static double[] doReadDoubleArray(BinaryInputStream in) {
+ int len = in.readInt();
+
+ return in.readDoubleArray(len);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static BigDecimal doReadDecimal(BinaryInputStream in) {
+ int scale = in.readInt();
+ byte[] mag = doReadByteArray(in);
+
+ BigInteger intVal = new BigInteger(mag);
+
+ if (scale < 0) {
+ scale &= 0x7FFFFFFF;
+
+ intVal = intVal.negate();
+ }
+
+ return new BigDecimal(intVal, scale);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static String doReadString(BinaryInputStream in) {
+ if (!in.hasArray())
+ return new String(doReadByteArray(in), UTF_8);
+
+ int strLen = in.readInt();
+
+ int pos = in.position();
+
+ // String will copy necessary array part for us.
+ String res = new String(in.array(), pos, strLen, UTF_8);
+
+ in.position(pos + strLen);
+
+ return res;
+ }
+
+ /**
+ * @return Value.
+ */
+ public static UUID doReadUuid(BinaryInputStream in) {
+ return new UUID(in.readLong(), in.readLong());
+ }
+
+ /**
+ * @return Value.
+ */
+ public static Date doReadDate(BinaryInputStream in) {
+ long time = in.readLong();
+
+ return new Date(time);
+ }
+
+ /**
+ * @return Value.
+ */
+ public static Timestamp doReadTimestamp(BinaryInputStream in) {
+ long time = in.readLong();
+ int nanos = in.readInt();
+
+ Timestamp ts = new Timestamp(time);
+
+ ts.setNanos(ts.getNanos() + nanos);
+
+ return ts;
+ }
+
+ /**
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ public static BigDecimal[] doReadDecimalArray(BinaryInputStream in) throws BinaryObjectException {
+ int len = in.readInt();
+
+ BigDecimal[] arr = new BigDecimal[len];
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else {
+ if (flag != GridBinaryMarshaller.DECIMAL)
+ throw new BinaryObjectException("Invalid flag value: " + flag);
+
+ arr[i] = doReadDecimal(in);
+ }
+ }
+
+ return arr;
+ }
+
+ /**
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ public static String[] doReadStringArray(BinaryInputStream in) throws BinaryObjectException {
+ int len = in.readInt();
+
+ String[] arr = new String[len];
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else {
+ if (flag != GridBinaryMarshaller.STRING)
+ throw new BinaryObjectException("Invalid flag value: " + flag);
+
+ arr[i] = doReadString(in);
+ }
+ }
+
+ return arr;
+ }
+
+ /**
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ public static UUID[] doReadUuidArray(BinaryInputStream in) throws BinaryObjectException {
+ int len = in.readInt();
+
+ UUID[] arr = new UUID[len];
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else {
+ if (flag != GridBinaryMarshaller.UUID)
+ throw new BinaryObjectException("Invalid flag value: " + flag);
+
+ arr[i] = doReadUuid(in);
+ }
+ }
+
+ return arr;
+ }
+
+ /**
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ public static Date[] doReadDateArray(BinaryInputStream in) throws BinaryObjectException {
+ int len = in.readInt();
+
+ Date[] arr = new Date[len];
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else {
+ if (flag != GridBinaryMarshaller.DATE)
+ throw new BinaryObjectException("Invalid flag value: " + flag);
+
+ arr[i] = doReadDate(in);
+ }
+ }
+
+ return arr;
+ }
+
+ /**
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ public static Timestamp[] doReadTimestampArray(BinaryInputStream in) throws BinaryObjectException {
+ int len = in.readInt();
+
+ Timestamp[] arr = new Timestamp[len];
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else {
+ if (flag != GridBinaryMarshaller.TIMESTAMP)
+ throw new BinaryObjectException("Invalid flag value: " + flag);
+
+ arr[i] = doReadTimestamp(in);
+ }
+ }
+
+ return arr;
+ }
+
+ /**
+ * @return Value.
+ */
+ public static BinaryObject doReadPortableObject(BinaryInputStream in, BinaryContext ctx) {
+ if (in.offheapPointer() > 0) {
+ int len = in.readInt();
+
+ int pos = in.position();
+
+ in.position(in.position() + len);
+
+ int start = in.readInt();
+
+ return new BinaryObjectOffheapImpl(ctx, in.offheapPointer() + pos, start, len);
+ }
+ else {
+ byte[] arr = doReadByteArray(in);
+ int start = in.readInt();
+
+ return new BinaryObjectImpl(ctx, arr, start);
+ }
+ }
+
+ /**
+ * @return Value.
+ */
+ public static Class doReadClass(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr)
+ throws BinaryObjectException {
+ int typeId = in.readInt();
+
+ return doReadClass(in, ctx, ldr, typeId);
+ }
+
+ /**
+ * Read plain type.
+ *
+ * @param in Input stream.
+ * @return Plain type.
+ */
+ private static EnumType doReadEnumType(BinaryInputStream in) {
+ int typeId = in.readInt();
+
+ if (typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
+ return new EnumType(typeId, null);
+ else {
+ String clsName = doReadClassName(in);
+
+ return new EnumType(GridBinaryMarshaller.UNREGISTERED_TYPE_ID, clsName);
+ }
+ }
+
+ /**
+ * @param in Input stream.
+ * @return Class name.
+ */
+ private static String doReadClassName(BinaryInputStream in) {
+ byte flag = in.readByte();
+
+ if (flag != GridBinaryMarshaller.STRING)
+ throw new BinaryObjectException("Failed to read class name [position=" + (in.position() - 1) + ']');
+
+ return doReadString(in);
+ }
+
+ /**
+ * @param typeId Type id.
+ * @return Value.
+ */
+ public static Class doReadClass(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, int typeId)
+ throws BinaryObjectException {
+ Class cls;
+
+ if (typeId == GridBinaryMarshaller.OBJECT_TYPE_ID)
+ return Object.class;
+
+ if (typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
+ cls = ctx.descriptorForTypeId(true, typeId, ldr, false).describedClass();
+ else {
+ String clsName = doReadClassName(in);
+
+ try {
+ cls = U.forName(clsName, ldr);
+ }
+ catch (ClassNotFoundException e) {
+ throw new BinaryInvalidTypeException("Failed to load the class: " + clsName, e);
+ }
+
+ // forces registering of class by type id, at least locally
+ ctx.descriptorForClass(cls, true);
+ }
+
+ return cls;
+ }
+
+ /**
+ * Resolve the class.
+ *
+ * @param ctx Portable context.
+ * @param typeId Type ID.
+ * @param clsName Class name.
+ * @param ldr Class loaded.
+ * @return Resovled class.
+ */
+ public static Class resolveClass(BinaryContext ctx, int typeId, @Nullable String clsName,
+ @Nullable ClassLoader ldr, boolean deserialize) {
+ Class cls;
+
+ if (typeId == GridBinaryMarshaller.OBJECT_TYPE_ID)
+ return Object.class;
+
+ if (typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID)
+ cls = ctx.descriptorForTypeId(true, typeId, ldr, deserialize).describedClass();
+ else {
+ try {
+ cls = U.forName(clsName, ldr);
+ }
+ catch (ClassNotFoundException e) {
+ throw new BinaryInvalidTypeException("Failed to load the class: " + clsName, e);
+ }
+
+ // forces registering of class by type id, at least locally
+ ctx.descriptorForClass(cls, true);
+ }
+
+ return cls;
+ }
+
+ /**
+ * Read portable enum.
+ *
+ * @param in Input stream.
+ * @param ctx Portable context.
+ * @param type Plain type.
+ * @return Enum.
+ */
+ private static BinaryEnumObjectImpl doReadPortableEnum(BinaryInputStream in, BinaryContext ctx,
+ EnumType type) {
+ return new BinaryEnumObjectImpl(ctx, type.typeId, type.clsName, in.readInt());
+ }
+
+ /**
+ * Read portable enum array.
+ *
+ * @param in Input stream.
+ * @param ctx Portable context.
+ * @return Enum array.
+ */
+ private static Object[] doReadPortableEnumArray(BinaryInputStream in, BinaryContext ctx) {
+ int len = in.readInt();
+
+ Object[] arr = (Object[]) Array.newInstance(BinaryObject.class, len);
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else
+ arr[i] = doReadPortableEnum(in, ctx, doReadEnumType(in));
+ }
+
+ return arr;
+ }
+
+ /**
+ * Having target class in place we simply read ordinal and create final representation.
+ *
+ * @param cls Enum class.
+ * @return Value.
+ */
+ public static Enum<?> doReadEnum(BinaryInputStream in, Class<?> cls) throws BinaryObjectException {
+ assert cls != null;
+
+ if (!cls.isEnum())
+ throw new BinaryObjectException("Class does not represent enum type: " + cls.getName());
+
+ int ord = in.readInt();
+
+ return BinaryEnumCache.get(cls, ord);
+ }
+
+ /**
+ * @param cls Enum class.
+ * @return Value.
+ */
+ public static Object[] doReadEnumArray(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr, Class<?> cls)
+ throws BinaryObjectException {
+ int len = in.readInt();
+
+ Object[] arr = (Object[]) Array.newInstance(cls, len);
+
+ for (int i = 0; i < len; i++) {
+ byte flag = in.readByte();
+
+ if (flag == GridBinaryMarshaller.NULL)
+ arr[i] = null;
+ else
+ arr[i] = doReadEnum(in, doReadClass(in, ctx, ldr));
+ }
+
+ return arr;
+ }
+
+ /**
+ * Read object serialized using optimized marshaller.
+ *
+ * @return Result.
+ */
+ public static Object doReadOptimized(BinaryInputStream in, BinaryContext ctx, @Nullable ClassLoader clsLdr) {
+ int len = in.readInt();
+
+ ByteArrayInputStream input = new ByteArrayInputStream(in.array(), in.position(), len);
+
+ try {
+ return ctx.optimizedMarsh().unmarshal(input, clsLdr);
+ }
+ catch (IgniteCheckedException e) {
+ throw new BinaryObjectException("Failed to unmarshal object with optimized marshaller", e);
+ }
+ finally {
+ in.position(in.position() + len);
+ }
+ }
+
+ /**
+ * @return Object.
+ * @throws BinaryObjectException In case of error.
+ */
+ @Nullable public static Object doReadObject(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles) throws BinaryObjectException {
+ return new BinaryReaderExImpl(ctx, in, ldr, handles.handles()).deserialize();
+ }
+
+ /**
+ * @return Unmarshalled value.
+ * @throws BinaryObjectException In case of error.
+ */
+ @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr)
+ throws BinaryObjectException {
+ return unmarshal(in, ctx, ldr, new BinaryReaderHandlesHolderImpl());
+ }
+
+ /**
+ * @return Unmarshalled value.
+ * @throws BinaryObjectException In case of error.
+ */
+ @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles) throws BinaryObjectException {
+ return unmarshal(in, ctx, ldr, handles, false);
+ }
+
+ /**
+ * @return Unmarshalled value.
+ * @throws BinaryObjectException In case of error.
+ */
+ @Nullable public static Object unmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles, boolean detach) throws BinaryObjectException {
+ int start = in.position();
+
+ byte flag = in.readByte();
+
+ switch (flag) {
+ case GridBinaryMarshaller.NULL:
+ return null;
+
+ case GridBinaryMarshaller.HANDLE: {
+ int handlePos = start - in.readInt();
+
+ Object obj = handles.getHandle(handlePos);
+
+ if (obj == null) {
+ int retPos = in.position();
+
+ in.position(handlePos);
+
+ obj = unmarshal(in, ctx, ldr, handles);
+
+ in.position(retPos);
+ }
+
+ return obj;
+ }
+
+ case GridBinaryMarshaller.OBJ: {
+ checkProtocolVersion(in.readByte());
+
+ int len = length(in, start);
+
+ BinaryObjectExImpl po;
+
+ if (detach) {
+ // In detach mode we simply copy object's content.
+ in.position(start);
+
+ po = new BinaryObjectImpl(ctx, in.readByteArray(len), 0);
+ }
+ else {
+ if (in.offheapPointer() == 0)
+ po = new BinaryObjectImpl(ctx, in.array(), start);
+ else
+ po = new BinaryObjectOffheapImpl(ctx, in.offheapPointer(), start,
+ in.remaining() + in.position());
+
+ in.position(start + po.length());
+ }
+
+ handles.setHandle(po, start);
+
+ return po;
+ }
+
+ case GridBinaryMarshaller.BYTE:
+ return in.readByte();
+
+ case GridBinaryMarshaller.SHORT:
+ return in.readShort();
+
+ case GridBinaryMarshaller.INT:
+ return in.readInt();
+
+ case GridBinaryMarshaller.LONG:
+ return in.readLong();
+
+ case GridBinaryMarshaller.FLOAT:
+ return in.readFloat();
+
+ case GridBinaryMarshaller.DOUBLE:
+ return in.readDouble();
+
+ case GridBinaryMarshaller.CHAR:
+ return in.readChar();
+
+ case GridBinaryMarshaller.BOOLEAN:
+ return in.readBoolean();
+
+ case GridBinaryMarshaller.DECIMAL:
+ return doReadDecimal(in);
+
+ case GridBinaryMarshaller.STRING:
+ return doReadString(in);
+
+ case GridBinaryMarshaller.UUID:
+ return doReadUuid(in);
+
+ case GridBinaryMarshaller.DATE:
+ return doReadDate(in);
+
+ case GridBinaryMarshaller.TIMESTAMP:
+ return doReadTimestamp(in);
+
+ case GridBinaryMarshaller.BYTE_ARR:
+ return doReadByteArray(in);
+
+ case GridBinaryMarshaller.SHORT_ARR:
+ return doReadShortArray(in);
+
+ case GridBinaryMarshaller.INT_ARR:
+ return doReadIntArray(in);
+
+ case GridBinaryMarshaller.LONG_ARR:
+ return doReadLongArray(in);
+
+ case GridBinaryMarshaller.FLOAT_ARR:
+ return doReadFloatArray(in);
+
+ case GridBinaryMarshaller.DOUBLE_ARR:
+ return doReadDoubleArray(in);
+
+ case GridBinaryMarshaller.CHAR_ARR:
+ return doReadCharArray(in);
+
+ case GridBinaryMarshaller.BOOLEAN_ARR:
+ return doReadBooleanArray(in);
+
+ case GridBinaryMarshaller.DECIMAL_ARR:
+ return doReadDecimalArray(in);
+
+ case GridBinaryMarshaller.STRING_ARR:
+ return doReadStringArray(in);
+
+ case GridBinaryMarshaller.UUID_ARR:
+ return doReadUuidArray(in);
+
+ case GridBinaryMarshaller.DATE_ARR:
+ return doReadDateArray(in);
+
+ case GridBinaryMarshaller.TIMESTAMP_ARR:
+ return doReadTimestampArray(in);
+
+ case GridBinaryMarshaller.OBJ_ARR:
+ return doReadObjectArray(in, ctx, ldr, handles, false);
+
+ case GridBinaryMarshaller.COL:
+ return doReadCollection(in, ctx, ldr, handles, false, null);
+
+ case GridBinaryMarshaller.MAP:
+ return doReadMap(in, ctx, ldr, handles, false, null);
+
+ case GridBinaryMarshaller.PORTABLE_OBJ:
+ return doReadPortableObject(in, ctx);
+
+ case GridBinaryMarshaller.ENUM:
+ return doReadPortableEnum(in, ctx, doReadEnumType(in));
+
+ case GridBinaryMarshaller.ENUM_ARR:
+ doReadEnumType(in); // Simply skip this part as we do not need it.
+
+ return doReadPortableEnumArray(in, ctx);
+
+ case GridBinaryMarshaller.CLASS:
+ return doReadClass(in, ctx, ldr);
+
+ case GridBinaryMarshaller.OPTM_MARSH:
+ return doReadOptimized(in, ctx, ldr);
+
+ default:
+ throw new BinaryObjectException("Invalid flag value: " + flag);
+ }
+ }
+
+ /**
+ * @param deserialize Deep flag.
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ public static Object[] doReadObjectArray(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles, boolean deserialize) throws BinaryObjectException {
+ int hPos = positionForHandle(in);
+
+ Class compType = doReadClass(in, ctx, ldr);
+
+ int len = in.readInt();
+
+ Object[] arr = deserialize ? (Object[])Array.newInstance(compType, len) : new Object[len];
+
+ handles.setHandle(arr, hPos);
+
+ for (int i = 0; i < len; i++)
+ arr[i] = deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize);
+
+ return arr;
+ }
+
+ /**
+ * @param deserialize Deep flag.
+ * @param factory Collection factory.
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ @SuppressWarnings("unchecked")
+ public static Collection<?> doReadCollection(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles, boolean deserialize, BinaryCollectionFactory factory)
+ throws BinaryObjectException {
+ int hPos = positionForHandle(in);
+
+ int size = in.readInt();
+
+ assert size >= 0;
+
+ byte colType = in.readByte();
+
+ Collection<Object> col;
+
+ if (factory != null)
+ col = factory.create(size);
+ else {
+ switch (colType) {
+ case GridBinaryMarshaller.ARR_LIST:
+ col = new ArrayList<>(size);
+
+ break;
+
+ case GridBinaryMarshaller.LINKED_LIST:
+ col = new LinkedList<>();
+
+ break;
+
+ case GridBinaryMarshaller.HASH_SET:
+ col = U.newHashSet(size);
+
+ break;
+
+ case GridBinaryMarshaller.LINKED_HASH_SET:
+ col = U.newLinkedHashSet(size);
+
+ break;
+
+ case GridBinaryMarshaller.USER_SET:
+ col = U.newHashSet(size);
+
+ break;
+
+ case GridBinaryMarshaller.USER_COL:
+ col = new ArrayList<>(size);
+
+ break;
+
+ default:
+ throw new BinaryObjectException("Invalid collection type: " + colType);
+ }
+ }
+
+ handles.setHandle(col, hPos);
+
+ for (int i = 0; i < size; i++)
+ col.add(deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize));
+
+ return col;
+ }
+
+ /**
+ * @param deserialize Deep flag.
+ * @param factory Map factory.
+ * @return Value.
+ * @throws BinaryObjectException In case of error.
+ */
+ @SuppressWarnings("unchecked")
+ public static Map<?, ?> doReadMap(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles, boolean deserialize, BinaryMapFactory factory)
+ throws BinaryObjectException {
+ int hPos = positionForHandle(in);
+
+ int size = in.readInt();
+
+ assert size >= 0;
+
+ byte mapType = in.readByte();
+
+ Map<Object, Object> map;
+
+ if (factory != null)
+ map = factory.create(size);
+ else {
+ switch (mapType) {
+ case GridBinaryMarshaller.HASH_MAP:
+ map = U.newHashMap(size);
+
+ break;
+
+ case GridBinaryMarshaller.LINKED_HASH_MAP:
+ map = U.newLinkedHashMap(size);
+
+ break;
+
+ case GridBinaryMarshaller.USER_COL:
+ map = U.newHashMap(size);
+
+ break;
+
+ default:
+ throw new BinaryObjectException("Invalid map type: " + mapType);
+ }
+ }
+
+ handles.setHandle(map, hPos);
+
+ for (int i = 0; i < size; i++) {
+ Object key = deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize);
+ Object val = deserializeOrUnmarshal(in, ctx, ldr, handles, deserialize);
+
+ map.put(key, val);
+ }
+
+ return map;
+ }
+
+ /**
+ * Deserialize or unmarshal the object.
+ *
+ * @param deserialize Deserialize.
+ * @return Result.
+ */
+ private static Object deserializeOrUnmarshal(BinaryInputStream in, BinaryContext ctx, ClassLoader ldr,
+ BinaryReaderHandlesHolder handles, boolean deserialize) {
+ return deserialize ? doReadObject(in, ctx, ldr, handles) : unmarshal(in, ctx, ldr, handles);
+ }
+
+ /**
+ * Get position to be used for handle. We assume here that the hdr byte was read, hence subtract -1.
+ *
+ * @return Position for handle.
+ */
+ public static int positionForHandle(BinaryInputStream in) {
+ return in.position() - 1;
+ }
+
+ /**
+ * Enum type.
+ */
+ private static class EnumType {
+ /** Type ID. */
+ private final int typeId;
+
+ /** Class name. */
+ private final String clsName;
+
+ /**
+ * Constructor.
+ *
+ * @param typeId Type ID.
+ * @param clsName Class name.
+ */
+ public EnumType(int typeId, @Nullable String clsName) {
+ assert typeId != GridBinaryMarshaller.UNREGISTERED_TYPE_ID && clsName == null ||
+ typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID && clsName != null;
+
+ this.typeId = typeId;
+ this.clsName = clsName;
+ }
+ }
+}