ByteArrayAccess.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2006, 2015, 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 26package sun.security.provider; 27 28import static java.lang.Integer.reverseBytes; 29import static java.lang.Long.reverseBytes; 30 31import java.nio.ByteOrder; 32 33import 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 */ 56final 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 static final 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} 458