src_utils_Nifti_viewer_FILE.js

//   _    _ _                              _
//  | |  | (_)                            | |
//  | |  | |_ _ __ ___  ___ _ __ __ _  ___| | _____ _ __
//  | |/\| | | '__/ _ \/ __| '__/ _` |/ __| |/ / _ \ '__|
//  \  /\  / | | |  __/ (__| | | (_| | (__|   <  __/ |
//   \/  \/|_|_|  \___|\___|_|  \__,_|\___|_|\_\___|_|
//
//
//  Interface for ArrayBuffer to make translation of MATLAB
//  script easier. This class provides methods to interact
//  with binary data in a manner similar to MATLAB file operations.
//  It supports reading various data types (e.g., integers, floats,
//  strings) from a given ArrayBuffer.
//
//  Developed by Wirecracker team and distributed under MIT license.

/**
 * @module nifti_viewer
 */

class FILE
{
    /**
     * @class FILE
     * @param {string} [filename] - The name of the file.
     * @param {('ieee-le'|'ieee-be')} [machine='ieee-le'] - The byte order used by the machine ('ieee-le' for little-endian, 'ieee-be' for big-endian).
     */
    constructor( filename = '', machine = 'ieee-le' )
    {
        this.filename = filename;
        this.offset = 0;
        this.littleEndian = machine === 'ieee-le';
    }

    /**
     * Opens a data source and initializes the content buffer.
     * @param {ArrayBuffer} data - The binary data to read from.
     */
    fopen ( data )
    {
        this.content = new DataView(data);
        this.offset = 0;
    }

    /**
     * Closes the file, clearing the content buffer and resetting the offset.
     */
    fclose ()
    {
        this.content = null;
        this.offset = 0;
    }

    /**
     * Reads data from the file at the current offset.
     * @param {number} number - The number of items to read.
     * @param {string} [type=''] - The data type to read (e.g., 'int32', 'float64', 'string', etc.). Defaults to 'uint8'.
     * @returns {any} The read data, either as a single value or an array of values.
     */
    fread ( number, type = '' )
    {
        var result;
        switch ( type )
        {
            case 'int32':
                if ( number == 1 )
                {
                    result = this.content.getInt32(this.offset, this.littleEndian);
                    this.offset += 4;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getInt32(this.offset, this.littleEndian));
                        this.offset += 4;
                    }
                }
                break;

            case 'int16':
                if ( number == 1 )
                {
                    result = this.content.getInt16(this.offset, this.littleEndian);
                    this.offset += 2;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getInt16(this.offset, this.littleEndian));
                        this.offset += 2;
                    }
                }
                break;

            case 'char':
            case 'int8':
                if ( number == 1 )
                {
                    result = this.content.getInt8(this.offset, this.littleEndian);
                    this.offset += 1;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getInt8(this.offset, this.littleEndian));
                        this.offset += 1;
                    }
                }
                break;

            case 'uint32':
                if ( number == 1 )
                {
                    result = this.content.getUint32(this.offset, this.littleEndian);
                    this.offset += 4;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getUint32(this.offset, this.littleEndian));
                        this.offset += 4;
                    }
                }
                break;

            case 'uint16':
                if ( number == 1 )
                {
                    result = this.content.getUint16(this.offset, this.littleEndian);
                    this.offset += 2;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getUint16(this.offset, this.littleEndian));
                        this.offset += 2;
                    }
                }
                break;

            case '':
            case 'uchar':
            case 'uint8':
                if ( number == 1 )
                {
                    result = this.content.getUint8(this.offset, this.littleEndian);
                    this.offset += 1;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getUint8(this.offset, this.littleEndian));
                        this.offset += 1;
                    }
                }
                break;

            case 'float64':
                if ( number == 1 )
                {
                    result = this.content.getFloat64(this.offset, this.littleEndian);
                    this.offset += 8;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getFloat64(this.offset, this.littleEndian));
                        this.offset += 8;
                    }
                }
                break;

            case 'float32':
                if ( number == 1 )
                {
                    result = this.content.getFloat32(this.offset, this.littleEndian);
                    this.offset += 4;
                }
                else
                {
                    result = [];
                    for ( var i = 0; i < number; i++ )
                    {
                        result.push(this.content.getFloat32(this.offset, this.littleEndian));
                        this.offset += 4;
                    }
                }
                break;

            case 'string':
                const stringView = new DataView(this.content.buffer, this.offset, number);
                const decoder = new TextDecoder('utf-8');
                result = decoder.decode(stringView).replace(/\0/g, '').trim();
                this.offset += number;
        }
        return result;
    }

    /**
     * Changes the current read/write position in the file.
     * @param {number} offset - The offset to move to.
     * @param {string} origin - The reference point for the offset ('bof' for beginning, 'cof' for current, 'eof' for end).
     */
    fseek ( offset, origin )
    {
        switch ( origin )
        {
            case 'bof': // beginning of file
                this.offset = offset;
                return;

            case 'cof': // current location
                this.offset += offset;
                return;

            case 'eof': // end of file
                this.offset = this.content.byteLength + offset;
                return;
        }
    }

    /**
     * Resets the file pointer to the beginning of the file.
     */
    frewind ()
    {
        this.fseek(0, 'bof');
    }

    /**
     * Gets the current offset within the file.
     * @returns {number} The current file offset.
     */
    ftell ()
    {
        return this.offset;
    }
}

export default FILE;