-
Notifications
You must be signed in to change notification settings - Fork 403
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Java] Add CRC-32 and CRC-32C checksums.
- Loading branch information
1 parent
718f7c9
commit 1e00e98
Showing
6 changed files
with
305 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright 2014-2024 Real Logic Limited. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.agrona.checksum; | ||
|
||
/** | ||
* An interface representing API to compute a data checksum. | ||
* <p> | ||
* <b>Note:</b> Instances should be threadsafe and stateless. | ||
*/ | ||
@FunctionalInterface | ||
public interface Checksum | ||
{ | ||
/** | ||
* Computes a checksum based on the contents of a {@code java.nio.DirectByteBuffer}. | ||
* | ||
* @param address of the buffer. | ||
* @param offset within the buffer to begin at. | ||
* @param length of the data to read. | ||
* @return computed checksum value. | ||
*/ | ||
int compute(long address, int offset, int length); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* Copyright 2014-2024 Real Logic Limited. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.agrona.checksum; | ||
|
||
import org.agrona.LangUtil; | ||
|
||
import java.lang.invoke.MethodHandle; | ||
import java.lang.invoke.MethodHandles; | ||
import java.lang.reflect.Method; | ||
import java.util.zip.CRC32; | ||
|
||
/** | ||
* Implementation of the {@link Checksum} interface that computes CRC-32 checksum. | ||
*/ | ||
public final class Crc32 implements Checksum | ||
{ | ||
/** | ||
* Singleton instance to compute CRC-32 checksum. | ||
*/ | ||
public static final Crc32 INSTANCE = new Crc32(); | ||
|
||
private static final MethodHandle UPDATE_BYTE_BUFFER; | ||
|
||
static | ||
{ | ||
try | ||
{ | ||
final Method method = | ||
CRC32.class.getDeclaredMethod("updateByteBuffer0", int.class, long.class, int.class, int.class); | ||
method.setAccessible(true); | ||
MethodHandle methodHandle = MethodHandles.lookup().unreflect(method); | ||
methodHandle = MethodHandles.insertArguments(methodHandle, 0, 0); | ||
UPDATE_BYTE_BUFFER = methodHandle; | ||
} | ||
catch (final Exception ex) | ||
{ | ||
throw new Error(ex); | ||
} | ||
} | ||
|
||
private Crc32() | ||
{ | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public int compute(final long address, final int offset, final int length) | ||
{ | ||
try | ||
{ | ||
return (int)UPDATE_BYTE_BUFFER.invokeExact(address, offset, length); | ||
} | ||
catch (final Throwable t) | ||
{ | ||
LangUtil.rethrowUnchecked(t); | ||
return -1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* Copyright 2014-2024 Real Logic Limited. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.agrona.checksum; | ||
|
||
import org.agrona.LangUtil; | ||
|
||
import java.lang.invoke.MethodHandle; | ||
import java.lang.reflect.Method; | ||
import java.util.zip.CRC32C; | ||
|
||
import static java.lang.invoke.MethodHandles.*; | ||
import static java.lang.invoke.MethodType.methodType; | ||
|
||
/** | ||
* Implementation of the {@link Checksum} interface that computes CRC-32C checksum. | ||
* | ||
* <p> | ||
* CRC-32C is defined in <a href="http://www.ietf.org/rfc/rfc3720.txt">RFC 3720</a>: | ||
* Internet Small Computer Systems Interface (iSCSI). | ||
* </p> | ||
*/ | ||
public final class Crc32c implements Checksum | ||
{ | ||
/** | ||
* Single instance to compute CRC-32C checksum. | ||
*/ | ||
public static final Crc32c INSTANCE = new Crc32c(); | ||
|
||
private static final MethodHandle UPDATE_DIRECT_BYTE_BUFFER; | ||
|
||
static | ||
{ | ||
MethodHandle methodHandle; | ||
try | ||
{ | ||
final Method method = CRC32C.class.getDeclaredMethod( | ||
"updateDirectByteBuffer", int.class, long.class, int.class, int.class); | ||
method.setAccessible(true); | ||
final Lookup lookup = lookup(); | ||
methodHandle = lookup.unreflect(method); | ||
final MethodHandle bitwiseComplement = lookup.findStatic( | ||
Crc32c.class, "bitwiseComplement", methodType(int.class, int.class)); | ||
// Always invoke with the 0xFFFFFFFF as first argument, i.e. empty CRC value | ||
methodHandle = insertArguments(methodHandle, 0, 0xFFFFFFFF); | ||
// Always compute bitwise complement on the result value | ||
methodHandle = filterReturnValue(methodHandle, bitwiseComplement); | ||
} | ||
catch (final Exception ex) | ||
{ | ||
throw new Error(ex); | ||
} | ||
|
||
UPDATE_DIRECT_BYTE_BUFFER = methodHandle; | ||
} | ||
|
||
private static int bitwiseComplement(final int value) | ||
{ | ||
return ~value; | ||
} | ||
|
||
private Crc32c() | ||
{ | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public int compute(final long address, final int offset, final int length) | ||
{ | ||
try | ||
{ | ||
return (int)UPDATE_DIRECT_BYTE_BUFFER.invokeExact(address, offset, offset + length /* end */); | ||
} | ||
catch (final Throwable t) | ||
{ | ||
LangUtil.rethrowUnchecked(t); | ||
return -1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright 2014-2024 Real Logic Limited. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.agrona.checksum; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.util.Random; | ||
import java.util.zip.CRC32; | ||
|
||
import static org.agrona.BufferUtil.address; | ||
import static org.junit.jupiter.api.Assertions.*; | ||
|
||
class Crc32Test | ||
{ | ||
@Test | ||
void compute() | ||
{ | ||
final Random random = new Random(-1234); | ||
final int offset = 3; | ||
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024 + offset); | ||
final long address = address(buffer); | ||
for (int i = 1; i <= 1024; i++) | ||
{ | ||
final int length = i; | ||
final byte[] data = new byte[length]; | ||
random.nextBytes(data); | ||
buffer.clear().position(offset); | ||
buffer.put(data); | ||
buffer.flip().position(offset); | ||
final CRC32 crc32 = new CRC32(); | ||
crc32.update(buffer); | ||
final int checksum = (int)crc32.getValue(); | ||
assertEquals( | ||
checksum, Crc32.INSTANCE.compute(address, offset, length), () -> "Failed on length: " + length); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* Copyright 2014-2024 Real Logic Limited. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.agrona.checksum; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.util.Random; | ||
import java.util.zip.CRC32C; | ||
|
||
import static org.agrona.BufferUtil.address; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
class Crc32cTest | ||
{ | ||
@Test | ||
void compute() | ||
{ | ||
final Random random = new Random(54893045794L); | ||
final int offset = 7; | ||
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024 + offset); | ||
final long address = address(buffer); | ||
|
||
for (int i = 1; i <= 1024; i++) | ||
{ | ||
final int length = i; | ||
final byte[] data = new byte[length]; | ||
random.nextBytes(data); | ||
buffer.clear().position(offset); | ||
buffer.put(data); | ||
buffer.flip().position(offset); | ||
final CRC32C crc32c = new CRC32C(); | ||
crc32c.update(buffer); | ||
final int checksum = (int)crc32c.getValue(); | ||
assertEquals( | ||
checksum, Crc32c.INSTANCE.compute(address, offset, length), () -> "Failed on length: " + length); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters