Source: Tools/Streams/AxStream.js

/**
 * Defines a mode for seeking through a stream
 */
var StreamSeekMode = Object.freeze(
{
    FromBeginning : 0,
    Relative : 1,
    FromEnd : 2
});


/**
 * Creates a stream object
 * This prototype is meant to be inherited by overriding the following two methods:
 * ReadData(data, size) where data is an ArrayBuffer, size is the amount of bytes to read from the stream into the ArrayBuffer and the return value is the amount of bytes actually read.
 * WriteData(data, size) where data is an ArrayBuffer, size is the amount of bytes to write to the stream from the ArrayBuffer and the return value is the amount of bytes actually written.
 * @constructor
 */
function AxStream()
{
    this.position = 0;
    this.length = 0;
    
    this.buffer = new ArrayBuffer(8);
    this.bufferUInt8 = new Uint8Array(this.buffer);
    this.bufferInt8 = new Int8Array(this.buffer);
    this.bufferUInt16 = new Uint16Array(this.buffer);
    this.bufferInt16 = new Int16Array(this.buffer);
    this.bufferUInt32 = new Uint32Array(this.buffer);
    this.bufferInt32 = new Int32Array(this.buffer);
    this.bufferFloat32 = new Float32Array(this.buffer);
    this.bufferFloat64 = new Float64Array(this.buffer);
}

/**
 * Explicitly dispose of the resources allocated by the stream
 */
AxStream.prototype.Dispose = function() { };

/**
 * Reads data from the memory stream into an ArrayBuffer and returns the number of bytes which were successfully read
 * @param {ArrayBuffer} destination An array buffer of the appropriate size, into which the read data will be stored
 * @param {Integer} size Number of bytes to read
 * @return {Integer} The number of bytes which were successfully read
 */
AxStream.prototype.ReadData = function(destination, size) { };

/**
 * Writes data to the memory stream from an ArrayBuffer and returns the number of bytes which were successfully written
 * If writing at a position before the end of the stream, depending on the stream's write mode, new data can either overwite bytes ahead of the position or be inserted - pushing the existing data forward.
 * @param {ArrayBuffer} source An array buffer which holds the data to be written
 * @param {Integer} size Number of bytes to write
 * @return {Integer} The number of bytes which were successfully written
 */
AxStream.prototype.WriteData = function(source, size) { };

/**
 * Seeks number of offset bytes, in a manner determined by a seek type
 * @param {Integer} offset The offset of seeking is a number of bytes
 * @param {StreamSeekMode} seekType A seeking mode determines how to perform the seek. If omitted, seeking is performed from the beginning of the stream
 * @return {Boolean} True if seek was successful, False other wise, if for example it was outside the bounds of the stream
 */
AxStream.prototype.Seek = function(offset, seekType)
{
    if (AxUtils.IsUndefined(seekType))
        seekType = StreamSeekMode.FromBeginning;
    
    var newPosition = seekType === StreamSeekMode.FromBeginning ? offset : seekType === StreamSeekMode.Relative ? this.position + offset : seekType === StreamSeekMode.FromEnd ? this.length - offset : -1;

    if ((newPosition >= 0) && (newPosition <= this.length))
    {
        this.position = newPosition;
        return true;
    }

    return false;
};

/**
 * Writes data from another stream
 * After the writing is performed, the source stream's location is changed, according to the size of the data which was written
 * @param {AxStream} source Stream which is the source of the data to write
 * @param {Integer} size Size in bytes of the data to be written
 * @param {Integer} maxBufferSize Maximum size of the buffer to use for the writing. Can be omitted.
 * @return {Integer} The number of bytes actually written
 */
AxStream.prototype.WriteStreamData = function(source, size, maxBufferSize)
{
    if (AxUtils.IsUndefined(maxBufferSize))
        maxBufferSize = 65536;
    
     if (size === -1)
        size = source.length - source.position;

    var chunkSize = AxMath.Min(size, maxBufferSize);
    var dataChunk = new ArrayBuffer(chunkSize);

    var bytesToWrite = size;
    while (bytesToWrite > 0)
    {
        var bytesToRead = AxMath.Min(bytesToWrite, chunkSize);
        var bytesRead = source.ReadData(dataChunk, bytesToRead);
        var bytesWritten = this.WriteData(dataChunk, bytesRead);

        bytesToWrite -= bytesWritten;

        if (bytesToRead !== bytesWritten)
            break;
    }

    return size - bytesToWrite;
};


/**
 * Reads a Boolean value from the stream
 * @return {Boolean} A Boolean value read from the stream
 */
AxStream.prototype.ReadBool = function()
{
    this.ReadData(this.buffer, 1);
    return this.bufferUInt8[0] !== 0;
};

