iostream.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    IOStream – модуль для создания потоков ввода-вывода.

    Copyright © 2017 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit IOStream;

{$MODE DELPHI,EXTENDEDSYNTAX ON}

interface

uses
    Lang;

{$ASMMODE INTEL,CALLING REGISTER,INLINE ON,GOTO ON}
{$H+,I-,J-,M-,Q-,R-,T-}

type
    Connection = interface;
    Input = interface;
    Output = interface;
    DataInput = interface;
    DataOutput = interface;
    InputStream = class;
    OutputStream = class;
    InputOutputStream = class;
    DataInputStream = class;
    DataOutputStream = class;
    ByteArrayStream = class;
    IOException = class;
    EOFException = class;
    ReadOnlyStreamException = class;
    WriteOnlyStreamException = class;
    UTFDataFormatException = class;

    Connection = interface(_Interface) ['{570879EA-220D-4569-904F-2C84ED0F5BAD}']
        procedure close();
    end;

    Input = interface(Connection) ['{4F945778-9735-4045-9D2B-262E7DBCA406}']
        function seekSupported(): boolean;
        function seek(delta: long): long;
        function size(): long;
        function position(): long;
        function available(): long;
        function read(): int; overload;
        function read(const dst: byte_Array1d): int; overload;
        function read(const dst: byte_Array1d; offset, length: int): int; overload;
    end;

    Output = interface(Connection) ['{4D190AE0-CB0A-4081-AFD4-73CA4FBBBDC5}']
        procedure flush();
        function write(value: int): boolean; overload;
        function write(const src: byte_Array1d): int; overload;
        function write(const src: byte_Array1d; offset, length: int): int; overload;
    end;

    DataInput = interface(Input) ['{F7D51944-D8D6-4FF9-B53F-1C351F76AFE5}']
        function readBoolean(): boolean;
        function readChar(): char;
        function readUChar(): uchar;
        function readUCharLE(): uchar;
        function readByte(): int;
        function readShort(): int;
        function readShortLE(): int;
        function readInt(): int;
        function readIntLE(): int;
        function readLong(): long;
        function readLongLE(): long;
        function readUltra(): ultra;
        function readUltraLE(): ultra;
        function readFloat(): real;
        function readFloatLE(): real;
        function readDouble(): real;
        function readDoubleLE(): real;
        function readReal(): real;
        function readRealLE(): real;
        function readXVector(): xvector;
        function readXVectorLE(): xvector;
        function readUnsignedByte(): int;
        function readUnsignedShort(): int;
        function readUnsignedShortLE(): int;
        function readString(): String;
        function readUTF(): UnicodeString;
        function readUTF16(): UnicodeString;
        procedure readFully(const dst: byte_Array1d); overload;
        procedure readFully(const dst: byte_Array1d; offset, length: int); overload;
        procedure skipBytes(count: int);
    end;

    DataOutput = interface(Output) ['{44EBF7E6-0A8A-4B5A-BF68-1C84441D883C}']
        procedure writeBoolean(const data: boolean);
        procedure writeChar(const data: char);
        procedure writeUChar(const data: uchar);
        procedure writeUCharLE(const data: uchar);
        procedure writeByte(const data: int);
        procedure writeShort(const data: int);
        procedure writeShortLE(const data: int);
        procedure writeInt(const data: int);
        procedure writeIntLE(const data: int);
        procedure writeLong(const data: long);
        procedure writeLongLE(const data: long);
        procedure writeUltra(const data: ultra);
        procedure writeUltraLE(const data: ultra);
        procedure writeFloat(const data: real);
        procedure writeFloatLE(const data: real);
        procedure writeDouble(const data: real);
        procedure writeDoubleLE(const data: real);
        procedure writeReal(const data: real);
        procedure writeRealLE(const data: real);
        procedure writeXVector(const data: xvector);
        procedure writeXVectorLE(const data: xvector);
        procedure writeString(const data: String);
        procedure writeUTF(const data: UnicodeString);
        procedure writeUTF16(const data: UnicodeString);
        procedure writeFully(const src: byte_Array1d); overload;
        procedure writeFully(const src: byte_Array1d; offset, length: int); overload;
    end;

    InputStream = class(RefCountInterfacedObject, Connection, Input)
    public
        constructor create();
        procedure close(); virtual;
        function seekSupported(): boolean; virtual;
        function seek(delta: long): long; virtual;
        function size(): long; virtual; abstract;
        function position(): long; virtual; abstract;
        function available(): long; virtual;
        function read(): int; overload; virtual; abstract;
        function read(const dst: byte_Array1d): int; overload; virtual;
        function read(const dst: byte_Array1d; offset, length: int): int; overload; virtual;
    end;

    OutputStream = class(RefCountInterfacedObject, Connection, Output)
    public
        constructor create();
        procedure close(); virtual;
        procedure flush(); virtual;
        function write(value: int): boolean; overload; virtual; abstract;
        function write(const src: byte_Array1d): int; overload; virtual;
        function write(const src: byte_Array1d; offset, length: int): int; overload; virtual;
    end;

    InputOutputStream = class(RefCountInterfacedObject, Connection, Input, Output)
    public
        constructor create();
        procedure close(); virtual;
        procedure flush(); virtual;
        function seekSupported(): boolean; virtual;
        function seek(delta: long): long; virtual;
        function size(): long; virtual; abstract;
        function position(): long; virtual; abstract;
        function available(): long; virtual;
        function read(): int; overload; virtual; abstract;
        function read(const dst: byte_Array1d): int; overload; virtual;
        function read(const dst: byte_Array1d; offset, length: int): int; overload; virtual;
        function write(value: int): boolean; overload; virtual; abstract;
        function write(const src: byte_Array1d): int; overload; virtual;
        function write(const src: byte_Array1d; offset, length: int): int; overload; virtual;
        function readAllowed(): boolean; virtual;
        function writeAllowed(): boolean; virtual;
        procedure truncate(); virtual; abstract;
    end;

    DataInputStream = class(InputStream, DataInput)
    public
        constructor create(inp: Input);
        procedure close(); override;
        function seekSupported(): boolean; override;
        function seek(delta: long): long; override;
        function size(): long; override;
        function position(): long; override;
        function available(): long; override;
        function read(): int; overload; override; final;
        function read(const dst: byte_Array1d): int; overload; override; final;
        function read(const dst: byte_Array1d;
                offset, length: int): int; overload; override; final;
        function readBoolean(): boolean;
        function readChar(): char;
        function readUChar(): uchar;
        function readUCharLE(): uchar;
        function readByte(): int;
        function readShort(): int;
        function readShortLE(): int;
        function readInt(): int;
        function readIntLE(): int;
        function readLong(): long;
        function readLongLE(): long;
        function readUltra(): ultra;
        function readUltraLE(): ultra;
        function readFloat(): real;
        function readFloatLE(): real;
        function readDouble(): real;
        function readDoubleLE(): real;
        function readReal(): real;
        function readRealLE(): real;
        function readXVector(): xvector;
        function readXVectorLE(): xvector;
        function readUnsignedByte(): int;
        function readUnsignedShort(): int;
        function readUnsignedShortLE(): int;
        function readString(): String;
        function readUTF(): UnicodeString;
        function readUTF16(): UnicodeString;
        procedure readFully(const dst: byte_Array1d); overload;
        procedure readFully(const dst: byte_Array1d; offset, length: int); overload;
        procedure skipBytes(count: int);
    protected
        inp: Input;
    end;

    DataOutputStream = class(OutputStream, DataOutput)
    public
        constructor create(outp: Output);
        procedure close(); override;
        procedure flush(); override;
        function write(value: int): boolean; overload; override; final;
        function write(const src: byte_Array1d): int; overload; override; final;
        function write(const src: byte_Array1d;
                offset, length: int): int; overload; override; final;
        procedure writeBoolean(const data: boolean);
        procedure writeChar(const data: char);
        procedure writeUChar(const data: uchar);
        procedure writeUCharLE(const data: uchar);
        procedure writeByte(const data: int);
        procedure writeShort(const data: int);
        procedure writeShortLE(const data: int);
        procedure writeInt(const data: int);
        procedure writeIntLE(const data: int);
        procedure writeLong(const data: long);
        procedure writeLongLE(const data: long);
        procedure writeUltra(const data: ultra);
        procedure writeUltraLE(const data: ultra);
        procedure writeFloat(const data: real);
        procedure writeFloatLE(const data: real);
        procedure writeDouble(const data: real);
        procedure writeDoubleLE(const data: real);
        procedure writeReal(const data: real);
        procedure writeRealLE(const data: real);
        procedure writexVector(const data: xvector);
        procedure writeXVectorLE(const data: xvector);
        procedure writeString(const data: String);
        procedure writeUTF(const data: UnicodeString);
        procedure writeUTF16(const data: UnicodeString);
        procedure writeFully(const src: byte_Array1d); overload;
        procedure writeFully(const src: byte_Array1d; offset, length: int); overload;
    protected
        outp: Output;
    end;

    ByteArrayStream = class(InputOutputStream)
    public
        constructor create(); overload;
        constructor create(const buf: byte_Array1d; size: int); overload;
        constructor create(const src: byte_Array1d; offset, length: int); overload;
        function seekSupported(): boolean; override;
        function seek(delta: long): long; override;
        function size(): long; override;
        function position(): long; override;
        function available(): long; override;
        function read(): int; overload; override;
        function read(const dst: byte_Array1d; offset, length: int): int; overload; override;
        function write(value: int): boolean; overload; override;
        function write(const src: byte_Array1d; offset, length: int): int; overload; override;
        function writeAllowed(): boolean; override;
        function toString(): AnsiString; override;
        procedure truncate(); override;
        function toByteArray(): byte_Array1d; virtual;
    strict private
        writable: boolean;
        start: int;
        finish: int;
        offset: int;
        buf: byte_Array1d;
    end;

    IOException = class(Exception);

    EOFException = class(IOException);

    ReadOnlyStreamException = class(IOException);

    WriteOnlyStreamException = class(IOException);

    UTFDataFormatException = class(IOException);

