// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.util.internal;

import java.lang.reflect.Array;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.nio.ByteBuffer;
import java.lang.invoke.MethodHandle;
import io.netty.util.internal.logging.InternalLogger;

public class CleanerJava24Linker implements Cleaner
{
    private static final InternalLogger logger;
    private static final MethodHandle INVOKE_MALLOC;
    private static final MethodHandle INVOKE_CREATE_BYTEBUFFER;
    private static final MethodHandle INVOKE_FREE;
    
    static boolean isSupported() {
        return CleanerJava24Linker.INVOKE_MALLOC != null;
    }
    
    @Override
    public CleanableDirectBuffer allocate(final int capacity) {
        return new CleanableDirectBufferImpl(capacity);
    }
    
    @Override
    public void freeDirectBuffer(final ByteBuffer buffer) {
        throw new UnsupportedOperationException("Cannot clean arbitrary ByteBuffer instances");
    }
    
    static long malloc(final int capacity) {
        long addr;
        try {
            addr = CleanerJava24Linker.INVOKE_MALLOC.invokeExact((long)capacity);
        }
        catch (final Throwable e) {
            throw new Error(e);
        }
        if (addr == 0L) {
            throw new OutOfMemoryError("malloc(2) failed to allocate " + capacity + " bytes");
        }
        return addr;
    }
    
    static void free(final long memoryAddress) {
        try {
            CleanerJava24Linker.INVOKE_FREE.invokeExact(memoryAddress);
        }
        catch (final Throwable e) {
            throw new Error(e);
        }
    }
    
