/*
 * Copyright 2014-2020 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 io.aeron.archive.checksum;

import org.agrona.LangUtil;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;

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>
 * <em>Note: Available only JDK 9+.</em>
 * </p>
 */
final class Crc32c implements Checksum
{
    private static final MethodHandle UPDATE_DIRECT_BYTE_BUFFER;
    public static final Crc32c INSTANCE;

    static
    {
        MethodHandle methodHandle = null;
        try
        {
            final Class<?> klass = Class.forName("java.util.zip.CRC32C");
            final Method method = klass.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 complete on the result value
            methodHandle = filterReturnValue(methodHandle, bitwiseComplement);
        }
        catch (final ClassNotFoundException ex)
        {
            // Expected case when executed on JDK 8
        }
        catch (final NoSuchMethodException | IllegalAccessException ex)
        {
            throw new Error("Failed to acquire method handle", ex);
        }

        UPDATE_DIRECT_BYTE_BUFFER = methodHandle;
        INSTANCE = null != methodHandle ? new Crc32c() : null;
    }

    private static int bitwiseComplement(final int value)
    {
        return ~value;
    }

    private Crc32c()
    {
    }

    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 throwable)
        {
            LangUtil.rethrowUnchecked(throwable);
            return -1;
        }
    }
}