resourcestring
    msgUTFDataFormat = 'Ошибка в UTF-данных.';
    msgUTFTooLong = 'Размер UTF-данных не может превышать 64 КБ.';
    msgEndOfStreamReached = 'Достигнут конец потока ввода-вывода.';
    msgReadOnlyStream = 'Поток ввода-вывода доступен только для чтения.';
    msgWriteOnlyStream = 'Поток ввода-вывода доступен только для записи.';
    msgSeekNotSupported = 'Произвольное позиционирование не поддерживается';

implementation

{ InputStream }

constructor InputStream.create();
begin
    inherited create();
end;

procedure InputStream.close();
begin
end;

function InputStream.seekSupported(): boolean;
begin
    result := false;
end;

function InputStream.seek(delta: long): long;
begin
    result := delta; { чтобы избежать сообщений компилятора. Этой строки не должно быть здесь. }
    raise IOException.create(msgSeekNotSupported);
end;

function InputStream.available(): long;
var
    p: long;
    s: long;
begin
    p := position();
    s := size();
    if (p >= 0) and (p <= s) then begin
        result := s - p;
    end else begin
        result := -1;
    end;
end;

function InputStream.read(const dst: byte_Array1d): int;
begin
    result := read(dst, 0, length(dst));
end;

function InputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    b: int;
    i: int;
