/* * 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.Time; 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.BinaryObjectBuilder; import org.apache.ignite.binary.BinaryObjectException; import org.apache.ignite.binary.BinaryType; import org.apache.ignite.internal.binary.builder.BinaryObjectBuilderImpl; import org.apache.ignite.internal.binary.streams.BinaryOffheapInputStream; import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.CacheObjectContext; 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; /** * Binary object implementation over offheap memory */ public class BinaryObjectOffheapImpl extends BinaryObjectExImpl implements Externalizable, CacheObject { /** */ private static final long serialVersionUID = 0L; /** */ private final BinaryContext 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(BinaryContext 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() { int typeId = BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.TYPE_ID_POS); if (typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID) { int off = start + GridBinaryMarshaller.DFLT_HDR_LEN; String clsName = BinaryUtils.doReadClassName(new BinaryOffheapInputStream(ptr + off, size)); typeId = ctx.typeId(clsName); } return typeId; } /** {@inheritDoc} */ @Override public int length() { return BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.TOTAL_LEN_POS); } /** {@inheritDoc} */ @Override public int hashCode() { return BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.HASH_CODE_POS); } /** {@inheritDoc} */ @Override public boolean hasSchema() { short flags = BinaryPrimitives.readShort(ptr, start + GridBinaryMarshaller.FLAGS_POS); return BinaryUtils.hasSchema(flags); } /** {@inheritDoc} */ @Override public int schemaId() { return BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.SCHEMA_ID_POS); } /** {@inheritDoc} */ @Override public BinarySchema createSchema() { return reader(null, false).getOrCreateSchema(); } /** {@inheritDoc} */ @Override public BinaryContext context() { return ctx; } /** {@inheritDoc} */ @Override public int start() { return start; } /** {@inheritDoc} */ @Override public byte[] array() { return null; } /** {@inheritDoc} */ @Override public boolean putValue(ByteBuffer buf) throws IgniteCheckedException { throw new UnsupportedOperationException("TODO implement"); } /** {@inheritDoc} */ @Override public int putValue(long addr) throws IgniteCheckedException { throw new UnsupportedOperationException("TODO implement"); } /** {@inheritDoc} */ @Override public boolean putValue(final ByteBuffer buf, final int off, final int len) throws IgniteCheckedException { throw new UnsupportedOperationException("TODO implement"); } /** {@inheritDoc} */ @Override public int valueBytesLength(CacheObjectContext ctx) throws IgniteCheckedException { throw new UnsupportedOperationException("TODO implement"); } /** {@inheritDoc} */ @Override public long offheapAddress() { return ptr; } /** {@inheritDoc} */ @Override public boolean hasArray() { return false; } /** {@inheritDoc} */ @Override public boolean isFlagSet(short flag) { short flags = BinaryPrimitives.readShort(ptr, start + GridBinaryMarshaller.FLAGS_POS); return BinaryUtils.isFlagSet(flags, flag); } /** {@inheritDoc} */ @Nullable @Override public BinaryType type() throws BinaryObjectException { return BinaryUtils.typeProxy(ctx, this); } /** {@inheritDoc} */ @Nullable @Override public BinaryType rawType() throws BinaryObjectException { return BinaryUtils.type(ctx, this); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Nullable @Override public <F> F field(String fieldName) throws BinaryObjectException { return (F) reader(null, false).unmarshalField(fieldName); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Nullable @Override public <F> F field(int fieldId) throws BinaryObjectException { return (F) reader(null, false).unmarshalField(fieldId); } /** {@inheritDoc} */ @Override public BinarySerializedFieldComparator createFieldComparator() { int schemaOff = BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS); short flags = BinaryPrimitives.readShort(ptr, start + GridBinaryMarshaller.FLAGS_POS); int fieldIdLen = BinaryUtils.isCompactFooter(flags) ? 0 : BinaryUtils.FIELD_ID_LEN; int fieldOffLen = BinaryUtils.fieldOffsetLength(flags); int orderBase = start + schemaOff + fieldIdLen; int orderMultiplier = fieldIdLen + fieldOffLen; return new BinarySerializedFieldComparator(this, null, ptr, start, orderBase, orderMultiplier, fieldOffLen); } /** {@inheritDoc} */ @Override public int dataStartOffset() { int typeId = BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.TYPE_ID_POS); if (typeId == GridBinaryMarshaller.UNREGISTERED_TYPE_ID) { int len = BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.DFLT_HDR_LEN + 1); return start + GridBinaryMarshaller.DFLT_HDR_LEN + len + 5; } else return start + GridBinaryMarshaller.DFLT_HDR_LEN; } /** {@inheritDoc} */ @Override public int footerStartOffset() { short flags = BinaryPrimitives.readShort(ptr, start + GridBinaryMarshaller.FLAGS_POS); if (!BinaryUtils.hasSchema(flags)) return start + length(); return start + BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS); } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Nullable @Override public <F> F fieldByOrder(int order) { if (order == BinarySchema.ORDER_NOT_FOUND) return null; Object val; // Calculate field position. int schemaOff = BinaryPrimitives.readInt(ptr, start + GridBinaryMarshaller.SCHEMA_OR_RAW_OFF_POS); short flags = BinaryPrimitives.readShort(ptr, start + GridBinaryMarshaller.FLAGS_POS); int fieldIdLen = BinaryUtils.isCompactFooter(flags) ? 0 : BinaryUtils.FIELD_ID_LEN; int fieldOffLen = BinaryUtils.fieldOffsetLength(flags); int fieldOffsetPos = start + schemaOff + order * (fieldIdLen + fieldOffLen) + fieldIdLen; int fieldPos; if (fieldOffLen == BinaryUtils.OFFSET_1) fieldPos = start + ((int)BinaryPrimitives.readByte(ptr, fieldOffsetPos) & 0xFF); else if (fieldOffLen == BinaryUtils.OFFSET_2) fieldPos = start + ((int)BinaryPrimitives.readShort(ptr, fieldOffsetPos) & 0xFFFF); else fieldPos = start + BinaryPrimitives.readInt(ptr, fieldOffsetPos); // Read header and try performing fast lookup for well-known types (the most common types go first). byte hdr = BinaryPrimitives.readByte(ptr, fieldPos); switch (hdr) { case GridBinaryMarshaller.INT: val = BinaryPrimitives.readInt(ptr, fieldPos + 1); break; case GridBinaryMarshaller.LONG: val = BinaryPrimitives.readLong(ptr, fieldPos + 1); break; case GridBinaryMarshaller.BOOLEAN: val = BinaryPrimitives.readBoolean(ptr, fieldPos + 1); break; case GridBinaryMarshaller.SHORT: val = BinaryPrimitives.readShort(ptr, fieldPos + 1); break; case GridBinaryMarshaller.BYTE: val = BinaryPrimitives.readByte(ptr, fieldPos + 1); break; case GridBinaryMarshaller.CHAR: val = BinaryPrimitives.readChar(ptr, fieldPos + 1); break; case GridBinaryMarshaller.FLOAT: val = BinaryPrimitives.readFloat(ptr, fieldPos + 1); break; case GridBinaryMarshaller.DOUBLE: val = BinaryPrimitives.readDouble(ptr, fieldPos + 1); break; case GridBinaryMarshaller.STRING: { int dataLen = BinaryPrimitives.readInt(ptr, fieldPos + 1); byte[] data = BinaryPrimitives.readByteArray(ptr, fieldPos + 5, dataLen); val = new String(data, UTF_8); break; } case GridBinaryMarshaller.DATE: { long time = BinaryPrimitives.readLong(ptr, fieldPos + 1); val = new Date(time); break; } case GridBinaryMarshaller.TIMESTAMP: { long time = BinaryPrimitives.readLong(ptr, fieldPos + 1); int nanos = BinaryPrimitives.readInt(ptr, fieldPos + 1 + 8); Timestamp ts = new Timestamp(time); ts.setNanos(ts.getNanos() + nanos); val = ts; break; } case GridBinaryMarshaller.TIME: { long time = BinaryPrimitives.readLong(ptr, fieldPos + 1); val = new Time(time); break; } case GridBinaryMarshaller.UUID: { long most = BinaryPrimitives.readLong(ptr, fieldPos + 1); long least = BinaryPrimitives.readLong(ptr, fieldPos + 1 + 8); val = new UUID(most, least); break; } case GridBinaryMarshaller.DECIMAL: { int scale = BinaryPrimitives.readInt(ptr, fieldPos + 1); int dataLen = BinaryPrimitives.readInt(ptr, fieldPos + 5); byte[] data = BinaryPrimitives.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 GridBinaryMarshaller.NULL: val = null; break; default: BinaryOffheapInputStream stream = new BinaryOffheapInputStream(ptr, size, false); stream.position(fieldPos); val = BinaryUtils.unmarshal(stream, ctx, null); break; } return (F)val; } /** {@inheritDoc} */ @Override protected boolean writeFieldByOrder(int fieldOffset, ByteBuffer buf) { return false; } /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Nullable @Override protected <F> F field(BinaryReaderHandles rCtx, String fieldName) { return (F)reader(rCtx, false).unmarshalField(fieldName); } /** {@inheritDoc} */ @Override public boolean hasField(String fieldName) { return reader(null, false).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 BinaryObjectBuilder toBuilder() throws BinaryObjectException { return BinaryObjectBuilderImpl.wrap(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 short 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. } /** {@inheritDoc} */ @Override public void onAckReceived() { // No-op. } /** * @return Deserialized value. */ private Object deserializeValue() { return reader(null, true).deserialize(); } /** * Create new reader for this object. * * @param rCtx Reader context. * @param forUnmarshal {@code True} if reader is needed to unmarshal object. * @return Reader. */ private BinaryReaderExImpl reader(@Nullable BinaryReaderHandles rCtx, boolean forUnmarshal) { BinaryOffheapInputStream stream = new BinaryOffheapInputStream(ptr, size, false); stream.position(start); return new BinaryReaderExImpl(ctx, stream, ctx.configuration().getClassLoader(), rCtx, forUnmarshal); } }