    static {
        boolean suitableJavaVersion;
        if (System.getProperty("org.graalvm.nativeimage.imagecode") != null) {
            final String v = System.getProperty("java.specification.version");
            try {
                suitableJavaVersion = (Integer.parseInt(v) >= 25);
            }
            catch (final NumberFormatException e) {
                suitableJavaVersion = false;
            }
            logger = null;
        }
        else {
            suitableJavaVersion = (PlatformDependent0.javaVersion() >= 24);
            logger = InternalLoggerFactory.getInstance(CleanerJava24Linker.class);
        }
        MethodHandle mallocMethod;
        MethodHandle freeMethod;
        MethodHandle wrapMethod;
        Throwable error;
        if (suitableJavaVersion) {
            try {
                final MethodHandles.Lookup lookup = MethodHandles.lookup();
                final Class<?> moduleCls = Class.forName("java.lang.Module");
                final MethodHandle getModule = lookup.findVirtual(Class.class, "getModule", MethodType.methodType(moduleCls));
                final MethodHandle isNativeAccessEnabledModule = lookup.findVirtual(moduleCls, "isNativeAccessEnabled", MethodType.methodType(Boolean.TYPE));
                final MethodHandle isNativeAccessEnabledForClass = MethodHandles.filterArguments(isNativeAccessEnabledModule, 0, getModule);
                final boolean isNativeAccessEnabled = isNativeAccessEnabledForClass.invokeExact((Class)CleanerJava24Linker.class);
                if (!isNativeAccessEnabled) {
                    throw new UnsupportedOperationException("Native access (restricted methods) is not enabled for the io.netty.common module.");
                }
                final Class<?> memoryLayoutCls = Class.forName("java.lang.foreign.MemoryLayout");
                final Class<?> memoryLayoutArrayCls = Class.forName("[Ljava.lang.foreign.MemoryLayout;");
                final Class<?> valueLayoutCls = Class.forName("java.lang.foreign.ValueLayout");
                final Class<?> valueLayoutAddressCls = Class.forName("java.lang.foreign.AddressLayout");
                final MethodHandle addressLayoutGetter = lookup.findStaticGetter(valueLayoutCls, "ADDRESS", valueLayoutAddressCls);
                final MethodHandle byteSize = lookup.findVirtual(valueLayoutAddressCls, "byteSize", MethodType.methodType(Long.TYPE));
                final MethodHandle byteSizeOfAddress = MethodHandles.foldArguments(byteSize, addressLayoutGetter);
                final long addressSize = byteSizeOfAddress.invokeExact();
                if (addressSize != 8L) {
                    throw new UnsupportedOperationException("Linking to malloc and free is only supported on 64-bit platforms.");
                }
                final Class<?> ofLongValueLayoutCls = Class.forName("java.lang.foreign.ValueLayout$OfLong");
                final Class<?> linkerCls = Class.forName("java.lang.foreign.Linker");
                final Class<?> linkerOptionCls = Class.forName("java.lang.foreign.Linker$Option");
                final Class<?> linkerOptionArrayCls = Class.forName("[Ljava.lang.foreign.Linker$Option;");
                final Class<?> symbolLookupCls = Class.forName("java.lang.foreign.SymbolLookup");
                final Class<?> memSegCls = Class.forName("java.lang.foreign.MemorySegment");
                final Class<?> funcDescCls = Class.forName("java.lang.foreign.FunctionDescriptor");
                final MethodHandle nativeLinker = lookup.findStatic(linkerCls, "nativeLinker", MethodType.methodType(linkerCls));
                final MethodHandle defaultLookupStatic = MethodHandles.foldArguments(lookup.findVirtual(linkerCls, "defaultLookup", MethodType.methodType(symbolLookupCls)), nativeLinker);
                final MethodHandle downcallHandleStatic = MethodHandles.foldArguments(lookup.findVirtual(linkerCls, "downcallHandle", MethodType.methodType(MethodHandle.class, memSegCls, funcDescCls, linkerOptionArrayCls)), nativeLinker);
                final MethodHandle findSymbol = MethodHandles.foldArguments(lookup.findVirtual(symbolLookupCls, "findOrThrow", MethodType.methodType(memSegCls, String.class)), defaultLookupStatic);
                final Object longLayout = lookup.findStaticGetter(valueLayoutCls, "JAVA_LONG", ofLongValueLayoutCls).invoke();
                final Object layoutArray = Array.newInstance(memoryLayoutCls, 1);
                Array.set(layoutArray, 0, longLayout);
                final MethodHandle mallocFuncDesc = MethodHandles.insertArguments(lookup.findStatic(funcDescCls, "of", MethodType.methodType(funcDescCls, memoryLayoutCls, memoryLayoutArrayCls)), 0, longLayout, layoutArray);
                final MethodHandle mallocLinker = MethodHandles.foldArguments(MethodHandles.foldArguments(downcallHandleStatic, MethodHandles.foldArguments(findSymbol, MethodHandles.constant(String.class, "malloc"))), mallocFuncDesc);
                mallocMethod = mallocLinker.invoke(Array.newInstance(linkerOptionCls, 0));
                final MethodHandle freeFuncDesc = MethodHandles.insertArguments(lookup.findStatic(funcDescCls, "ofVoid", MethodType.methodType(funcDescCls, memoryLayoutArrayCls)), 0, layoutArray);
                final MethodHandle freeLinker = MethodHandles.foldArguments(MethodHandles.foldArguments(downcallHandleStatic, MethodHandles.foldArguments(findSymbol, MethodHandles.constant(String.class, "free"))), freeFuncDesc);
                freeMethod = freeLinker.invoke(Array.newInstance(linkerOptionCls, 0));
                final MethodHandle ofAddress = lookup.findStatic(memSegCls, "ofAddress", MethodType.methodType(memSegCls, Long.TYPE));
                final MethodHandle reinterpret = lookup.findVirtual(memSegCls, "reinterpret", MethodType.methodType(memSegCls, Long.TYPE));
                final MethodHandle asByteBuffer = lookup.findVirtual(memSegCls, "asByteBuffer", MethodType.methodType(ByteBuffer.class));
                wrapMethod = MethodHandles.filterReturnValue(MethodHandles.filterArguments(reinterpret, 0, ofAddress), asByteBuffer);
                error = null;
            }
            catch (final Throwable throwable) {
                mallocMethod = null;
                wrapMethod = null;
                freeMethod = null;
                error = throwable;
            }
        }
        else {
            mallocMethod = null;
            wrapMethod = null;
            freeMethod = null;
            error = new UnsupportedOperationException("java.lang.foreign.MemorySegment unavailable");
        }
        if (CleanerJava24Linker.logger != null) {
            if (error == null) {
                CleanerJava24Linker.logger.debug("java.nio.ByteBuffer.cleaner(): available");
            }
            else {
                CleanerJava24Linker.logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
            }
        }
        INVOKE_MALLOC = mallocMethod;
        INVOKE_CREATE_BYTEBUFFER = wrapMethod;
        INVOKE_FREE = freeMethod;
    }
    
    private static final class CleanableDirectBufferImpl implements CleanableDirectBuffer
    {
        private final ByteBuffer buffer;
        private final long memoryAddress;
        
        private CleanableDirectBufferImpl(final int capacity) {
            final long addr = CleanerJava24Linker.malloc(capacity);
            try {
                this.memoryAddress = addr;
                this.buffer = CleanerJava24Linker.INVOKE_CREATE_BYTEBUFFER.invokeExact(addr, (long)capacity);
            }
            catch (final Throwable throwable) {
                final Error error = new Error(throwable);
                try {
                    CleanerJava24Linker.free(addr);
                }
                catch (final Throwable e) {
                    error.addSuppressed(e);
                }
                throw error;
            }
        }
        
        @Override
        public ByteBuffer buffer() {
            return this.buffer;
        }
        
        @Override
        public void clean() {
            CleanerJava24Linker.free(this.memoryAddress);
        }
        
        @Override
        public boolean hasMemoryAddress() {
            return true;
        }
        
        @Override
        public long memoryAddress() {
            return this.memoryAddress;
        }
    }
}
