diff options
| -rw-r--r-- | compiler/optimizing/intrinsics_arm64.cc | 145 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_arm_vixl.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips64.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_x86.cc | 1 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_x86_64.cc | 1 | ||||
| -rw-r--r-- | runtime/hidden_api.h | 1 | ||||
| -rw-r--r-- | runtime/image.cc | 2 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter_intrinsics.cc | 1 | ||||
| -rw-r--r-- | runtime/intrinsics_list.h | 1 | ||||
| -rw-r--r-- | test/580-crc32/src/Main.java | 361 |
11 files changed, 444 insertions, 72 deletions
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 6d04b0e9d9..1688ea7811 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2950,6 +2950,151 @@ void IntrinsicCodeGeneratorARM64::VisitCRC32Update(HInvoke* invoke) { __ Mvn(out, out); } +// The threshold for sizes of arrays to use the library provided implementation +// of CRC32.updateBytes instead of the intrinsic. +static constexpr int32_t kCRC32UpdateBytesThreshold = 64 * 1024; + +void IntrinsicLocationsBuilderARM64::VisitCRC32UpdateBytes(HInvoke* invoke) { + if (!codegen_->GetInstructionSetFeatures().HasCRC()) { + return; + } + + LocationSummary* locations + = new (allocator_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RegisterOrConstant(invoke->InputAt(2))); + locations->SetInAt(3, Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +// Lower the invoke of CRC32.updateBytes(int crc, byte[] b, int off, int len) +// +// Note: The intrinsic is not used if len exceeds a threshold. +void IntrinsicCodeGeneratorARM64::VisitCRC32UpdateBytes(HInvoke* invoke) { + DCHECK(codegen_->GetInstructionSetFeatures().HasCRC()); + + auto masm = GetVIXLAssembler(); + auto locations = invoke->GetLocations(); + + auto slow_path = + new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + + Register length = WRegisterFrom(locations->InAt(3)); + __ Cmp(length, kCRC32UpdateBytesThreshold); + __ B(slow_path->GetEntryLabel(), hi); + + const uint32_t array_data_offset = + mirror::Array::DataOffset(Primitive::kPrimByte).Uint32Value(); + Register ptr = XRegisterFrom(locations->GetTemp(0)); + Register array = XRegisterFrom(locations->InAt(1)); + auto offset = locations->InAt(2); + if (offset.IsConstant()) { + int32_t offset_value = offset.GetConstant()->AsIntConstant()->GetValue(); + __ Add(ptr, array, array_data_offset + offset_value); + } else { + __ Add(ptr, array, array_data_offset); + __ Add(ptr, ptr, XRegisterFrom(offset)); + } + + // The algorithm of CRC32 of bytes is: + // crc = ~crc + // process a few first bytes to make the array 8-byte aligned + // while array has 8 bytes do: + // crc = crc32_of_8bytes(crc, 8_bytes(array)) + // if array has 4 bytes: + // crc = crc32_of_4bytes(crc, 4_bytes(array)) + // if array has 2 bytes: + // crc = crc32_of_2bytes(crc, 2_bytes(array)) + // if array has a byte: + // crc = crc32_of_byte(crc, 1_byte(array)) + // crc = ~crc + + vixl::aarch64::Label loop, done; + vixl::aarch64::Label process_4bytes, process_2bytes, process_1byte; + vixl::aarch64::Label aligned2, aligned4, aligned8; + + // Use VIXL scratch registers as the VIXL macro assembler won't use them in + // instructions below. + UseScratchRegisterScope temps(masm); + Register len = temps.AcquireW(); + Register array_elem = temps.AcquireW(); + + Register out = WRegisterFrom(locations->Out()); + __ Mvn(out, WRegisterFrom(locations->InAt(0))); + __ Mov(len, length); + + __ Tbz(ptr, 0, &aligned2); + __ Subs(len, len, 1); + __ B(&done, lo); + __ Ldrb(array_elem, MemOperand(ptr, 1, PostIndex)); + __ Crc32b(out, out, array_elem); + + __ Bind(&aligned2); + __ Tbz(ptr, 1, &aligned4); + __ Subs(len, len, 2); + __ B(&process_1byte, lo); + __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex)); + __ Crc32h(out, out, array_elem); + + __ Bind(&aligned4); + __ Tbz(ptr, 2, &aligned8); + __ Subs(len, len, 4); + __ B(&process_2bytes, lo); + __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex)); + __ Crc32w(out, out, array_elem); + + __ Bind(&aligned8); + __ Subs(len, len, 8); + // If len < 8 go to process data by 4 bytes, 2 bytes and a byte. + __ B(&process_4bytes, lo); + + // The main loop processing data by 8 bytes. + __ Bind(&loop); + __ Ldr(array_elem.X(), MemOperand(ptr, 8, PostIndex)); + __ Subs(len, len, 8); + __ Crc32x(out, out, array_elem.X()); + // if len >= 8, process the next 8 bytes. + __ B(&loop, hs); + + // Process the data which is less than 8 bytes. + // The code generated below works with values of len + // which come in the range [-8, 0]. + // The first three bits are used to detect whether 4 bytes or 2 bytes or + // a byte can be processed. + // The checking order is from bit 2 to bit 0: + // bit 2 is set: at least 4 bytes available + // bit 1 is set: at least 2 bytes available + // bit 0 is set: at least a byte available + __ Bind(&process_4bytes); + // Goto process_2bytes if less than four bytes available + __ Tbz(len, 2, &process_2bytes); + __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex)); + __ Crc32w(out, out, array_elem); + + __ Bind(&process_2bytes); + // Goto process_1bytes if less than two bytes available + __ Tbz(len, 1, &process_1byte); + __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex)); + __ Crc32h(out, out, array_elem); + + __ Bind(&process_1byte); + // Goto done if no bytes available + __ Tbz(len, 0, &done); + __ Ldrb(array_elem, MemOperand(ptr)); + __ Crc32b(out, out, array_elem); + + __ Bind(&done); + __ Mvn(out, out); + + __ Bind(slow_path->GetExitLabel()); +} + UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 4d45a9991c..88f1457c20 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -3060,6 +3060,7 @@ UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update) +UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateBytes) UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 21fb7d7f1c..08ba0a0adf 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2697,6 +2697,7 @@ UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update) +UNIMPLEMENTED_INTRINSIC(MIPS, CRC32UpdateBytes) UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 4b86f5d423..59d3ba2488 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -2347,6 +2347,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitReachabilityFence(HInvoke* invoke ATTRIB UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update) +UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32UpdateBytes) UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index a73f4e8b94..1d94950e4d 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -3071,6 +3071,7 @@ UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite) UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, CRC32Update) +UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateBytes) UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 88c766fabc..4f0b61d88e 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2738,6 +2738,7 @@ UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update) +UNIMPLEMENTED_INTRINSIC(X86_64, CRC32UpdateBytes) UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 614154c7a0..9daf825777 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -235,6 +235,7 @@ ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtMethod* method) case Intrinsics::kUnsafeStoreFence: case Intrinsics::kUnsafeFullFence: case Intrinsics::kCRC32Update: + case Intrinsics::kCRC32UpdateBytes: case Intrinsics::kStringNewStringFromBytes: case Intrinsics::kStringNewStringFromChars: case Intrinsics::kStringNewStringFromString: diff --git a/runtime/image.cc b/runtime/image.cc index ae3d8e34e6..fb581f94d2 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -29,7 +29,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '1', '\0' }; // Add image blocks. +const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '2', '\0' }; // CRC32UpdateBytes intrinsic ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 24a026a92e..16e118c9cf 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -559,6 +559,7 @@ bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */) UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */) UNIMPLEMENTED_CASE(CRC32Update /* (II)I */) + UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */) INTRINSIC_CASE(VarHandleFullFence) INTRINSIC_CASE(VarHandleAcquireFence) INTRINSIC_CASE(VarHandleReleaseFence) diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h index 093dd7f400..82ea47609b 100644 --- a/runtime/intrinsics_list.h +++ b/runtime/intrinsics_list.h @@ -220,6 +220,7 @@ V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \ V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \ V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \ + V(CRC32UpdateBytes, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "updateBytes", "(I[BII)I") \ SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V) #endif // ART_RUNTIME_INTRINSICS_LIST_H_ diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java index 7fc1273600..6199e9b2a9 100644 --- a/test/580-crc32/src/Main.java +++ b/test/580-crc32/src/Main.java @@ -15,29 +15,29 @@ */ import java.util.zip.CRC32; +import java.util.Random; /** - * The ART compiler can use intrinsics for the java.util.zip.CRC32 method: - * private native static int update(int crc, int b) + * The ART compiler can use intrinsics for the java.util.zip.CRC32 methods: + * private native static int update(int crc, int b) + * private native static int updateBytes(int crc, byte[] b, int off, int len) * - * As the method is private it is not possible to check the use of intrinsics - * for it directly. + * As the methods are private it is not possible to check the use of intrinsics + * for them directly. * The tests check that correct checksums are produced. */ public class Main { - private static CRC32 crc32 = new CRC32(); - public Main() { } - public static long TestInt(int value) { - crc32.reset(); + public static long CRC32Byte(int value) { + CRC32 crc32 = new CRC32(); crc32.update(value); return crc32.getValue(); } - public static long TestInt(int... values) { - crc32.reset(); + public static long CRC32BytesUsingUpdateInt(int... values) { + CRC32 crc32 = new CRC32(); for (int value : values) { crc32.update(value); } @@ -50,82 +50,301 @@ public class Main { } } - public static void main(String args[]) { + private static void assertEqual(boolean expected, boolean actual) { + if (expected != actual) { + throw new Error("Expected: " + expected + ", found: " + actual); + } + } + + private static void TestCRC32Update() { // public void update(int b) // // Tests for checksums of the byte 0x0 - assertEqual(0xD202EF8DL, TestInt(0x0)); - assertEqual(0xD202EF8DL, TestInt(0x0100)); - assertEqual(0xD202EF8DL, TestInt(0x010000)); - assertEqual(0xD202EF8DL, TestInt(0x01000000)); - assertEqual(0xD202EF8DL, TestInt(0xff00)); - assertEqual(0xD202EF8DL, TestInt(0xffff00)); - assertEqual(0xD202EF8DL, TestInt(0xffffff00)); - assertEqual(0xD202EF8DL, TestInt(0x1200)); - assertEqual(0xD202EF8DL, TestInt(0x123400)); - assertEqual(0xD202EF8DL, TestInt(0x12345600)); - assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE)); + // Check that only the low eight bits of the argument are used. + assertEqual(0xD202EF8DL, CRC32Byte(0x0)); + assertEqual(0xD202EF8DL, CRC32Byte(0x0100)); + assertEqual(0xD202EF8DL, CRC32Byte(0x010000)); + assertEqual(0xD202EF8DL, CRC32Byte(0x01000000)); + assertEqual(0xD202EF8DL, CRC32Byte(0xff00)); + assertEqual(0xD202EF8DL, CRC32Byte(0xffff00)); + assertEqual(0xD202EF8DL, CRC32Byte(0xffffff00)); + assertEqual(0xD202EF8DL, CRC32Byte(0x1200)); + assertEqual(0xD202EF8DL, CRC32Byte(0x123400)); + assertEqual(0xD202EF8DL, CRC32Byte(0x12345600)); + assertEqual(0xD202EF8DL, CRC32Byte(Integer.MIN_VALUE)); // Tests for checksums of the byte 0x1 - assertEqual(0xA505DF1BL, TestInt(0x1)); - assertEqual(0xA505DF1BL, TestInt(0x0101)); - assertEqual(0xA505DF1BL, TestInt(0x010001)); - assertEqual(0xA505DF1BL, TestInt(0x01000001)); - assertEqual(0xA505DF1BL, TestInt(0xff01)); - assertEqual(0xA505DF1BL, TestInt(0xffff01)); - assertEqual(0xA505DF1BL, TestInt(0xffffff01)); - assertEqual(0xA505DF1BL, TestInt(0x1201)); - assertEqual(0xA505DF1BL, TestInt(0x123401)); - assertEqual(0xA505DF1BL, TestInt(0x12345601)); + // Check that only the low eight bits of the argument are used. + assertEqual(0xA505DF1BL, CRC32Byte(0x1)); + assertEqual(0xA505DF1BL, CRC32Byte(0x0101)); + assertEqual(0xA505DF1BL, CRC32Byte(0x010001)); + assertEqual(0xA505DF1BL, CRC32Byte(0x01000001)); + assertEqual(0xA505DF1BL, CRC32Byte(0xff01)); + assertEqual(0xA505DF1BL, CRC32Byte(0xffff01)); + assertEqual(0xA505DF1BL, CRC32Byte(0xffffff01)); + assertEqual(0xA505DF1BL, CRC32Byte(0x1201)); + assertEqual(0xA505DF1BL, CRC32Byte(0x123401)); + assertEqual(0xA505DF1BL, CRC32Byte(0x12345601)); // Tests for checksums of the byte 0x0f - assertEqual(0x42BDF21CL, TestInt(0x0f)); - assertEqual(0x42BDF21CL, TestInt(0x010f)); - assertEqual(0x42BDF21CL, TestInt(0x01000f)); - assertEqual(0x42BDF21CL, TestInt(0x0100000f)); - assertEqual(0x42BDF21CL, TestInt(0xff0f)); - assertEqual(0x42BDF21CL, TestInt(0xffff0f)); - assertEqual(0x42BDF21CL, TestInt(0xffffff0f)); - assertEqual(0x42BDF21CL, TestInt(0x120f)); - assertEqual(0x42BDF21CL, TestInt(0x12340f)); - assertEqual(0x42BDF21CL, TestInt(0x1234560f)); + // Check that only the low eight bits of the argument are used. + assertEqual(0x42BDF21CL, CRC32Byte(0x0f)); + assertEqual(0x42BDF21CL, CRC32Byte(0x010f)); + assertEqual(0x42BDF21CL, CRC32Byte(0x01000f)); + assertEqual(0x42BDF21CL, CRC32Byte(0x0100000f)); + assertEqual(0x42BDF21CL, CRC32Byte(0xff0f)); + assertEqual(0x42BDF21CL, CRC32Byte(0xffff0f)); + assertEqual(0x42BDF21CL, CRC32Byte(0xffffff0f)); + assertEqual(0x42BDF21CL, CRC32Byte(0x120f)); + assertEqual(0x42BDF21CL, CRC32Byte(0x12340f)); + assertEqual(0x42BDF21CL, CRC32Byte(0x1234560f)); // Tests for checksums of the byte 0xff - assertEqual(0xFF000000L, TestInt(0x00ff)); - assertEqual(0xFF000000L, TestInt(0x01ff)); - assertEqual(0xFF000000L, TestInt(0x0100ff)); - assertEqual(0xFF000000L, TestInt(0x010000ff)); - assertEqual(0xFF000000L, TestInt(0x0000ffff)); - assertEqual(0xFF000000L, TestInt(0x00ffffff)); - assertEqual(0xFF000000L, TestInt(0xffffffff)); - assertEqual(0xFF000000L, TestInt(0x12ff)); - assertEqual(0xFF000000L, TestInt(0x1234ff)); - assertEqual(0xFF000000L, TestInt(0x123456ff)); - assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE)); + // Check that only the low eight bits of the argument are used. + assertEqual(0xFF000000L, CRC32Byte(0x00ff)); + assertEqual(0xFF000000L, CRC32Byte(0x01ff)); + assertEqual(0xFF000000L, CRC32Byte(0x0100ff)); + assertEqual(0xFF000000L, CRC32Byte(0x010000ff)); + assertEqual(0xFF000000L, CRC32Byte(0x0000ffff)); + assertEqual(0xFF000000L, CRC32Byte(0x00ffffff)); + assertEqual(0xFF000000L, CRC32Byte(0xffffffff)); + assertEqual(0xFF000000L, CRC32Byte(0x12ff)); + assertEqual(0xFF000000L, CRC32Byte(0x1234ff)); + assertEqual(0xFF000000L, CRC32Byte(0x123456ff)); + assertEqual(0xFF000000L, CRC32Byte(Integer.MAX_VALUE)); // Tests for sequences - assertEqual(0xFF41D912L, TestInt(0, 0, 0)); - assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000)); - assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00)); - assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600)); + // Check that only the low eight bits of the values are used. + assertEqual(0xFF41D912L, CRC32BytesUsingUpdateInt(0, 0, 0)); + assertEqual(0xFF41D912L, + CRC32BytesUsingUpdateInt(0x0100, 0x010000, 0x01000000)); + assertEqual(0xFF41D912L, + CRC32BytesUsingUpdateInt(0xff00, 0xffff00, 0xffffff00)); + assertEqual(0xFF41D912L, + CRC32BytesUsingUpdateInt(0x1200, 0x123400, 0x12345600)); + + assertEqual(0x909FB2F2L, CRC32BytesUsingUpdateInt(1, 1, 1)); + assertEqual(0x909FB2F2L, + CRC32BytesUsingUpdateInt(0x0101, 0x010001, 0x01000001)); + assertEqual(0x909FB2F2L, + CRC32BytesUsingUpdateInt(0xff01, 0xffff01, 0xffffff01)); + assertEqual(0x909FB2F2L, + CRC32BytesUsingUpdateInt(0x1201, 0x123401, 0x12345601)); + + assertEqual(0xE33A9F71L, CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f)); + assertEqual(0xE33A9F71L, + CRC32BytesUsingUpdateInt(0x010f, 0x01000f, 0x0100000f)); + assertEqual(0xE33A9F71L, + CRC32BytesUsingUpdateInt(0xff0f, 0xffff0f, 0xffffff0f)); + assertEqual(0xE33A9F71L, + CRC32BytesUsingUpdateInt(0x120f, 0x12340f, 0x1234560f)); + + assertEqual(0xFFFFFF00L, CRC32BytesUsingUpdateInt(0x0ff, 0x0ff, 0x0ff)); + assertEqual(0xFFFFFF00L, + CRC32BytesUsingUpdateInt(0x01ff, 0x0100ff, 0x010000ff)); + assertEqual(0xFFFFFF00L, + CRC32BytesUsingUpdateInt(0x00ffff, 0x00ffffff, 0xffffffff)); + assertEqual(0xFFFFFF00L, + CRC32BytesUsingUpdateInt(0x12ff, 0x1234ff, 0x123456ff)); + + assertEqual(0xB6CC4292L, CRC32BytesUsingUpdateInt(0x01, 0x02)); + + assertEqual(0xB2DE047CL, + CRC32BytesUsingUpdateInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE)); + } + + private static long CRC32ByteArray(byte[] bytes, int off, int len) { + CRC32 crc32 = new CRC32(); + crc32.update(bytes, off, len); + return crc32.getValue(); + } + + // This is used to test we generate correct code for constant offsets. + // In this case the offset is 0. + private static long CRC32ByteArray(byte[] bytes) { + CRC32 crc32 = new CRC32(); + crc32.update(bytes); + return crc32.getValue(); + } + + private static long CRC32ByteAndByteArray(int value, byte[] bytes) { + CRC32 crc32 = new CRC32(); + crc32.update(value); + crc32.update(bytes); + return crc32.getValue(); + } + + private static long CRC32ByteArrayAndByte(byte[] bytes, int value) { + CRC32 crc32 = new CRC32(); + crc32.update(bytes); + crc32.update(value); + return crc32.getValue(); + } + + private static boolean CRC32ByteArrayThrowsAIOOBE(byte[] bytes, int off, int len) { + try { + CRC32 crc32 = new CRC32(); + crc32.update(bytes, off, len); + } catch (ArrayIndexOutOfBoundsException ex) { + return true; + } + return false; + } + + private static boolean CRC32ByteArrayThrowsNPE() { + try { + CRC32 crc32 = new CRC32(); + crc32.update(null, 0, 0); + return false; + } catch (NullPointerException e) {} + + try { + CRC32 crc32 = new CRC32(); + crc32.update(null, 1, 2); + return false; + } catch (NullPointerException e) {} - assertEqual(0x909FB2F2L, TestInt(1, 1, 1)); - assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001)); - assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01)); - assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601)); + try { + CRC32 crc32 = new CRC32(); + crc32.update((byte[])null); + return false; + } catch (NullPointerException e) {} + + return true; + } + + private static long CRC32BytesUsingUpdateInt(byte[] bytes, int off, int len) { + CRC32 crc32 = new CRC32(); + while (len-- > 0) { + crc32.update(bytes[off++]); + } + return crc32.getValue(); + } - assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f)); - assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f)); - assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f)); - assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f)); + private static void TestCRC32UpdateBytes() { + assertEqual(0L, CRC32ByteArray(new byte[] {})); + assertEqual(0L, CRC32ByteArray(new byte[] {}, 0, 0)); + assertEqual(0L, CRC32ByteArray(new byte[] {0}, 0, 0)); + assertEqual(0L, CRC32ByteArray(new byte[] {0}, 1, 0)); + assertEqual(0L, CRC32ByteArray(new byte[] {0, 0}, 1, 0)); - assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff)); - assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff)); - assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff)); - assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff)); + assertEqual(true, CRC32ByteArrayThrowsNPE()); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, -1, 0)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, -1, 1)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, 0, -1)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, -1)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 1, 0)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, -1, 1)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 1, -1)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, 1)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, 10)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, 0, 10)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 10, 10)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0, 0, 0, 0}, 2, 3)); + assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0, 0, 0, 0}, 3, 2)); - assertEqual(0xB6CC4292L, TestInt(0x01, 0x02)); + assertEqual(CRC32Byte(0), CRC32ByteArray(new byte[] {0})); + assertEqual(CRC32Byte(0), CRC32ByteArray(new byte[] {0}, 0, 1)); + assertEqual(CRC32Byte(1), CRC32ByteArray(new byte[] {1})); + assertEqual(CRC32Byte(1), CRC32ByteArray(new byte[] {1}, 0, 1)); + assertEqual(CRC32Byte(0x0f), CRC32ByteArray(new byte[] {0x0f})); + assertEqual(CRC32Byte(0x0f), CRC32ByteArray(new byte[] {0x0f}, 0, 1)); + assertEqual(CRC32Byte(0xff), CRC32ByteArray(new byte[] {-1})); + assertEqual(CRC32Byte(0xff), CRC32ByteArray(new byte[] {-1}, 0, 1)); + assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0), + CRC32ByteArray(new byte[] {0, 0, 0})); + assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0), + CRC32ByteArray(new byte[] {0, 0, 0}, 0, 3)); + assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1), + CRC32ByteArray(new byte[] {1, 1, 1})); + assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1), + CRC32ByteArray(new byte[] {1, 1, 1}, 0, 3)); + assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f), + CRC32ByteArray(new byte[] {0x0f, 0x0f, 0x0f})); + assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f), + CRC32ByteArray(new byte[] {0x0f, 0x0f, 0x0f}, 0, 3)); + assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff), + CRC32ByteArray(new byte[] {-1, -1, -1})); + assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff), + CRC32ByteArray(new byte[] {-1, -1, -1}, 0, 3)); + assertEqual(CRC32BytesUsingUpdateInt(1, 2), + CRC32ByteArray(new byte[] {1, 2})); + assertEqual(CRC32BytesUsingUpdateInt(1, 2), + CRC32ByteArray(new byte[] {1, 2}, 0, 2)); + assertEqual( + CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE), + CRC32ByteArray(new byte[] {0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE})); + assertEqual( + CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE), + CRC32ByteArray(new byte[] {0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE}, 0, 4)); - assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE)); + assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0), + CRC32ByteAndByteArray(0, new byte[] {0, 0})); + assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1), + CRC32ByteAndByteArray(1, new byte[] {1, 1})); + assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f), + CRC32ByteAndByteArray(0x0f, new byte[] {0x0f, 0x0f})); + assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff), + CRC32ByteAndByteArray(-1, new byte[] {-1, -1})); + assertEqual(CRC32BytesUsingUpdateInt(1, 2, 3), + CRC32ByteAndByteArray(1, new byte[] {2, 3})); + assertEqual( + CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE), + CRC32ByteAndByteArray(0, new byte[] {-1, Byte.MIN_VALUE, Byte.MAX_VALUE})); + + assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0), + CRC32ByteArrayAndByte(new byte[] {0, 0}, 0)); + assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1), + CRC32ByteArrayAndByte(new byte[] {1, 1}, 1)); + assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f), + CRC32ByteArrayAndByte(new byte[] {0x0f, 0x0f}, 0x0f)); + assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff), + CRC32ByteArrayAndByte(new byte[] {-1, -1}, -1)); + assertEqual(CRC32BytesUsingUpdateInt(1, 2, 3), + CRC32ByteArrayAndByte(new byte[] {1, 2}, 3)); + assertEqual( + CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE), + CRC32ByteArrayAndByte(new byte[] {0, -1, Byte.MIN_VALUE}, Byte.MAX_VALUE)); + + byte[] bytes = new byte[128 * 1024]; + Random rnd = new Random(0); + rnd.nextBytes(bytes); + + assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, bytes.length), + CRC32ByteArray(bytes)); + assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, 8 * 1024), + CRC32ByteArray(bytes, 0, 8 * 1024)); + + int off = rnd.nextInt(bytes.length / 2); + for (int len = 0; len <= 16; ++len) { + assertEqual(CRC32BytesUsingUpdateInt(bytes, off, len), + CRC32ByteArray(bytes, off, len)); + } + + // Check there are no issues with unaligned accesses. + for (int o = 1; o < 8; ++o) { + for (int l = 0; l <= 16; ++l) { + assertEqual(CRC32BytesUsingUpdateInt(bytes, o, l), + CRC32ByteArray(bytes, o, l)); + } + } + + int len = bytes.length / 2; + assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len - 1), + CRC32ByteArray(bytes, 0, len - 1)); + assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len), + CRC32ByteArray(bytes, 0, len)); + assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len + 1), + CRC32ByteArray(bytes, 0, len + 1)); + + len = rnd.nextInt(bytes.length + 1); + off = rnd.nextInt(bytes.length - len); + assertEqual(CRC32BytesUsingUpdateInt(bytes, off, len), + CRC32ByteArray(bytes, off, len)); + } + + public static void main(String args[]) { + TestCRC32Update(); + TestCRC32UpdateBytes(); } } |