IGNITE-12911 CacheObjectAdapter#put incorrect offset handling fixed - Fixes #7742.

Signed-off-by: Ilya Kasnacheev <ilya.kasnacheev@gmail.com>
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryEnumObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryEnumObjectImpl.java
index c09cda1..d4e3d07 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryEnumObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryEnumObjectImpl.java
@@ -349,7 +349,7 @@
     @Override public int putValue(long addr) throws IgniteCheckedException {
         assert valBytes != null : "Value bytes must be initialized before object is stored";
 
-        return CacheObjectAdapter.putValue(addr, cacheObjectType(), valBytes, 0);
+        return CacheObjectAdapter.putValue(addr, cacheObjectType(), valBytes);
     }
 
     /** {@inheritDoc} */
diff --git 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
index ef654cc..e3873ee 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
@@ -174,7 +174,7 @@
 
     /** {@inheritDoc} */
     @Override public int putValue(long addr) throws IgniteCheckedException {
-        return CacheObjectAdapter.putValue(addr, cacheObjectType(), arr, start);
+        return CacheObjectAdapter.putValue(addr, cacheObjectType(), arr, start, length());
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectAdapter.java
index fa51ce5..eb1f3b3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectAdapter.java
@@ -82,27 +82,38 @@
     @Override public int putValue(long addr) throws IgniteCheckedException {
         assert valBytes != null : "Value bytes must be initialized before object is stored";
 
-        return putValue(addr, cacheObjectType(), valBytes, 0);
+        return putValue(addr, cacheObjectType(), valBytes);
     }
 
     /**
      * @param addr Write address.
      * @param type Object type.
      * @param valBytes Value bytes array.
-     * @param valOff Value bytes array offset.
      * @return Offset shift compared to initial address.
      */
-    public static int putValue(long addr, byte type, byte[] valBytes, int valOff) {
+    public static int putValue(long addr, byte type, byte[] valBytes) {
+        return putValue(addr, type, valBytes, 0, valBytes.length);
+    }
+
+    /**
+     * @param addr Write address.
+     * @param type Object type.
+     * @param srcBytes Source value bytes array.
+     * @param srcOff Start position in sourceBytes.
+     * @param len Number of bytes for write.
+     * @return Offset shift compared to initial address.
+     */
+    public static int putValue(long addr, byte type, byte[] srcBytes, int srcOff, int len) {
         int off = 0;
 
-        PageUtils.putInt(addr, off, valBytes.length);
+        PageUtils.putInt(addr, off, len);
         off += 4;
 
         PageUtils.putByte(addr, off, type);
         off++;
 
-        PageUtils.putBytes(addr, off, valBytes, valOff);
-        off += valBytes.length - valOff;
+        PageUtils.putBytes(addr, off, srcBytes, srcOff, len);
+        off += len;
 
         return off;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectByteArrayImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectByteArrayImpl.java
index 5c033b6..6eac6a5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectByteArrayImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheObjectByteArrayImpl.java
@@ -88,7 +88,7 @@
 
     /** {@inheritDoc} */
     @Override public int putValue(long addr) throws IgniteCheckedException {
-        return CacheObjectAdapter.putValue(addr, cacheObjectType(), val, 0);
+        return CacheObjectAdapter.putValue(addr, cacheObjectType(), val);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePutKeyAttachedBinaryObjectTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePutKeyAttachedBinaryObjectTest.java
new file mode 100644
index 0000000..91277f8
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePutKeyAttachedBinaryObjectTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.processors.cache;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.binary.BinaryBasicNameMapper;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryObjectBuilder;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.failure.StopNodeFailureHandler;
+import org.apache.ignite.internal.binary.BinaryObjectImpl;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class IgniteCachePutKeyAttachedBinaryObjectTest extends GridCommonAbstractTest {
+    /** */
+    public static final String CACHE_NAME = "test";
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return super.getConfiguration(gridName)
+            .setFailureHandler(new StopNodeFailureHandler())
+            .setCacheConfiguration(new CacheConfiguration<>(CACHE_NAME))
+            // For brevity, not needed to reproduce the issue.
+            .setBinaryConfiguration(new BinaryConfiguration().setNameMapper(new BinaryBasicNameMapper(true)));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testAttachedBinaryKeyStoredSuccessfullyToNotEmptyCache() throws Exception {
+        startGrid(0);
+
+        IgniteCache<Object, Object> binCache = grid(0).cache(CACHE_NAME);
+
+        //Ensure that cache not empty.
+        AttachedKey ordinaryKey = new AttachedKey(0);
+
+        binCache.put(ordinaryKey, 1);
+
+        BinaryObjectBuilder holdBuilder = grid(0).binary().builder(HolderKey.class.getName());
+
+        //Creating attached key which stores as byte array.
+        BinaryObjectImpl attachedKey = holdBuilder.setField("id", new AttachedKey(1))
+            .build()
+            .field("id");
+
+        //Put data with attached key.
+        binCache.put(attachedKey, 2);
+
+        assertEquals(1, binCache.get(ordinaryKey));
+        assertEquals(2, binCache.get(attachedKey));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    @Ignore("https://issues.apache.org/jira/browse/IGNITE-13080")
+    public void testKeyRefersToSubkeyInValue() throws Exception {
+        startGrid(0);
+
+        IgniteCache<CompositeKey, CompositeValue> cache = grid(0).cache(CACHE_NAME);
+
+        SubKey subKey = new SubKey(10);
+        CompositeKey compositeKey = new CompositeKey(20, subKey);
+        CompositeValue compositeVal = new CompositeValue("foo", subKey, compositeKey);
+
+        cache.put(compositeKey, compositeVal);
+
+        IgniteCache<Object, Object> binCache = cache.withKeepBinary();
+
+        BinaryObject binObj = (BinaryObject)binCache.get(compositeKey);
+
+        binCache.put(binObj.field("key"), binObj.toBuilder().setField("val", "bar").build());
+
+        assertEquals("bar", cache.get(compositeKey).val());
+
+        assertEquals(1, cache.size());
+    }
+
+    /** */
+    public static class AttachedKey {
+        /** */
+        public int id;
+
+        /** */
+        public AttachedKey(int id) {
+            this.id = id;
+        }
+    }
+
+    /** */
+    public static class HolderKey {
+        /** */
+        public AttachedKey key;
+    }
+
+    /** */
+    public static class SubKey {
+        /** */
+        private int subId;
+
+        /** */
+        public SubKey(int subId) {
+            this.subId = subId;
+        }
+    }
+
+    /** */
+    public static class CompositeKey {
+        /** */
+        private int id;
+
+        /** */
+        private SubKey subKey;
+
+        /** */
+        public CompositeKey(int id, SubKey subKey) {
+            this.id = id;
+            this.subKey = subKey;
+        }
+    }
+
+    /** */
+    public static class CompositeValue {
+        /** */
+        private String val;
+
+        /** */
+        private SubKey subKey;
+
+        /** */
+        private CompositeKey key;
+
+        /** */
+        public CompositeValue(String val, SubKey subKey, CompositeKey key) {
+            this.val = val;
+            this.subKey = subKey;
+            this.key = key;
+        }
+
+        /** */
+        public String val() {
+            return val;
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite8.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite8.java
index 43784b3..065fff5 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite8.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite8.java
@@ -23,6 +23,7 @@
 import org.apache.ignite.cache.ClientCreateCacheGroupOnJoinNodeMapsTest;
 import org.apache.ignite.internal.processors.cache.CacheStoreTxPutAllMultiNodeTest;
 import org.apache.ignite.internal.processors.cache.GridCacheOrderedPreloadingSelfTest;
+import org.apache.ignite.internal.processors.cache.IgniteCachePutKeyAttachedBinaryObjectTest;
 import org.apache.ignite.internal.processors.cache.distributed.rebalancing.GridCacheRabalancingDelayedPartitionMapExchangeSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.rebalancing.GridCacheRebalanceOrderTest;
 import org.apache.ignite.internal.processors.cache.distributed.rebalancing.GridCacheRebalancingAsyncSelfTest;
@@ -80,6 +81,8 @@
         GridTestUtils.addTestIfNeeded(suite, CleanupRestoredCachesSlowTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, ClientCreateCacheGroupOnJoinNodeMapsTest.class, ignoredTests);
 
+        GridTestUtils.addTestIfNeeded(suite, IgniteCachePutKeyAttachedBinaryObjectTest.class, ignoredTests);
+
         return suite;
     }
 }