1 /*
   2  *  Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  *  This code is free software; you can redistribute it and/or modify it
   6  *  under the terms of the GNU General Public License version 2 only, as
   7  *  published by the Free Software Foundation.
   8  *
   9  *  This code is distributed in the hope that it will be useful, but WITHOUT
  10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  *  version 2 for more details (a copy is included in the LICENSE file that
  13  *  accompanied this code).
  14  *
  15  *  You should have received a copy of the GNU General Public License version
  16  *  2 along with this work; if not, write to the Free Software Foundation,
  17  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  *  Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  *  or visit www.oracle.com if you need additional information or have any
  21  *  questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @run testng TestMemoryAccess
  27  */
  28 
  29 import java.foreign.layout.Group;
  30 import java.foreign.layout.Layout;
  31 import java.foreign.layout.LayoutPath;
  32 import java.foreign.layout.Padding;
  33 import java.foreign.layout.Sequence;
  34 import java.foreign.layout.Value;
  35 import java.foreign.layout.Value.Endianness;
  36 import java.foreign.memory.MemoryAddress;
  37 import java.foreign.memory.MemoryScope;
  38 import java.lang.invoke.VarHandle;
  39 
  40 import org.testng.annotations.*;
  41 import static org.testng.Assert.*;
  42 
  43 public class TestMemoryAccess {
  44 
  45     @Test(dataProvider = "elements")
  46     public void testAccess(Layout elemLayout, Class<?> carrier, Checker checker) {
  47         Layout layout = elemLayout.withName("elem");
  48         testAccessInternal(layout, carrier, checker);
  49     }
  50 
  51     @Test(dataProvider = "elements")
  52     public void testPaddedAccess(Layout elemLayout, Class<?> carrier, Checker checker) {
  53         Layout layout = Group.struct(Padding.of(elemLayout.bitsSize()), elemLayout.withName("elem"));
  54         testAccessInternal(layout, carrier, checker);
  55     }
  56 
  57     @Test(dataProvider = "arrayElements")
  58     public void testArrayAccess(Layout elemLayout, Class<?> carrier, ArrayChecker checker) {
  59         Sequence seq = Sequence.of(10, elemLayout.withName("elem"));
  60         testArrayAccessInternal(seq, carrier, checker);
  61     }
  62 
  63     @Test(dataProvider = "arrayElements")
  64     public void testPaddedArrayAccess(Layout elemLayout, Class<?> carrier, ArrayChecker checker) {
  65         Sequence seq = Sequence.of(10, Group.struct(Padding.of(elemLayout.bitsSize()), elemLayout.withName("elem")));
  66         testArrayAccessInternal(seq, carrier, checker);
  67     }
  68 
  69     private void testAccessInternal(Layout layout, Class<?> carrier, Checker checker) {
  70         LayoutPath path = LayoutPath.of(layout).lookup("elem").findFirst().get();
  71         VarHandle handle = path.dereferenceHandle(carrier);
  72 
  73         MemoryAddress outer_address;
  74         try (MemoryScope scope = MemoryScope.globalScope().fork()) {
  75             MemoryAddress addr = scope.allocate(layout);
  76             checker.check(handle, addr);
  77             try {
  78                 checker.check(handle, addr.offset(layout.bitsSize() / 8));
  79                 throw new AssertionError(); //not ok, out of bounds
  80             } catch (IllegalStateException ex) {
  81                 //ok, should fail (out of bounds)
  82             }
  83             outer_address = addr; //leak!
  84         }
  85         try {
  86             checker.check(handle, outer_address);
  87             throw new AssertionError(); //not ok, scope is closed
  88         } catch (IllegalStateException ex) {
  89             //ok, should fail (scope is closed)
  90         }
  91     }
  92 
  93     private void testArrayAccessInternal(Sequence seq, Class<?> carrier, ArrayChecker checker) {
  94         LayoutPath path = LayoutPath.of(seq).lookup("elem").findFirst().get();
  95         VarHandle handle = path.dereferenceHandle(carrier);
  96 
  97         MemoryAddress outer_address;
  98         try (MemoryScope scope = MemoryScope.globalScope().fork()) {
  99             MemoryAddress addr = scope.allocate(seq);
 100             for (int i = 0 ; i < seq.elementsSize() ; i++) {
 101                 checker.check(handle, addr, i);
 102             }
 103             try {
 104                 checker.check(handle, addr, seq.elementsSize());
 105                 throw new AssertionError(); //not ok, out of bounds
 106             } catch (IllegalStateException ex) {
 107                 //ok, should fail (out of bounds)
 108             }
 109             outer_address = addr; //leak!
 110         }
 111         try {
 112             checker.check(handle, outer_address, 0);
 113             throw new AssertionError(); //not ok, scope is closed
 114         } catch (IllegalStateException ex) {
 115             //ok, should fail (scope is closed)
 116         }
 117     }
 118 
 119     @Test(dataProvider = "matrixElements")
 120     public void testMatrixAccess(Layout elemLayout, Class<?> carrier, MatrixChecker checker) {
 121         Sequence seq = Sequence.of(20,
 122                 Sequence.of(10, elemLayout.withName("elem")));
 123         testMatrixAccessInternal(seq, carrier, checker);
 124     }
 125 
 126     @Test(dataProvider = "matrixElements")
 127     public void testPaddedMatrixAccess(Layout elemLayout, Class<?> carrier, MatrixChecker checker) {
 128         Sequence seq = Sequence.of(20,
 129                 Sequence.of(10, Group.struct(Padding.of(elemLayout.bitsSize()), elemLayout.withName("elem"))));
 130         testMatrixAccessInternal(seq, carrier, checker);
 131     }
 132 
 133     private void testMatrixAccessInternal(Sequence seq, Class<?> carrier, MatrixChecker checker) {
 134         LayoutPath path = LayoutPath.of(seq).lookup("elem").findFirst().get();
 135         VarHandle handle = path.dereferenceHandle(carrier);
 136 
 137         MemoryAddress outer_address;
 138         try (MemoryScope scope = MemoryScope.globalScope().fork()) {
 139             MemoryAddress addr = scope.allocate(seq);
 140             for (int i = 0 ; i < seq.elementsSize() ; i++) {
 141                 for (int j = 0 ; j < ((Sequence)seq.element()).elementsSize() ; j++) {
 142                     checker.check(handle, addr, i, j);
 143                 }
 144             }
 145             try {
 146                 checker.check(handle, addr, seq.elementsSize(),
 147                         ((Sequence)seq.element()).elementsSize());
 148                 throw new AssertionError(); //not ok, out of bounds
 149             } catch (IllegalStateException ex) {
 150                 //ok, should fail (out of bounds)
 151             }
 152             outer_address = addr; //leak!
 153         }
 154         try {
 155             checker.check(handle, outer_address, 0, 0);
 156             throw new AssertionError(); //not ok, scope is closed
 157         } catch (IllegalStateException ex) {
 158             //ok, should fail (scope is closed)
 159         }
 160     }
 161 
 162     @DataProvider(name = "elements")
 163     public Object[][] createData() {
 164         return new Object[][] {
 165                 //BE
 166                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 8), byte.class, Checker.BYTE },
 167                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 8), byte.class, Checker.BYTE },
 168                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 16), short.class, Checker.SHORT },
 169                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 16), short.class, Checker.SHORT },
 170                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 16), char.class, Checker.CHAR },
 171                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 16), char.class, Checker.CHAR },
 172                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 32), int.class, Checker.INT },
 173                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 32), int.class, Checker.INT },
 174                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 64), long.class, Checker.LONG },
 175                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 64), long.class, Checker.LONG },
 176                 { Value.ofFloatingPoint(Endianness.BIG_ENDIAN, 32), float.class, Checker.FLOAT },
 177                 { Value.ofFloatingPoint(Endianness.BIG_ENDIAN, 64), double.class, Checker.DOUBLE },
 178                 //LE
 179                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 8), byte.class, Checker.BYTE },
 180                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 8), byte.class, Checker.BYTE },
 181                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 16), short.class, Checker.SHORT },
 182                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 16), short.class, Checker.SHORT },
 183                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 16), char.class, Checker.CHAR },
 184                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 16), char.class, Checker.CHAR },
 185                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 32), int.class, Checker.INT },
 186                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 32), int.class, Checker.INT },
 187                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 64), long.class, Checker.LONG },
 188                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 64), long.class, Checker.LONG },
 189                 { Value.ofFloatingPoint(Endianness.LITTLE_ENDIAN, 32), float.class, Checker.FLOAT },
 190                 { Value.ofFloatingPoint(Endianness.LITTLE_ENDIAN, 64), double.class, Checker.DOUBLE },
 191         };
 192     }
 193 
 194     interface Checker {
 195         void check(VarHandle handle, MemoryAddress addr);
 196 
 197         Checker BYTE = (handle, addr) -> {
 198             handle.set(addr, (byte)42);
 199             assertEquals(42, (byte)handle.get(addr));
 200         };
 201 
 202         Checker SHORT = (handle, addr) -> {
 203             handle.set(addr, (short)42);
 204             assertEquals(42, (short)handle.get(addr));
 205         };
 206 
 207         Checker CHAR = (handle, addr) -> {
 208             handle.set(addr, (char)42);
 209             assertEquals(42, (char)handle.get(addr));
 210         };
 211 
 212         Checker INT = (handle, addr) -> {
 213             handle.set(addr, 42);
 214             assertEquals(42, (int)handle.get(addr));
 215         };
 216 
 217         Checker LONG = (handle, addr) -> {
 218             handle.set(addr, (long)42);
 219             assertEquals(42, (long)handle.get(addr));
 220         };
 221 
 222         Checker FLOAT = (handle, addr) -> {
 223             handle.set(addr, (float)42);
 224             assertEquals((float)42, (float)handle.get(addr));
 225         };
 226 
 227         Checker DOUBLE = (handle, addr) -> {
 228             handle.set(addr, (double)42);
 229             assertEquals((double)42, (double)handle.get(addr));
 230         };
 231     }
 232 
 233     @DataProvider(name = "arrayElements")
 234     public Object[][] createArrayData() {
 235         return new Object[][] {
 236                 //BE
 237                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 8), byte.class, ArrayChecker.BYTE },
 238                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 8), byte.class, ArrayChecker.BYTE },
 239                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 16), short.class, ArrayChecker.SHORT },
 240                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 16), short.class, ArrayChecker.SHORT },
 241                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 16), char.class, ArrayChecker.CHAR },
 242                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 16), char.class, ArrayChecker.CHAR },
 243                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 32), int.class, ArrayChecker.INT },
 244                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 32), int.class, ArrayChecker.INT },
 245                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 64), long.class, ArrayChecker.LONG },
 246                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 64), long.class, ArrayChecker.LONG },
 247                 { Value.ofFloatingPoint(Endianness.BIG_ENDIAN, 32), float.class, ArrayChecker.FLOAT },
 248                 { Value.ofFloatingPoint(Endianness.BIG_ENDIAN, 64), double.class, ArrayChecker.DOUBLE },
 249                 //LE
 250                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 8), byte.class, ArrayChecker.BYTE },
 251                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 8), byte.class, ArrayChecker.BYTE },
 252                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 16), short.class, ArrayChecker.SHORT },
 253                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 16), short.class, ArrayChecker.SHORT },
 254                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 16), char.class, ArrayChecker.CHAR },
 255                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 16), char.class, ArrayChecker.CHAR },
 256                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 32), int.class, ArrayChecker.INT },
 257                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 32), int.class, ArrayChecker.INT },
 258                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 64), long.class, ArrayChecker.LONG },
 259                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 64), long.class, ArrayChecker.LONG },
 260                 { Value.ofFloatingPoint(Endianness.LITTLE_ENDIAN, 32), float.class, ArrayChecker.FLOAT },
 261                 { Value.ofFloatingPoint(Endianness.LITTLE_ENDIAN, 64), double.class, ArrayChecker.DOUBLE },
 262         };
 263     }
 264 
 265     interface ArrayChecker {
 266         void check(VarHandle handle, MemoryAddress addr, long index);
 267 
 268         ArrayChecker BYTE = (handle, addr, i) -> {
 269             handle.set(addr, i, (byte)i);
 270             assertEquals(i, (byte)handle.get(addr, i));
 271         };
 272 
 273         ArrayChecker SHORT = (handle, addr, i) -> {
 274             handle.set(addr, i, (short)i);
 275             assertEquals(i, (short)handle.get(addr, i));
 276         };
 277 
 278         ArrayChecker CHAR = (handle, addr, i) -> {
 279             handle.set(addr, i, (char)i);
 280             assertEquals(i, (char)handle.get(addr, i));
 281         };
 282 
 283         ArrayChecker INT = (handle, addr, i) -> {
 284             handle.set(addr, i, (int)i);
 285             assertEquals(i, (int)handle.get(addr, i));
 286         };
 287 
 288         ArrayChecker LONG = (handle, addr, i) -> {
 289             handle.set(addr, i, (long)i);
 290             assertEquals(i, (long)handle.get(addr, i));
 291         };
 292 
 293         ArrayChecker FLOAT = (handle, addr, i) -> {
 294             handle.set(addr, i, (float)i);
 295             assertEquals((float)i, (float)handle.get(addr, i));
 296         };
 297 
 298         ArrayChecker DOUBLE = (handle, addr, i) -> {
 299             handle.set(addr, i, (double)i);
 300             assertEquals((double)i, (double)handle.get(addr, i));
 301         };
 302     }
 303 
 304     @DataProvider(name = "matrixElements")
 305     public Object[][] createMatrixData() {
 306         return new Object[][] {
 307                 //BE
 308                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 8), byte.class, MatrixChecker.BYTE },
 309                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 8), byte.class, MatrixChecker.BYTE },
 310                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 16), short.class, MatrixChecker.SHORT },
 311                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 16), short.class, MatrixChecker.SHORT },
 312                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 16), char.class, MatrixChecker.CHAR },
 313                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 16), char.class, MatrixChecker.CHAR },
 314                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 32), int.class, MatrixChecker.INT },
 315                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 32), int.class, MatrixChecker.INT },
 316                 { Value.ofUnsignedInt(Endianness.BIG_ENDIAN, 64), long.class, MatrixChecker.LONG },
 317                 { Value.ofSignedInt(Endianness.BIG_ENDIAN, 64), long.class, MatrixChecker.LONG },
 318                 { Value.ofFloatingPoint(Endianness.BIG_ENDIAN, 32), float.class, MatrixChecker.FLOAT },
 319                 { Value.ofFloatingPoint(Endianness.BIG_ENDIAN, 64), double.class, MatrixChecker.DOUBLE },
 320                 //LE
 321                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 8), byte.class, MatrixChecker.BYTE },
 322                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 8), byte.class, MatrixChecker.BYTE },
 323                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 16), short.class, MatrixChecker.SHORT },
 324                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 16), short.class, MatrixChecker.SHORT },
 325                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 16), char.class, MatrixChecker.CHAR },
 326                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 16), char.class, MatrixChecker.CHAR },
 327                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 32), int.class, MatrixChecker.INT },
 328                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 32), int.class, MatrixChecker.INT },
 329                 { Value.ofUnsignedInt(Endianness.LITTLE_ENDIAN, 64), long.class, MatrixChecker.LONG },
 330                 { Value.ofSignedInt(Endianness.LITTLE_ENDIAN, 64), long.class, MatrixChecker.LONG },
 331                 { Value.ofFloatingPoint(Endianness.LITTLE_ENDIAN, 32), float.class, MatrixChecker.FLOAT },
 332                 { Value.ofFloatingPoint(Endianness.LITTLE_ENDIAN, 64), double.class, MatrixChecker.DOUBLE },
 333         };
 334     }
 335 
 336     interface MatrixChecker {
 337         void check(VarHandle handle, MemoryAddress addr, long row, long col);
 338 
 339         MatrixChecker BYTE = (handle, addr, r, c) -> {
 340             handle.set(addr, r, c, (byte)(r + c));
 341             assertEquals(r + c, (byte)handle.get(addr, r, c));
 342         };
 343 
 344         MatrixChecker SHORT = (handle, addr, r, c) -> {
 345             handle.set(addr, r, c, (short)(r + c));
 346             assertEquals(r + c, (short)handle.get(addr, r, c));
 347         };
 348 
 349         MatrixChecker CHAR = (handle, addr, r, c) -> {
 350             handle.set(addr, r, c, (char)(r + c));
 351             assertEquals(r + c, (char)handle.get(addr, r, c));
 352         };
 353 
 354         MatrixChecker INT = (handle, addr, r, c) -> {
 355             handle.set(addr, r, c, (int)(r + c));
 356             assertEquals(r + c, (int)handle.get(addr, r, c));
 357         };
 358 
 359         MatrixChecker LONG = (handle, addr, r, c) -> {
 360             handle.set(addr, r, c, r + c);
 361             assertEquals(r + c, (long)handle.get(addr, r, c));
 362         };
 363 
 364         MatrixChecker FLOAT = (handle, addr, r, c) -> {
 365             handle.set(addr, r, c, (float)(r + c));
 366             assertEquals((float)(r + c), (float)handle.get(addr, r, c));
 367         };
 368 
 369         MatrixChecker DOUBLE = (handle, addr, r, c) -> {
 370             handle.set(addr, r, c, (double)(r + c));
 371             assertEquals((double)(r + c), (double)handle.get(addr, r, c));
 372         };
 373     }
 374 }