/**
 * Writes a Boolean value to the stream
 * @param {Boolean} value The Boolean value to be written
*/
AxStream.prototype.WriteBool = function(value)
{
    this.bufferUInt8[0] = value ? 1 : 0;
    this.WriteData(this.buffer, 1);
};


/**
 * Reads a signed byte value from the stream
 * @return {Integer} A signed byte value read from the stream
 */
AxStream.prototype.ReadInt8 = function()
{
    this.ReadData(this.buffer, 1);
    return this.bufferInt8[0];
};

/**
 * Writes a signed byte value to the stream
 * @param {Integer} value The signed 8 bit integer value to be written
*/
AxStream.prototype.WriteInt8 = function(value)
{
    this.bufferInt8[0] = value;
    this.WriteData(this.buffer, 1);
};

/**
 * Reads a signed 16 bit integer value from the stream
 * @return {Integer} A signed 16 bit integer value read from the stream
 */
AxStream.prototype.ReadInt16 = function()
{
    this.ReadData(this.buffer, 2);
    return this.bufferInt16[0];
};

/**
 * Writes a signed 16 bit integer value to the stream
 * @param {Integer} value The signed 16 bit integer value to be written
*/
AxStream.prototype.WriteInt16 = function(value)
{
    this.bufferInt16[0] = value;
    this.WriteData(this.buffer, 2);
};

/**
 * Reads a signed 32 bit integer value from the stream
 * @return {Integer} A signed 32 bit integer value read from the stream
 */
AxStream.prototype.ReadInt32 = function()
{
    this.ReadData(this.buffer, 4);
    return this.bufferInt32[0];
};

/**
 * Writes a signed 32 bit integer value to the stream
 * @param {Integer} value The signed 32 bit integer value to be written
 */
AxStream.prototype.WriteInt32 = function(value)
{
    this.bufferInt32[0] = value;
    this.WriteData(this.buffer, 4);
};

/**
 * Reads a signed 64 bit integer value from the stream
 * @return {Integer} A signed 64 bit integer value read from the stream
 */
AxStream.prototype.ReadInt64 = function()
{
    this.ReadData(this.buffer, 8);
    return this.bufferInt32[1] << 32 | this.bufferInt32[0];
};

/**
 * Writes a signed 64 bit integer value to the stream
 * @param {Integer} value The signed 64 bit integer value to be written
 */
AxStream.prototype.WriteInt64 = function(value)
{
    this.bufferInt32[0] = value & 0xFFFFFFFF;
    this.bufferInt32[1] = value & 0xFFFFFFFF00000000;
    this.WriteData(this.buffer, 8);
};


/**
 * Reads a signed 32 bit integer value of variable size from the stream
 * In the 7-bit encoding, the last bit of each of the bytes of the value represent a flag which denotes whether this is the last byte of the value. This means that the value, if small enough, can be encoded in less than 4 bytes (or 32 bits)
 * A value which uses this encoding practically uses 7 of the total 8 bits of each of its bytes, because the last one is a flag.
 * This encoding potentially saves space for small values, while in the same time gives the ability to have large values as well.
 * @return {Integer} A signed 32 bit integer value read from the stream
 */
AxStream.prototype.Read7BitEncodedInt32 = function()
{
    var result = 0;
    var bits = new ArrayBuffer(1);
    var bitsArray = new Uint8Array(bits);
    do
    {
        this.ReadData(bits, 1);
        result = (result << 7) | (bitsArray[0] & 0x7f);
    } 
    while ((bitsArray & (1 << 7)) !== 0);

    return result;
};

/**
 * Writes a signed 32 bit integer value of variable size to the stream
 * In the 7-bit encoding, the last bit of each of the bytes of the value represent a flag which denotes whether this is the last byte of the value. This means that the value, if small enough, can be encoded in less than 4 bytes (or 32 bits)
 * A value which uses this encoding practically uses 7 of the total 8 bits of each of its bytes, because the last one is a flag.
 * This encoding potentially saves space for small values, while in the same time gives the ability to have large values as well.
 * @param {Integer} value The signed 32 bit integer value to be written
 */
AxStream.prototype.Write7BitEncodedInt32 = function(value)
{
    var writeValue = 0;
    var writeBytes = 0;

    for (;;)
    {
        writeValue = (writeValue << 8) | (value & 0x7f);
        value = value >> 7;
        writeBytes++;

        if (value !== 0)
            writeValue |= 0x80;
        else
            break;
    }

    var data = new ArrayBuffer(4);
    var dataArray = new Int32Buffer(data);
    dataArray[0] = writeValue;
    this.WriteData(data, writeBytes);
};

/**
 * Reads an unsigned byte value from the stream
 * @return {Integer} An unsigned byte value read from the stream
 */
AxStream.prototype.ReadUInt8 = function()
{
    this.ReadData(this.buffer, 1);
    return this.bufferUInt8[0];
};

