Merge branch 'ignite-2064-4' into ignite-1.5 2
(cherry picked from commit 599d2edc6c4f3ce60654a53bb431d36db982bc87) Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/0ac39c97 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/0ac39c97 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/0ac39c97 Branch: refs/heads/ignite-1537 Commit: 0ac39c976116b655304699d2477f207802cb5f8e Parents: 1dbf20e 599d2ed Author: Anton Vinogradov <[email protected]> Authored: Fri Dec 11 12:54:50 2015 +0300 Committer: Anton Vinogradov <[email protected]> Committed: Fri Dec 11 12:54:50 2015 +0300 ---------------------------------------------------------------------- .../org/apache/ignite/internal/binary/BinaryObjectImpl.java | 1 - .../ignite/internal/binary/BinaryObjectOffheapImpl.java | 6 ++++-- .../internal/binary/builder/BinaryObjectBuilderImpl.java | 3 +-- .../ignite/internal/binary/builder/PortableBuilderEnum.java | 3 +-- .../ignite/internal/binary/builder/PortableBuilderReader.java | 5 +++-- .../internal/binary/builder/PortableEnumArrayLazyValue.java | 3 +-- .../internal/binary/builder/PortableObjectArrayLazyValue.java | 3 +-- .../internal/processors/platform/PlatformContextImpl.java | 7 +++++-- .../context/IgniteCacheAbstractExecutionContextTest.java | 3 --- 9 files changed, 16 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/0ac39c97/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java index a23330e,0000000..18adbc1 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java @@@ -1,570 -1,0 +1,569 @@@ +/* + * 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.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.binary.BinaryObject; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.binary.BinaryType; +import org.apache.ignite.internal.GridDirectTransient; +import org.apache.ignite.internal.IgniteCodeGeneratingFail; +import org.apache.ignite.internal.binary.streams.PortableHeapInputStream; +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.binary.CacheObjectBinaryProcessorImpl; +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; +import org.jetbrains.annotations.Nullable; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Portable object implementation. + */ +@IgniteCodeGeneratingFail // Fields arr and start should not be generated by MessageCodeGenerator. +public final class BinaryObjectImpl extends BinaryObjectExImpl implements Externalizable, KeyCacheObject { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + @GridDirectTransient + private PortableContext ctx; + + /** */ + private byte[] arr; + + /** */ + private int start; + + /** */ + @GridDirectTransient + private Object obj; + + /** */ + @GridDirectTransient + private boolean detachAllowed; + + /** + * For {@link Externalizable}. + */ + public BinaryObjectImpl() { + // No-op. + } + + /** + * @param ctx Context. + * @param arr Array. + * @param start Start. + */ + public BinaryObjectImpl(PortableContext ctx, byte[] arr, int start) { + assert ctx != null; + assert arr != null; + + this.ctx = ctx; + this.arr = arr; + this.start = start; + } + + /** {@inheritDoc} */ + @Override public byte cacheObjectType() { + return TYPE_BINARY; + } + + /** {@inheritDoc} */ + @Override public boolean isPlatformType() { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean internal() { + return false; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <T> T value(CacheObjectContext ctx, boolean cpy) { + Object obj0 = obj; + + if (obj0 == null || (cpy && needCopy(ctx))) + obj0 = deserializeValue(ctx); + + return (T)obj0; + } + + /** {@inheritDoc} */ + @Override public byte[] valueBytes(CacheObjectContext ctx) throws IgniteCheckedException { + if (detached()) + return array(); + + int len = length(); + + byte[] arr0 = new byte[len]; + + U.arrayCopy(arr, start, arr0, 0, len); + + return arr0; + } + + /** {@inheritDoc} */ + @Override public CacheObject prepareForCache(CacheObjectContext ctx) { + if (detached()) + return this; + + return (BinaryObjectImpl)detach(); + } + + /** {@inheritDoc} */ + @Override public void finishUnmarshal(CacheObjectContext ctx, ClassLoader ldr) throws IgniteCheckedException { + this.ctx = ((CacheObjectBinaryProcessorImpl)ctx.processor()).portableContext(); + } + + /** {@inheritDoc} */ + @Override public void prepareMarshal(CacheObjectContext ctx) throws IgniteCheckedException { + // No-op. + } + + /** {@inheritDoc} */ + @Override public int length() { + return PortablePrimitives.readInt(arr, start + GridPortableMarshaller.TOTAL_LEN_POS); + } + + /** + * @return Detached portable object. + */ + public BinaryObject detach() { + if (!detachAllowed || detached()) + return this; + + int len = length(); + + byte[] arr0 = new byte[len]; + + U.arrayCopy(arr, start, arr0, 0, len); + + return new BinaryObjectImpl(ctx, arr0, 0); + } + + /** + * @return Detached or not. + */ + public boolean detached() { + return start == 0 && length() == arr.length; + } + + /** + * @param detachAllowed Detach allowed flag. + */ + public void detachAllowed(boolean detachAllowed) { + this.detachAllowed = detachAllowed; + } + + /** + * @return Context. + */ + public PortableContext context() { + return ctx; + } + + /** + * @param ctx Context. + */ + public void context(PortableContext ctx) { + this.ctx = ctx; + } + + /** {@inheritDoc} */ + @Override public byte[] array() { + return arr; + } + + /** {@inheritDoc} */ + @Override public int start() { + return start; + } + + /** {@inheritDoc} */ + @Override public long offheapAddress() { + return 0; + } + + /** {@inheritDoc} */ + @Override protected boolean hasArray() { + return true; + } + + /** {@inheritDoc} */ + @Override public int typeId() { + return PortablePrimitives.readInt(arr, start + GridPortableMarshaller.TYPE_ID_POS); + } + + /** {@inheritDoc} */ + @Nullable @Override public BinaryType type() throws BinaryObjectException { + if (ctx == null) + throw new BinaryObjectException("PortableContext is not set for the object."); + + return ctx.metadata(typeId()); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <F> F field(String fieldName) throws BinaryObjectException { + return (F) reader(null).unmarshalField(fieldName); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <F> F field(int fieldId) throws BinaryObjectException { + return (F) reader(null).unmarshalField(fieldId); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override protected <F> F fieldByOrder(int order) { + Object val; + + // Calculate field position. + int schemaOffset = PortablePrimitives.readInt(arr, start + GridPortableMarshaller.SCHEMA_OR_RAW_OFF_POS); + + short flags = PortablePrimitives.readShort(arr, start + GridPortableMarshaller.FLAGS_POS); + + int fieldIdLen = PortableUtils.isCompactFooter(flags) ? 0 : PortableUtils.FIELD_ID_LEN; + int fieldOffsetLen = PortableUtils.fieldOffsetLength(flags); + + int fieldOffsetPos = start + schemaOffset + order * (fieldIdLen + fieldOffsetLen) + fieldIdLen; + + int fieldPos; + + if (fieldOffsetLen == PortableUtils.OFFSET_1) + fieldPos = start + ((int)PortablePrimitives.readByte(arr, fieldOffsetPos) & 0xFF); + else if (fieldOffsetLen == PortableUtils.OFFSET_2) + fieldPos = start + ((int)PortablePrimitives.readShort(arr, fieldOffsetPos) & 0xFFFF); + else + fieldPos = start + PortablePrimitives.readInt(arr, fieldOffsetPos); + + // Read header and try performing fast lookup for well-known types (the most common types go first). + byte hdr = PortablePrimitives.readByte(arr, fieldPos); + + switch (hdr) { + case GridPortableMarshaller.INT: + val = PortablePrimitives.readInt(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.LONG: + val = PortablePrimitives.readLong(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.BOOLEAN: + val = PortablePrimitives.readBoolean(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.SHORT: + val = PortablePrimitives.readShort(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.BYTE: + val = PortablePrimitives.readByte(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.CHAR: + val = PortablePrimitives.readChar(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.FLOAT: + val = PortablePrimitives.readFloat(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.DOUBLE: + val = PortablePrimitives.readDouble(arr, fieldPos + 1); + + break; + + case GridPortableMarshaller.STRING: { + int dataLen = PortablePrimitives.readInt(arr, fieldPos + 1); + + val = new String(arr, fieldPos + 5, dataLen, UTF_8); + + break; + } + + case GridPortableMarshaller.DATE: { + long time = PortablePrimitives.readLong(arr, fieldPos + 1); + + val = new Date(time); + + break; + } + + case GridPortableMarshaller.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 GridPortableMarshaller.UUID: { + long most = PortablePrimitives.readLong(arr, fieldPos + 1); + long least = PortablePrimitives.readLong(arr, fieldPos + 1 + 8); + + val = new UUID(most, least); + + break; + } + + case GridPortableMarshaller.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 GridPortableMarshaller.NULL: + val = null; + + break; + + default: + val = PortableUtils.unmarshal(PortableHeapInputStream.create(arr, fieldPos), ctx, null); + + break; + } + + return (F)val; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override protected <F> F field(BinaryReaderHandles rCtx, String fieldName) { + return (F)reader(rCtx).unmarshalField(fieldName); + } + + /** {@inheritDoc} */ + @Override public boolean hasField(String fieldName) { + return reader(null).findFieldByName(fieldName); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <T> T deserialize() throws BinaryObjectException { + Object obj0 = obj; + + if (obj0 == null) + obj0 = deserializeValue(null); + + return (T)obj0; + } + + /** {@inheritDoc} */ + @Override public BinaryObject clone() throws CloneNotSupportedException { + return super.clone(); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return PortablePrimitives.readInt(arr, start + GridPortableMarshaller.HASH_CODE_POS); + } + + /** {@inheritDoc} */ + @Override protected int schemaId() { + return PortablePrimitives.readInt(arr, start + GridPortableMarshaller.SCHEMA_ID_POS); + } + + /** {@inheritDoc} */ + @Override protected PortableSchema createSchema() { + return reader(null).getOrCreateSchema(); + } + + /** {@inheritDoc} */ + @Override public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(ctx); + + if (detachAllowed) { + int len = length(); + + out.writeInt(len); + out.write(arr, start, len); + out.writeInt(0); + } + else { + out.writeInt(arr.length); + out.write(arr); + out.writeInt(start); + } + } + + /** {@inheritDoc} */ + @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + ctx = (PortableContext)in.readObject(); + + arr = new byte[in.readInt()]; + + in.readFully(arr); + + start = in.readInt(); + } + + /** {@inheritDoc} */ + @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) { + writer.setBuffer(buf); + + if (!writer.isHeaderWritten()) { + if (!writer.writeHeader(directType(), fieldsCount())) + return false; + + writer.onHeaderWritten(); + } + + switch (writer.state()) { + case 0: + if (!writer.writeByteArray("arr", + arr, + detachAllowed ? start : 0, + detachAllowed ? length() : arr.length)) + return false; + + writer.incrementState(); + + case 1: + if (!writer.writeInt("start", detachAllowed ? 0 : start)) + return false; + + writer.incrementState(); + + } + + return true; + } + + /** {@inheritDoc} */ + @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) { + reader.setBuffer(buf); + + if (!reader.beforeMessageRead()) + return false; + + switch (reader.state()) { + case 0: + arr = reader.readByteArray("arr"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 1: + start = reader.readInt("start"); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + } + + return true; + } + + /** {@inheritDoc} */ + @Override public byte directType() { + return 113; + } + + /** {@inheritDoc} */ + @Override public byte fieldsCount() { + return 3; + } + + /** + * Runs value deserialization regardless of whether obj already has the deserialized value. + * Will set obj if descriptor is configured to keep deserialized values. + * @param coCtx CacheObjectContext. + * @return Object. + */ + private Object deserializeValue(@Nullable CacheObjectContext coCtx) { - // TODO: IGNITE-1272 - Deserialize with proper class loader. + BinaryReaderExImpl reader = reader(null); + + Object obj0 = reader.deserialize(); + + PortableClassDescriptor desc = reader.descriptor(); + + assert desc != null; + + if (coCtx != null && coCtx.storeValue()) + obj = obj0; + + return obj0; + } + + /** + * @param ctx Context. + * @return {@code True} need to copy value returned to user. + */ + private boolean needCopy(CacheObjectContext ctx) { + return ctx.copyOnGet() && obj != null && !ctx.processor().immutable(obj); + } + + /** + * Create new reader for this object. + * + * @param rCtx Reader context. + * @return Reader. + */ + private BinaryReaderExImpl reader(@Nullable BinaryReaderHandles rCtx) { + return new BinaryReaderExImpl(ctx, + PortableHeapInputStream.create(arr, start), + ctx.configuration().getClassLoader(), + rCtx); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/0ac39c97/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectOffheapImpl.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectOffheapImpl.java index 27b46c1,0000000..2944099 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectOffheapImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectOffheapImpl.java @@@ -1,430 -1,0 +1,432 @@@ +/* + * 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.BinaryObject; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.binary.BinaryType; +import org.apache.ignite.internal.binary.streams.PortableOffheapInputStream; +import org.apache.ignite.internal.binary.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.U; +import org.apache.ignite.plugin.extensions.communication.MessageReader; +import org.apache.ignite.plugin.extensions.communication.MessageWriter; +import org.jetbrains.annotations.Nullable; +import sun.misc.Unsafe; + +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.binary.GridPortableMarshaller.BOOLEAN; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.BYTE; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.CHAR; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.DATE; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.DECIMAL; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.DOUBLE; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.FLOAT; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.INT; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.LONG; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.NULL; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.SHORT; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.STRING; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.TIMESTAMP; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.UUID; + +/** + * Portable object implementation over offheap memory + */ +public class BinaryObjectOffheapImpl extends BinaryObjectExImpl implements Externalizable, CacheObject { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private static final Unsafe UNSAFE = GridUnsafe.unsafe(); + + /** */ + private final PortableContext ctx; + + /** */ + private final long ptr; + + /** */ + private final int start; + + /** */ + private final int size; + + /** + * For {@link Externalizable} (not supported). + */ + public BinaryObjectOffheapImpl() { + throw new UnsupportedOperationException(); + } + + /** + * @param ctx Context. + * @param ptr Memory address. + * @param start Object start. + * @param size Memory size. + */ + public BinaryObjectOffheapImpl(PortableContext ctx, long ptr, int start, int size) { + this.ctx = ctx; + this.ptr = ptr; + this.start = start; + this.size = size; + } + + /** + * @return Heap-based copy. + */ + public BinaryObject heapCopy() { + return new BinaryObjectImpl(ctx, U.copyMemory(ptr, size), start); + } + + /** {@inheritDoc} */ + @Override public int typeId() { + return UNSAFE.getInt(ptr + start + GridPortableMarshaller.TYPE_ID_POS); + } + + /** {@inheritDoc} */ + @Override public int length() { + return UNSAFE.getInt(ptr + start + GridPortableMarshaller.TOTAL_LEN_POS); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return UNSAFE.getInt(ptr + start + GridPortableMarshaller.HASH_CODE_POS); + } + + /** {@inheritDoc} */ + @Override protected int schemaId() { + return UNSAFE.getInt(ptr + start + GridPortableMarshaller.SCHEMA_ID_POS); + } + + /** {@inheritDoc} */ + @Override protected PortableSchema createSchema() { + return reader(null).getOrCreateSchema(); + } + + /** {@inheritDoc} */ + @Override public int start() { + return start; + } + + /** {@inheritDoc} */ + @Override public byte[] array() { + return null; + } + + /** {@inheritDoc} */ + @Override public long offheapAddress() { + return ptr; + } + + /** {@inheritDoc} */ + @Override protected boolean hasArray() { + return false; + } + + /** {@inheritDoc} */ + @Nullable @Override public BinaryType type() throws BinaryObjectException { + if (ctx == null) + throw new BinaryObjectException("PortableContext is not set for the object."); + + return ctx.metadata(typeId()); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <F> F field(String fieldName) throws BinaryObjectException { + return (F) reader(null).unmarshalField(fieldName); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <F> F field(int fieldId) throws BinaryObjectException { + return (F) reader(null).unmarshalField(fieldId); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override protected <F> F fieldByOrder(int order) { + Object val; + + // Calculate field position. + int schemaOffset = PortablePrimitives.readInt(ptr, start + GridPortableMarshaller.SCHEMA_OR_RAW_OFF_POS); + + short flags = PortablePrimitives.readShort(ptr, start + GridPortableMarshaller.FLAGS_POS); + + int fieldIdLen = PortableUtils.isCompactFooter(flags) ? 0 : PortableUtils.FIELD_ID_LEN; + int fieldOffsetLen = PortableUtils.fieldOffsetLength(flags); + + int fieldOffsetPos = start + schemaOffset + order * (fieldIdLen + fieldOffsetLen) + fieldIdLen; + + int fieldPos; + + if (fieldOffsetLen == PortableUtils.OFFSET_1) + fieldPos = start + ((int)PortablePrimitives.readByte(ptr, fieldOffsetPos) & 0xFF); + else if (fieldOffsetLen == PortableUtils.OFFSET_2) + fieldPos = start + ((int)PortablePrimitives.readShort(ptr, fieldOffsetPos) & 0xFFFF); + else + fieldPos = start + PortablePrimitives.readInt(ptr, fieldOffsetPos); + + // Read header and try performing fast lookup for well-known types (the most common types go first). + byte hdr = PortablePrimitives.readByte(ptr, fieldPos); + + switch (hdr) { + case GridPortableMarshaller.INT: + val = PortablePrimitives.readInt(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.LONG: + val = PortablePrimitives.readLong(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.BOOLEAN: + val = PortablePrimitives.readBoolean(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.SHORT: + val = PortablePrimitives.readShort(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.BYTE: + val = PortablePrimitives.readByte(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.CHAR: + val = PortablePrimitives.readChar(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.FLOAT: + val = PortablePrimitives.readFloat(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.DOUBLE: + val = PortablePrimitives.readDouble(ptr, fieldPos + 1); + + break; + + case GridPortableMarshaller.STRING: { + int dataLen = PortablePrimitives.readInt(ptr, fieldPos + 1); + byte[] data = PortablePrimitives.readByteArray(ptr, fieldPos + 5, dataLen); + + val = new String(data, UTF_8); + + break; + } + + case GridPortableMarshaller.DATE: { + long time = PortablePrimitives.readLong(ptr, fieldPos + 1); + + val = new Date(time); + + break; + } + + case GridPortableMarshaller.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 GridPortableMarshaller.UUID: { + long most = PortablePrimitives.readLong(ptr, fieldPos + 1); + long least = PortablePrimitives.readLong(ptr, fieldPos + 1 + 8); + + val = new UUID(most, least); + + break; + } + + case GridPortableMarshaller.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 GridPortableMarshaller.NULL: + val = null; + + break; + + default: + PortableOffheapInputStream stream = new PortableOffheapInputStream(ptr, size, false); + + stream.position(fieldPos); + + val = PortableUtils.unmarshal(stream, ctx, null); + + break; + } + + return (F)val; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override protected <F> F field(BinaryReaderHandles rCtx, String fieldName) { + return (F)reader(rCtx).unmarshalField(fieldName); + } + + /** {@inheritDoc} */ + @Override public boolean hasField(String fieldName) { + return reader(null).findFieldByName(fieldName); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <T> T deserialize() throws BinaryObjectException { + return (T)deserializeValue(); + } + + /** {@inheritDoc} */ + @SuppressWarnings("CloneDoesntCallSuperClone") + @Override public BinaryObject clone() throws CloneNotSupportedException { + return heapCopy(); + } + + /** {@inheritDoc} */ + @Override public byte cacheObjectType() { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public boolean isPlatformType() { + return false; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Nullable @Override public <T> T value(CacheObjectContext ctx, boolean cpy) { + return (T)deserializeValue(); + } + + /** {@inheritDoc} */ + @Override public byte[] valueBytes(CacheObjectContext ctx) throws IgniteCheckedException { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public CacheObject prepareForCache(CacheObjectContext ctx) { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public void finishUnmarshal(CacheObjectContext ctx, ClassLoader ldr) throws IgniteCheckedException { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public void prepareMarshal(CacheObjectContext ctx) throws IgniteCheckedException { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public byte directType() { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public byte fieldsCount() { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public void writeExternal(ObjectOutput out) throws IOException { + throw new UnsupportedOperationException(); // To make sure it is not marshalled. + } + + /** {@inheritDoc} */ + @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + throw new UnsupportedOperationException(); // To make sure it is not marshalled. + } + + /** + * @return Deserialized value. + */ + private Object deserializeValue() { - // TODO: IGNITE-1272 - Deserialize with proper class loader. + return reader(null).deserialize(); + } + + /** + * Create new reader for this object. + * + * @param rCtx Reader context. + * @return Reader. + */ + private BinaryReaderExImpl reader(@Nullable BinaryReaderHandles rCtx) { + PortableOffheapInputStream stream = new PortableOffheapInputStream(ptr, size, false); + + stream.position(start); + - return new BinaryReaderExImpl(ctx, stream, null, rCtx); ++ return new BinaryReaderExImpl(ctx, ++ stream, ++ ctx.configuration().getClassLoader(), ++ rCtx); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/0ac39c97/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/BinaryObjectBuilderImpl.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/internal/binary/builder/BinaryObjectBuilderImpl.java index f5e4e06,0000000..f1d4185 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/BinaryObjectBuilderImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/BinaryObjectBuilderImpl.java @@@ -1,587 -1,0 +1,586 @@@ +/* + * 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.builder; + +import org.apache.ignite.binary.BinaryInvalidTypeException; +import org.apache.ignite.binary.BinaryObject; +import org.apache.ignite.binary.BinaryObjectBuilder; +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.binary.BinaryType; +import org.apache.ignite.internal.binary.BinaryMetadata; +import org.apache.ignite.internal.binary.BinaryObjectImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.binary.GridPortableMarshaller; +import org.apache.ignite.internal.binary.PortableContext; +import org.apache.ignite.internal.binary.PortableSchema; +import org.apache.ignite.internal.binary.PortableSchemaRegistry; +import org.apache.ignite.internal.binary.BinaryMetadata; +import org.apache.ignite.internal.binary.BinaryObjectImpl; +import org.apache.ignite.internal.binary.BinaryObjectOffheapImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.binary.GridPortableMarshaller; +import org.apache.ignite.internal.binary.PortableContext; +import org.apache.ignite.internal.binary.PortableSchema; +import org.apache.ignite.internal.binary.PortableSchemaRegistry; +import org.apache.ignite.internal.binary.PortableUtils; +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 java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import static org.apache.ignite.internal.binary.GridPortableMarshaller.DFLT_HDR_LEN; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.FLAGS_POS; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.HASH_CODE_POS; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.PROTO_VER_POS; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.TYPE_ID_POS; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.UNREGISTERED_TYPE_ID; + +/** + * + */ +public class BinaryObjectBuilderImpl implements BinaryObjectBuilder { + /** */ + private static final Object REMOVED_FIELD_MARKER = new Object(); + + /** */ + private final PortableContext ctx; + + /** */ + private final int typeId; + + /** May be null. */ + private String typeName; + + /** May be null. */ + private String clsNameToWrite; + + /** */ + private boolean registeredType = true; + + /** */ + private Map<String, Object> assignedVals; + + /** */ + private Map<Integer, Object> readCache; + + /** Position of object in source array, or -1 if object is not created from PortableObject. */ + private final int start; + + /** Flags. */ + private final short flags; + + /** Total header length */ + private final int hdrLen; + + /** Context of PortableObject reading process. Or {@code null} if object is not created from PortableObject. */ + private final PortableBuilderReader reader; + + /** */ + private int hashCode; + + /** + * @param clsName Class name. + * @param ctx Portable context. + */ + public BinaryObjectBuilderImpl(PortableContext ctx, String clsName) { + this(ctx, ctx.typeId(clsName), PortableContext.typeName(clsName)); + } + + /** + * @param typeName Type name. + * @param ctx Context. + * @param typeId Type id. + */ + public BinaryObjectBuilderImpl(PortableContext ctx, int typeId, String typeName) { + this.typeId = typeId; + this.typeName = typeName; + this.ctx = ctx; + + start = -1; + flags = -1; + reader = null; + hdrLen = GridPortableMarshaller.DFLT_HDR_LEN; + + readCache = Collections.emptyMap(); + } + + /** + * @param obj Object to wrap. + */ + public BinaryObjectBuilderImpl(BinaryObjectImpl obj) { + this(new PortableBuilderReader(obj), obj.start()); + + reader.registerObject(this); + } + + /** + * @param reader ctx + * @param start Start. + */ + BinaryObjectBuilderImpl(PortableBuilderReader reader, int start) { + this.reader = reader; + this.start = start; + this.flags = reader.readShortPositioned(start + GridPortableMarshaller.FLAGS_POS); + + byte ver = reader.readBytePositioned(start + GridPortableMarshaller.PROTO_VER_POS); + + PortableUtils.checkProtocolVersion(ver); + + int typeId = reader.readIntPositioned(start + GridPortableMarshaller.TYPE_ID_POS); + ctx = reader.portableContext(); + hashCode = reader.readIntPositioned(start + GridPortableMarshaller.HASH_CODE_POS); + + if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) { + int mark = reader.position(); + + reader.position(start + GridPortableMarshaller.DFLT_HDR_LEN); + + clsNameToWrite = reader.readString(); + + Class cls; + + try { - // TODO: IGNITE-1272 - Is class loader needed here? - cls = U.forName(clsNameToWrite, null); ++ cls = U.forName(clsNameToWrite, ctx.configuration().getClassLoader()); + } + catch (ClassNotFoundException e) { + throw new BinaryInvalidTypeException("Failed to load the class: " + clsNameToWrite, e); + } + + this.typeId = ctx.descriptorForClass(cls, false).typeId(); + + registeredType = false; + + hdrLen = reader.position() - mark; + + reader.position(mark); + } + else { + this.typeId = typeId; + hdrLen = GridPortableMarshaller.DFLT_HDR_LEN; + } + } + + /** {@inheritDoc} */ + @Override public BinaryObject build() { + try (BinaryWriterExImpl writer = new BinaryWriterExImpl(ctx)) { + writer.typeId(typeId); + + PortableBuilderSerializer serializationCtx = new PortableBuilderSerializer(); + + serializationCtx.registerObjectWriting(this, 0); + + serializeTo(writer, serializationCtx); + + byte[] arr = writer.array(); + + return new BinaryObjectImpl(ctx, arr, 0); + } + } + + /** + * @param writer Writer. + * @param serializer Serializer. + */ + void serializeTo(BinaryWriterExImpl writer, PortableBuilderSerializer serializer) { + try { + writer.preWrite(registeredType ? null : clsNameToWrite); + + Set<Integer> remainsFlds = null; + + if (reader != null) { + PortableSchema schema = reader.schema(); + + Map<Integer, Object> assignedFldsById; + + if (assignedVals != null) { + assignedFldsById = U.newHashMap(assignedVals.size()); + + for (Map.Entry<String, Object> entry : assignedVals.entrySet()) { + int fieldId = ctx.fieldId(typeId, entry.getKey()); + + assignedFldsById.put(fieldId, entry.getValue()); + } + + remainsFlds = assignedFldsById.keySet(); + } + else + assignedFldsById = Collections.emptyMap(); + + // Get footer details. + int fieldIdLen = PortableUtils.fieldIdLength(flags); + int fieldOffsetLen = PortableUtils.fieldOffsetLength(flags); + + IgniteBiTuple<Integer, Integer> footer = PortableUtils.footerAbsolute(reader, start); + + int footerPos = footer.get1(); + int footerEnd = footer.get2(); + + // Get raw position. + int rawPos = PortableUtils.rawOffsetAbsolute(reader, start); + + // Position reader on data. + reader.position(start + hdrLen); + + int idx = 0; + + while (reader.position() < rawPos) { + int fieldId = schema.fieldId(idx++); + int fieldLen = + fieldPositionAndLength(footerPos, footerEnd, rawPos, fieldIdLen, fieldOffsetLen).get2(); + + int postPos = reader.position() + fieldLen; // Position where reader will be placed afterwards. + + footerPos += fieldIdLen + fieldOffsetLen; + + if (assignedFldsById.containsKey(fieldId)) { + Object assignedVal = assignedFldsById.remove(fieldId); + + if (assignedVal != REMOVED_FIELD_MARKER) { + writer.writeFieldId(fieldId); + + serializer.writeValue(writer, assignedVal); + } + } + else { + int type = fieldLen != 0 ? reader.readByte(0) : 0; + + if (fieldLen != 0 && !PortableUtils.isPlainArrayType(type) && PortableUtils.isPlainType(type)) { + writer.writeFieldId(fieldId); + + writer.write(reader.array(), reader.position(), fieldLen); + } + else { + writer.writeFieldId(fieldId); + + Object val; + + if (fieldLen == 0) + val = null; + else if (readCache == null) { + val = reader.parseValue(); + + assert reader.position() == postPos; + } + else + val = readCache.get(fieldId); + + serializer.writeValue(writer, val); + } + } + + reader.position(postPos); + } + } + + BinaryType meta = ctx.metadata(typeId); + + Map<String, Integer> fieldsMeta = null; + + if (assignedVals != null && (remainsFlds == null || !remainsFlds.isEmpty())) { + for (Map.Entry<String, Object> entry : assignedVals.entrySet()) { + Object val = entry.getValue(); + + if (val == REMOVED_FIELD_MARKER) + continue; + + String name = entry.getKey(); + + int fieldId = ctx.fieldId(typeId, name); + + if (remainsFlds != null && !remainsFlds.contains(fieldId)) + continue; + + writer.writeFieldId(fieldId); + + serializer.writeValue(writer, val); + + String oldFldTypeName = meta == null ? null : meta.fieldTypeName(name); + + boolean nullObjField = false; + + int newFldTypeId; + + if (val instanceof PortableValueWithType) { + newFldTypeId = ((PortableValueWithType)val).typeId(); + + if (newFldTypeId == GridPortableMarshaller.OBJ && ((PortableValueWithType)val).value() == null) + nullObjField = true; + } + else + newFldTypeId = PortableUtils.typeByClass(val.getClass()); + + String newFldTypeName = PortableUtils.fieldTypeName(newFldTypeId); + + if (oldFldTypeName == null) { + // It's a new field, we have to add it to metadata. + if (fieldsMeta == null) + fieldsMeta = new HashMap<>(); + + fieldsMeta.put(name, PortableUtils.fieldTypeId(newFldTypeName)); + } + else if (!nullObjField) { + String objTypeName = PortableUtils.fieldTypeName(GridPortableMarshaller.OBJ); + + if (!objTypeName.equals(oldFldTypeName) && !oldFldTypeName.equals(newFldTypeName)) { + throw new BinaryObjectException( + "Wrong value has been set [" + + "typeName=" + (typeName == null ? meta.typeName() : typeName) + + ", fieldName=" + name + + ", fieldType=" + oldFldTypeName + + ", assignedValueType=" + newFldTypeName + ']' + ); + } + } + } + } + + if (reader != null) { + // Write raw data if any. + int rawOff = PortableUtils.rawOffsetAbsolute(reader, start); + int footerStart = PortableUtils.footerStartAbsolute(reader, start); + + if (rawOff < footerStart) { + writer.rawWriter(); + + writer.write(reader.array(), rawOff, footerStart - rawOff); + } + + // Shift reader to the end of the object. + reader.position(start + PortableUtils.length(reader, start)); + } + + writer.postWrite(true, registeredType, hashCode); + + // Update metadata if needed. + int schemaId = writer.schemaId(); + + PortableSchemaRegistry schemaReg = ctx.schemaRegistry(typeId); + + if (schemaReg.schema(schemaId) == null) { + String typeName = this.typeName; + + if (typeName == null) { + assert meta != null; + + typeName = meta.typeName(); + } + + PortableSchema curSchema = writer.currentSchema(); + + ctx.updateMetadata(typeId, new BinaryMetadata(typeId, typeName, fieldsMeta, + ctx.affinityKeyFieldName(typeId), Collections.singleton(curSchema), false)); + + schemaReg.addSchema(curSchema.schemaId(), curSchema); + } + } + finally { + writer.popSchema(); + } + } + + /** {@inheritDoc} */ + @Override public BinaryObjectBuilderImpl hashCode(int hashCode) { + this.hashCode = hashCode; + + return this; + } + + /** + * Get field position and length. + * + * @param footerPos Field position inside the footer (absolute). + * @param footerEnd Footer end (absolute). + * @param rawPos Raw data position (absolute). + * @param fieldIdLen Field ID length. + * @param fieldOffsetLen Field offset length. + * @return Tuple with field position and length. + */ + private IgniteBiTuple<Integer, Integer> fieldPositionAndLength(int footerPos, int footerEnd, int rawPos, + int fieldIdLen, int fieldOffsetLen) { + // Get field offset first. + int fieldOffset = PortableUtils.fieldOffsetRelative(reader, footerPos + fieldIdLen, fieldOffsetLen); + int fieldPos = start + fieldOffset; + + // Get field length. + int fieldLen; + + if (footerPos + fieldIdLen + fieldOffsetLen == footerEnd) + // This is the last field, compare to raw offset. + fieldLen = rawPos - fieldPos; + else { + // Field is somewhere in the middle, get difference with the next offset. + int nextFieldOffset = PortableUtils.fieldOffsetRelative(reader, + footerPos + fieldIdLen + fieldOffsetLen + fieldIdLen, fieldOffsetLen); + + fieldLen = nextFieldOffset - fieldOffset; + } + + return F.t(fieldPos, fieldLen); + } + + /** + * Initialize read cache if needed. + */ + private void ensureReadCacheInit() { + assert reader != null; + + if (readCache == null) { + int fieldIdLen = PortableUtils.fieldIdLength(flags); + int fieldOffsetLen = PortableUtils.fieldOffsetLength(flags); + + PortableSchema schema = reader.schema(); + + Map<Integer, Object> readCache = new HashMap<>(); + + IgniteBiTuple<Integer, Integer> footer = PortableUtils.footerAbsolute(reader, start); + + int footerPos = footer.get1(); + int footerEnd = footer.get2(); + + int rawPos = PortableUtils.rawOffsetAbsolute(reader, start); + + int idx = 0; + + while (footerPos + fieldIdLen < footerEnd) { + int fieldId = schema.fieldId(idx++); + + IgniteBiTuple<Integer, Integer> posAndLen = + fieldPositionAndLength(footerPos, footerEnd, rawPos, fieldIdLen, fieldOffsetLen); + + Object val = reader.getValueQuickly(posAndLen.get1(), posAndLen.get2()); + + readCache.put(fieldId, val); + + // Shift current footer position. + footerPos += fieldIdLen + fieldOffsetLen; + } + + this.readCache = readCache; + } + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public <T> T getField(String name) { + Object val; + + if (assignedVals != null && assignedVals.containsKey(name)) { + val = assignedVals.get(name); + + if (val == REMOVED_FIELD_MARKER) + return null; + } + else { + ensureReadCacheInit(); + + int fldId = ctx.fieldId(typeId, name); + + val = readCache.get(fldId); + } + + return (T)PortableUtils.unwrapLazy(val); + } + + /** {@inheritDoc} */ + @Override public BinaryObjectBuilder setField(String name, Object val0) { + Object val = val0 == null ? new PortableValueWithType(PortableUtils.typeByClass(Object.class), null) : val0; + + if (assignedVals == null) + assignedVals = new LinkedHashMap<>(); + + Object oldVal = assignedVals.put(name, val); + + if (oldVal instanceof PortableValueWithType && val0 != null) { + ((PortableValueWithType)oldVal).value(val); + + assignedVals.put(name, oldVal); + } + + return this; + } + + /** {@inheritDoc} */ + @Override public <T> BinaryObjectBuilder setField(String name, @Nullable T val, Class<? super T> type) { + if (assignedVals == null) + assignedVals = new LinkedHashMap<>(); + + assignedVals.put(name, new PortableValueWithType(PortableUtils.typeByClass(type), val)); + + return this; + } + + /** {@inheritDoc} */ + @Override public BinaryObjectBuilder setField(String name, @Nullable BinaryObjectBuilder builder) { + if (builder == null) + return setField(name, null, Object.class); + else + return setField(name, (Object)builder); + } + + /** + * Removes field from portable object. + * + * @param name Field name. + * @return {@code this} instance for chaining. + */ + @Override public BinaryObjectBuilderImpl removeField(String name) { + if (assignedVals == null) + assignedVals = new LinkedHashMap<>(); + + assignedVals.put(name, REMOVED_FIELD_MARKER); + + return this; + } + + /** + * Creates builder initialized by specified portable object. + * + * @param obj Portable object to initialize builder. + * @return New builder. + */ + public static BinaryObjectBuilderImpl wrap(BinaryObject obj) { + BinaryObjectImpl heapObj; + + if (obj instanceof BinaryObjectOffheapImpl) + heapObj = (BinaryObjectImpl)((BinaryObjectOffheapImpl)obj).heapCopy(); + else + heapObj = (BinaryObjectImpl)obj; + + return new BinaryObjectBuilderImpl(heapObj); + } + + /** + * @return Object start position in source array. + */ + int start() { + return start; + } + + /** + * @return Object type id. + */ + public int typeId() { + return typeId; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/0ac39c97/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderEnum.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderEnum.java index 3bb8194,0000000..779c514 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderEnum.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderEnum.java @@@ -1,116 -1,0 +1,115 @@@ +/* + * 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.builder; + +import org.apache.ignite.internal.binary.GridPortableMarshaller; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.binary.BinaryInvalidTypeException; + +/** + * + */ +public class PortableBuilderEnum implements PortableBuilderSerializationAware { + /** */ + private final int ordinal; + + /** */ + private final int typeId; + + /** */ + private final String clsName; + + /** + * @param typeId Type ID. + * @param anEnum Enum instance. + */ + public PortableBuilderEnum(int typeId, Enum anEnum) { + ordinal = anEnum.ordinal(); + this.typeId = typeId; + clsName = null; + } + + /** + * @param reader PortableBuilderReader. + */ + public PortableBuilderEnum(PortableBuilderReader reader) { + int typeId = reader.readInt(); + + if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) { + clsName = reader.readString(); + + Class cls; + + try { - // TODO: IGNITE-1272 - Is class loader needed here? - cls = U.forName(reader.readString(), null); ++ cls = U.forName(reader.readString(), reader.portableContext().configuration().getClassLoader()); + } + catch (ClassNotFoundException e) { + throw new BinaryInvalidTypeException("Failed to load the class: " + clsName, e); + } + + this.typeId = reader.portableContext().descriptorForClass(cls, false).typeId(); + } + else { + this.typeId = typeId; + this.clsName = null; + } + + ordinal = reader.readInt(); + } + + /** + * @return Ordinal. + */ + public int getOrdinal() { + return ordinal; + } + + /** {@inheritDoc} */ + @Override public void writeTo(BinaryWriterExImpl writer, PortableBuilderSerializer ctx) { + writer.writeByte(GridPortableMarshaller.ENUM); + + if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) { + writer.writeInt(GridPortableMarshaller.UNREGISTERED_TYPE_ID); + writer.writeString(clsName); + } + else + writer.writeInt(typeId); + + writer.writeInt(ordinal); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) + return false; + + PortableBuilderEnum that = (PortableBuilderEnum)o; + + return ordinal == that.ordinal && typeId == that.typeId; + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int result = ordinal; + + result = 31 * result + typeId; + + return result; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/0ac39c97/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderReader.java ---------------------------------------------------------------------- diff --cc modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderReader.java index 2b28e3d,0000000..c86fb95 mode 100644,000000..100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderReader.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/builder/PortableBuilderReader.java @@@ -1,856 -1,0 +1,857 @@@ +/* + * 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.builder; + +import org.apache.ignite.binary.BinaryObjectException; +import org.apache.ignite.internal.binary.BinaryObjectImpl; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.binary.GridPortableMarshaller; +import org.apache.ignite.internal.binary.PortableContext; +import org.apache.ignite.internal.binary.PortablePositionReadable; +import org.apache.ignite.internal.binary.PortablePrimitives; +import org.apache.ignite.internal.binary.PortableSchema; +import org.apache.ignite.internal.binary.streams.PortableHeapInputStream; +import org.apache.ignite.internal.binary.BinaryObjectImpl; +import org.apache.ignite.internal.binary.BinaryReaderExImpl; +import org.apache.ignite.internal.binary.BinaryWriterExImpl; +import org.apache.ignite.internal.binary.GridPortableMarshaller; +import org.apache.ignite.internal.binary.PortableContext; +import org.apache.ignite.internal.binary.PortablePositionReadable; +import org.apache.ignite.internal.binary.PortablePrimitives; +import org.apache.ignite.internal.binary.PortableSchema; +import org.apache.ignite.internal.binary.PortableUtils; +import org.apache.ignite.internal.binary.streams.PortableHeapInputStream; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.NULL; +import static org.apache.ignite.internal.binary.GridPortableMarshaller.STRING; + +/** + * + */ +public class PortableBuilderReader implements PortablePositionReadable { + /** */ + private final PortableContext ctx; + + /** */ + private final byte[] arr; + + /** */ + private final BinaryReaderExImpl reader; + + /** */ + private final Map<Integer, BinaryObjectBuilderImpl> objMap; + + /** */ + private int pos; + + /* + * Constructor. + * + * @param objImpl Portable object + */ + PortableBuilderReader(BinaryObjectImpl objImpl) { + ctx = objImpl.context(); + arr = objImpl.array(); + pos = objImpl.start(); + - // TODO: IGNITE-1272 - Is class loader needed here? - reader = new BinaryReaderExImpl(ctx, PortableHeapInputStream.create(arr, pos), null); ++ reader = new BinaryReaderExImpl(ctx, ++ PortableHeapInputStream.create(arr, pos), ++ ctx.configuration().getClassLoader()); + + objMap = new HashMap<>(); + } + + /** + * Copying constructor. + * + * @param other Other reader. + * @param start Start position. + */ + PortableBuilderReader(PortableBuilderReader other, int start) { + this.ctx = other.ctx; + this.arr = other.arr; + this.pos = start; + + reader = new BinaryReaderExImpl(ctx, PortableHeapInputStream.create(arr, start), null, other.reader.handles()); + + this.objMap = other.objMap; + } + + /** + * @return Portable context. + */ + public PortableContext portableContext() { + return ctx; + } + + /** + * @param obj Mutable portable object. + */ + public void registerObject(BinaryObjectBuilderImpl obj) { + objMap.put(obj.start(), obj); + } + + /** + * Get schema of the object, starting at the given position. + * + * @return Object's schema. + */ + public PortableSchema schema() { + return reader.getOrCreateSchema(); + } + + /** + * @return Read int value. + */ + public int readInt() { + int res = readInt(0); + + pos += 4; + + return res; + } + + /** + * @return Read int value. + */ + public byte readByte() { + return arr[pos++]; + } + + /** + * @return Read boolean value. + */ + public boolean readBoolean() { + return readByte() == 1; + } + + /** + * @return Read int value. + */ + public byte readByte(int off) { + return arr[pos + off]; + } + + /** + * @param off Offset related to {@link #pos} + * @return Read int value. + */ + public int readInt(int off) { + return PortablePrimitives.readInt(arr, pos + off); + } + + /** + * @param pos Position in the source array. + * @return Read byte value. + */ + public byte readBytePositioned(int pos) { + return PortablePrimitives.readByte(arr, pos); + } + + /** {@inheritDoc} */ + @Override public short readShortPositioned(int pos) { + return PortablePrimitives.readShort(arr, pos); + } + + /** {@inheritDoc} */ + @Override public int readIntPositioned(int pos) { + return PortablePrimitives.readInt(arr, pos); + } + + /** + * @return Read length of array. + */ + public int readLength() { + return PortablePrimitives.readInt(arr, pos); + } + + /** + * Read string length. + * + * @return String length. + */ + public int readStringLength() { + return PortablePrimitives.readInt(arr, pos); + } + + /** + * Reads string. + * + * @return String. + */ + public String readString() { + byte flag = readByte(); + + if (flag == GridPortableMarshaller.NULL) + return null; + + if (flag != GridPortableMarshaller.STRING) + throw new BinaryObjectException("Failed to deserialize String."); + + int len = readInt(); + + String str = new String(arr, pos, len, UTF_8); + + pos += len; + + return str; + } + + /** + * + */ + public void skipValue() { + byte type = arr[pos++]; + + int len; + + switch (type) { + case GridPortableMarshaller.NULL: + return; + + case GridPortableMarshaller.OBJ: + pos += readInt(GridPortableMarshaller.TOTAL_LEN_POS - 1) - 1; + + return; + + case GridPortableMarshaller.BOOLEAN: + case GridPortableMarshaller.BYTE: + len = 1; + break; + + case GridPortableMarshaller.CHAR: + case GridPortableMarshaller.SHORT: + len = 2; + + break; + + case GridPortableMarshaller.HANDLE: + case GridPortableMarshaller.FLOAT: + case GridPortableMarshaller.INT: + len = 4; + + break; + + case GridPortableMarshaller.ENUM: + //skipping type id and ordinal value + len = 8; + + break; + + case GridPortableMarshaller.LONG: + case GridPortableMarshaller.DOUBLE: + len = 8; + + break; + + case GridPortableMarshaller.BYTE_ARR: + case GridPortableMarshaller.BOOLEAN_ARR: + len = 4 + readLength(); + + break; + + case GridPortableMarshaller.STRING: + len = 4 + readStringLength(); + + break; + + case GridPortableMarshaller.DECIMAL: + len = /** scale */ 4 + /** mag len */ 4 + /** mag bytes count */ readInt(4); + + break; + + case GridPortableMarshaller.UUID: + len = 8 + 8; + + break; + + case GridPortableMarshaller.DATE: + len = 8; + + break; + + case GridPortableMarshaller.TIMESTAMP: + len = 8 + 4; + + break; + + case GridPortableMarshaller.CHAR_ARR: + case GridPortableMarshaller.SHORT_ARR: + len = 4 + readLength() * 2; + + break; + + case GridPortableMarshaller.INT_ARR: + case GridPortableMarshaller.FLOAT_ARR: + len = 4 + readLength() * 4; + + break; + + case GridPortableMarshaller.LONG_ARR: + case GridPortableMarshaller.DOUBLE_ARR: + len = 4 + readLength() * 8; + + break; + + case GridPortableMarshaller.DECIMAL_ARR: + case GridPortableMarshaller.DATE_ARR: + case GridPortableMarshaller.TIMESTAMP_ARR: + case GridPortableMarshaller.OBJ_ARR: + case GridPortableMarshaller.ENUM_ARR: + case GridPortableMarshaller.UUID_ARR: + case GridPortableMarshaller.STRING_ARR: { + int size = readInt(); + + for (int i = 0; i < size; i++) + skipValue(); + + return; + } + + case GridPortableMarshaller.COL: { + int size = readInt(); + + pos++; // skip collection type + + for (int i = 0; i < size; i++) + skipValue(); + + return; + } + + case GridPortableMarshaller.MAP: { + int size = readInt(); + + pos++; // skip collection type + + for (int i = 0; i < size; i++) { + skipValue(); // skip key. + skipValue(); // skip value. + } + + return; + } + + case GridPortableMarshaller.PORTABLE_OBJ: + len = readInt() + 4; + + break; + + default: + throw new BinaryObjectException("Invalid flag value: " + type); + } + + pos += len; + } + + /** + * @param pos Position. + * @param len Length. + * @return Object. + */ + public Object getValueQuickly(int pos, int len) { + byte type = arr[pos]; + + switch (type) { + case GridPortableMarshaller.NULL: + return null; + + case GridPortableMarshaller.HANDLE: { + int objStart = pos - readIntPositioned(pos + 1); + + BinaryObjectBuilderImpl res = objMap.get(objStart); + + if (res == null) { + res = new BinaryObjectBuilderImpl(new PortableBuilderReader(this, objStart), objStart); + + objMap.put(objStart, res); + } + + return res; + } + + case GridPortableMarshaller.OBJ: { + BinaryObjectBuilderImpl res = objMap.get(pos); + + if (res == null) { + res = new BinaryObjectBuilderImpl(new PortableBuilderReader(this, pos), pos); + + objMap.put(pos, res); + } + + return res; + } + + case GridPortableMarshaller.BYTE: + return arr[pos + 1]; + + case GridPortableMarshaller.SHORT: + return PortablePrimitives.readShort(arr, pos + 1); + + case GridPortableMarshaller.INT: + return PortablePrimitives.readInt(arr, pos + 1); + + case GridPortableMarshaller.LONG: + return PortablePrimitives.readLong(arr, pos + 1); + + case GridPortableMarshaller.FLOAT: + return PortablePrimitives.readFloat(arr, pos + 1); + + case GridPortableMarshaller.DOUBLE: + return PortablePrimitives.readDouble(arr, pos + 1); + + case GridPortableMarshaller.CHAR: + return PortablePrimitives.readChar(arr, pos + 1); + + case GridPortableMarshaller.BOOLEAN: + return arr[pos + 1] != 0; + + case GridPortableMarshaller.DECIMAL: + case GridPortableMarshaller.STRING: + case GridPortableMarshaller.UUID: + case GridPortableMarshaller.DATE: + case GridPortableMarshaller.TIMESTAMP: + return new PortablePlainLazyValue(this, pos, len); + + case GridPortableMarshaller.BYTE_ARR: + case GridPortableMarshaller.SHORT_ARR: + case GridPortableMarshaller.INT_ARR: + case GridPortableMarshaller.LONG_ARR: + case GridPortableMarshaller.FLOAT_ARR: + case GridPortableMarshaller.DOUBLE_ARR: + case GridPortableMarshaller.CHAR_ARR: + case GridPortableMarshaller.BOOLEAN_ARR: + case GridPortableMarshaller.DECIMAL_ARR: + case GridPortableMarshaller.DATE_ARR: + case GridPortableMarshaller.TIMESTAMP_ARR: + case GridPortableMarshaller.UUID_ARR: + case GridPortableMarshaller.STRING_ARR: + case GridPortableMarshaller.ENUM_ARR: + case GridPortableMarshaller.OBJ_ARR: + case GridPortableMarshaller.COL: + case GridPortableMarshaller.MAP: + return new LazyCollection(pos); + + case GridPortableMarshaller.ENUM: { + if (len == 1) { + assert readByte(pos) == GridPortableMarshaller.NULL; + + return null; + } + + int mark = position(); + position(pos + 1); + + PortableBuilderEnum builderEnum = new PortableBuilderEnum(this); + + position(mark); + + return builderEnum; + } + + case GridPortableMarshaller.PORTABLE_OBJ: { + int size = readIntPositioned(pos + 1); + + int start = readIntPositioned(pos + 4 + size); + + BinaryObjectImpl portableObj = new BinaryObjectImpl(ctx, arr, pos + 4 + start); + + return new PortablePlainPortableObject(portableObj); + } + + default: + throw new BinaryObjectException("Invalid flag value: " + type); + } + } + + /** + * @return Parsed value. + */ + public Object parseValue() { + int valPos = pos; + + byte type = arr[pos++]; + + int plainLazyValLen; + + boolean modifiableLazyVal = false; + + switch (type) { + case GridPortableMarshaller.NULL: + return null; + + case GridPortableMarshaller.HANDLE: { + int objStart = pos - 1 - readInt(); + + BinaryObjectBuilderImpl res = objMap.get(objStart); + + if (res == null) { + res = new BinaryObjectBuilderImpl(new PortableBuilderReader(this, objStart), objStart); + + objMap.put(objStart, res); + } + + return res; + } + + case GridPortableMarshaller.OBJ: { + pos--; + + BinaryObjectBuilderImpl res = objMap.get(pos); + + if (res == null) { + res = new BinaryObjectBuilderImpl(new PortableBuilderReader(this, pos), pos); + + objMap.put(pos, res); + } + + pos += readInt(GridPortableMarshaller.TOTAL_LEN_POS); + + return res; + } + + case GridPortableMarshaller.BYTE: + return arr[pos++]; + + case GridPortableMarshaller.SHORT: { + Object res = PortablePrimitives.readShort(arr, pos); + pos += 2; + return res; + } + + case GridPortableMarshaller.INT: + return readInt(); + + case GridPortableMarshaller.LONG: + plainLazyValLen = 8; + + break; + + case GridPortableMarshaller.FLOAT: + plainLazyValLen = 4; + + break; + + case GridPortableMarshaller.DOUBLE: + plainLazyValLen = 8; + + break; + + case GridPortableMarshaller.CHAR: + plainLazyValLen = 2; + + break; + + case GridPortableMarshaller.BOOLEAN: + return arr[pos++] != 0; + + case GridPortableMarshaller.DECIMAL: + plainLazyValLen = /** scale */ 4 + /** mag len */ 4 + /** mag bytes count */ readInt(4); + + break; + + case GridPortableMarshaller.STRING: + plainLazyValLen = 4 + readStringLength(); + + break; + + case GridPortableMarshaller.UUID: + plainLazyValLen = 8 + 8; + + break; + + case GridPortableMarshaller.DATE: + plainLazyValLen = 8; + + break; + + case GridPortableMarshaller.TIMESTAMP: + plainLazyValLen = 8 + 4; + + break; + + case GridPortableMarshaller.BYTE_ARR: + plainLazyValLen = 4 + readLength(); + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.SHORT_ARR: + plainLazyValLen = 4 + readLength() * 2; + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.INT_ARR: + plainLazyValLen = 4 + readLength() * 4; + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.LONG_ARR: + plainLazyValLen = 4 + readLength() * 8; + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.FLOAT_ARR: + plainLazyValLen = 4 + readLength() * 4; + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.DOUBLE_ARR: + plainLazyValLen = 4 + readLength() * 8; + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.CHAR_ARR: + plainLazyValLen = 4 + readLength() * 2; + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.BOOLEAN_ARR: + plainLazyValLen = 4 + readLength(); + modifiableLazyVal = true; + + break; + + case GridPortableMarshaller.OBJ_ARR: + return new PortableObjectArrayLazyValue(this); + + case GridPortableMarshaller.DATE_ARR: { + int size = readInt(); + + Date[] res = new Date[size]; + + for (int i = 0; i < res.length; i++) { + byte flag = arr[pos++]; + + if (flag == GridPortableMarshaller.NULL) continue; + + if (flag != GridPortableMarshaller.DATE) + throw new BinaryObjectException("Invalid flag value: " + flag); + + long time = PortablePrimitives.readLong(arr, pos); + + pos += 8; + + res[i] = new Date(time); + } + + return res; + } + + case GridPortableMarshaller.TIMESTAMP_ARR: { + int size = readInt(); + + Timestamp[] res = new Timestamp[size]; + + for (int i = 0; i < res.length; i++) { + byte flag = arr[pos++]; + + if (flag == GridPortableMarshaller.NULL) + continue; + + if (flag != GridPortableMarshaller.TIMESTAMP) + throw new BinaryObjectException("Invalid flag value: " + flag); + + long time = PortablePrimitives.readLong(arr, pos); + + pos += 8; + + int nano = PortablePrimitives.readInt(arr, pos); + + pos += 4; + + Timestamp ts = new Timestamp(time); + + ts.setNanos(ts.getNanos() + nano); + + res[i] = ts; + } + + return res; + } + + case GridPortableMarshaller.UUID_ARR: + case GridPortableMarshaller.STRING_ARR: + case GridPortableMarshaller.DECIMAL_ARR: { + int size = readInt(); + + for (int i = 0; i < size; i++) { + byte flag = arr[pos++]; + + if (flag == GridPortableMarshaller.UUID) + pos += 8 + 8; + else if (flag == GridPortableMarshaller.STRING) + pos += 4 + readStringLength(); + else if (flag == GridPortableMarshaller.DECIMAL) { + pos += 4; // scale value + pos += 4 + readLength(); + } + else + assert flag == GridPortableMarshaller.NULL; + } + + return new PortableModifiableLazyValue(this, valPos, pos - valPos); + } + + case GridPortableMarshaller.COL: { + int size = readInt(); + byte colType = arr[pos++]; + + switch (colType) { + case GridPortableMarshaller.USER_COL: + case GridPortableMarshaller.ARR_LIST: + return new PortableLazyArrayList(this, size); + + case GridPortableMarshaller.LINKED_LIST: + return new PortableLazyLinkedList(this, size); + + case GridPortableMarshaller.HASH_SET: + case GridPortableMarshaller.LINKED_HASH_SET: + return new PortableLazySet(this, size); + } + + throw new BinaryObjectException("Unknown collection type: " + colType); + } + + case GridPortableMarshaller.MAP: + return PortableLazyMap.parseMap(this); + + case GridPortableMarshaller.ENUM: + return new PortableBuilderEnum(this); + + case GridPortableMarshaller.ENUM_ARR: + return new PortableEnumArrayLazyValue(this); + + case GridPortableMarshaller.PORTABLE_OBJ: { + int size = readInt(); + + pos += size; + + int start = readInt(); + + BinaryObjectImpl portableObj = new BinaryObjectImpl(ctx, arr, + pos - 4 - size + start); + + return new PortablePlainPortableObject(portableObj); + } + + default: + throw new BinaryObjectException("Invalid flag value: " + type); + } + + PortableAbstractLazyValue res; + + if (modifiableLazyVal) + res = new PortableModifiableLazyValue(this, valPos, 1 + plainLazyValLen); + else + res = new PortablePlainLazyValue(this, valPos, 1 + plainLazyValLen); + + pos += plainLazyValLen; + + return res; + } + + /** + * @return Array. + */ + public byte[] array() { + return arr; + } + + /** + * @return Position of reader. + */ + public int position() { + return pos; + } + + /** + * @param pos New pos. + */ + public void position(int pos) { + this.pos = pos; + } + + /** + * @param n Number of bytes to skip. + */ + public void skip(int n) { + pos += n; + } + + /** + * @return Reader. + */ + BinaryReaderExImpl reader() { + return reader; + } + + /** + * + */ + private class LazyCollection implements PortableLazyValue { + /** */ + private final int valOff; + + /** */ + private Object col; + + /** + * @param valOff Value. + */ + protected LazyCollection(int valOff) { + this.valOff = valOff; + } + + /** + * @return Object. + */ + private Object wrappedCollection() { + if (col == null) { + position(valOff); + + col = parseValue(); + } + + return col; + } + + /** {@inheritDoc} */ + @Override public void writeTo(BinaryWriterExImpl writer, PortableBuilderSerializer ctx) { + ctx.writeValue(writer, wrappedCollection()); + } + + /** {@inheritDoc} */ + @Override public Object value() { + return PortableUtils.unwrapLazy(wrappedCollection()); + } + } +}
