SquirrelJME/SquirrelJME

View on GitHub
modules/cldc-compact/src/main/java/cc/squirreljme/jvm/pack/mem/ReadableMemoryInputStream.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// 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.jvm.pack.mem;

import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;

/**
 * This is an input stream which can read from readable memory.
 * 
 * For anything from {@link DataInput} the proper type based methods are used,
 * as such if there is a specific byte order that is used it will be
 * derived from this.
 *
 * @since 2019/04/21
 */
public final class ReadableMemoryInputStream
    extends InputStream
    implements DataInput, MemoryStream
{
    /** The base read address. */
    protected final long address;
    
    /** The number of bytes that can be read. */
    protected final long length;
    
    /** The input memory. */
    protected final ReadableMemory memory;
    
    /** The current read offset. */
    private int _at;
    
    /**
     * Initializes the input read memory, the byte order is big endian.
     * 
     * @param __mem The memory to read from.
     * @throws NullPointerException On null arguments.
     * @since 2020/12/09
     */
    public ReadableMemoryInputStream(ReadableMemory __mem)
        throws NullPointerException
    {
        // The address is zero and not __mem.memRegionOffset() because this
        // address is relative to the piece of memory and not the address
        // of that memory
        this(__mem, 0, __mem.memRegionSize());
    }
    
    /**
     * Initializes the stream, the byte order is big endian.
     *
     * @param __mem The memory.
     * @param __addr The start address.
     * @param __len The length.
     * @throws IllegalArgumentException If the length is negative.
     * @throws NullPointerException On null arguments.
     * @since 2019/04/21
     */
    public ReadableMemoryInputStream(ReadableMemory __mem,
        long __addr, long __len)
        throws IllegalArgumentException, NullPointerException
    {
        if (__mem == null)
            throw new NullPointerException("NARG");
        if (__len < 0)
            throw new IllegalArgumentException("NEGV");
        
        this.memory = __mem;
        this.address = __addr;
        this.length = __len;
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/04
     * @return
     */
    @Override
    public long address()
    {
        return this.address + this._at;
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/04/21
     */
    @Override
    public final int available()
    {
        return (int)Math.max(Integer.MAX_VALUE, this.length - this._at);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/04/21
     */
    @Override
    public final void close()
    {
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/04/21
     */
    @SuppressWarnings("MagicNumber")
    @Override
    public final int read()
    {
        // EOF?
        int at = this._at;
        if (at >= this.length)
            return -1;
        
        // Read
        this._at = at + 1;
        return this.memory.memReadByte(this.address + at) & 0xFF;
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/04/21
     */
    @Override
    public final int read(byte[] __b)
        throws NullPointerException
    {
        return this.read(__b, 0, __b.length);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/04/21
     */
    @Override
    public final int read(byte[] __b, int __o, int __l)
        throws IndexOutOfBoundsException, NullPointerException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        if (__o < 0 || __l < 0 || (__o + __l) > __b.length)
            throw new IndexOutOfBoundsException("IOOB");
        
        // Used to check bounds
        int at = this._at;
        long length = this.length;
        long address = this.address;
        
        // Is already at EOF
        if (at >= length)
            return -1;
        
        // Read in all bytes
        ReadableMemory memory = this.memory;
        long from = address + at;
        for (int i = 0; i < __l; i++)
        {
            // EOF?
            if (at >= length)
            {
                this._at = at;
                return (i == 0 ? -1 : i);
            }
            
            // Read in
            __b[__o++] = (byte)memory.memReadByte(from++);
            at++;
        }
        
        // Count
        this._at = at;
        return __l;
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public boolean readBoolean()
        throws IOException
    {
        return this.readByte() != 0;
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public byte readByte()
        throws IOException
    {
        return (byte)this.memory.memReadByte(this.__checkIncr(1));
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public char readChar()
        throws IOException
    {
        return (char)this.readShort();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public double readDouble()
        throws IOException
    {
        return Double.longBitsToDouble(this.readLong());
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public float readFloat()
        throws IOException
    {
        return Float.intBitsToFloat(this.readInt());
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public void readFully(byte[] __b)
        throws IOException
    {
        throw Debugging.todo();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public void readFully(byte[] __b, int __o, int __l)
        throws IOException
    {
        throw Debugging.todo();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public int readInt()
        throws IOException
    {
        return this.memory.memReadInt(this.__checkIncr(4));
    }
    
    /**
     * This method is not in CLDC, do not use!
     * 
     * @deprecated Not in CLDC, do not use!
     * @since 2021/02/09
     */
    @Deprecated
    @SuppressWarnings({"override", "ConstantConditions"})
    public String readLine()
        throws IOException
    {
        if (false)
            throw new IOException();
        throw Debugging.oops();
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public long readLong()
        throws IOException
    {
        return this.memory.memReadLong(this.__checkIncr(8));
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public short readShort()
        throws IOException
    {
        return (short)this.memory.memReadShort(this.__checkIncr(2));
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public String readUTF()
        throws IOException
    {
        return DataInputStream.readUTF(this);
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @SuppressWarnings("MagicNumber")
    @Override
    public int readUnsignedByte()
        throws IOException
    {
        return this.readByte() & 0xFF;
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @SuppressWarnings("MagicNumber")
    @Override
    public int readUnsignedShort()
        throws IOException
    {
        return this.readShort() & 0xFFFF;
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/04
     */
    @Override
    public int offset()
    {
        return this._at;
    }
    
    /**
     * {@inheritDoc}
     * @since 2021/02/09
     */
    @Override
    public int skipBytes(int __n)
        throws IOException
    {
        throw Debugging.todo();
    }
    
    /**
     * Checks for a read which would overflow and if it is successful will
     * increment accordingly for the next address.
     * 
     * @param __len The number of bytes to check on read.
     * @return The address to read from.
     * @throws IllegalArgumentException If the length is zero or negative.
     * @throws EOFException If the end of file is reached.
     * @since 2021/02/14
     */
    private long __checkIncr(int __len)
        throws IllegalArgumentException, EOFException
    {
        if (__len <= 0)
            throw new IllegalArgumentException("INEG");
        
        /* {@squirreljme.error ZZ3z Reached end of memory stream.
        (The position; The length of the stream; The length checked)} */
        int at = this._at;
        if (at + (__len - 1) > this.length)
            throw new EOFException(String.format("ZZ3z %d %d %d",
                at, this.length, __len));
        
        // Increment and use the old address
        this._at = at + __len;
        return this.address + at;
    }
}