Skip to content

Commit

Permalink
[Java] Add CRC-32 and CRC-32C checksums.
Browse files Browse the repository at this point in the history
  • Loading branch information
vyazelenko committed Nov 26, 2024
1 parent 718f7c9 commit 1e00e98
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 0 deletions.
35 changes: 35 additions & 0 deletions agrona/src/main/java/org/agrona/checksum/Checksum.java
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);
}
73 changes: 73 additions & 0 deletions agrona/src/main/java/org/agrona/checksum/Crc32.java
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;
}
}
}
93 changes: 93 additions & 0 deletions agrona/src/main/java/org/agrona/checksum/Crc32c.java
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;
}
}
}
51 changes: 51 additions & 0 deletions agrona/src/test/java/org/agrona/checksum/Crc32Test.java
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);
}
}
}
52 changes: 52 additions & 0 deletions agrona/src/test/java/org/agrona/checksum/Crc32cTest.java
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);
}
}
}
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ subprojects {

test {
jvmArgs('--add-opens', 'java.base/jdk.internal.misc=ALL-UNNAMED')
jvmArgs('--add-opens', 'java.base/java.util.zip=ALL-UNNAMED')

if (buildJavaVersion >= 21) {
jvmArgs('-XX:+EnableDynamicAgentLoading')
Expand Down

0 comments on commit 1e00e98

Please sign in to comment.