/**
 * Writes an unsigned byte value to the stream
 * @param {Integer} value The unsigned 8 bit integer value to be written
 */
AxStream.prototype.WriteUInt8 = function(value)
{
    this.bufferUInt8[0] = value;
    this.WriteData(this.buffer, 1);
};

/**
 * Reads an unsigned 16 bit integer value from the stream
 * @return {Integer} An unsigned 16 bit integer value read from the stream
 */
AxStream.prototype.ReadUInt16 = function()
{
    this.ReadData(this.buffer, 2);
    return this.bufferUInt16[0];
};

/**
 * Writes an unsigned 16 bit integer value to the stream
 * @param {Integer} value The unsigned 16 bit integer value to be written
 */
AxStream.prototype.WriteUInt16 = function(value)
{
    this.bufferUInt16[0] = value;
    this.WriteData(this.buffer, 2);
};

/**
 * Reads an unsigned 32 bit integer value from the stream
 * @return {Integer} An unsigned 32 bit integer value read from the stream
 */
AxStream.prototype.ReadUInt32 = function()
{
    this.ReadData(this.buffer, 4);
    return this.bufferUInt32[0];
};

/**
 * Writes an unsigned 32 bit integer value to the stream
 * @param {Integer} value The unsigned 32 bit integer value to be written
 */
AxStream.prototype.WriteUInt32 = function(value)
{
    this.bufferUInt32[0] = value;
    this.WriteData(this.buffer, 4);
};

/**
 * Reads an unsigned 64 bit integer value from the stream
 * @return {Integer} An unsigned 64 bit integer value read from the stream
 */
AxStream.prototype.ReadUInt64 = function()
{
    this.ReadData(this.buffer, 8);
    return this.bufferUInt32[1] << 32 | this.bufferUInt32[0];
};

/**
 * Writes an unsigned 64 bit integer value to the stream
 * @param {Integer} value The unsigned 64 bit integer value to be written
 */
AxStream.prototype.WriteUInt64 = function(value)
{
    this.bufferUInt32[0] = value & 0xFFFFFFFF;
    this.bufferUInt32[1] = value & 0xFFFFFFFF00000000;
    this.WriteData(this.buffer, 8);
};


/**
 * Reads a 32 bit floating point value from the stream
 * @return {Number} A 32 bit floating point value read from the stream
 */
AxStream.prototype.ReadFloat32 = function()
{
    this.ReadData(this.buffer, 4);
    return this.bufferFloat32[0];
};

/**
 * Writes a 32 bit floating point value to the stream
 * @param {Number} value The 32 bit floating point value to be written
 */
AxStream.prototype.WriteFloat32 = function(value)
{
    this.bufferFloat32[0] = value;
    this.WriteData(this.buffer, 4);
};

/**
 * Reads a 64 bit floating point value from the stream
 * @return {Number} A 64 bit floating point value read from the stream
 */
AxStream.prototype.ReadFloat64 = function()
{
    this.ReadData(this.buffer, 8);
    return this.bufferFloat64[0];
};

/**
 * Writes a 64 bit floating point value to the stream
 * @param {Number} value The 64 bit floating point value to be written
 */
AxStream.prototype.WriteFloat64 = function(value)
{
    this.bufferFloat64[0] = value;
    this.WriteData(this.buffer, 8);
};


/**
 * Reads a string value from the stream
 * @return {AxString} A string value read from the stream
 */
AxStream.prototype.ReadString = function()
{
    var dataLength = this.Read7BitEncodedInt32();
    var charCodeData = new ArrayBuffer(dataLength);
    var charCodeDataArray = new Uint8Array(charCodeData);

    this.ReadData(charCodeData, dataLength);

    var result = new AxString();
    AxString.DecodeUtf8String(result, charCodeDataArray, dataLength, 0);

    return result;
};

/**
 * Writes a string value to the stream
 * @param {AxString|String} value The string value to be written
 */
AxStream.prototype.WriteString = function(value)
{
    var dataLength = AxString.GetStringAsUTF8Length(value);
    var data = new ArrayBuffer(dataLength);
    AxString.EncodeUtf8String(data, value);

    this.Write7BitEncodedInt32(dataLength);
    this.WriteData(data, dataLength);
};

/**
 * Reads an uninterrupted set of text characters from the stream.
 * Useful for reading a single word or number in a series of such
 * TODO: make parameter for the interruption characters (or strings)
 * @return {Number} An uninterrupted set of text characters read from the stream
 */
//AxString AxStream.prototype.ReadText = function()
// Follow more AxString methods.
// TODO: implement them

Documentation generated by JSDoc 3.5.3 on Mon Feb 19 2018 20:39:26 GMT+0200 (FLE Standard Time)