src/share/classes/java/util/zip/CRC32.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File jdk Cdiff src/share/classes/java/util/zip/CRC32.java

src/share/classes/java/util/zip/CRC32.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 1996, 2011, 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. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1996, 2013, 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. Oracle designates this
*** 23,34 **** * questions. */ package java.util.zip; - import java.nio.ByteBuffer; import sun.nio.ch.DirectBuffer; /** * A class that can be used to compute the CRC-32 of a data stream. * * <p> Passing a {@code null} argument to a method in this class will cause --- 23,38 ---- * questions. */ package java.util.zip; import sun.nio.ch.DirectBuffer; + import java.lang.reflect.Field; + import java.nio.ByteBuffer; + import java.util.concurrent.RecursiveTask; + import java.util.concurrent.ForkJoinPool; + import sun.misc.Unsafe; /** * A class that can be used to compute the CRC-32 of a data stream. * * <p> Passing a {@code null} argument to a method in this class will cause
*** 53,63 **** * eight bits of the argument b). * * @param b the byte to update the checksum with */ public void update(int b) { ! crc = update(crc, b); } /** * Updates the CRC-32 checksum with the specified array of bytes. */ --- 57,70 ---- * eight bits of the argument b). * * @param b the byte to update the checksum with */ public void update(int b) { ! int c = ~ crc; ! b = timesXtoThe32[(b ^ c) & 0xFF]; ! b = b ^ (c >>> 8); ! crc = ~b; } /** * Updates the CRC-32 checksum with the specified array of bytes. */
*** 66,85 **** throw new NullPointerException(); } if (off < 0 || len < 0 || off > b.length - len) { throw new ArrayIndexOutOfBoundsException(); } ! crc = updateBytes(crc, b, off, len); } /** * Updates the CRC-32 checksum with the specified array of bytes. * * @param b the array of bytes to update the checksum with */ public void update(byte[] b) { ! crc = updateBytes(crc, b, 0, b.length); } /** * Updates the checksum with the bytes from the specified buffer. * --- 73,97 ---- throw new NullPointerException(); } if (off < 0 || len < 0 || off > b.length - len) { throw new ArrayIndexOutOfBoundsException(); } ! ! if (len < javaCRCIfSmallerThan) { ! crc = updateBytesSimple(crc, b, off, len); ! } else { ! crc = updateBytesFJ(crc, b, off, len); ! } } /** * Updates the CRC-32 checksum with the specified array of bytes. * * @param b the array of bytes to update the checksum with */ public void update(byte[] b) { ! crc = updateBytesFJ(crc, b, 0, b.length); } /** * Updates the checksum with the bytes from the specified buffer. *
*** 99,115 **** assert (pos <= limit); int rem = limit - pos; if (rem <= 0) return; if (buffer instanceof DirectBuffer) { ! crc = updateByteBuffer(crc, ((DirectBuffer)buffer).address(), pos, rem); } else if (buffer.hasArray()) { ! crc = updateBytes(crc, buffer.array(), pos + buffer.arrayOffset(), rem); } else { byte[] b = new byte[rem]; buffer.get(b); ! crc = updateBytes(crc, b, 0, b.length); } buffer.position(limit); } /** --- 111,127 ---- assert (pos <= limit); int rem = limit - pos; if (rem <= 0) return; if (buffer instanceof DirectBuffer) { ! crc = updateByteBufferFJ(crc, ((DirectBuffer)buffer).address(), pos, rem); } else if (buffer.hasArray()) { ! crc = updateBytesFJ(crc, buffer.array(), pos + buffer.arrayOffset(), rem); } else { byte[] b = new byte[rem]; buffer.get(b); ! crc = updateBytesFJ(crc, b, 0, b.length); } buffer.position(limit); } /**
*** 129,134 **** --- 141,409 ---- private native static int update(int crc, int b); private native static int updateBytes(int crc, byte[] b, int off, int len); private native static int updateByteBuffer(int adler, long addr, int off, int len); + + + private static int updateBytesSimple(int crc, byte[] b, int off, int len) { + int[] a = timesXtoThe32; + if (a.length < 256) + throw new ArrayIndexOutOfBoundsException(); + int c = ~crc; + for (int i = 0; i < len; i++ ) { + int x0 = b[i + off]; + x0 = a[(x0 ^ c) & 0xFF]; + c = x0 ^ (c >>> 8); + } + return ~c; + } + + private native static boolean init(int[] timesXtoThe32, boolean try_use_clmul); + + /** + * timesXtoThe32[a] = rep(poly(a)*x**32) + */ + static int[] timesXtoThe32; + + /** + * powersByLog[i] = rep(x**(2**i)) + */ + static final int[] powersByLog = new int[32]; + + static final int LOG_PB8_LEN = 8; + + /** + * powersBy8[i] = rep(x**(8*i)) + */ + static final int[] powersBy8 = new int[1 << LOG_PB8_LEN]; + + static final int X_to_the_1 = 0x40000000; + static final int X_to_the_0 = 0x80000000; + + /** + * Indicates if the clmul instruction is enabled in the native + * code; this changes the estimated cost of computing a CRC. + */ + private static boolean clmulEnabled; + + /** + * Helpful for deciding whether to use workstealing or not. + */ + private static int fjParallelism = ForkJoinPool.getCommonPoolParallelism(); + + /** + * Estimated task size below which the fork-join overhead could be too large. + * May be modified depending on platform properties. + */ + static int serialIfSmallerThan = 512 * 1024; + + /** + * Estimated CRC size below which the JNI overhead is too large. + * May be modified depending on platform properties. + */ + static int javaCRCIfSmallerThan = 80; + + static int ARRAY_BYTE_BASE_OFFSET = 0; + + static boolean debug = false; + + static { + timesXtoThe32 = new int[256]; + boolean try_use_clmul = + "true".equals(sun.misc.VM.getSavedProperty("sun.zip.clmulSupported")); + clmulEnabled = init(timesXtoThe32, try_use_clmul); + + if (clmulEnabled) + serialIfSmallerThan *= 2; + + if ("true".equals(sun.misc.VM.getSavedProperty("sun.zip.serialOnly"))) + fjParallelism = 1; + + powersByLog[0] = X_to_the_1; + for (int i = 1; i < powersByLog.length; i++) { + int x = powersByLog[i-1]; + powersByLog[i] = mul(x,x); + } + + powersBy8[0] = X_to_the_0; + for (int i = 1; i < powersBy8.length; i++) { + int x = powersBy8[i-1]; + powersBy8[i] = mul(x,powersByLog[3]); + } + + /* Attempt to do all fork-join splits so that they land on a 16-byte boundary. + Even if arrays are only 8-byte aligned, this improves our chances. */ + try { + Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + Unsafe u = (Unsafe) field.get(null); + ARRAY_BYTE_BASE_OFFSET = u.ARRAY_BYTE_BASE_OFFSET; + } catch (NoSuchFieldException | SecurityException | + IllegalArgumentException | IllegalAccessException e) { + // It was just an optimization, no need to fail hard. + } + } + + /* Java implementation of enough GF arithmetic to combine two CRCs for fork/join. */ + + /** + * Performs a carryless 32x32 into 63 (NOT 64) bit multiply. + * Note that the low-order term of the polynomial lands in bit + * 62, not 63. + * + * The Intel pclmulqdq instruction works in much the same way, + * except that it is 64 x 64 into 128. + */ + private static long clmul32x32(int a, int b) { + long accum = 0; + long la = (long) a & 0xffffffffL; + long lb = (long) b & 0xffffffffL; + while (la != 0) { + if (0 != (la & 1)) + accum ^= lb; + la = la >>> 1; + lb = lb << 1; + } + return accum; + } + + /** + * Converts a 64-bit polynomial into a 32-bit polynomial, modulo P. + */ + static int reduceLongTable(long x) { + x = (x >>> 8) ^ (timesXtoThe32[(int)(x & 0xFF)] & 0xffffffffL); + x = (x >>> 8) ^ (timesXtoThe32[(int)(x & 0xFF)] & 0xffffffffL); + x = (x >>> 8) ^ (timesXtoThe32[(int)(x & 0xFF)] & 0xffffffffL); + x = (x >>> 8) ^ (timesXtoThe32[(int)(x & 0xFF)] & 0xffffffffL); + return (int) x; + } + + /** + * Returns polynomial a times b modulo P. + * The least (x**0) term of the polynomial is + * aligned with the sign bit of the returned int. + * + * @param a + * @param b + * @return + */ + static int mul(int a, int b) { + long product = clmul32x32(a, b); + return reduceLongTable(product << 1); + } + + /** + * Returns the polynomial for a * x ** 8n, where a + * is some other polynomial. n is typically a byte count, + * and 8n is the number of bits in n bytes. + */ + static int timesXtoThe8NTable(int a, int n) { + if (n == 0) + return a; + if (n < powersBy8.length) { + return mul(a,powersBy8[n]); + } + int lo = powersBy8.length - 1; + int accum = mul(a,powersBy8[n & lo]); + n = n >>> LOG_PB8_LEN; + int i = LOG_PB8_LEN + 3; + while (n != 0) { + if (0 != (n & 1)) + accum = mul(accum,powersByLog[i]); + n = n >>> 1; + i++; + } + return accum; + } + + static int combine(int prev, int next, int next_length) { + // x**(8 * length) * prev + next + return next ^ timesXtoThe8NTable(prev, next_length); + } + + private static int updateBytesFJ(int crc, byte[] b, int off, int len) { + if (fjParallelism < 2 || len < serialIfSmallerThan) + return updateBytes(crc, b, off, len); + CRCArrayTask cat = new CRCArrayTask(crc, b, off, len); + cat.invoke(); + return cat.join(); + } + + private static int updateByteBufferFJ(int crc, long addr, + int off, int len) { + if (fjParallelism < 2 || len < serialIfSmallerThan) + return updateByteBuffer(crc, addr, off, len); + CRCBufferTask cat = new CRCBufferTask(crc, addr, off, len); + cat.invoke(); + return cat.join(); + } + + static final class CRCArrayTask extends RecursiveTask<Integer> { + final byte[] ba; + final int start; + final int length; + final int crc; + + CRCArrayTask(int crc, byte[] ba, int start, int length) { + this.crc = crc; + this.ba = ba; + this.start = start; + this.length = length; + } + + @Override + protected Integer compute() { + if (length < serialIfSmallerThan) { + if (length < javaCRCIfSmallerThan) { + return updateBytesSimple(crc, ba, start, length); + } else { + return updateBytes(crc, ba, start, length); + } + } else { + int half = length/2; + /* Avoid gratuitous misalignment. */ + long addr = ARRAY_BYTE_BASE_OFFSET; // Best we can do given limited info. + int unaligned = (int) (addr + start + half) & 15; + if (half - unaligned >= 32) + half -= unaligned; + CRCArrayTask task2 = new CRCArrayTask(0, ba, start + half, length - half); + task2.fork(); + CRCArrayTask task1 = new CRCArrayTask(crc, ba, start, half); + int result1 = task1.compute(); + return combine(result1, task2.join(), length - half); + } + } + } + + static final class CRCBufferTask extends RecursiveTask<Integer> { + final long addr; + final int start; + final int length; + final int crc; + + CRCBufferTask(int crc, long addr, int start, int length) { + this.crc = crc; + this.addr = addr; + this.start = start; + this.length = length; + } + + @Override + protected Integer compute() { + if (length < serialIfSmallerThan) { + return updateByteBuffer(crc, addr, start, length); + } else { + int half = length/2; + /* Avoid gratuitous misalignment. */ + int unaligned = (int) (addr + start + half) & 15; + if (half - unaligned >= 32) + half -= unaligned; + CRCBufferTask task2 = new CRCBufferTask(0, addr, start + half, length - half); + task2.fork(); + CRCBufferTask task1 = new CRCBufferTask(crc, addr, start, half); + int result1 = task1.compute(); + return combine(result1, task2.join(), length - half); + } + } + } }
src/share/classes/java/util/zip/CRC32.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File