SquirrelJME/SquirrelJME

View on GitHub
modules/io/src/main/java/net/multiphasicapps/io/ChunkSection.java

Summary

Maintainability
A
0 mins
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 net.multiphasicapps.io;

import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * This represents a single section within the output.
 *
 * @since 2019/08/11
 */
public final class ChunkSection
    extends OutputStream
    implements DataOutput
{
    /** The size of the buffer. */
    private static final int _BUFFER_SIZE =
        512;
    
    /** The fixed size of this section. */
    protected final int fixedSize;
    
    /** The alignment of this section. */
    protected final int alignment;
    
    /** Is this a variable size section? */
    protected final boolean isVariable;
    
    /** Futures and where they go. */
    final List<__FuturePoint__> _futures =
        new LinkedList<>();
    
    /** The tracker for the dirtiness. */
    final __Dirty__ _dirty;
    
    /** The byte buffer data. */
    byte[] _data;
    
    /** The current size of the section. */
    int _size;
    
    /** The write address of this section. */
    int _writeAddr =
        -1;
    
    /** The write size of this section. */
    int _writeSize =
        -1;
    
    /**
     * Initializes the written section.
     *
     * @param __size The size to use.
     * @param __align The alignment to use.
     * @param __d The dirty flag.
     * @throws IllegalArgumentException If the size is zero or negative.
     * @throws NullPointerException On null arguments.
     * @since 2019/08/11
     */
    ChunkSection(int __size, int __align, __Dirty__ __d)
        throws IllegalArgumentException, NullPointerException
    {
        if (__d == null)
            throw new NullPointerException("NARG");
        
        /* {@squirreljme.error BD3l Zero or negative size. (The size)} */
        if (__size != ChunkWriter.VARIABLE_SIZE && __size <= 0)
            throw new IllegalArgumentException("BD3l " + __size);
        
        // Set
        this.fixedSize = __size;
        this.alignment = (Math.max(__align, 1));
        this.isVariable = (__size == ChunkWriter.VARIABLE_SIZE);
        
        // Dirty flag storage
        this._dirty = __d;
        
        // If this is a fixed size section, we never have to expand it
        // so we can allocate all the needed data!
        if (__size != ChunkWriter.VARIABLE_SIZE)
            this._data = new byte[__size];
        else
            this._data = new byte[ChunkSection._BUFFER_SIZE];
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void close()
    {
        // Do nothing
    }
    
    /**
     * Returns a byte array of all the data that was written.
     * 
     * @return A copy of the byte array that makes up this data.
     * @since 2020/12/16
     */
    public byte[] currentBytes()
    {
        return Arrays.copyOf(this._data, this._size);
    }
    
    /**
     * Returns an input stream of what is currently in the input buffer, it is
     * undefined whether or not future data is written or not.
     * 
     * @return An input stream of the current buffer data.
     * @since 2020/12/13
     */
    public final InputStream currentStream()
    {
        return new ByteArrayInputStream(this._data, 0, this._size);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void flush()
    {
        // Do nothing
    }
    
    /**
     * Returns a future on the section address.
     * 
     * @return The future.
     * @since 2020/12/04
     */
    public final ChunkFutureSection futureAddress()
    {
        return this.futureAddress(0);
    }
    
    /**
     * Returns a future on the section address with a given offset.
     * 
     * @param __off The offset used.
     * @return The future.
     * @since 2020/12/04
     */
    public final ChunkFutureSection futureAddress(int __off)
    {
        return new ChunkFutureSection(
            ChunkFutureSectionKind.ADDRESS, this, __off);
    }
    
    /**
     * Returns a future on the section size.
     * 
     * @return The future.
     * @since 2020/12/04
     */
    public final ChunkFutureSection futureSize()
    {
        return this.futureSize(0);
    }
    
    /**
     * Returns a future on the section size with a given offset.
     * 
     * @param __off The offset used.
     * @return The future.
     * @since 2020/12/04
     */
    public final ChunkFutureSection futureSize(int __off)
    {
        return new ChunkFutureSection(
            ChunkFutureSectionKind.SIZE, this, __off);
    }
    
    /**
     * Returns the current written size of the section.
     *
     * @return The current section size.
     * @since 2019/08/11
     */
    public final int size()
    {
        return this._size;
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void write(int __b)
        throws IOException
    {
        /* {@squirreljme.error BD3m Size of section exceeded. (The size
        of the section)} */
        int size = this._size;
        if (!this.isVariable && size + 1 > this.fixedSize)
            throw new IOException("BD3m " + size);
        
        // Possibly resize the data array, only when variable
        byte[] data = this._data;
        if (this.isVariable && size >= data.length)
        {
            data = Arrays.copyOf(data, size + ChunkSection._BUFFER_SIZE);
            this._data = data;
        }
        
        // Write into the data
        data[size] = (byte)__b;
        
        // Size up
        this._size = size + 1;
        
        // Becomes dirty
        this._dirty._dirty = true;
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void write(byte[] __b)
        throws IOException, NullPointerException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        
        this.write(__b, 0, __b.length);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void write(byte[] __b, int __o, int __l)
        throws IndexOutOfBoundsException, IOException, NullPointerException
    {
        if (__b == null)
            throw new NullPointerException("NARG");
        if (__o < 0 || __l < 0 || (__o + __l) > __b.length)
            throw new IndexOutOfBoundsException("IOOB");
        
        /* {@squirreljme.error BD3p Size of section exceeded.} */
        int size = this._size;
        if (!this.isVariable && size + __l > this.fixedSize)
            throw new IOException("BD3p");
        
        // Possibly resize the data array (only when variable)
        byte[] data = this._data;
        if (this.isVariable && size + __l >= data.length)
        {
            data = Arrays.copyOf(data,
                size + (Math.max(__l, ChunkSection._BUFFER_SIZE)));
            this._data = data;
        }
        
        // Write into the data
        for (int i = 0; i < __l; i++)
            data[size++] = __b[__o++];
        
        // Size up
        this._size = size;
        
        // Becomes dirty
        this._dirty._dirty = true;
    }
    
    /**
     * Writes padding which aligns to a given amount from within the
     * data stream as itself.
     *
     * @param __n The number of bytes to align to.
     * @return The number of alignment bytes written.
     * @throws IllegalArgumentException If the requested alignment is
     * negative.
     * @throws IOException On write errors.
     * @since 2019/08/11
     */
    public final int writeAlignment(int __n)
        throws IllegalArgumentException, IOException
    {
        /* {@squirreljme.error BD3k Cannot align to a negative amount.
        (The alignment)} */
        if (__n < 1)
            throw new IllegalArgumentException("BD3k " + __n);
        
        // Not point aligning to a byte
        if (__n == 1)
            return 0;
        
        throw Debugging.todo();
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeBoolean(boolean __v)
        throws IOException
    {
        throw Debugging.todo();
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeByte(int __v)
        throws IOException
    {
        this.write(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeBytes(String __v)
        throws IOException, NullPointerException
    {
        if (__v == null)
            throw new NullPointerException("NARG");
        
        for (int i = 0, n = __v.length(); i < n; i++)
            this.write(__v.charAt(i));
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeChar(int __v)
        throws IOException
    {
        this.writeShort(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeChars(String __v)
        throws IOException, NullPointerException
    {
        if (__v == null)
            throw new NullPointerException("NARG");
        
        for (int i = 0, n = __v.length(); i < n; i++)
        {
            char c = __v.charAt(i);
            
            this.write(c >> 8);
            this.write(c);
        }
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeDouble(double __v)
        throws IOException
    {
        this.writeLong(Double.doubleToLongBits(__v));
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeFloat(float __v)
        throws IOException
    {
        this.writeInt(Float.floatToIntBits(__v));
    }
    
    /**
     * @param __dt The data type to write.
     * @param __val The value to write.
     * @throws IOException On write errors.
     * @throws NullPointerException On null arguments.
     * @since 2020/12/04
     */
    public final void writeFuture(ChunkDataType __dt, ChunkFuture __val)
        throws IOException, NullPointerException
    {
        if (__dt == null || __val == null)
            throw new NullPointerException("NARG");
        
        // Record rewrite
        this._futures.add(new __FuturePoint__(this._size, __dt, __val));
        
        // Place padding
        this.writePadding(__dt.numBytes);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeInt(int __v)
        throws IOException
    {
        this.write(__v >> 24);
        this.write(__v >> 16);
        this.write(__v >> 8);
        this.write(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeLong(long __v)
        throws IOException
    {
        this.write((int)(__v >> 56));
        this.write((int)(__v >> 48));
        this.write((int)(__v >> 40));
        this.write((int)(__v >> 32));
        this.write((int)(__v >> 24));
        this.write((int)(__v >> 16));
        this.write((int)(__v >> 8));
        this.write((int)(__v));
    }
    
    /**
     * Writes the specified number of bytes as padding, the padding
     * value is zero.
     *
     * @param __n The number of bytes to pad with.
     * @throws IOException On write errors.
     * @since 2019/08/11
     */
    public final void writePadding(int __n)
        throws IOException
    {
        this.writePadding(__n, 0);
    }
    
    /**
     * Writes the specified number of bytes as padding.
     *
     * @param __n The number of bytes to pad with.
     * @param __v The padding value to write.
     * @throws IllegalArgumentException If the padding amount is negative.
     * @throws IOException On write errors.
     * @since 2019/08/11
     */
    public final void writePadding(int __n, int __v)
        throws IllegalArgumentException, IOException
    {
        /* {@squirreljme.error BD3j Negative padding. (The padding)} */
        if (__n < 0)
            throw new IllegalArgumentException("BD3j " + __n);
        
        // Not writing anything, so ignore
        if (__n == 0)
            return;
        
        // Write the padding
        for (int i = 0; i < __n; i++)
            this.write(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeShort(int __v)
        throws IOException
    {
        this.write(__v >> 8);
        this.write(__v);
    }
    
    /**
     * Writes the specified short value and checks to make sure it is
     * within the range of a short.
     *
     * @param __v The value to write.
     * @throws IOException If the short could not be written or it
     * exceeds the range of a short value.
     * @since 2019/08/11
     */
    public final void writeShortChecked(int __v)
        throws IOException
    {
        /* {@squirreljme.error BD3o Signed short value out of range.
        (The value)} */
        if (__v < -32768 || __v > 32767)
            throw new IOException("BD3o " + __v);
        
        this.write(__v >> 8);
        this.write(__v);
    }
    
    /**
     * Writes the specified unsigned short value and checks to make sure it
     * is within the range of an unsigned short.
     *
     * @param __v The value to write.
     * @throws IOException If the unsigned short could not be written or it
     * exceeds the range of an unsigned short value.
     * @since 2019/08/11
     */
    public final void writeUnsignedShortChecked(int __v)
        throws IOException
    {
        /* {@squirreljme.error BD3n Unsigned short value out of range.
        (The value)} */
        if (__v < 0 || __v > 65535)
            throw new IOException("BD3n " + __v);
        
        this.write(__v >> 8);
        this.write(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2019/08/11
     */
    @Override
    public final void writeUTF(String __v)
        throws IOException, NullPointerException
    {
        if (__v == null)
            throw new NullPointerException("NARG");
        
        // Write into a buffer
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream(
            2 + (__v.length() * 2));
             DataOutputStream dos = new DataOutputStream(baos))
        {
            // Write data
            dos.writeUTF(__v);
            
            // Dump to this internally
            baos.writeTo(this);
        }
    }
}