Skip to content

Commit 820ed16

Browse files
joviegaszoewangg
authored andcommitted
Enhancements in CRC32 and CRC32C Checksum implementations
1 parent eb2a0c8 commit 820ed16

23 files changed

+1002
-126
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "The SDK now defaults to Java built-in CRC32 and CRC32C(if it's Java 9+) implementations, resulting in improved performance."
6+
}

core/checksums/src/main/java/software/amazon/awssdk/checksums/SdkChecksum.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
import java.nio.ByteBuffer;
1919
import java.util.zip.Checksum;
2020
import software.amazon.awssdk.annotations.SdkProtectedApi;
21-
import software.amazon.awssdk.checksums.internal.Crc32CChecksum;
2221
import software.amazon.awssdk.checksums.internal.Crc32Checksum;
22+
import software.amazon.awssdk.checksums.internal.Crc32cProvider;
2323
import software.amazon.awssdk.checksums.internal.Crc64NvmeChecksum;
2424
import software.amazon.awssdk.checksums.internal.Md5Checksum;
2525
import software.amazon.awssdk.checksums.internal.Sha1Checksum;
@@ -39,7 +39,7 @@ public interface SdkChecksum extends Checksum {
3939
static SdkChecksum forAlgorithm(ChecksumAlgorithm algorithm) {
4040
switch (algorithm.algorithmId()) {
4141
case "CRC32C":
42-
return new Crc32CChecksum();
42+
return Crc32cProvider.create();
4343
case "CRC32":
4444
return new Crc32Checksum();
4545
case "SHA1":
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.checksums.internal;
17+
18+
import java.lang.ref.WeakReference;
19+
import java.lang.reflect.Constructor;
20+
import java.util.Collections;
21+
import java.util.Map;
22+
import java.util.Optional;
23+
import java.util.WeakHashMap;
24+
import java.util.concurrent.ConcurrentHashMap;
25+
import software.amazon.awssdk.annotations.SdkInternalApi;
26+
import software.amazon.awssdk.utils.ClassLoaderHelper;
27+
import software.amazon.awssdk.utils.Logger;
28+
29+
/**
30+
* A cache that stores classes and their constructors by class name and class loader.
31+
* <p>
32+
* This cache uses weak references to both class loaders and classes, allowing them to be garbage collected
33+
* when no longer needed. It provides methods to retrieve the zero-argument constructor for a class,
34+
* based on the current thread's context class loader or the system class loader.
35+
* <p>
36+
* If a class or its zero-argument constructor cannot be found, an empty result is returned.
37+
*/
38+
@SdkInternalApi
39+
public final class ConstructorCache {
40+
private static final Logger log = Logger.loggerFor(ConstructorCache.class);
41+
42+
/**
43+
* Cache storing classes by class name and class loader.
44+
* Uses weak references to allow garbage collection when not needed.
45+
*/
46+
private final Map<String, Map<ClassLoader, Optional<WeakReference<Class<?>>>>> classesByClassName =
47+
new ConcurrentHashMap<>();
48+
49+
/**
50+
* Retrieve the class for the given class name from the context or system class loader.
51+
* Returns an empty result if the class is not found.
52+
*/
53+
private Optional<Class<?>> getClass(String className) {
54+
Map<ClassLoader, Optional<WeakReference<Class<?>>>> classesByClassLoader =
55+
classesByClassName.computeIfAbsent(className, k -> Collections.synchronizedMap(new WeakHashMap<>()));
56+
57+
ClassLoader classLoader = ClassLoaderHelper.contextClassLoader();
58+
Optional<WeakReference<Class<?>>> classRef = classesByClassLoader.computeIfAbsent(classLoader, k -> {
59+
try {
60+
Class<?> clazz = classLoader.loadClass(className);
61+
return Optional.of(new WeakReference<>(clazz));
62+
} catch (ClassNotFoundException e) {
63+
return Optional.empty();
64+
}
65+
});
66+
return classRef.map(WeakReference::get);
67+
}
68+
69+
/**
70+
* Retrieve the zero-argument constructor for the given class name.
71+
* Returns an empty result if no such constructor is found.
72+
*/
73+
public Optional<Constructor<?>> getConstructor(String className) {
74+
return getClass(className).flatMap(clazz -> {
75+
try {
76+
return Optional.of(clazz.getConstructor());
77+
} catch (NoSuchMethodException e) {
78+
log.debug(() -> "Classloader contains " + className + ", but without a zero-arg constructor.", e);
79+
return Optional.empty();
80+
}
81+
});
82+
}
83+
}

core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/Crc32CChecksum.java

Lines changed: 0 additions & 78 deletions
This file was deleted.

core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/Crc32Checksum.java

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,64 +15,50 @@
1515

1616
package software.amazon.awssdk.checksums.internal;
1717

18-
import static software.amazon.awssdk.utils.NumericUtils.longToByte;
1918

20-
import java.util.Arrays;
21-
import java.util.zip.Checksum;
19+
import java.util.zip.CRC32;
2220
import software.amazon.awssdk.annotations.SdkInternalApi;
2321
import software.amazon.awssdk.checksums.SdkChecksum;
24-
import software.amazon.awssdk.crt.checksums.CRC32;
25-
import software.amazon.awssdk.utils.ClassLoaderHelper;
2622

27-
/**
28-
* Implementation of {@link SdkChecksum} to calculate an CRC32 checksum.
29-
*/
3023
@SdkInternalApi
31-
public class Crc32Checksum extends BaseCrcChecksum {
32-
private static final String CRT_CLASSPATH_FOR_CRC32 = "software.amazon.awssdk.crt.checksums.CRC32";
33-
private static final ThreadLocal<Boolean> IS_CRT_AVAILABLE = ThreadLocal.withInitial(Crc32Checksum::isCrtAvailable);
24+
public final class Crc32Checksum implements SdkChecksum {
25+
private final CrcCombineOnMarkChecksum crc32;
3426

35-
/**
36-
* Creates CRT Based Crc32 checksum if Crt classpath for Crc32 is loaded, else create Sdk Implemented Crc32.
37-
*/
3827
public Crc32Checksum() {
39-
super(createChecksum());
28+
// Delegates to CrcCombineOnMarkChecksum with CRC32
29+
this.crc32 = new CrcCombineOnMarkChecksum(
30+
new CRC32(),
31+
SdkCrc32Checksum::combine
32+
);
4033
}
4134

42-
private static Checksum createChecksum() {
43-
if (IS_CRT_AVAILABLE.get()) {
44-
return new CRC32();
45-
}
46-
47-
// TODO: use Java implementation
48-
return SdkCrc32Checksum.create();
35+
@Override
36+
public byte[] getChecksumBytes() {
37+
return crc32.getChecksumBytes();
4938
}
5039

51-
private static boolean isCrtAvailable() {
52-
try {
53-
ClassLoaderHelper.loadClass(CRT_CLASSPATH_FOR_CRC32, false);
54-
} catch (ClassNotFoundException e) {
55-
return false;
56-
}
57-
58-
return true;
40+
@Override
41+
public void mark(int readLimit) {
42+
crc32.mark(readLimit);
5943
}
6044

6145
@Override
62-
public byte[] getChecksumBytes() {
63-
return Arrays.copyOfRange(longToByte(getChecksum().getValue()), 4, 8);
46+
public void update(int b) {
47+
crc32.update(b);
6448
}
6549

6650
@Override
67-
public Checksum cloneChecksum(Checksum checksum) {
68-
if (checksum instanceof CRC32) {
69-
return (Checksum) ((CRC32) checksum).clone();
70-
}
51+
public void update(byte[] b, int off, int len) {
52+
crc32.update(b, off, len);
53+
}
7154

72-
if (checksum instanceof SdkCrc32Checksum) {
73-
return (Checksum) ((SdkCrc32Checksum) checksum).clone();
74-
}
55+
@Override
56+
public long getValue() {
57+
return crc32.getValue();
58+
}
7559

76-
throw new IllegalStateException("Unsupported checksum");
60+
@Override
61+
public void reset() {
62+
crc32.reset();
7763
}
7864
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.checksums.internal;
17+
18+
import java.util.zip.Checksum;
19+
import software.amazon.awssdk.annotations.SdkInternalApi;
20+
import software.amazon.awssdk.checksums.SdkChecksum;
21+
import software.amazon.awssdk.crt.checksums.CRC32C;
22+
23+
/**
24+
* Utility class to provide different implementations of CRC32C checksum. This class supports the use of: 1. Java-based CRC32C
25+
* (Java 9+ when available) 2. CRT-based CRC32C (when available) 3. SDK-based CRC32C (as fallback)
26+
*/
27+
@SdkInternalApi
28+
public final class Crc32cProvider {
29+
30+
31+
// Class paths for different CRC32C implementations
32+
private static final String CRT_CRC32C_CLASS_PATH = "software.amazon.awssdk.crt.checksums.CRC32C";
33+
private static final String JAVA_CRC32C_CLASS_PATH = "java.util.zip.CRC32C";
34+
private static final ConstructorCache CONSTRUCTOR_CACHE = new ConstructorCache();
35+
36+
// Private constructor to prevent instantiation
37+
private Crc32cProvider() {
38+
}
39+
40+
/**
41+
* Creates an instance of the SDK-based CRC32C checksum as a fallback.
42+
*
43+
* @return An SdkChecksum instance.
44+
*/
45+
static SdkChecksum createSdkBasedCrc32C() {
46+
SdkCrc32CChecksum sdkChecksum = SdkCrc32CChecksum.create();
47+
return new CrcCloneOnMarkChecksum(sdkChecksum, checksumToClone -> ((SdkCrc32CChecksum) checksumToClone).clone());
48+
}
49+
50+
/**
51+
* Tries to create a Java 9-based CRC32C checksum.
52+
* If it's not available, it tries to create a CRT-based checksum.
53+
* If both are not available, it falls back to an SDK-based CRC32C checksum.
54+
*
55+
* @return An instance of {@link SdkChecksum}, based on the first available option.
56+
*/
57+
public static SdkChecksum create() {
58+
SdkChecksum checksum = createJavaCrc32C();
59+
if (checksum == null) {
60+
checksum = createCrtCrc32C();
61+
}
62+
return checksum != null ? checksum : createSdkBasedCrc32C();
63+
}
64+
65+
static SdkChecksum createCrtCrc32C() {
66+
return CONSTRUCTOR_CACHE.getConstructor(CRT_CRC32C_CLASS_PATH).map(constructor -> {
67+
try {
68+
return new CrcCloneOnMarkChecksum((Checksum) constructor.newInstance(), checksumToClone ->
69+
(Checksum) ((CRC32C) checksumToClone).clone());
70+
} catch (ClassCastException | ReflectiveOperationException e) {
71+
throw new IllegalStateException("Failed to instantiate " + JAVA_CRC32C_CLASS_PATH, e);
72+
}
73+
}).orElse(null);
74+
}
75+
76+
static SdkChecksum createJavaCrc32C() {
77+
return CONSTRUCTOR_CACHE.getConstructor(JAVA_CRC32C_CLASS_PATH).map(constructor -> {
78+
try {
79+
return new CrcCombineOnMarkChecksum((Checksum) constructor.newInstance(), SdkCrc32CChecksum::combine);
80+
} catch (ClassCastException | ReflectiveOperationException e) {
81+
throw new IllegalStateException("Failed to instantiate " + JAVA_CRC32C_CLASS_PATH, e);
82+
}
83+
}).orElse(null);
84+
}
85+
86+
}

0 commit comments

Comments
 (0)