SquirrelJME/SquirrelJME

View on GitHub
emulators/nanocoat-vm/src/main/java/cc/squirreljme/vm/nanocoat/AllocLink.java

Summary

Maintainability
B
5 hrs
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// Multi-Phasic Applications: SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.vm.nanocoat;

import cc.squirreljme.emulator.vm.VMException;
import cc.squirreljme.runtime.cldc.debug.Debugging;

/**
 * Represents an allocation link.
 *
 * @since 2023/12/14
 */
public final class AllocLink
    implements Destructable, Pointer
{
    /** The pointer to the memory block. */
    private final long _blockPtr;
    
    /** The pointer to the link block. */
    private final long _linkPtr;
    
    /**
     * Initializes the allocation link.
     *
     * @param __blockPtr The block pointer address.
     * @param __linkPtr The link pointer address.
     * @throws NullPointerException If either address is null.
     * @since 2023/12/14
     */
    AllocLink(long __blockPtr, long __linkPtr)
        throws NullPointerException
    {
        if (__blockPtr == 0 || __linkPtr == 0)
            throw new NullPointerException("Null AllocLink.");
        
        this._blockPtr = __blockPtr;
        this._linkPtr = __linkPtr;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/12/16
     */
    @Override
    public void close()
        throws VMException
    {
        throw Debugging.todo();
    }
    
    /**
     * Returns the link address.
     *
     * @return The address of the link.
     * @since 2023/12/14
     */
    public long linkAddress()
    {
        return this._linkPtr;
    }
    
    /**
     * Reads bytes at the given offset.
     *
     * @param __at The offset to read from.
     * @param __b The buffer to read into.
     * @throws IndexOutOfBoundsException If the address is not valid or the
     * number of bytes exceeds the allocation link size.
     * @throws NullPointerException On null arguments.
     * @throws VMException If it could not be read.
     * @since 2023/12/17
     */
    public void read(int __at, byte[] __b)
        throws IndexOutOfBoundsException, NullPointerException, VMException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        
        this.read(__at, __b, 0, __b.length);
    }
    
    /**
     * Reads bytes at the given offset.
     *
     * @param __at The offset to read from.
     * @param __b The buffer to read into.
     * @param __o The offset into the buffer.
     * @param __l The length of the buffer.
     * @throws IndexOutOfBoundsException If the address is not valid or the
     * number of bytes exceeds the allocation link size; or the offset
     * and/or length are negative or exceed the array bounds.
     * @throws NullPointerException On null arguments.
     * @throws VMException If it could not be read.
     * @since 2023/12/17
     */
    public void read(int __at, byte[] __b, int __o, int __l)
        throws IndexOutOfBoundsException, NullPointerException, VMException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        
        // Check size first
        int size = this.size();
        if (__at < 0 || __at >= size || (__at + __l) > size ||
            (__at + __l) < 0 || __o < 0 || __l < 0 ||
            (__o + __l) > __b.length || (__o + __l) < 0)
            throw new IndexOutOfBoundsException(
                String.format("read(%d, %h, %d, %d) in %d from %d",
                    __at, __b, __o, __l, size, __b.length));
        
        // Call native read function
        AllocLink.__read(this._blockPtr, __at, __b, __o, __l);
    }
    
    public int readInt(int __at)
        throws IndexOutOfBoundsException, VMException
    {
        byte[] buf = new byte[4];
        
        // Read in bytes.
        this.read(__at, buf, 0, 4);
        
        // Map bytes.
        int result = 0;
        if (Utils.isBigEndian())
        {
            result |= ((buf[0] << 24) & 0xFF000000);
            result |= ((buf[1] << 16) & 0xFF0000);
            result |= ((buf[2] << 8) & 0xFF00);
            result |= ((buf[3]) & 0xFF);
        }
        else
        {
            result |= ((buf[0]) & 0xFF);
            result |= ((buf[1] << 8) & 0xFF00);
            result |= ((buf[2] << 16) & 0xFF0000);
            result |= ((buf[3] << 24) & 0xFF000000);
        }
        
        return result;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/12/14
     */
    @Override
    public long pointerAddress()
    {
        return this._blockPtr;
    }
    
    /**
     * Returns the size of the allocation link.
     *
     * @return The size of the link.
     * @throws VMException If it could not be calculated.
     * @since 2023/12/14
     */
    public int size()
        throws VMException
    {
        return AllocLink.__size(this._linkPtr);
    }
    
    /**
     * Writes the given bytes at the address.
     *
     * @param __at The address to write at.
     * @param __b The bytes to write.
     * @throws IndexOutOfBoundsException If the address is not valid or the
     * number of bytes exceeds the allocation link size.
     * @throws NullPointerException On null arguments.
     * @throws VMException If it could not be written.
     * @since 2023/12/16
     */
    public void write(int __at, byte[] __b)
        throws IndexOutOfBoundsException, NullPointerException, VMException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        
        this.write(__at, __b, 0, __b.length);
    }
    
    /**
     * Writes the given bytes at the address.
     *
     * @param __at The address to write at.
     * @param __b The bytes to write.
     * @param __o The offset into the buffer.
     * @param __l The number of bytes to copy.
     * @throws IndexOutOfBoundsException If the address is not valid or the
     * number of bytes exceeds the allocation link size; or the offset
     * and/or length are negative or exceed the array bounds.
     * @throws NullPointerException On null arguments.
     * @throws VMException If it could not be written.
     * @since 2023/12/16
     */
    public void write(int __at, byte[] __b, int __o, int __l)
        throws IndexOutOfBoundsException, NullPointerException, VMException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        
        // Check size first
        int size = this.size();
        if (__at < 0 || __at >= size || (__at + __l) > size ||
            (__at + __l) < 0 || __o < 0 || __l < 0 ||
            (__o + __l) > __b.length || (__o + __l) < 0)
            throw new IndexOutOfBoundsException(
                String.format("write(%d, %h, %d, %d) in %d from %d",
                    __at, __b, __o, __l, size, __b.length));
        
        // Call native write function
        AllocLink.__write(this._blockPtr, __at, __b, __o, __l);
    }
    
    /**
     * Writes the value at the given address.
     *
     * @param __at The address to write at.
     * @param __value The value to write.
     * @throws IndexOutOfBoundsException If the address is out of range.
     * @throws VMException If it could not be written.
     * @since 2023/12/16
     */
    public void writeInt(int __at, int __value)
        throws IndexOutOfBoundsException, VMException
    {
        int size = this.size();
        if (__at < 0 || (__at + 4) > size)
            throw new IndexOutOfBoundsException("IOOB");
        
        // Map bytes
        byte[] buf = new byte[4];
        if (Utils.isBigEndian())
        {
            buf[0] = (byte)(__value >>> 24);
            buf[1] = (byte)(__value >>> 16);
            buf[2] = (byte)(__value >>> 8);
            buf[3] = (byte)(__value);
        }
        else
        {
            buf[0] = (byte)(__value);
            buf[1] = (byte)(__value >>> 8);
            buf[2] = (byte)(__value >>> 16);
            buf[3] = (byte)(__value >>> 24);
        }
        
        // Write them
        this.write(__at, buf, 0, 4);
    }
    
    /**
     * Writes the value at the given address.
     *
     * @param __at The address to write at.
     * @param __value The value to write.
     * @throws IndexOutOfBoundsException If the address is out of range.
     * @throws VMException If it could not be written.
     * @since 2023/12/16
     */
    public void writeLong(int __at, long __value)
        throws IndexOutOfBoundsException, VMException
    {
        int size = this.size();
        if (__at < 0 || (__at + 8) > size)
            throw new IndexOutOfBoundsException("IOOB");
        
        // Map bytes
        byte[] buf = new byte[8];
        if (Utils.isBigEndian())
        {
            buf[0] = (byte)(__value >>> 56);
            buf[1] = (byte)(__value >>> 48);
            buf[2] = (byte)(__value >>> 40);
            buf[3] = (byte)(__value >>> 32);
            buf[4] = (byte)(__value >>> 24);
            buf[5] = (byte)(__value >>> 16);
            buf[6] = (byte)(__value >>> 8);
            buf[7] = (byte)(__value);
        }
        else
        {
            buf[0] = (byte)(__value);
            buf[1] = (byte)(__value >>> 8);
            buf[2] = (byte)(__value >>> 16);
            buf[3] = (byte)(__value >>> 24);
            buf[4] = (byte)(__value >>> 32);
            buf[5] = (byte)(__value >>> 40);
            buf[6] = (byte)(__value >>> 48);
            buf[7] = (byte)(__value >>> 56);
        }
        
        // Write them
        this.write(__at, buf, 0, 8);
    }
    
    /**
     * Writes a pointer value.
     *
     * @param __at The address to write at.
     * @param __pointer The pointer to write.
     * @return The size of the pointer, the number of bytes to move forward
     * for an adjacent write.
     * @throws IndexOutOfBoundsException If the address is not valid.
     * @throws VMException If the pointer could not be written.
     * @since 2023/12/16
     */
    public int writePointer(int __at, long __pointer)
        throws IndexOutOfBoundsException, VMException
    {
        // What is the pointer size?
        int pointerSize = Utils.pointerSize();
        switch (pointerSize)
        {
            case 4:
                this.writeInt(__at, (int)__pointer);
                return pointerSize;
                
            case 8:
                this.writeLong(__at, __pointer);
                return pointerSize;
                
            default:
                throw Debugging.todo(pointerSize);
        }
    }
    
    /**
     * Initializes a link from the given block pointer.
     *
     * @param __blockPtr The block pointer.
     * @return The resultant link.
     * @since 2023/12/20
     */
    public static AllocLink ofBlockPtr(long __blockPtr)
    {
        if (__blockPtr == 0)
            return null;
        
        return new AllocLink(__blockPtr, AllocPool.__getLink(__blockPtr));
    }
    
    /**
     * Reads bytes at the given offset.
     *
     * @param __blockPtr The block pointer.
     * @param __at The offset to read from.
     * @param __b The buffer to read into.
     * @param __o The offset into the buffer.
     * @param __l The length of the buffer.
     * @throws VMException If it could not be read.
     * @since 2023/12/17
     */
    private static native void __read(long __blockPtr, int __at,
        byte[] __b, int __o, int __l)
        throws VMException;
    
    /**
     * Returns the size of the allocation pointer. 
     *
     * @param __linkPtr The link pointer.
     * @return The resultant size.
     * @throws VMException If it could not be determined.
     * @since 2023/12/14
     */
    private static native int __size(long __linkPtr)
        throws VMException;
    
    /**
     * Writes the given bytes at the address.
     *
     * @param __blockPtr The block pointer.
     * @param __at The address to write at.
     * @param __b The buffer to write.
     * @param __o The offset into the buffer.
     * @param __l The length of the buffer.
     * @throws VMException If it could not be written.
     * @since 2023/12/16
     */
    private static native void __write(long __blockPtr, int __at,
        byte[] __b, int __o, int __l)
        throws VMException;
}