| /* | |
| * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| import org.testng.Assert; | |
| import org.testng.annotations.DataProvider; | |
| import org.testng.annotations.Test; | |
| import java.io.IOException; | |
| import java.lang.invoke.MethodHandle; | |
| import java.lang.invoke.MethodHandles; | |
| import java.lang.invoke.MethodType; | |
| import java.nio.Buffer; | |
| import java.nio.ByteBuffer; | |
| import java.nio.ByteOrder; | |
| import java.nio.CharBuffer; | |
| import java.nio.DoubleBuffer; | |
| import java.nio.FloatBuffer; | |
| import java.nio.IntBuffer; | |
| import java.nio.LongBuffer; | |
| import java.nio.MappedByteBuffer; | |
| import java.nio.ShortBuffer; | |
| import java.nio.channels.FileChannel; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| import java.util.Set; | |
| import java.util.function.BiFunction; | |
| import java.util.function.LongFunction; | |
| import java.util.stream.IntStream; | |
| import static java.nio.charset.StandardCharsets.UTF_8; | |
| import static java.nio.file.StandardOpenOption.*; | |
| /* | |
| * @test | |
| * @bug 8193085 8199773 | |
| * @summary tests for buffer equals and compare | |
| * @run testng EqualsCompareTest | |
| */ | |
| public class EqualsCompareTest { | |
| // Maximum width in bits | |
| static final int MAX_WIDTH = 512; | |
| static final Map<Class, Integer> typeToWidth; | |
| static { | |
| typeToWidth = new HashMap<>(); | |
| typeToWidth.put(byte.class, Byte.SIZE); | |
| typeToWidth.put(short.class, Short.SIZE); | |
| typeToWidth.put(char.class, Character.SIZE); | |
| typeToWidth.put(int.class, Integer.SIZE); | |
| typeToWidth.put(long.class, Long.SIZE); | |
| typeToWidth.put(float.class, Float.SIZE); | |
| typeToWidth.put(double.class, Double.SIZE); | |
| } | |
| static int arraySizeFor(Class<?> type) { | |
| assert type.isPrimitive(); | |
| return 4 * MAX_WIDTH / typeToWidth.get(type); | |
| } | |
| enum BufferKind { | |
| HEAP, | |
| HEAP_VIEW, | |
| DIRECT; | |
| } | |
| static abstract class BufferType<T extends Buffer, E> { | |
| final BufferKind k; | |
| final Class<?> bufferType; | |
| final Class<?> elementType; | |
| final MethodHandle eq; | |
| final MethodHandle cmp; | |
| final MethodHandle mismtch; | |
| final MethodHandle getter; | |
| final MethodHandle setter; | |
| BufferType(BufferKind k, Class<T> bufferType, Class<?> elementType) { | |
| this.k = k; | |
| this.bufferType = bufferType; | |
| this.elementType = elementType; | |
| var lookup = MethodHandles.lookup(); | |
| try { | |
| eq = lookup.findVirtual(bufferType, "equals", MethodType.methodType(boolean.class, Object.class)); | |
| cmp = lookup.findVirtual(bufferType, "compareTo", MethodType.methodType(int.class, bufferType)); | |
| mismtch = lookup.findVirtual(bufferType, "mismatch", MethodType.methodType(int.class, bufferType)); | |
| getter = lookup.findVirtual(bufferType, "get", MethodType.methodType(elementType, int.class)); | |
| setter = lookup.findVirtual(bufferType, "put", MethodType.methodType(bufferType, int.class, elementType)); | |
| } | |
| catch (Exception e) { | |
| throw new AssertionError(e); | |
| } | |
| } | |
| @Override | |
| public String toString() { | |
| return bufferType.getName() + " " + k; | |
| } | |
| T construct(int length) { | |
| return construct(length, ByteOrder.BIG_ENDIAN); | |
| } | |
| abstract T construct(int length, ByteOrder bo); | |
| @SuppressWarnings("unchecked") | |
| T slice(T a, int from, int to, boolean dupOtherwiseSlice) { | |
| a = (T) a.position(from).limit(to); | |
| return (T) (dupOtherwiseSlice ? a.duplicate() : a.slice()); | |
| } | |
| @SuppressWarnings("unchecked") | |
| E get(T a, int i) { | |
| try { | |
| return (E) getter.invoke(a, i); | |
| } | |
| catch (RuntimeException | Error e) { | |
| throw e; | |
| } | |
| catch (Throwable t) { | |
| throw new Error(t); | |
| } | |
| } | |
| void set(T a, int i, Object v) { | |
| try { | |
| setter.invoke(a, i, convert(v)); | |
| } | |
| catch (RuntimeException | Error e) { | |
| throw e; | |
| } | |
| catch (Throwable t) { | |
| throw new Error(t); | |
| } | |
| } | |
| abstract Object convert(Object o); | |
| boolean equals(T a, T b) { | |
| try { | |
| return (boolean) eq.invoke(a, b); | |
| } | |
| catch (RuntimeException | Error e) { | |
| throw e; | |
| } | |
| catch (Throwable t) { | |
| throw new Error(t); | |
| } | |
| } | |
| int compare(T a, T b) { | |
| try { | |
| return (int) cmp.invoke(a, b); | |
| } | |
| catch (RuntimeException | Error e) { | |
| throw e; | |
| } | |
| catch (Throwable t) { | |
| throw new Error(t); | |
| } | |
| } | |
| boolean pairWiseEquals(T a, T b) { | |
| if (a.remaining() != b.remaining()) | |
| return false; | |
| int p = a.position(); | |
| for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) | |
| if (!get(a, i).equals(get(b, j))) | |
| return false; | |
| return true; | |
| } | |
| int mismatch(T a, T b) { | |
| try { | |
| return (int) mismtch.invoke(a, b); | |
| } | |
| catch (RuntimeException | Error e) { | |
| throw e; | |
| } | |
| catch (Throwable t) { | |
| throw new Error(t); | |
| } | |
| } | |
| static class Bytes extends BufferType<ByteBuffer, Byte> { | |
| Bytes(BufferKind k) { | |
| super(k, ByteBuffer.class, byte.class); | |
| } | |
| @Override | |
| ByteBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length).order(bo); | |
| default: | |
| case HEAP_VIEW: | |
| case HEAP: | |
| return ByteBuffer.allocate(length).order(bo); | |
| } | |
| } | |
| @Override | |
| Object convert(Object o) { | |
| return o instanceof Integer | |
| ? ((Integer) o).byteValue() | |
| : o; | |
| } | |
| } | |
| static class Chars extends BufferType<CharBuffer, Character> { | |
| Chars(BufferKind k) { | |
| super(k, CharBuffer.class, char.class); | |
| } | |
| @Override | |
| CharBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length * Character.BYTES). | |
| order(bo). | |
| asCharBuffer(); | |
| case HEAP_VIEW: | |
| return ByteBuffer.allocate(length * Character.BYTES). | |
| order(bo). | |
| asCharBuffer(); | |
| default: | |
| case HEAP: | |
| return CharBuffer.allocate(length); | |
| } | |
| } | |
| @Override | |
| Object convert(Object o) { | |
| return o instanceof Integer | |
| ? (char) ((Integer) o).intValue() | |
| : o; | |
| } | |
| CharBuffer transformToStringBuffer(CharBuffer c) { | |
| char[] chars = new char[c.remaining()]; | |
| c.get(chars); | |
| return CharBuffer.wrap(new String(chars)); | |
| } | |
| } | |
| static class Shorts extends BufferType<ShortBuffer, Short> { | |
| Shorts(BufferKind k) { | |
| super(k, ShortBuffer.class, short.class); | |
| } | |
| @Override | |
| ShortBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length * Short.BYTES). | |
| order(bo). | |
| asShortBuffer(); | |
| case HEAP_VIEW: | |
| return ByteBuffer.allocate(length * Short.BYTES). | |
| order(bo). | |
| asShortBuffer(); | |
| default: | |
| case HEAP: | |
| return ShortBuffer.allocate(length); | |
| } | |
| } | |
| @Override | |
| Object convert(Object o) { | |
| return o instanceof Integer | |
| ? ((Integer) o).shortValue() | |
| : o; | |
| } | |
| } | |
| static class Ints extends BufferType<IntBuffer, Integer> { | |
| Ints(BufferKind k) { | |
| super(k, IntBuffer.class, int.class); | |
| } | |
| @Override | |
| IntBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length * Integer.BYTES). | |
| order(bo). | |
| asIntBuffer(); | |
| case HEAP_VIEW: | |
| return ByteBuffer.allocate(length * Integer.BYTES). | |
| order(bo). | |
| asIntBuffer(); | |
| default: | |
| case HEAP: | |
| return IntBuffer.allocate(length); | |
| } | |
| } | |
| Object convert(Object o) { | |
| return o; | |
| } | |
| } | |
| static class Floats extends BufferType<FloatBuffer, Float> { | |
| Floats(BufferKind k) { | |
| super(k, FloatBuffer.class, float.class); | |
| } | |
| @Override | |
| FloatBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length * Float.BYTES). | |
| order(bo). | |
| asFloatBuffer(); | |
| case HEAP_VIEW: | |
| return ByteBuffer.allocate(length * Float.BYTES). | |
| order(bo). | |
| asFloatBuffer(); | |
| default: | |
| case HEAP: | |
| return FloatBuffer.allocate(length); | |
| } | |
| } | |
| @Override | |
| Object convert(Object o) { | |
| return o instanceof Integer | |
| ? ((Integer) o).floatValue() | |
| : o; | |
| } | |
| @Override | |
| boolean pairWiseEquals(FloatBuffer a, FloatBuffer b) { | |
| if (a.remaining() != b.remaining()) | |
| return false; | |
| int p = a.position(); | |
| for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) { | |
| float av = a.get(i); | |
| float bv = b.get(j); | |
| if (av != bv && (!Float.isNaN(av) || !Float.isNaN(bv))) | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| static class Longs extends BufferType<LongBuffer, Long> { | |
| Longs(BufferKind k) { | |
| super(k, LongBuffer.class, long.class); | |
| } | |
| @Override | |
| LongBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length * Long.BYTES). | |
| order(bo). | |
| asLongBuffer(); | |
| case HEAP_VIEW: | |
| return ByteBuffer.allocate(length * Long.BYTES). | |
| order(bo). | |
| asLongBuffer(); | |
| default: | |
| case HEAP: | |
| return LongBuffer.allocate(length); | |
| } | |
| } | |
| @Override | |
| Object convert(Object o) { | |
| return o instanceof Integer | |
| ? ((Integer) o).longValue() | |
| : o; | |
| } | |
| } | |
| static class Doubles extends BufferType<DoubleBuffer, Double> { | |
| Doubles(BufferKind k) { | |
| super(k, DoubleBuffer.class, double.class); | |
| } | |
| @Override | |
| DoubleBuffer construct(int length, ByteOrder bo) { | |
| switch (k) { | |
| case DIRECT: | |
| return ByteBuffer.allocateDirect(length * Double.BYTES). | |
| order(bo). | |
| asDoubleBuffer(); | |
| case HEAP_VIEW: | |
| return ByteBuffer.allocate(length * Double.BYTES). | |
| order(bo). | |
| asDoubleBuffer(); | |
| default: | |
| case HEAP: | |
| return DoubleBuffer.allocate(length); | |
| } | |
| } | |
| @Override | |
| Object convert(Object o) { | |
| return o instanceof Integer | |
| ? ((Integer) o).doubleValue() | |
| : o; | |
| } | |
| @Override | |
| boolean pairWiseEquals(DoubleBuffer a, DoubleBuffer b) { | |
| if (a.remaining() != b.remaining()) | |
| return false; | |
| int p = a.position(); | |
| for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) { | |
| double av = a.get(i); | |
| double bv = b.get(j); | |
| if (av != bv && (!Double.isNaN(av) || !Double.isNaN(bv))) | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| } | |
| static Object[][] bufferTypes; | |
| @DataProvider | |
| public static Object[][] bufferTypesProvider() { | |
| if (bufferTypes == null) { | |
| bufferTypes = new Object[][]{ | |
| {new BufferType.Bytes(BufferKind.HEAP)}, | |
| {new BufferType.Bytes(BufferKind.DIRECT)}, | |
| {new BufferType.Chars(BufferKind.HEAP)}, | |
| {new BufferType.Chars(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Chars(BufferKind.DIRECT)}, | |
| {new BufferType.Shorts(BufferKind.HEAP)}, | |
| {new BufferType.Shorts(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Shorts(BufferKind.DIRECT)}, | |
| {new BufferType.Ints(BufferKind.HEAP)}, | |
| {new BufferType.Ints(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Ints(BufferKind.DIRECT)}, | |
| {new BufferType.Floats(BufferKind.HEAP)}, | |
| {new BufferType.Floats(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Floats(BufferKind.DIRECT)}, | |
| {new BufferType.Longs(BufferKind.HEAP)}, | |
| {new BufferType.Longs(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Longs(BufferKind.DIRECT)}, | |
| {new BufferType.Doubles(BufferKind.HEAP)}, | |
| {new BufferType.Doubles(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Doubles(BufferKind.DIRECT)}, | |
| }; | |
| } | |
| return bufferTypes; | |
| } | |
| static Object[][] floatbufferTypes; | |
| @DataProvider | |
| public static Object[][] floatBufferTypesProvider() { | |
| if (floatbufferTypes == null) { | |
| LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb); | |
| LongFunction<Object> bToD = Double::longBitsToDouble; | |
| floatbufferTypes = new Object[][]{ | |
| // canonical and non-canonical NaNs | |
| // If conversion is a signalling NaN it may be subject to conversion to a | |
| // quiet NaN on some processors, even if a copy is performed | |
| // The tests assume that if conversion occurs it does not convert to the | |
| // canonical NaN | |
| new Object[]{new BufferType.Floats(BufferKind.HEAP), 0x7fc00000L, 0x7f800001L, bTof}, | |
| new Object[]{new BufferType.Floats(BufferKind.HEAP_VIEW), 0x7fc00000L, 0x7f800001L, bTof}, | |
| new Object[]{new BufferType.Floats(BufferKind.DIRECT), 0x7fc00000L, 0x7f800001L, bTof}, | |
| new Object[]{new BufferType.Doubles(BufferKind.HEAP), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, | |
| new Object[]{new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, | |
| new Object[]{new BufferType.Doubles(BufferKind.DIRECT), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD}, | |
| // +0.0 and -0.0 | |
| new Object[]{new BufferType.Floats(BufferKind.HEAP), 0x0L, 0x80000000L, bTof}, | |
| new Object[]{new BufferType.Floats(BufferKind.HEAP_VIEW), 0x0L, 0x80000000L, bTof}, | |
| new Object[]{new BufferType.Floats(BufferKind.DIRECT), 0x0L, 0x80000000L, bTof}, | |
| new Object[]{new BufferType.Doubles(BufferKind.HEAP), 0x0L, 0x8000000000000000L, bToD}, | |
| new Object[]{new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x0L, 0x8000000000000000L, bToD}, | |
| new Object[]{new BufferType.Doubles(BufferKind.DIRECT), 0x0L, 0x8000000000000000L, bToD}, | |
| }; | |
| } | |
| return floatbufferTypes; | |
| } | |
| static Object[][] charBufferTypes; | |
| @DataProvider | |
| public static Object[][] charBufferTypesProvider() { | |
| if (charBufferTypes == null) { | |
| charBufferTypes = new Object[][]{ | |
| {new BufferType.Chars(BufferKind.HEAP)}, | |
| {new BufferType.Chars(BufferKind.HEAP_VIEW)}, | |
| {new BufferType.Chars(BufferKind.DIRECT)}, | |
| }; | |
| } | |
| return charBufferTypes; | |
| } | |
| // Tests all primitive buffers | |
| @Test(dataProvider = "bufferTypesProvider") | |
| <E> | |
| void testBuffers(BufferType<Buffer, E> bufferType) { | |
| // Test with buffers of the same byte order (BE) | |
| BiFunction<BufferType<Buffer, E>, Integer, Buffer> constructor = (at, s) -> { | |
| Buffer a = at.construct(s); | |
| for (int x = 0; x < s; x++) { | |
| at.set(a, x, x % 8); | |
| } | |
| return a; | |
| }; | |
| testBufferType(bufferType, constructor, constructor); | |
| // Test with buffers of different byte order | |
| if (bufferType.elementType != byte.class && | |
| (bufferType.k == BufferKind.HEAP_VIEW || | |
| bufferType.k == BufferKind.DIRECT)) { | |
| BiFunction<BufferType<Buffer, E>, Integer, Buffer> leConstructor = (at, s) -> { | |
| Buffer a = at.construct(s, ByteOrder.LITTLE_ENDIAN); | |
| for (int x = 0; x < s; x++) { | |
| at.set(a, x, x % 8); | |
| } | |
| return a; | |
| }; | |
| testBufferType(bufferType, constructor, leConstructor); | |
| } | |
| } | |
| // Tests float and double buffers with edge-case values (NaN, -0.0, +0.0) | |
| @Test(dataProvider = "floatBufferTypesProvider") | |
| public void testFloatBuffers( | |
| BufferType<Buffer, Float> bufferType, | |
| long rawBitsA, long rawBitsB, | |
| LongFunction<Object> bitsToFloat) { | |
| Object av = bitsToFloat.apply(rawBitsA); | |
| Object bv = bitsToFloat.apply(rawBitsB); | |
| BiFunction<BufferType<Buffer, Float>, Integer, Buffer> allAs = (at, s) -> { | |
| Buffer b = at.construct(s); | |
| for (int x = 0; x < s; x++) { | |
| at.set(b, x, av); | |
| } | |
| return b; | |
| }; | |
| BiFunction<BufferType<Buffer, Float>, Integer, Buffer> allBs = (at, s) -> { | |
| Buffer b = at.construct(s); | |
| for (int x = 0; x < s; x++) { | |
| at.set(b, x, bv); | |
| } | |
| return b; | |
| }; | |
| BiFunction<BufferType<Buffer, Float>, Integer, Buffer> halfBs = (at, s) -> { | |
| Buffer b = at.construct(s); | |
| for (int x = 0; x < s / 2; x++) { | |
| at.set(b, x, bv); | |
| } | |
| for (int x = s / 2; x < s; x++) { | |
| at.set(b, x, 1); | |
| } | |
| return b; | |
| }; | |
| // Sanity check | |
| int size = arraySizeFor(bufferType.elementType); | |
| Assert.assertTrue(bufferType.pairWiseEquals(allAs.apply(bufferType, size), | |
| allBs.apply(bufferType, size))); | |
| Assert.assertTrue(bufferType.equals(allAs.apply(bufferType, size), | |
| allBs.apply(bufferType, size))); | |
| testBufferType(bufferType, allAs, allBs); | |
| testBufferType(bufferType, allAs, halfBs); | |
| } | |
| // Tests CharBuffer for region sources and CharSequence sources | |
| @Test(dataProvider = "charBufferTypesProvider") | |
| public void testCharBuffers(BufferType.Chars charBufferType) { | |
| BiFunction<BufferType.Chars, Integer, CharBuffer> constructor = (at, s) -> { | |
| CharBuffer a = at.construct(s); | |
| for (int x = 0; x < s; x++) { | |
| at.set(a, x, x % 8); | |
| } | |
| return a; | |
| }; | |
| BiFunction<BufferType.Chars, Integer, CharBuffer> constructorX = constructor. | |
| andThen(charBufferType::transformToStringBuffer); | |
| testBufferType(charBufferType, constructor, constructorX); | |
| } | |
| <B extends Buffer, E, BT extends BufferType<B, E>> | |
| void testBufferType(BT bt, | |
| BiFunction<BT, Integer, B> aConstructor, | |
| BiFunction<BT, Integer, B> bConstructor) { | |
| int n = arraySizeFor(bt.elementType); | |
| for (boolean dupOtherwiseSlice : new boolean[]{ false, true }) { | |
| for (int s : ranges(0, n)) { | |
| B a = aConstructor.apply(bt, s); | |
| B b = bConstructor.apply(bt, s); | |
| for (int aFrom : ranges(0, s)) { | |
| for (int aTo : ranges(aFrom, s)) { | |
| int aLength = aTo - aFrom; | |
| B as = aLength != s | |
| ? bt.slice(a, aFrom, aTo, dupOtherwiseSlice) | |
| : a; | |
| for (int bFrom : ranges(0, s)) { | |
| for (int bTo : ranges(bFrom, s)) { | |
| int bLength = bTo - bFrom; | |
| B bs = bLength != s | |
| ? bt.slice(b, bFrom, bTo, dupOtherwiseSlice) | |
| : b; | |
| boolean eq = bt.pairWiseEquals(as, bs); | |
| Assert.assertEquals(bt.equals(as, bs), eq); | |
| Assert.assertEquals(bt.equals(bs, as), eq); | |
| if (eq) { | |
| Assert.assertEquals(bt.compare(as, bs), 0); | |
| Assert.assertEquals(bt.compare(bs, as), 0); | |
| // If buffers are equal, there shall be no mismatch | |
| Assert.assertEquals(bt.mismatch(as, bs), -1); | |
| Assert.assertEquals(bt.mismatch(bs, as), -1); | |
| } | |
| else { | |
| int aCb = bt.compare(as, bs); | |
| int bCa = bt.compare(bs, as); | |
| int v = Integer.signum(aCb) * Integer.signum(bCa); | |
| Assert.assertTrue(v == -1); | |
| int aMs = bt.mismatch(as, bs); | |
| int bMs = bt.mismatch(bs, as); | |
| Assert.assertNotEquals(aMs, -1); | |
| Assert.assertEquals(aMs, bMs); | |
| } | |
| } | |
| } | |
| if (aLength > 0 && !a.isReadOnly()) { | |
| for (int i = aFrom; i < aTo; i++) { | |
| B c = aConstructor.apply(bt, a.capacity()); | |
| B cs = aLength != s | |
| ? bt.slice(c, aFrom, aTo, dupOtherwiseSlice) | |
| : c; | |
| // Create common prefix with a length of i - aFrom | |
| bt.set(c, i, -1); | |
| Assert.assertFalse(bt.equals(c, a)); | |
| int cCa = bt.compare(cs, as); | |
| int aCc = bt.compare(as, cs); | |
| int v = Integer.signum(cCa) * Integer.signum(aCc); | |
| Assert.assertTrue(v == -1); | |
| int cMa = bt.mismatch(cs, as); | |
| int aMc = bt.mismatch(as, cs); | |
| Assert.assertEquals(cMa, aMc); | |
| Assert.assertEquals(cMa, i - aFrom); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| static int[] ranges(int from, int to) { | |
| int width = to - from; | |
| switch (width) { | |
| case 0: | |
| return new int[]{}; | |
| case 1: | |
| return new int[]{from, to}; | |
| case 2: | |
| return new int[]{from, from + 1, to}; | |
| case 3: | |
| return new int[]{from, from + 1, from + 2, to}; | |
| default: | |
| return IntStream.of(from, from + 1, from + 2, to / 2 - 1, to / 2, to / 2 + 1, to - 2, to - 1, to) | |
| .filter(i -> i >= from && i <= to) | |
| .distinct().toArray(); | |
| } | |
| } | |
| @Test | |
| void testHashCode() throws IOException { | |
| byte[] bytes = "hello world".getBytes(UTF_8); | |
| Path path = Files.createTempFile("", ""); | |
| Files.write(path, bytes); | |
| try (FileChannel fc = FileChannel.open(path, READ, DELETE_ON_CLOSE)) { | |
| MappedByteBuffer one = fc.map(FileChannel.MapMode.READ_ONLY, 0, bytes.length); | |
| ByteBuffer two = ByteBuffer.wrap(bytes); | |
| Assert.assertEquals(one, two); | |
| Assert.assertEquals(one.hashCode(), two.hashCode()); | |
| } | |
| } | |
| } |