/** * Copyright 2011-2012 Akiban Technologies, Inc. * * Licensed 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 com.persistit; import static com.persistit.MVV.STORE_EXISTED_MASK; import static com.persistit.MVV.STORE_LENGTH_MASK; import static com.persistit.MVV.TYPE_MVV; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Map; import java.util.TreeMap; import junit.framework.Assert; import org.junit.Test; import com.persistit.exception.PersistitException; import com.persistit.util.Util; public class MVVTest { @Test public void requireBigEndian() { // Tests have many expected arrays explicitly typed out, sanity check // setting assertEquals(true, Persistit.BIG_ENDIAN); } @Test public void lengthEstimate() { byte[] source = {}; assertTrue(MVV.estimateRequiredLength(source, -1, 5) >= 5); assertTrue(MVV.estimateRequiredLength(source, source.length, 5) >= 5); source = newArray(0xA, 0xB, 0xC, 0xD, 0xE, 0xF); assertTrue(MVV.estimateRequiredLength(source, 2, 74) >= (2 + 74)); assertTrue(MVV.estimateRequiredLength(source, source.length, 74) >= (source.length + 74)); source = newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0x1, 0x2, 0, 0, 0, 0, 0, 0, 0, 5, 0, 1, 0xA); assertTrue(MVV.estimateRequiredLength(source, source.length, 1) >= (source.length + 1)); } @Test public void lengthExactly() { byte[] source = {}; assertEquals(MVV.exactRequiredLength(source, 0, -1, 1, 5), MVV.overheadLength(1) + 5); assertEquals(MVV.exactRequiredLength(source, 0, source.length, 1, 5), MVV.overheadLength(2) + 5); source = newArray(0xA, 0xB, 0xC, 0xD, 0xE, 0xF); assertEquals(MVV.exactRequiredLength(source, 0, 2, 1, 74), MVV.overheadLength(2) + 2 + 74); assertEquals(MVV.exactRequiredLength(source, 0, source.length, 1, 74), MVV.overheadLength(2) + source.length + 74); source = newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0x1, 0x2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0xA, /* extra */ 0, 0, 0, 0, 0); final int usedLength = source.length - 5; // new version (-1 = MVV type ID) assertEquals(MVV.exactRequiredLength(source, 0, usedLength, 3, 3), usedLength + MVV.overheadLength(1) + 3 - 1); // replace version, shorter assertEquals(MVV.exactRequiredLength(source, 0, usedLength, 1, 1), usedLength - 1); // replace version, longer assertEquals(MVV.exactRequiredLength(source, 0, usedLength, 1, 3), usedLength + 1); } @Test public void isMVVAllInputs() { final byte[] empty = {}; assertEquals("empty and unused", false, MVV.isArrayMVV(empty, 0, -1)); assertEquals("empty and undefined", false, MVV.isArrayMVV(empty, 0, 0)); final byte[] primordial = newArray(0xA, 0xB, 0xC); assertEquals("primordial and unused", false, MVV.isArrayMVV(primordial, 0, -1)); assertEquals("primordial and used", false, MVV.isArrayMVV(primordial, 0, primordial.length)); final byte[] mvvEmptyVersion = newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0); assertEquals("mvv empty value", true, MVV.isArrayMVV(mvvEmptyVersion, 0, mvvEmptyVersion.length)); assertEquals("mvv array but unused", false, MVV.isArrayMVV(mvvEmptyVersion, 0, -1)); final byte[] mvvTwoVersions = newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0xA, 0xB, 0, 0, 0, 0, 0, 0, 0, 9, 0, 1, 0xC); assertEquals("mvv two versions", true, MVV.isArrayMVV(mvvTwoVersions, 0, mvvTwoVersions.length)); } @Test public void storeToUnused() { final int vh = 200; final byte[] source = { 0xA, 0xB, 0xC }; final byte[] target = new byte[100]; final int storedLength = storeVersion(target, -1, vh, source, source.length); assertEquals(source.length + MVV.overheadLength(1), storedLength); assertArrayEqualsLen(newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh, 0, 3, 0xA, 0xB, 0xC), target, storedLength); } @Test public void storeToUndefined() { final int vh = 200; final byte[] source = { 0xA, 0xB, 0xC }; final byte[] target = new byte[100]; final int storedLength = storeVersion(target, 0, vh, source, source.length); assertEquals(source.length + MVV.overheadLength(2), storedLength); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, vh, 0, 3, 0xA, 0xB, 0xC), target, storedLength); } @Test public void storeToPrimordial() { final int vh = 200; final byte[] source = { 0xD, 0xE, 0xF }; final byte[] target = new byte[100]; final int targetLength = writeArray(target, 0xA, 0xB, 0xC); final int storedLength = storeVersion(target, targetLength, vh, source, source.length); assertEquals(targetLength + source.length + MVV.overheadLength(2), storedLength); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0xA, 0xB, 0xC, 0, 0, 0, 0, 0, 0, 0, vh, 0, 3, 0xD, 0xE, 0xF), target, storedLength); } @Test public void storeToExisting() { final int vh1 = 10, vh2 = 200; final byte[] target = new byte[100]; final int targetContentsLength = 4; final int targetLength = writeArray(target, TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 4, 0xA, 0xB, 0xC, 0xD); final byte[] source = { 0xE, 0xF }; final int storedLength = storeVersion(target, targetLength, vh2, source, source.length); assertEquals(targetContentsLength + source.length + MVV.overheadLength(2), storedLength); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 4, 0xA, 0xB, 0xC, 0xD, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 2, 0xE, 0xF), target, storedLength); } @Test public void storeToExistingVersionEqualLength() { final int vh1 = 199, vh2 = 200, vh3 = 201; final byte[] target = new byte[100]; final int targetLength = writeArray(target, TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 3, 0xA, 0xB, 0xC, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9); final byte[] source = { 0xD, 0xE, 0xF }; final int storedLength = storeVersion(target, targetLength, vh2, source, source.length); assertTrue("version existed", (storedLength & STORE_EXISTED_MASK) != 0); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 3, 0xD, 0xE, 0xF, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9), target, storedLength); } @Test public void storeToExistingVersionShorterLength() { final int vh1 = 199, vh2 = 200, vh3 = 201; final byte[] target = new byte[100]; final int targetLength = writeArray(target, TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 3, 0xA, 0xB, 0xC, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9); final byte[] source = { 0xD, 0xE }; int storedLength = storeVersion(target, targetLength, vh2, source, source.length); assertTrue("version existed", (storedLength & STORE_EXISTED_MASK) != 0); storedLength &= STORE_LENGTH_MASK; assertEquals(targetLength - 1, storedLength); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 2, 0xD, 0xE, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9), target, storedLength); } @Test public void storeToExistingVersionLongerLength() { final int vh1 = 199, vh2 = 200, vh3 = 201; final byte[] target = new byte[100]; final int targetLength = writeArray(target, TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 3, 0xA, 0xB, 0xC, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9); final byte[] source = { 0xC, 0xD, 0xE, 0xF }; int storedLength = storeVersion(target, targetLength, vh2, source, source.length); assertTrue("version existed", (storedLength & STORE_EXISTED_MASK) != 0); storedLength &= STORE_LENGTH_MASK; assertEquals(targetLength + 1, storedLength); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, vh1, 0, 2, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, vh2, 0, 4, 0xC, 0xD, 0xE, 0xF, 0, 0, 0, 0, 0, 0, 0, vh3, 0, 4, 0x6, 0x7, 0x8, 0x9), target, storedLength); } @Test public void storeToExistingVersionIfComparedAsInt() { final long vh1 = 0x0000000000AABBCCL; final byte[] source1 = { 0xA }; final long vh2 = 0x00FFFFFF00AABBCCL; final byte[] source2 = { 0xB }; int targetLength = 0; final byte[] target = new byte[100]; targetLength = storeVersion(target, targetLength, vh1, source1, source1.length); targetLength = storeVersion(target, targetLength, vh2, source2, source2.length); assertArrayEqualsLen( newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xAA, 0xBB, 0xCC, 0, 1, 0xA, 0, 0xFF, 0xFF, 0xFF, 0, 0xAA, 0xBB, 0xCC, 0, 1, 0xB), target, targetLength); } @Test public void storeBigVersions() { final long versions[] = { 10, Short.MAX_VALUE, Integer.MAX_VALUE, 1844674407370955161L, 8301034833169298227L, Long.MAX_VALUE }; final byte contents[][] = { newArray(0xA0), newArray(0xB0, 0xB1), newArray(0xC0, 0xC1, 0xC2), newArray(0xD0, 0xD1, 0xD2, 0xD3), newArray(0xE0, 0xE1, 0xE2, 0xE3, 0xE4), newArray(0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5) }; assertEquals(versions.length, contents.length); // Build expected final byte[] expectedArray = new byte[1000]; expectedArray[0] = (byte) TYPE_MVV; Util.putLong(expectedArray, 1, 0); Util.putShort(expectedArray, 9, 0); int off = 11; for (int i = 0; i < versions.length; ++i) { Util.putLong(expectedArray, off, versions[i]); Util.putShort(expectedArray, off + 8, contents[i].length); System.arraycopy(contents[i], 0, expectedArray, off + 10, contents[i].length); off += 10 + contents[i].length; } // Build actual int targetLength = 0; final byte[] target = new byte[1000]; for (int i = 0; i < versions.length; ++i) { targetLength = storeVersion(target, targetLength, versions[i], contents[i], contents[i].length); } assertArrayEqualsLen(expectedArray, target, targetLength); } @Test(expected = IllegalArgumentException.class) public void storeToUndefinedOverCapacity() { final long vh = 10; final byte[] source = { 0xA, 0xB, 0xC }; final int neededLength = MVV.overheadLength(2) + source.length; final byte[] target = new byte[neededLength - 1]; storeVersion(target, 0, vh, source, source.length); } @Test(expected = IllegalArgumentException.class) public void storeToPrimordialOverCapacity() { final long vh = 10; final byte[] source = { 0xA, 0xB, 0xC }; final int neededLength = MVV.overheadLength(2) + source.length + 3; final byte[] target = new byte[neededLength - 1]; final int targetLength = writeArray(target, 0xD, 0xE, 0xF); storeVersion(target, targetLength, vh, source, source.length); } @Test(expected = IllegalArgumentException.class) public void storeToExistingOverCapacity() { final long vh1 = 10, vh2 = 11; final byte[] source1 = { 0xA, 0xB, 0xC }; final byte[] source2 = { 0xD, 0xE, 0xF }; final int neededLength = MVV.overheadLength(3) + source1.length + source2.length; final byte[] target = new byte[neededLength - 1]; int targetLength = 0; try { targetLength = storeVersion(target, targetLength, vh1, source1, source1.length); } catch (final IllegalArgumentException e) { Assert.fail("Expected success on first store"); } storeVersion(target, targetLength, vh2, source2, source2.length); } @Test(expected = IllegalArgumentException.class) public void storeToExistingVersionLongerLengthOverCapacity() { final long vh = 10; final byte[] source = { 0xA, 0xB, 0xC, 0xD }; final int neededLength = MVV.overheadLength(2) + source.length; final byte[] target = new byte[neededLength - 1]; int targetLength = 0; try { targetLength = storeVersion(target, targetLength, vh, source, source.length - 1); } catch (final IllegalArgumentException e) { Assert.fail("Expected success on first store"); } storeVersion(target, targetLength, vh, source, source.length); } @Test public void storeToExistingVersionAtCapacityShorterLength() { final long vh = 10; final byte[] source = { 0xA, 0xB, 0xC, 0xD }; final int neededLength = MVV.overheadLength(2) + source.length; final byte[] target = new byte[neededLength]; int targetLength = 0; targetLength = storeVersion(target, targetLength, vh, source, source.length); storeVersion(target, targetLength, vh, source, source.length - 1); } @Test public void fetchVersionFromUnused() { final long vh = 10; final byte[] source = {}; final byte[] target = {}; assertEquals(MVV.VERSION_NOT_FOUND, MVV.fetchVersion(source, -1, vh, target)); } @Test public void fetchVersionFromUndefined() { final long vh = 10; final byte[] source = {}; final byte[] target = {}; assertEquals(MVV.VERSION_NOT_FOUND, MVV.fetchVersion(source, source.length, vh, target)); } @Test public void fetchVersionFromPrimordial() { final long vh = 10; final byte[] source = { 0xA, 0xB, 0xC }; final byte[] target = {}; assertEquals(MVV.VERSION_NOT_FOUND, MVV.fetchVersion(source, source.length, vh, target)); } @Test public void fetchVersionFromExistingNoFound() { final long vh = 10; final byte[] source = { (byte) TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0xA, 0xB, 0xC, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0xD, 0xE }; final byte[] target = {}; assertEquals(MVV.VERSION_NOT_FOUND, MVV.fetchVersion(source, source.length, vh, target)); } @Test public void fetchVersionFromExisting() { final long vh = 10; final byte[] source = { (byte) TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 10, 0, 2, 0xA, 0xB, 0, 0, 0, 0, 0, 0, 0, 11, 0, 3, 0xB, 0xC }; final byte[] expected = { 0xA, 0xB }; final byte[] target = new byte[20]; final int fetchedLen = MVV.fetchVersion(source, source.length, vh, target); assertEquals(expected.length, fetchedLen); assertArrayEqualsLen(expected, target, expected.length); } @Test(expected = IllegalArgumentException.class) public void fetchVersionFromExistingOverCapacity() { final long vh = 10; final byte[] source = { (byte) TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 11, 0, 5, 0x1, 0x2, 0x3, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, 9, 0, 1, 0xA, 0, 0, 0, 0, 0, 0, 0, 10, 0, 3, 0xB, 0xC, 0xD }; final byte[] expected = { 0xB, 0xC, 0xD }; final byte[] target = new byte[expected.length - 1]; MVV.fetchVersion(source, source.length, vh, target); } @Test public void visitUnused() throws PersistitException { final byte[] source = {}; final TestVisitor visitor = new TestVisitor(); MVV.visitAllVersions(visitor, source, 0, -1); assertTrue(visitor.initCalled); assertEquals(newVisitorMap(), visitor.versions); } @Test public void visitUndefined() throws PersistitException { final byte[] source = {}; final TestVisitor visitor = new TestVisitor(); MVV.visitAllVersions(visitor, source, 0, source.length); assertTrue(visitor.initCalled); assertEquals(newVisitorMap(0, 0, 0), visitor.versions); } @Test public void visitAndFetchByOffsetPrimordial() throws PersistitException { final byte[] source = { 0xA, 0xB, 0xC }; final TestVisitor visitor = new TestVisitor(); MVV.visitAllVersions(visitor, source, 0, source.length); assertTrue(visitor.initCalled); assertEquals(newVisitorMap(0, 3, 0), visitor.versions); final byte[] target = new byte[3]; MVV.fetchVersionByOffset(source, source.length, 0, target); assertArrayEquals(source, target); } @Test public void visitAndFetchByOffsetMVV() throws PersistitException { final byte[] source = { (byte) TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0xA, 0xB, 0xC, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0xD, 0xE, 0, 0, 0, 0, 0, 0, 0, 11, 0, 5, 0x1, 0x2, 0x3, 0x4, 0x5, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 1, 0xA, 0, 1, 2, 3, 4, 5, 6, 7, 0, 3, 0xB, 0xC, 0xD, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }; final TestVisitor visitor = new TestVisitor(); MVV.visitAllVersions(visitor, source, 0, source.length); assertTrue(visitor.initCalled); assertEquals( newVisitorMap(1, 3, 11, 2, 2, 24, 11, 5, 36, 127, 0, 51, 9, 1, 61, 283686952306183L, 3, 72, 3, 0, 85), visitor.versions); for (final Map.Entry<Long, LengthAndOffset> entry : visitor.versions.entrySet()) { final int length = (int) entry.getValue().length; final int offset = (int) entry.getValue().offset; final byte[] target = new byte[length]; MVV.fetchVersionByOffset(source, source.length, offset, target); assertArrayEqualsLen(source, offset, target, length); } } @Test(expected = IllegalArgumentException.class) public void fetchByOffsetNegative() { final byte[] source = {}; final byte[] target = new byte[10]; MVV.fetchVersionByOffset(source, source.length, -1, target); } @Test(expected = IllegalArgumentException.class) public void fetchByOffsetTooLarge() { final byte[] source = newArray(TYPE_MVV, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0xA, 0xB, 0xC); final byte[] target = new byte[10]; MVV.fetchVersionByOffset(source, source.length, source.length + 1, target); } @Test public void storeAndFetchVersionMany() { final int VERSION_COUNT = 10; final int versions[] = new int[VERSION_COUNT]; final byte sources[][] = new byte[VERSION_COUNT][]; for (int i = 0; i < VERSION_COUNT; ++i) { versions[i] = (i + 1) * 5; final int length = (versions[i] % 4 + 1) * 5; sources[i] = new byte[length]; for (int j = 0; j < length; ++j) { sources[i][j] = (byte) (2 * j); } } int targetLength = 0; final byte target[] = new byte[MVV.overheadLength(VERSION_COUNT) + VERSION_COUNT * 20]; for (int i = 0; i < VERSION_COUNT; ++i) { targetLength = storeVersion(target, targetLength, versions[i], sources[i], sources[i].length); } final byte fetchtarget[] = new byte[50]; for (int i = 0; i < VERSION_COUNT; ++i) { final int fetchedLen = MVV.fetchVersion(target, targetLength, versions[i], fetchtarget); assertEquals(sources[i].length, fetchedLen); assertArrayEqualsLen(sources[i], fetchtarget, sources[i].length); } } @Test(expected = IllegalArgumentException.class) public void tryValueTooLong() { final long VERSION = 10; final int LENGTH = MVV.MAX_LENGTH_MASK + 1; final byte[] target = new byte[LENGTH + 100]; final byte[] source = new byte[LENGTH]; MVV.storeVersion(target, 0, 0, target.length, VERSION, source, 0, source.length); } // // Test helper methods // private static int writeArray(final byte[] array, final int... contents) { assert contents.length <= array.length : "Too many values for array"; for (int i = 0; i < contents.length; ++i) { final int value = contents[i]; assert value >= 0 && value <= 255 : "Value " + value + " out of byte range at index " + i; array[i] = (byte) value; } return contents.length; } private static byte[] newArray(final int... contents) { final byte[] array = new byte[contents.length]; writeArray(array, contents); return array; } private static void assertArrayEqualsLen(final byte[] expected, final byte[] actual, final int length) { assertArrayEqualsLen(expected, 0, actual, length); } private static void assertArrayEqualsLen(final byte[] expected, final int offset, final byte[] actual, final int length) { if (expected.length < length) { throw new AssertionError(String.format("Expected array is too short: %d vs %d", actual.length, length)); } if (actual.length < length) { throw new AssertionError(String.format("Actual array is too short: %d vs %d", actual.length, length)); } for (int i = 0; i < length; ++i) { final byte bE = expected[offset + i]; final byte bA = actual[i]; if (bE != bA) { throw new AssertionError(String.format("Arrays differed at element [%d]: expected <%d> but was <%d>", i, bE, bA)); } } } private static class LengthAndOffset { long length; long offset; public LengthAndOffset(final long length, final long offset) { this.length = length; this.offset = offset; } @Override public String toString() { return String.format("(%d,%d)", length, offset); } @Override public boolean equals(final Object o) { if (this == o) return true; if (!(o instanceof LengthAndOffset)) return false; final LengthAndOffset that = (LengthAndOffset) o; return length == that.length && offset == that.offset; } } private static class TestVisitor implements MVV.VersionVisitor { boolean initCalled = false; Map<Long, LengthAndOffset> versions = new TreeMap<Long, LengthAndOffset>(); @Override public void init() { initCalled = true; versions.clear(); } @Override public void sawVersion(final long version, final int offset, final int valueLength) { versions.put(version, new LengthAndOffset(valueLength, offset)); } } private static Map<Long, LengthAndOffset> newVisitorMap(final long... vals) { assertTrue("must be (version,length,offset) triplets", (vals.length % 3) == 0); final Map<Long, LengthAndOffset> outMap = new TreeMap<Long, LengthAndOffset>(); for (int i = 0; i < vals.length; i += 3) { outMap.put(vals[i], new LengthAndOffset(vals[i + 1], vals[i + 2])); } return outMap; } /** * Helper method to emulate the original MVV.storeVersion. Now the caller is * obligated to called and check the exact required length before calling * storeVersion and is likely to get an ArrayIndexOutOfBounds or other * undefined behavior if it fails to do so. */ static int storeVersion(final byte[] target, final int targetLength, final long versionHandle, final byte[] source, final int sourceLength) { return MVV.storeVersion(target, 0, targetLength, target.length, versionHandle, source, 0, sourceLength); } }