1 /*
   2  * Copyright (c) 2006, 2013, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider;
  27 
  28 import static java.lang.Integer.reverseBytes;
  29 import static java.lang.Long.reverseBytes;
  30 
  31 import java.nio.ByteOrder;
  32 
  33 import sun.misc.Unsafe;
  34 
  35 /**
  36  * Optimized methods for converting between byte[] and int[]/long[], both for
  37  * big endian and little endian byte orders.
  38  *
  39  * Currently, it includes a default code path plus two optimized code paths.
  40  * One is for little endian architectures that support full speed int/long
  41  * access at unaligned addresses (i.e. x86/amd64). The second is for big endian
  42  * architectures (that only support correctly aligned access), such as SPARC.
  43  * These are the only platforms we currently support, but other optimized
  44  * variants could be added as needed.
  45  *
  46  * NOTE that ArrayIndexOutOfBoundsException will be thrown if the bounds checks
  47  * failed.
  48  *
  49  * This class may also be helpful in improving the performance of the
  50  * crypto code in the SunJCE provider. However, for now it is only accessible by
  51  * the message digest implementation in the SUN provider.
  52  *
  53  * @since   1.6
  54  * @author  Andreas Sterbenz
  55  */
  56 final class ByteArrayAccess {
  57 
  58     private ByteArrayAccess() {
  59         // empty
  60     }
  61 
  62     private static final Unsafe unsafe = Unsafe.getUnsafe();
  63 
  64     // whether to use the optimized path for little endian platforms that
  65     // support full speed unaligned memory access.
  66     private static final boolean littleEndianUnaligned;
  67 
  68     // whether to use the optimzied path for big endian platforms that
  69     // support only correctly aligned full speed memory access.
  70     // (Note that on SPARC unaligned memory access is possible, but it is
  71     // implemented using a software trap and therefore very slow)
  72     private static final boolean bigEndian;
  73 
  74     private final static int byteArrayOfs = unsafe.arrayBaseOffset(byte[].class);
  75 
  76     static {
  77         boolean scaleOK = ((unsafe.arrayIndexScale(byte[].class) == 1)
  78             && (unsafe.arrayIndexScale(int[].class) == 4)
  79             && (unsafe.arrayIndexScale(long[].class) == 8)
  80             && ((byteArrayOfs & 3) == 0));
  81 
  82         ByteOrder byteOrder = ByteOrder.nativeOrder();
  83         littleEndianUnaligned =
  84             scaleOK && unaligned() && (byteOrder == ByteOrder.LITTLE_ENDIAN);
  85         bigEndian =
  86             scaleOK && (byteOrder == ByteOrder.BIG_ENDIAN);
  87     }
  88 
  89     // Return whether this platform supports full speed int/long memory access
  90     // at unaligned addresses.
  91     private static boolean unaligned() {
  92         return unsafe.unalignedAccess();
  93     }
  94 
  95     /**
  96      * byte[] to int[] conversion, little endian byte order.
  97      */
  98     static void b2iLittle(byte[] in, int inOfs, int[] out, int outOfs, int len) {
  99         if ((inOfs < 0) || ((in.length - inOfs) < len) ||
 100             (outOfs < 0) || ((out.length - outOfs) < len/4)) {
 101             throw new ArrayIndexOutOfBoundsException();
 102         }
 103         if (littleEndianUnaligned) {
 104             inOfs += byteArrayOfs;
 105             len += inOfs;
 106             while (inOfs < len) {
 107                 out[outOfs++] = unsafe.getInt(in, (long)inOfs);
 108                 inOfs += 4;
 109             }
 110         } else if (bigEndian && ((inOfs & 3) == 0)) {
 111             inOfs += byteArrayOfs;
 112             len += inOfs;
 113             while (inOfs < len) {
 114                 out[outOfs++] = reverseBytes(unsafe.getInt(in, (long)inOfs));
 115                 inOfs += 4;
 116             }
 117         } else {
 118             len += inOfs;
 119             while (inOfs < len) {
 120                 out[outOfs++] = ((in[inOfs    ] & 0xff)      )
 121                               | ((in[inOfs + 1] & 0xff) <<  8)
 122                               | ((in[inOfs + 2] & 0xff) << 16)
 123                               | ((in[inOfs + 3]       ) << 24);
 124                 inOfs += 4;
 125             }
 126         }
 127     }
 128 
 129     // Special optimization of b2iLittle(in, inOfs, out, 0, 64)
 130     static void b2iLittle64(byte[] in, int inOfs, int[] out) {
 131         if ((inOfs < 0) || ((in.length - inOfs) < 64) ||
 132             (out.length < 16)) {
 133             throw new ArrayIndexOutOfBoundsException();
 134         }
 135         if (littleEndianUnaligned) {
 136             inOfs += byteArrayOfs;
 137             out[ 0] = unsafe.getInt(in, (long)(inOfs     ));
 138             out[ 1] = unsafe.getInt(in, (long)(inOfs +  4));
 139             out[ 2] = unsafe.getInt(in, (long)(inOfs +  8));
 140             out[ 3] = unsafe.getInt(in, (long)(inOfs + 12));
 141             out[ 4] = unsafe.getInt(in, (long)(inOfs + 16));
 142             out[ 5] = unsafe.getInt(in, (long)(inOfs + 20));
 143             out[ 6] = unsafe.getInt(in, (long)(inOfs + 24));
 144             out[ 7] = unsafe.getInt(in, (long)(inOfs + 28));
 145             out[ 8] = unsafe.getInt(in, (long)(inOfs + 32));
 146             out[ 9] = unsafe.getInt(in, (long)(inOfs + 36));
 147             out[10] = unsafe.getInt(in, (long)(inOfs + 40));
 148             out[11] = unsafe.getInt(in, (long)(inOfs + 44));
 149             out[12] = unsafe.getInt(in, (long)(inOfs + 48));
 150             out[13] = unsafe.getInt(in, (long)(inOfs + 52));
 151             out[14] = unsafe.getInt(in, (long)(inOfs + 56));
 152             out[15] = unsafe.getInt(in, (long)(inOfs + 60));
 153         } else if (bigEndian && ((inOfs & 3) == 0)) {
 154             inOfs += byteArrayOfs;
 155             out[ 0] = reverseBytes(unsafe.getInt(in, (long)(inOfs     )));
 156             out[ 1] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  4)));
 157             out[ 2] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  8)));
 158             out[ 3] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 12)));
 159             out[ 4] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 16)));
 160             out[ 5] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 20)));
 161             out[ 6] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 24)));
 162             out[ 7] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 28)));
 163             out[ 8] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 32)));
 164             out[ 9] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 36)));
 165             out[10] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 40)));
 166             out[11] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 44)));
 167             out[12] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 48)));
 168             out[13] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 52)));
 169             out[14] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 56)));
 170             out[15] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 60)));
 171         } else {
 172             b2iLittle(in, inOfs, out, 0, 64);
 173         }
 174     }
 175 
 176     /**
 177      * int[] to byte[] conversion, little endian byte order.
 178      */
 179     static void i2bLittle(int[] in, int inOfs, byte[] out, int outOfs, int len) {
 180         if ((inOfs < 0) || ((in.length - inOfs) < len/4) ||
 181             (outOfs < 0) || ((out.length - outOfs) < len)) {
 182             throw new ArrayIndexOutOfBoundsException();
 183         }
 184         if (littleEndianUnaligned) {
 185             outOfs += byteArrayOfs;
 186             len += outOfs;
 187             while (outOfs < len) {
 188                 unsafe.putInt(out, (long)outOfs, in[inOfs++]);
 189                 outOfs += 4;
 190             }
 191         } else if (bigEndian && ((outOfs & 3) == 0)) {
 192             outOfs += byteArrayOfs;
 193             len += outOfs;
 194             while (outOfs < len) {
 195                 unsafe.putInt(out, (long)outOfs, reverseBytes(in[inOfs++]));
 196                 outOfs += 4;
 197             }
 198         } else {
 199             len += outOfs;
 200             while (outOfs < len) {
 201                 int i = in[inOfs++];
 202                 out[outOfs++] = (byte)(i      );
 203                 out[outOfs++] = (byte)(i >>  8);
 204                 out[outOfs++] = (byte)(i >> 16);
 205                 out[outOfs++] = (byte)(i >> 24);
 206             }
 207         }
 208     }
 209 
 210     // Store one 32-bit value into out[outOfs..outOfs+3] in little endian order.
 211     static void i2bLittle4(int val, byte[] out, int outOfs) {
 212         if ((outOfs < 0) || ((out.length - outOfs) < 4)) {
 213             throw new ArrayIndexOutOfBoundsException();
 214         }
 215         if (littleEndianUnaligned) {
 216             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), val);
 217         } else if (bigEndian && ((outOfs & 3) == 0)) {
 218             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), reverseBytes(val));
 219         } else {
 220             out[outOfs    ] = (byte)(val      );
 221             out[outOfs + 1] = (byte)(val >>  8);
 222             out[outOfs + 2] = (byte)(val >> 16);
 223             out[outOfs + 3] = (byte)(val >> 24);
 224         }
 225     }
 226 
 227     /**
 228      * byte[] to int[] conversion, big endian byte order.
 229      */
 230     static void b2iBig(byte[] in, int inOfs, int[] out, int outOfs, int len) {
 231         if ((inOfs < 0) || ((in.length - inOfs) < len) ||
 232             (outOfs < 0) || ((out.length - outOfs) < len/4)) {
 233             throw new ArrayIndexOutOfBoundsException();
 234         }
 235         if (littleEndianUnaligned) {
 236             inOfs += byteArrayOfs;
 237             len += inOfs;
 238             while (inOfs < len) {
 239                 out[outOfs++] = reverseBytes(unsafe.getInt(in, (long)inOfs));
 240                 inOfs += 4;
 241             }
 242         } else if (bigEndian && ((inOfs & 3) == 0)) {
 243             inOfs += byteArrayOfs;
 244             len += inOfs;
 245             while (inOfs < len) {
 246                 out[outOfs++] = unsafe.getInt(in, (long)inOfs);
 247                 inOfs += 4;
 248             }
 249         } else {
 250             len += inOfs;
 251             while (inOfs < len) {
 252                 out[outOfs++] = ((in[inOfs + 3] & 0xff)      )
 253                               | ((in[inOfs + 2] & 0xff) <<  8)
 254                               | ((in[inOfs + 1] & 0xff) << 16)
 255                               | ((in[inOfs    ]       ) << 24);
 256                 inOfs += 4;
 257             }
 258         }
 259     }
 260 
 261     // Special optimization of b2iBig(in, inOfs, out, 0, 64)
 262     static void b2iBig64(byte[] in, int inOfs, int[] out) {
 263         if ((inOfs < 0) || ((in.length - inOfs) < 64) ||
 264             (out.length < 16)) {
 265             throw new ArrayIndexOutOfBoundsException();
 266         }
 267         if (littleEndianUnaligned) {
 268             inOfs += byteArrayOfs;
 269             out[ 0] = reverseBytes(unsafe.getInt(in, (long)(inOfs     )));
 270             out[ 1] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  4)));
 271             out[ 2] = reverseBytes(unsafe.getInt(in, (long)(inOfs +  8)));
 272             out[ 3] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 12)));
 273             out[ 4] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 16)));
 274             out[ 5] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 20)));
 275             out[ 6] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 24)));
 276             out[ 7] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 28)));
 277             out[ 8] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 32)));
 278             out[ 9] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 36)));
 279             out[10] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 40)));
 280             out[11] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 44)));
 281             out[12] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 48)));
 282             out[13] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 52)));
 283             out[14] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 56)));
 284             out[15] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 60)));
 285         } else if (bigEndian && ((inOfs & 3) == 0)) {
 286             inOfs += byteArrayOfs;
 287             out[ 0] = unsafe.getInt(in, (long)(inOfs     ));
 288             out[ 1] = unsafe.getInt(in, (long)(inOfs +  4));
 289             out[ 2] = unsafe.getInt(in, (long)(inOfs +  8));
 290             out[ 3] = unsafe.getInt(in, (long)(inOfs + 12));
 291             out[ 4] = unsafe.getInt(in, (long)(inOfs + 16));
 292             out[ 5] = unsafe.getInt(in, (long)(inOfs + 20));
 293             out[ 6] = unsafe.getInt(in, (long)(inOfs + 24));
 294             out[ 7] = unsafe.getInt(in, (long)(inOfs + 28));
 295             out[ 8] = unsafe.getInt(in, (long)(inOfs + 32));
 296             out[ 9] = unsafe.getInt(in, (long)(inOfs + 36));
 297             out[10] = unsafe.getInt(in, (long)(inOfs + 40));
 298             out[11] = unsafe.getInt(in, (long)(inOfs + 44));
 299             out[12] = unsafe.getInt(in, (long)(inOfs + 48));
 300             out[13] = unsafe.getInt(in, (long)(inOfs + 52));
 301             out[14] = unsafe.getInt(in, (long)(inOfs + 56));
 302             out[15] = unsafe.getInt(in, (long)(inOfs + 60));
 303         } else {
 304             b2iBig(in, inOfs, out, 0, 64);
 305         }
 306     }
 307 
 308     /**
 309      * int[] to byte[] conversion, big endian byte order.
 310      */
 311     static void i2bBig(int[] in, int inOfs, byte[] out, int outOfs, int len) {
 312         if ((inOfs < 0) || ((in.length - inOfs) < len/4) ||
 313             (outOfs < 0) || ((out.length - outOfs) < len)) {
 314             throw new ArrayIndexOutOfBoundsException();
 315         }
 316         if (littleEndianUnaligned) {
 317             outOfs += byteArrayOfs;
 318             len += outOfs;
 319             while (outOfs < len) {
 320                 unsafe.putInt(out, (long)outOfs, reverseBytes(in[inOfs++]));
 321                 outOfs += 4;
 322             }
 323         } else if (bigEndian && ((outOfs & 3) == 0)) {
 324             outOfs += byteArrayOfs;
 325             len += outOfs;
 326             while (outOfs < len) {
 327                 unsafe.putInt(out, (long)outOfs, in[inOfs++]);
 328                 outOfs += 4;
 329             }
 330         } else {
 331             len += outOfs;
 332             while (outOfs < len) {
 333                 int i = in[inOfs++];
 334                 out[outOfs++] = (byte)(i >> 24);
 335                 out[outOfs++] = (byte)(i >> 16);
 336                 out[outOfs++] = (byte)(i >>  8);
 337                 out[outOfs++] = (byte)(i      );
 338             }
 339         }
 340     }
 341 
 342     // Store one 32-bit value into out[outOfs..outOfs+3] in big endian order.
 343     static void i2bBig4(int val, byte[] out, int outOfs) {
 344         if ((outOfs < 0) || ((out.length - outOfs) < 4)) {
 345             throw new ArrayIndexOutOfBoundsException();
 346         }
 347         if (littleEndianUnaligned) {
 348             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), reverseBytes(val));
 349         } else if (bigEndian && ((outOfs & 3) == 0)) {
 350             unsafe.putInt(out, (long)(byteArrayOfs + outOfs), val);
 351         } else {
 352             out[outOfs    ] = (byte)(val >> 24);
 353             out[outOfs + 1] = (byte)(val >> 16);
 354             out[outOfs + 2] = (byte)(val >>  8);
 355             out[outOfs + 3] = (byte)(val      );
 356         }
 357     }
 358 
 359     /**
 360      * byte[] to long[] conversion, big endian byte order.
 361      */
 362     static void b2lBig(byte[] in, int inOfs, long[] out, int outOfs, int len) {
 363         if ((inOfs < 0) || ((in.length - inOfs) < len) ||
 364             (outOfs < 0) || ((out.length - outOfs) < len/8)) {
 365             throw new ArrayIndexOutOfBoundsException();
 366         }
 367         if (littleEndianUnaligned) {
 368             inOfs += byteArrayOfs;
 369             len += inOfs;
 370             while (inOfs < len) {
 371                 out[outOfs++] = reverseBytes(unsafe.getLong(in, (long)inOfs));
 372                 inOfs += 8;
 373             }
 374         } else if (bigEndian && ((inOfs & 3) == 0)) {
 375             // In the current HotSpot memory layout, the first element of a
 376             // byte[] is only 32-bit aligned, not 64-bit.
 377             // That means we could use getLong() only for offset 4, 12, etc.,
 378             // which would rarely occur in practice. Instead, we use an
 379             // optimization that uses getInt() so that it works for offset 0.
 380             inOfs += byteArrayOfs;
 381             len += inOfs;
 382             while (inOfs < len) {
 383                 out[outOfs++] =
 384                       ((long)unsafe.getInt(in, (long)inOfs) << 32)
 385                           | (unsafe.getInt(in, (long)(inOfs + 4)) & 0xffffffffL);
 386                 inOfs += 8;
 387             }
 388         } else {
 389             len += inOfs;
 390             while (inOfs < len) {
 391                 int i1 = ((in[inOfs + 3] & 0xff)      )
 392                        | ((in[inOfs + 2] & 0xff) <<  8)
 393                        | ((in[inOfs + 1] & 0xff) << 16)
 394                        | ((in[inOfs    ]       ) << 24);
 395                 inOfs += 4;
 396                 int i2 = ((in[inOfs + 3] & 0xff)      )
 397                        | ((in[inOfs + 2] & 0xff) <<  8)
 398                        | ((in[inOfs + 1] & 0xff) << 16)
 399                        | ((in[inOfs    ]       ) << 24);
 400                 out[outOfs++] = ((long)i1 << 32) | (i2 & 0xffffffffL);
 401                 inOfs += 4;
 402             }
 403         }
 404     }
 405 
 406     // Special optimization of b2lBig(in, inOfs, out, 0, 128)
 407     static void b2lBig128(byte[] in, int inOfs, long[] out) {
 408         if ((inOfs < 0) || ((in.length - inOfs) < 128) ||
 409             (out.length < 16)) {
 410             throw new ArrayIndexOutOfBoundsException();
 411         }
 412         if (littleEndianUnaligned) {
 413             inOfs += byteArrayOfs;
 414             out[ 0] = reverseBytes(unsafe.getLong(in, (long)(inOfs      )));
 415             out[ 1] = reverseBytes(unsafe.getLong(in, (long)(inOfs +   8)));
 416             out[ 2] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  16)));
 417             out[ 3] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  24)));
 418             out[ 4] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  32)));
 419             out[ 5] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  40)));
 420             out[ 6] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  48)));
 421             out[ 7] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  56)));
 422             out[ 8] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  64)));
 423             out[ 9] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  72)));
 424             out[10] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  80)));
 425             out[11] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  88)));
 426             out[12] = reverseBytes(unsafe.getLong(in, (long)(inOfs +  96)));
 427             out[13] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 104)));
 428             out[14] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 112)));
 429             out[15] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 120)));
 430         } else {
 431             // no optimization for big endian, see comments in b2lBig
 432             b2lBig(in, inOfs, out, 0, 128);
 433         }
 434     }
 435 
 436     /**
 437      * long[] to byte[] conversion, big endian byte order.
 438      */
 439     static void l2bBig(long[] in, int inOfs, byte[] out, int outOfs, int len) {
 440         if ((inOfs < 0) || ((in.length - inOfs) < len/8) ||
 441             (outOfs < 0) || ((out.length - outOfs) < len)) {
 442             throw new ArrayIndexOutOfBoundsException();
 443         }
 444         len += outOfs;
 445         while (outOfs < len) {
 446             long i = in[inOfs++];
 447             out[outOfs++] = (byte)(i >> 56);
 448             out[outOfs++] = (byte)(i >> 48);
 449             out[outOfs++] = (byte)(i >> 40);
 450             out[outOfs++] = (byte)(i >> 32);
 451             out[outOfs++] = (byte)(i >> 24);
 452             out[outOfs++] = (byte)(i >> 16);
 453             out[outOfs++] = (byte)(i >>  8);
 454             out[outOfs++] = (byte)(i      );
 455         }
 456     }
 457 }