begin
    lim := offset + length;
    len := System.length(dst);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    for i := offset to lim - 1 do begin
        b := read();
        if b < 0 then begin
            break;
        end;
        dst[i] := byte(b);
        inc(result);
    end;
end;

{ OutputStream }

constructor OutputStream.create();
begin
    inherited create();
end;

procedure OutputStream.close();
begin
end;

procedure OutputStream.flush();
begin
end;

function OutputStream.write(const src: byte_Array1d): int;
begin
    result := write(src, 0, length(src));
end;

function OutputStream.write(const src: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    i: int;
begin
    lim := offset + length;
    len := System.length(src);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    for i := offset to lim - 1 do begin
        if write(src[i]) = false then begin
            break;
        end;
        inc(result);
    end;
end;

{ InputOutputStream }

constructor InputOutputStream.create();
begin
    inherited create();
end;

procedure InputOutputStream.close();
begin
end;

procedure InputOutputStream.flush();
begin
end;

function InputOutputStream.seekSupported(): boolean;
begin
    result := false;
end;

function InputOutputStream.seek(delta: long): long;
begin
    result := delta; { чтобы избежать сообщений компилятора. Этой строки не должно быть здесь. }
    raise IOException.create(msgSeekNotSupported);
end;

function InputOutputStream.available(): long;
var
    p: long;
    s: long;
begin
    p := position();
    s := size();
    if (p >= 0) and (p <= s) then begin
        result := s - p;
    end else begin
        result := -1;
    end;
end;

function InputOutputStream.read(const dst: byte_Array1d): int;
begin
    result := read(dst, 0, length(dst));
end;

function InputOutputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    b: int;
    i: int;
begin
    lim := offset + length;
    len := System.length(dst);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    for i := offset to lim - 1 do begin
        b := read();
        if b < 0 then begin
            break;
        end;
        dst[i] := byte(b);
        inc(result);
    end;
end;

function InputOutputStream.write(const src: byte_Array1d): int;
begin
    result := write(src, 0, length(src));
end;

function InputOutputStream.write(const src: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    i: int;
begin
    lim := offset + length;
    len := System.length(src);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    result := 0;
    for i := offset to lim - 1 do begin
        if write(src[i]) = false then begin
            break;
        end;
        inc(result);
    end;
end;

function InputOutputStream.readAllowed(): boolean;
begin
    result := true;
end;

function InputOutputStream.writeAllowed(): boolean;
begin
    result := true;
end;

{ DataInputStream }

constructor DataInputStream.create(inp: Input);
begin
    inherited create();
    self.inp := inp;
end;

procedure DataInputStream.close();
begin
    inp.close();
end;

function DataInputStream.seekSupported(): boolean;
begin
    result := inp.seekSupported();
end;

function DataInputStream.seek(delta: long): long;
begin
    result := inp.seek(delta);
end;

function DataInputStream.size(): long;
begin
    result := inp.size();
end;

function DataInputStream.position(): long;
begin
    result := inp.position();
end;

function DataInputStream.available(): long;
begin
    result := inp.available();
end;

function DataInputStream.read(): int;
begin
    result := inp.read();
end;

function DataInputStream.read(const dst: byte_Array1d): int;
begin
    result := inp.read(dst);
end;

function DataInputStream.read(const dst: byte_Array1d; offset, length: int): int;
begin
    result := inp.read(dst, offset, length);
end;

function DataInputStream.readBoolean(): boolean;
begin
    result := readUnsignedByte() > 0;
end;

function DataInputStream.readChar(): char;
begin
    result := char(readUnsignedByte());
end;

function DataInputStream.readUChar(): uchar;
begin
    result := uchar(readUnsignedShort());
end;

function DataInputStream.readUCharLE(): uchar;
begin
    result := uchar(readUnsignedShortLE());
end;

function DataInputStream.readByte(): int;
begin
    result := byte(readUnsignedByte());
end;

function DataInputStream.readShort(): int;
begin
    result := short(readUnsignedShort());
end;

function DataInputStream.readShortLE(): int;
begin
    result := short(readUnsignedShortLE());
end;

function DataInputStream.readInt(): int;
var
    byte1: int;
    byte2: int;
    byte3: int;
    byte4: int;
begin
    byte1 := read();
    byte2 := read();
    byte3 := read();
    byte4 := read();
    if byte1 or byte2 or byte3 or byte4 < 0 then begin
        raise EOFException.create(msgEndOfStreamReached);
    end;
    result := (byte1 shl 24) or (byte2 shl 16) or (byte3 shl 8) or byte4;
end;

function DataInputStream.readIntLE(): int;
var
    byte1: int;
    byte2: int;
    byte3: int;
    byte4: int;
begin
    byte1 := read();
    byte2 := read();
    byte3 := read();
    byte4 := read();
    if byte1 or byte2 or byte3 or byte4 < 0 then begin
        raise EOFException.create(msgEndOfStreamReached);
    end;
    result := byte1 or (byte2 shl 8) or (byte3 shl 16) or (byte4 shl 24);
end;

function DataInputStream.readLong(): long;
var
    int1: int;
    int2: int;
begin
    int1 := readInt();
    int2 := readInt();
    result := longBuild(int1, int2);
end;

function DataInputStream.readLongLE(): long;
var
    int1: int;
    int2: int;
begin
    int1 := readIntLE();
    int2 := readIntLE();
    result := longBuild(int2, int1);
end;

function DataInputStream.readUltra(): ultra;
var
    int1: int;
    int2: int;
    int3: int;
    int4: int;
begin
    int1 := readInt();
    int2 := readInt();
    int3 := readInt();
    int4 := readInt();
    result := ultraBuild(longBuild(int1, int2), longBuild(int3, int4));
end;

function DataInputStream.readUltraLE(): ultra;
var
    int1: int;
    int2: int;
    int3: int;
    int4: int;
begin
    int1 := readIntLE();
    int2 := readIntLE();
    int3 := readIntLE();
    int4 := readIntLE();
    result := ultraBuild(longBuild(int4, int3), longBuild(int2, int1));
end;

function DataInputStream.readFloat(): real;
begin
    result := intBitsToFloat(readInt());
end;

function DataInputStream.readFloatLE(): real;
begin
    result := intBitsToFloat(readIntLE());
end;

function DataInputStream.readDouble(): real;
begin
    result := longBitsToDouble(readLong());
end;

function DataInputStream.readDoubleLE(): real;
begin
    result := longBitsToDouble(readLongLE());
end;

function DataInputStream.readReal(): real;
var
    exponent: int;
    significand: long;
begin
    exponent := readUnsignedShort();
    significand := readLong();
    result := realBuild(exponent, significand);
end;

function DataInputStream.readRealLE(): real;
var
    exponent: int;
    significand: long;
begin
    significand := readLongLE();
    exponent := readUnsignedShortLE();
    result := realBuild(exponent, significand);
end;

function DataInputStream.readXVector(): xvector;
var
    float1: float;
    float2: float;
    float3: float;
    float4: float;
begin
    float1 := readFloat();
    float2 := readFloat();
    float3 := readFloat();
    float4 := readFloat();
    result := xvectorBuild(float1, float2, float3, float4);
end;

function DataInputStream.readXVectorLE(): xvector;
var
    float1: float;
    float2: float;
    float3: float;
    float4: float;
begin
    float1 := readFloatLE();
    float2 := readFloatLE();
    float3 := readFloatLE();
    float4 := readFloatLE();
    result := xvectorBuild(float4, float3, float2, float1);
end;

function DataInputStream.readUnsignedByte(): int;
var
    byte1: int;
begin
    byte1 := read();
    if byte1 < 0 then begin
        raise EOFException.create(msgEndOfStreamReached);
    end;
    result := byte1;
end;

function DataInputStream.readUnsignedShort(): int;
var
    byte1: int;
    byte2: int;
begin
    byte1 := read();
    byte2 := read();
    if byte1 or byte2 < 0 then begin
        raise EOFException.create(msgEndOfStreamReached);
    end;
    result := (byte1 shl 8) or byte2;
end;

function DataInputStream.readUnsignedShortLE(): int;
var
    byte1: int;
    byte2: int;
begin
    byte1 := read();
    byte2 := read();
    if byte1 or byte2 < 0 then begin
        raise EOFException.create(msgEndOfStreamReached);
    end;
    result := byte1 or (byte2 shl 8);
end;

function DataInputStream.readString(): String;
var
    len: int;
    buf: byte_Array1d;
begin
    len := readUnsignedShort();
    if len = 0 then begin
        result := '';
        exit;
    end;
    buf := byte_Array1d_create(len);
    readFully(buf);
    result := String_create(buf, 0, len);
end;

function DataInputStream.readUTF(): UnicodeString;
var
    i: int;
    b1: int;
    b2: int;
    b3: int;
    slen: int;
    rlen: int;
    buf: uchar_Array1d;
    b: byte_Array1d;
begin
    slen := readUnsignedShort();
    if slen = 0 then begin
        result := '';
        exit;
    end;
    i := 0;
    rlen := 0;
    buf := uchar_Array1d_create(slen);
    b := byte_Array1d_create(slen);
    readFully(b);
    while i < slen do begin
        b1 := b[i] and $ff;
        case b1 shr 4 of
        $00..$07: begin
            inc(i);
            buf[rlen] := uchar(b1);
            inc(rlen);
        end;
        $0c..$0d: begin
            inc(i, 2);
            if i <= slen then begin
                b2 := b[i - 1];
                if b2 and $c0 <> $80 then begin
                    raise UTFDataFormatException.create(msgUTFDataFormat);
                end;
            end else begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            buf[rlen] := uchar(((b1 and $1f) shl 6) or (b2 and $3f));
            inc(rlen);
        end;
        $0e: begin
            inc(i, 3);
            if i <= slen then begin
                b2 := b[i - 2];
                b3 := b[i - 1];
                if (b2 and $c0 <> $80) or (b3 and $c0 <> $80) then begin
                    raise UTFDataFormatException.create(msgUTFDataFormat);
                end;
            end else begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            buf[rlen] := uchar(((b1 and $0f) shl 12) or ((b2 and $3f) shl 6) or (b3 and $3f));
            inc(rlen);
        end;
        else
            raise UTFDataFormatException.create(msgUTFDataFormat);
        end;
    end;
    result := UnicodeString_create(buf, 0, rlen);
end;

function DataInputStream.readUTF16(): UnicodeString;
var
    i: int;
    b1: int;
    b2: int;
    b3: int;
    b4: int;
    code: int;
    slen: int;
    rlen: int;
    buf: uchar_Array1d;
    b: byte_Array1d;
begin
    slen := readUnsignedShort();
    if slen = 0 then begin
        result := '';
        exit;
    end;
    i := 0;
    rlen := 0;
    buf := uchar_Array1d_create(slen);
    b := byte_Array1d_create(slen);
    readFully(b);
    while i < slen do begin
        b1 := b[i] and $ff;
        case b1 of
        $00..$7f: begin
            inc(i);
            buf[rlen] := uchar(b1);
            inc(rlen);
        end;
        $c0..$df: begin
            inc(i, 2);
            if i <= slen then begin
                b2 := b[i - 1];
                if b2 and $c0 <> $80 then begin
                    raise UTFDataFormatException.create(msgUTFDataFormat);
                end;
            end else begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            buf[rlen] := uchar(((b1 and $1f) shl 6) or (b2 and $3f));
            inc(rlen);
        end;
        $e0..$ef: begin
            inc(i, 3);
            if i <= slen then begin
                b2 := b[i - 2];
                b3 := b[i - 1];
                if (b2 and $c0 <> $80) or (b3 and $c0 <> $80) then begin
                    raise UTFDataFormatException.create(msgUTFDataFormat);
                end;
            end else begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            code := ((b1 and $0f) shl 12) or ((b2 and $3f) shl 6) or (b3 and $3f);
            if (code >= $d800) and (code < $e000) then begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            buf[rlen] := uchar(code);
            inc(rlen);
        end;
        $f0..$f7: begin
            inc(i, 4);
            if i <= slen then begin
                b2 := b[i - 3];
                b3 := b[i - 2];
                b4 := b[i - 1];
                if (b2 and $c0 <> $80) or (b3 and $c0 <> $80) or (b4 and $c0 <> $80) then begin
                    raise UTFDataFormatException.create(msgUTFDataFormat);
                end;
            end else begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            code := ((b1 and $07) shl 18) or ((b2 and $3f) shl 12) or
                    ((b3 and $3f) shl 6) or (b4 and $3f);
            if (code < $00d800) or (code >= $00e000) and (code < $010000) then begin
                buf[rlen] := uchar(code);
                inc(rlen);
            end else
            if (code >= $010000) and (code < $110000) then begin
                code := code - $010000;
                buf[rlen] := uchar($d800 or (code shr 10));
                inc(rlen);
                buf[rlen] := uchar($dc00 or (code and $03ff));
                inc(rlen);
            end else begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
        end;
        else
            raise UTFDataFormatException.create(msgUTFDataFormat);
        end;
    end;
    result := UnicodeString_create(buf, 0, rlen);
end;

procedure DataInputStream.readFully(const dst: byte_Array1d);
begin
    readFully(dst, 0, length(dst));
end;

procedure DataInputStream.readFully(const dst: byte_Array1d; offset, length: int);
var
    lim: int;
    len: int;
    c: int;
    p: int;
begin
    lim := offset + length;
    len := System.length(dst);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    p := 0;
    while p < length do begin
        c := read(dst, offset + p, length - p);
        if c <= 0 then begin
            raise EOFException.create(msgEndOfStreamReached);
        end;
        inc(p, c);
    end;
end;

procedure DataInputStream.skipBytes(count: int);
var
    sp: long;
    np: long;
begin
    if count <= 0 then begin
        exit;
    end;
    if seekSupported() then begin
        sp := position();
        repeat
            np := seek(long(count));
            if sp >= np then begin
                raise EOFException.create(msgEndOfStreamReached);
            end;
            dec(count, int(np - sp));
            sp := np;
        until count <= 0;
    end else begin
        repeat
            if read() < 0 then begin
                raise EOFException.create(msgEndOfStreamReached);
            end;
            dec(count);
        until count <= 0;
    end;
end;

{ DataOutputStream }

constructor DataOutputStream.create(outp: Output);
begin
    inherited create();
    self.outp := outp;
end;

procedure DataOutputStream.close();
begin
    outp.close();
end;

procedure DataOutputStream.flush();
begin
    outp.flush();
end;

function DataOutputStream.write(value: int): boolean;
begin
    result := outp.write(value);
end;

function DataOutputStream.write(const src: byte_Array1d): int;
begin
    result := outp.write(src);
end;

function DataOutputStream.write(const src: byte_Array1d; offset, length: int): int;
begin
    result := outp.write(src, offset, length);
end;

procedure DataOutputStream.writeBoolean(const data: boolean);
begin
    if data = true then begin
        writeByte($01);
    end else begin
        writeByte($00);
    end;
end;

procedure DataOutputStream.writeChar(const data: char);
begin
    writeByte(int(data));
end;

procedure DataOutputStream.writeUChar(const data: uchar);
begin
    writeByte(int(data) shr 8);
    writeByte(int(data));
end;

procedure DataOutputStream.writeUCharLE(const data: uchar);
begin
    writeByte(int(data));
    writeByte(int(data) shr 8);
end;

procedure DataOutputStream.writeByte(const data: int);
begin
    if write(data) = false then begin
        raise EOFException.create(msgEndOfStreamReached);
    end;
end;

procedure DataOutputStream.writeShort(const data: int);
begin
    writeByte(data shr 8);
    writeByte(data);
end;

procedure DataOutputStream.writeShortLE(const data: int);
begin
    writeByte(data);
    writeByte(data shr 8);
end;

procedure DataOutputStream.writeInt(const data: int);
begin
    writeByte(data shr $18);
    writeByte(data shr $10);
    writeByte(data shr $08);
    writeByte(data);
end;

procedure DataOutputStream.writeIntLE(const data: int);
begin
    writeByte(data);
    writeByte(data shr $08);
    writeByte(data shr $10);
    writeByte(data shr $18);
end;

procedure DataOutputStream.writeLong(const data: long);
begin
    writeByte(int(data shr $38));
    writeByte(int(data shr $30));
    writeByte(int(data shr $28));
    writeByte(int(data shr $20));
    writeByte(int(data shr $18));
    writeByte(int(data shr $10));
    writeByte(int(data shr $08));
    writeByte(int(data));
end;

procedure DataOutputStream.writeLongLE(const data: long);
begin
    writeByte(int(data));
    writeByte(int(data shr $08));
    writeByte(int(data shr $10));
    writeByte(int(data shr $18));
    writeByte(int(data shr $20));
    writeByte(int(data shr $28));
    writeByte(int(data shr $30));
    writeByte(int(data shr $38));
end;

procedure DataOutputStream.writeUltra(const data: ultra);
begin
    writeLong(data.longs[1]);
    writeLong(data.longs[0]);
end;

procedure DataOutputStream.writeUltraLE(const data: ultra);
begin
    writeLongLE(data.longs[0]);
    writeLongLE(data.longs[1]);
end;

procedure DataOutputStream.writeFloat(const data: real);
begin
    writeInt(floatToIntBits(realToFloat(data)));
end;

procedure DataOutputStream.writeFloatLE(const data: real);
begin
    writeIntLE(floatToIntBits(realToFloat(data)));
end;

procedure DataOutputStream.writeDouble(const data: real);
begin
    writeLong(doubleToLongBits(realToDouble(data)));
end;

procedure DataOutputStream.writeDoubleLE(const data: real);
begin
    writeLongLE(doubleToLongBits(realToDouble(data)));
end;

procedure DataOutputStream.writeReal(const data: real);
begin
    writeShort(realExtractExponent(data));
    writeLong(realExtractSignificand(data));
end;

procedure DataOutputStream.writeRealLE(const data: real);
begin
    writeLongLE(realExtractSignificand(data));
    writeShortLE(realExtractExponent(data));
end;

procedure DataOutputStream.writeXVector(const data: xvector);
begin
    writeFloat(data.floats[3]);
    writeFloat(data.floats[2]);
    writeFloat(data.floats[1]);
    writeFloat(data.floats[0]);
end;

procedure DataOutputStream.writeXVectorLE(const data: xvector);
begin
    writeFloatLE(data.floats[0]);
    writeFloatLE(data.floats[1]);
    writeFloatLE(data.floats[2]);
    writeFloatLE(data.floats[3]);
end;

procedure DataOutputStream.writeString(const data: String);
var
    len: int;
begin
    len := length(data);
    if (len < 0) or (len >= $00010000) then begin
        raise UTFDataFormatException.create(msgUTFTooLong);
    end;
    writeShort(len);
    writeFully(stringToByteArray(data), 0, len);
end;

procedure DataOutputStream.writeUTF(const data: UnicodeString);
var
    i: int;
    code: int;
    slen: int;
    rlen: int;
    buf: uchar_Array1d;
    res: byte_Array1d;
begin
    buf := stringToCharArray(data);
    slen := length(buf);
    rlen := 0;
    for i := 0 to slen - 1 do begin
        code := int(buf[i]);
        if (code >= $0001) and (code < $0080) then begin
            inc(rlen);
        end else
        if code < $0800 then begin
            inc(rlen, 2);
        end else begin
            inc(rlen, 3);
        end;
    end;
    if rlen >= $00010000 then begin
        raise UTFDataFormatException.create(msgUTFTooLong);
    end;
    res := byte_Array1d_create(rlen);
    rlen := 0;
    for i := 0 to slen - 1 do begin
        code := int(buf[i]);
        if (code >= $0001) and (code < $0080) then begin
            res[rlen] := byte(code);
            inc(rlen);
        end else
        if code < $0800 then begin
            res[rlen] := byte($c0 or ((code shr 6) and $1f));
            inc(rlen);
            res[rlen] := byte($80 or (code and $3f));
            inc(rlen);
        end else begin
            res[rlen] := byte($e0 or ((code shr 12) and $0f));
            inc(rlen);
            res[rlen] := byte($80 or ((code shr 6) and $3f));
            inc(rlen);
            res[rlen] := byte($80 or (code and $3f));
            inc(rlen);
        end;
    end;
    writeShort(rlen);
    writeFully(res, 0, rlen);
end;

procedure DataOutputStream.writeUTF16(const data: UnicodeString);
var
    i: int;
    code: int;
    surr: int;
    slen: int;
    rlen: int;
    buf: uchar_Array1d;
    res: byte_Array1d;
begin
    buf := stringToCharArray(data);
    slen := length(buf);
    rlen := 0;
    i := 0;
    while i < slen do begin
        code := int(buf[i]);
        if (code >= $0001) and (code < $0080) then begin
            inc(rlen);
        end else
        if code < $0800 then begin
            inc(rlen, 2);
        end else
        if (code < $d800) or (code >= $e000) then begin
            inc(rlen, 3);
        end else begin
            inc(i);
            if i = slen then begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            surr := int(buf[i]);
            if (surr < $dc00) or (surr >= $e000) then begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            code := ((code and $03ff) shl 10) + (surr and $03ff) + $010000;
            if (code >= $000001) and (code < $000080) then begin
                inc(rlen);
            end else
            if code < $000800 then begin
                inc(rlen, 2);
            end else
            if code < $010000 then begin
                inc(rlen, 3);
            end else begin
                inc(rlen, 4);
            end;
        end;
        inc(i);
    end;
    if rlen >= $00010000 then begin
        raise UTFDataFormatException.create(msgUTFTooLong);
    end;
    res := byte_Array1d_create(rlen);
    rlen := 0;
    i := 0;
    while i < slen do begin
        code := int(buf[i]);
        if (code >= $0001) and (code < $0080) then begin
            res[rlen] := byte(code);
            inc(rlen);
        end else
        if code < $0800 then begin
            res[rlen] := byte($c0 or ((code shr 6) and $1f));
            inc(rlen);
            res[rlen] := byte($80 or (code and $3f));
            inc(rlen);
        end else
        if (code < $d800) or (code >= $e000) then begin
            res[rlen] := byte($e0 or ((code shr 12) and $0f));
            inc(rlen);
            res[rlen] := byte($80 or ((code shr 6) and $3f));
            inc(rlen);
            res[rlen] := byte($80 or (code and $3f));
            inc(rlen);
        end else begin
            inc(i);
            if i = slen then begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            surr := int(buf[i]);
            if (surr < $dc00) or (surr >= $e000) then begin
                raise UTFDataFormatException.create(msgUTFDataFormat);
            end;
            code := ((code and $03ff) shl 10) + (surr and $03ff) + $010000;
            if (code >= $000001) and (code < $000080) then begin
                res[rlen] := byte(code);
                inc(rlen);
            end else
            if code < $000800 then begin
                res[rlen] := byte($c0 or ((code shr 6) and $1f));
                inc(rlen);
                res[rlen] := byte($80 or (code and $3f));
                inc(rlen);
            end else
            if code < $010000 then begin
                res[rlen] := byte($e0 or ((code shr 12) and $0f));
                inc(rlen);
                res[rlen] := byte($80 or ((code shr 6) and $3f));
                inc(rlen);
                res[rlen] := byte($80 or (code and $3f));
                inc(rlen);
            end else begin
                res[rlen] := byte($f0 or ((code shr 18) and $07));
                inc(rlen);
                res[rlen] := byte($80 or ((code shr 12) and $3f));
                inc(rlen);
                res[rlen] := byte($80 or ((code shr 6) and $3f));
                inc(rlen);
                res[rlen] := byte($80 or (code and $3f));
                inc(rlen);
            end;
        end;
        inc(i);
    end;
    writeShort(rlen);
    writeFully(res, 0, rlen);
end;

procedure DataOutputStream.writeFully(const src: byte_Array1d);
begin
    writeFully(src, 0, length(src));
end;

procedure DataOutputStream.writeFully(const src: byte_Array1d; offset, length: int);
var
    lim: int;
    len: int;
    c: int;
    p: int;
begin
    lim := offset + length;
    len := System.length(src);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    p := 0;
    while p < length do begin
        c := write(src, offset + p, length - p);
        if c <= 0 then begin
            raise EOFException.create(msgEndOfStreamReached);
        end;
        inc(p, c);
    end;
end;

{ ByteArrayStream }

constructor ByteArrayStream.create();
begin
    inherited create();
    self.writable := true;
    self.start := 0;
    self.finish := 0;
    self.offset := 0;
    self.buf := byte_Array1d_create(32);
end;

constructor ByteArrayStream.create(const buf: byte_Array1d; size: int);
begin
    inherited create();
    self.writable := true;
    self.start := 0;
    self.finish := Math.max(0, Math.min(length(buf), size));
    self.offset := 0;
    self.buf := buf;
end;

constructor ByteArrayStream.create(const src: byte_Array1d; offset, length: int);
var
    len: int;
    start: int;
begin
    inherited create();
    len := System.length(src);
    start := Math.max(0, Math.min(len, offset));
    self.writable := false;
    self.start := start;
    self.finish := Math.max(start, Math.min(len, offset + length));
    self.offset := start;
    self.buf := src;
end;

function ByteArrayStream.seekSupported(): boolean;
begin
    result := true;
end;

function ByteArrayStream.seek(delta: long): long;
var
    start: int;
    offset: int;
    realDelta: int;
begin
    start := self.start;
    offset := self.offset;
    if delta < 0 then begin
        realDelta := int(Math.max(long(start) - long(offset), delta));
    end else begin
        realDelta := int(Math.min(long(finish) - long(offset), delta));
    end;
    inc(offset, realDelta);
    self.offset := offset;
    result := long(offset) - long(start);
end;

function ByteArrayStream.size(): long;
begin
    result := long(finish) - long(start);
end;

function ByteArrayStream.position(): long;
begin
    result := long(offset) - long(start);
end;

function ByteArrayStream.available(): long;
begin
    result := long(finish) - long(offset);
end;

function ByteArrayStream.read(): int;
var
    offset: int;
begin
    offset := self.offset;
    if offset >= finish then begin
        result := -1;
        exit;
    end;
    result := buf[offset] and $ff;
    self.offset := offset + 1;
end;

function ByteArrayStream.read(const dst: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    bufofs: int;
begin
    lim := offset + length;
    len := System.length(dst);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    bufofs := self.offset;
    result := Math.min(finish - bufofs, length);
    self.offset := bufofs + result;
    arraycopyPrimitives(buf, bufofs, dst, offset, result);
end;

function ByteArrayStream.write(value: int): boolean;
var
    newlen: int;
    finish: int;
    bufofs: int;
    buflen: int;
    buf: byte_Array1d;
    newbuf: byte_Array1d;
begin
    if not writable then begin
        raise ReadOnlyStreamException.create(msgReadOnlyStream);
    end;
    buf := self.buf;
    finish := self.finish;
    bufofs := self.offset;
    buflen := length(buf);
    newlen := bufofs + 1;
    if buflen < newlen then begin
        newlen := Math.max(buflen shl 1, newlen);
        newbuf := byte_Array1d_create(newlen);
        arraycopyPrimitives(buf, 0, newbuf, 0, finish);
        buf := newbuf;
        self.buf := newbuf;
    end;
    buf[bufofs] := byte(value);
    inc(bufofs);
    if finish < bufofs then begin
        finish := bufofs;
    end;
    self.finish := finish;
    self.offset := bufofs;
    result := true;
end;

function ByteArrayStream.write(const src: byte_Array1d; offset, length: int): int;
var
    lim: int;
    len: int;
    newlen: int;
    finish: int;
    bufofs: int;
    buflen: int;
    buf: byte_Array1d;
    newbuf: byte_Array1d;
begin
    if not writable then begin
        raise ReadOnlyStreamException.create(msgReadOnlyStream);
    end;
    lim := offset + length;
    len := System.length(src);
    if (lim > len) or (lim < offset) or (offset > len) or (offset < 0) then begin
        raise ArrayIndexOutOfBoundsException.create(msgArrayIndexOutOfBounds);
    end;
    buf := self.buf;
    finish := self.finish;
    bufofs := self.offset;
    buflen := System.length(buf);
    newlen := bufofs + length;
    if buflen < newlen then begin
        newlen := Math.max(buflen shl 1, newlen);
        newbuf := byte_Array1d_create(newlen);
        arraycopyPrimitives(buf, 0, newbuf, 0, finish);
        buf := newbuf;
        self.buf := newbuf;
    end;
    arraycopyPrimitives(src, offset, buf, bufofs, length);
    inc(bufofs, length);
    if finish < bufofs then begin
        finish := bufofs;
    end;
    self.finish := finish;
    self.offset := bufofs;
    result := length;
end;

function ByteArrayStream.writeAllowed(): boolean;
begin
    result := writable;
end;

function ByteArrayStream.toString(): AnsiString;
var
    start: int;
begin
    start := self.start;
    result := String_create(buf, start, finish - start);
end;

procedure ByteArrayStream.truncate();
begin
    if not writable then begin
        raise ReadOnlyStreamException.create(msgReadOnlyStream);
    end;
    finish := offset;
end;

function ByteArrayStream.toByteArray(): byte_Array1d;
var
    len: int;
    start: int;
begin
    start := self.start;
    len := finish - start;
    result := byte_Array1d_create(len);
    arraycopyPrimitives(buf, start, result, 0, len);
end;

end.