pascalx.io.pas

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

{
    pascalx.io — основной модуль для разработки классов ввода-вывода.

    Copyright © 2021, 2026 Малик Разработчик

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

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

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

unit pascalx.io;

    {$MODE OBJFPC}

interface

    {%region} uses
        {$IFDEF WINDOWS}
        windows,
        {$ENDIF}
        pascalx.lang;
    {%endregion}

    {$TYPEINFO ON}
    {$CALLING REGISTER}

    const UNIT_NAME = 'pascalx.io';

    {%region} type
        Extension                       = interface;
        Extendable                      = interface;
        ByteReader                      = interface;
        ByteWriter                      = interface;
        ByteStream                      = interface;
        UCharReader                     = interface;
        UCharWriter                     = interface;
        UCharStream                     = interface;
        DataInput                       = interface;
        DataOutput                      = interface;
        DataInputStream                 = class;
        DataOutputStream                = class;
        UCharInputStream                = class;
        UCharOutputStream               = class;
        PrintStream                     = class;
        UResource                       = class;
        SResource                       = class;
        IOException                     = class;
        EndOfStreamException            = class;
        EndOfFreeSpaceException         = class;
        InvalidDataFormatException      = class;
        MalformedLineSeparatorException = class;
        UTFDataFormatException          = class;

        { Extension[] } Extension_Array1d = specialize DynamicArray<Extension>;

        Extension = interface(RawInterface) ['{C3512B0A-CC07-4A37-9514-C955A8359B10}']
        end;

        Extendable = interface(RawInterface) ['{C3512B0A-CC07-4A37-9514-C955A8359B11}']
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        end;

        ByteReader = interface(Extendable) ['{C3512B0A-CC07-4A37-9514-C955A8359B12}']
            procedure close();
            function read(): int;
            function read(const dst: byte_Array1d): int;
            function read(const dst: byte_Array1d; offset, length: int): int;
            function skip(bytesQuantity: long): long;
        end;

        ByteWriter = interface(Extendable) ['{C3512B0A-CC07-4A37-9514-C955A8359B13}']
            procedure close();
            procedure write(byteData: int);
            procedure write(const src: byte_Array1d);
            procedure write(const src: byte_Array1d; offset, length: int);
            procedure flush();
        end;

        ByteStream = interface(Extendable) ['{C3512B0A-CC07-4A37-9514-C955A8359B14}']
            procedure close();
            function getReader(): ByteReader;
            function getWriter(): ByteWriter;
            property reader: ByteReader read getReader;
            property writer: ByteWriter read getWriter;
        end;

        UCharReader = interface(Extendable) ['{C3512B0A-CC07-4A37-9514-C955A8359B15}']
            procedure close();
            function read(): int;
            function read(const dst: uchar_Array1d): int;
            function read(const dst: uchar_Array1d; offset, length: int): int;
            function skip(ucharsQuantity: long): long;
        end;

        UCharWriter = interface(Extendable) ['{C3512B0A-CC07-4A37-9514-C955A8359B16}']
            procedure close();
            procedure write(ucharData: int);
            procedure write(const src: uchar_Array1d);
            procedure write(const src: uchar_Array1d; offset, length: int);
            procedure flush();
        end;

        UCharStream = interface(Extendable) ['{C3512B0A-CC07-4A37-9514-C955A8359B17}']
            procedure close();
            function getReader(): UCharReader;
            function getWriter(): UCharWriter;
            property reader: UCharReader read getReader;
            property writer: UCharWriter read getWriter;
        end;

        DataInput = interface(RawInterface) ['{C3512B0A-CC07-4A37-9514-C955A8359B18}']
            procedure readFully(const dst: byte_Array1d);
            procedure readFully(const dst: byte_Array1d; offset, length: int);
            function readBoolean(): boolean;
            function readChar(): char;
            function readUChar(): uchar;
            function readUnsignedByte(): int;
            function readUnsignedShort(): int;
            function readByte(): int;
            function readShort(): int;
            function readInt(): int;
            function readLong(): long;
            function readFloat(): float;
            function readDouble(): double;
            function readReal(): real;
            function readUTF(): UnicodeString;
            function readln(): UnicodeString;
            function read(): UnicodeString;
        end;

        DataOutput = interface(RawInterface) ['{C3512B0A-CC07-4A37-9514-C955A8359B19}']
            procedure write(const src: byte_Array1d);
            procedure write(const src: byte_Array1d; offset, length: int);
            procedure writeBoolean(const data: boolean);
            procedure writeChar(const data: char);
            procedure writeUChar(const data: uchar);
            procedure writeByte(const data: int);
            procedure writeShort(const data: int);
            procedure writeInt(const data: int);
            procedure writeLong(const data: long);
            procedure writeFloat(const data: float);
            procedure writeDouble(const data: double);
            procedure writeReal(const data: real);
            procedure writeUTF(const data: UnicodeString);
            procedure writeln(const data: UnicodeString);
            procedure write(const data: UnicodeString);
        end;

        DataInputStream = class sealed(&Object, ByteReader, Extendable)
        private
            fldCharsetName: AnsiString;
            fldReader: ByteReader;
            fldBigEndianDataInputAsInterface: DataInput;
            fldBigEndianDataInputAsObject: &Object; { BigEndianDataInput }
            fldLittleEndianDataInputAsInterface: DataInput;
            fldLittleEndianDataInputAsObject: &Object; { LittleEndianDataInput }
            procedure setCharsetName(const newCharsetName: AnsiString);
        public
            constructor create(reader: ByteReader);
            constructor create(reader: ByteReader; const charsetName: AnsiString);
            destructor destroy; override;
            procedure close();
            function read(): int;
            function read(const dst: byte_Array1d): int;
            function read(const dst: byte_Array1d; offset, length: int): int;
            function skip(bytesQuantity: long): long;
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        published
            property charsetName: AnsiString read fldCharsetName write setCharsetName;
            property reader: ByteReader read fldReader;
            property bigEndianDataInput: DataInput read fldBigEndianDataInputAsInterface;
            property littleEndianDataInput: DataInput read fldLittleEndianDataInputAsInterface;
        end;

        DataOutputStream = class sealed(&Object, ByteWriter, Extendable)
        private
            fldCharsetName: AnsiString;
            fldWriter: ByteWriter;
            fldBigEndianDataOutputAsInterface: DataOutput;
            fldBigEndianDataOutputAsObject: &Object; { BigEndianDataOutput }
            fldLittleEndianDataOutputAsInterface: DataOutput;
            fldLittleEndianDataOutputAsObject: &Object; { LittleEndianDataOutput }
            procedure setCharsetName(const newCharsetName: AnsiString);
        public
            constructor create(writer: ByteWriter);
            constructor create(writer: ByteWriter; const charsetName: AnsiString);
            destructor destroy; override;
            procedure close();
            procedure write(byteData: int);
            procedure write(const src: byte_Array1d);
            procedure write(const src: byte_Array1d; offset, length: int);
            procedure flush();
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        published
            property charsetName: AnsiString read fldCharsetName write setCharsetName;
            property writer: ByteWriter read fldWriter;
            property bigEndianDataOutput: DataOutput read fldBigEndianDataOutputAsInterface;
            property littleEndianDataOutput: DataOutput read fldLittleEndianDataOutputAsInterface;
        end;

        UCharInputStream = class sealed(&Object, UCharReader, Extendable)
        private
            fldCharsetName: AnsiString;
            fldReader: ByteReader;
            fldDecoderRef: &Object; { CharDecoder }
            fldMarkExtension: &Object; { UCharInputStreamMarkExtension }
            fldStreamedExtension: &Object; { UCharInputStreamStreamedExtension }
            procedure setCharsetName(const newCharsetName: AnsiString);
            function readChars(length: int): &Object; { CharactersBuffer }
        public
            constructor create(reader: ByteReader);
            constructor create(reader: ByteReader; const charsetName: AnsiString);
            destructor destroy; override;
            procedure close();
            function read(): int;
            function read(const dst: uchar_Array1d): int;
            function read(const dst: uchar_Array1d; offset, length: int): int;
            function skip(ucharsQuantity: long): long;
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        published
            property charsetName: AnsiString read fldCharsetName write setCharsetName;
            property reader: ByteReader read fldReader;
        end;

        UCharOutputStream = class sealed(&Object, UCharWriter, Extendable)
        private
            fldCharsetName: AnsiString;
            fldWriter: ByteWriter;
            fldEncoderRef: &Object; { CharEncoder }
            procedure setCharsetName(const newCharsetName: AnsiString);
        public
            constructor create(writer: ByteWriter);
            constructor create(writer: ByteWriter; const charsetName: AnsiString);
            destructor destroy; override;
            procedure close();
            procedure write(ucharData: int);
            procedure write(const src: uchar_Array1d);
            procedure write(const src: uchar_Array1d; offset, length: int);
            procedure flush();
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        published
            property charsetName: AnsiString read fldCharsetName write setCharsetName;
            property writer: ByteWriter read fldWriter;
        end;

        PrintStream = class(&Object, ByteWriter, Extendable)
        private
            fldError: boolean;
            fldCharsetName: AnsiString;
            fldWriter: ByteWriter;
            fldEncoderRef: &Object; { CharEncoder }
            procedure write(const src: AnsiString);
            procedure write(const src: UnicodeString);
            procedure setCharsetName(const newCharsetName: AnsiString);
            function setError(newError: boolean): boolean;
        protected
            procedure errorSet(); virtual;
            procedure errorCleared(); virtual;
            procedure error();
        public
            constructor create(writer: ByteWriter);
            constructor create(writer: ByteWriter; const charsetName: AnsiString);
            destructor destroy; override;
            procedure close(); virtual;
            procedure write(byteData: int); virtual;
            procedure write(const src: byte_Array1d); virtual;
            procedure write(const src: byte_Array1d; offset, length: int); virtual;
            procedure flush(); virtual;
            procedure print(const src: boolean); virtual;
            procedure print(const src: int); virtual;
            procedure print(const src: long); virtual;
            procedure print(const src: float); virtual;
            procedure print(const src: double); virtual;
            procedure print(const src: real); virtual;
            procedure print(const src: AnsiString); virtual;
            procedure print(const src: UnicodeString); virtual;
            procedure print(const src: TObject); virtual;
            procedure print(const src: uchar_Array1d; offset, length: int); virtual;
            procedure printf(const form: AnsiString; const data: array of Value); virtual;
            procedure printf(const form: UnicodeString; const data: array of Value); virtual;
            procedure println(); virtual;
            procedure println(const src: boolean); virtual;
            procedure println(const src: int); virtual;
            procedure println(const src: long); virtual;
            procedure println(const src: float); virtual;
            procedure println(const src: double); virtual;
            procedure println(const src: real); virtual;
            procedure println(const src: AnsiString); virtual;
            procedure println(const src: UnicodeString); virtual;
            procedure println(const src: TObject); virtual;
            procedure println(const src: uchar_Array1d; offset, length: int); virtual;
            procedure printlnf(const form: AnsiString; const data: array of Value); virtual;
            procedure printlnf(const form: UnicodeString; const data: array of Value); virtual;
            function getExtensions(): Extension_Array1d; virtual;
            function getExtension(const typ: ShortString): Extension; virtual;
            function isError(): boolean;
        published
            property charsetName: AnsiString read fldCharsetName write setCharsetName;
            property writer: ByteWriter read fldWriter;
        end;

        UResource = class sealed
        public
            class function readResourceAsUnicodeString(const rsrcName: AnsiString): UnicodeString; static;
            class function readResourceAsUnicodeString(const rsrcName, charsetName: AnsiString): UnicodeString; static;
            class function readUnitResourceAsUnicodeString(const __unitName, rstrName: AnsiString): UnicodeString; static;
            class function readUnitResourceAsUnicodeString(const __unitName, rstrName, charsetName: AnsiString): UnicodeString; static;
        end;

        SResource = class sealed
        public
            class function isResourceExists(const rsrcName: AnsiString): boolean; static;
            class function isUnitResourceExists(const __unitName, rsrcName: AnsiString): boolean; static;
            class function openResourceAsStream(const rsrcName: AnsiString): ByteReader; static;
            class function openUnitResourceAsStream(const __unitName, rsrcName: AnsiString): ByteReader; static;
        end;

        IOException = class(Exception);

        EndOfStreamException = class(IOException);

        EndOfFreeSpaceException = class(IOException);

        InvalidDataFormatException = class(IOException);

        MalformedLineSeparatorException = class(InvalidDataFormatException);

        UTFDataFormatException = class(InvalidDataFormatException);
    {%endregion}

implementation

    {%region} uses
        pascalx.io.charset,
        pascalx.io.extension;
    {%endregion}

    {$R *.res}

    {$WARN 7046 OFF} { позволить инструкции вида mov rsi, [rsi+offset <поле>] для чтения поля объекта }

    {$ASMMODE INTEL}

    {$TYPEINFO OFF}
    {$CALLING REGISTER}

    {%region} type
        ResourceStream                    = class;
        LittleEndianDataInput             = class;
        BigEndianDataInput                = class;
        LittleEndianDataOutput            = class;
        BigEndianDataOutput               = class;
        UCharInputStreamMarkExtension     = class;
        UCharInputStreamStreamedExtension = class;

        ResourceStream = class(&Object, Extension, MarkExtension, ByteReader, SeekExtension, LimitedSizeExtension, Extendable)
        private type
            PByte = ^byte;
        private
            fldMarked: int;
            fldLength: int;
            fldPosition: int;
            fldHandle: long;
            fldData: PByte;
        public
            constructor create(handle: long; data: Pointer; length: int);
            procedure reset();
            procedure mark(transferLimit: int);
            procedure close();
            function read(): int;
            function read(const dst: byte_Array1d): int;
            function read(const dst: byte_Array1d; offset, length: int): int;
            function skip(bytesQuantity: long): long;
            function seek(offset: long; from: SeekFrom): long;
            function position(): long;
            function size(): long;
            function available(): long;
            function getExtensions(): Extension_Array1d;
            function getExtension(const typ: ShortString): Extension;
        end;

        LittleEndianDataInput = class(&Object, DataInput)
        protected
            fldData: byte_Array1d;
            fldReader: ByteReader;
            fldCharsetRef: Charset;
            procedure readData(length: int); virtual;
        public
            constructor create(reader: ByteReader; const data: byte_Array1d);
            procedure readFully(const dst: byte_Array1d);
            procedure readFully(const dst: byte_Array1d; offset, length: int);
            function readBoolean(): boolean;
            function readChar(): char;
            function readUChar(): uchar;
            function readUnsignedByte(): int;
            function readUnsignedShort(): int;
            function readByte(): int;
            function readShort(): int;
            function readInt(): int;
            function readLong(): long;
            function readFloat(): float;
            function readDouble(): double;
            function readReal(): real;
            function readUTF(): UnicodeString;
            function readln(): UnicodeString;
            function read(): UnicodeString;
        end;

        BigEndianDataInput = class(LittleEndianDataInput)
        protected
            procedure readData(length: int); override;
        end;

        LittleEndianDataOutput = class(&Object, DataOutput)
        protected
            fldData: byte_Array1d;
            fldWriter: ByteWriter;
            fldCharsetRef: Charset;
            procedure writeData(length: int); virtual;
        public
            constructor create(writer: ByteWriter; const data: byte_Array1d);
            procedure write(const src: byte_Array1d);
            procedure write(const src: byte_Array1d; offset, length: int);
            procedure writeBoolean(const data: boolean);
            procedure writeChar(const data: char);
            procedure writeUChar(const data: uchar);
            procedure writeByte(const data: int);
            procedure writeShort(const data: int);
            procedure writeInt(const data: int);
            procedure writeLong(const data: long);
            procedure writeFloat(const data: float);
            procedure writeDouble(const data: double);
            procedure writeReal(const data: real);
            procedure writeUTF(const data: UnicodeString);
            procedure writeln(const data: UnicodeString);
            procedure write(const data: UnicodeString);
        end;

        BigEndianDataOutput = class(LittleEndianDataOutput)
        protected
            procedure writeData(length: int); override;
        end;

        UCharInputStreamMarkExtension = class(&Object, Extension, MarkExtension)
        private
            fldMarkExtension: MarkExtension;
        public
            constructor create(ext: MarkExtension);
            procedure reset();
            procedure mark(transferLimit: int);
        end;

        UCharInputStreamStreamedExtension = class(&Object, Extension, StreamedExtension)
        private
            fldCharactersReaded: int;
            fldCharactersBuffer: CharactersBuffer;
            fldLimitedSizeExtension: LimitedSizeExtension;
        public
            constructor create(ext: LimitedSizeExtension);
            function ready(): boolean;
        end;
    {%endregion}

    {%region  DataInputStream }
        procedure DataInputStream.setCharsetName(const newCharsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
        begin
            try
                useCharsetRef := Charset.get(newCharsetName);
                useCharsetName := newCharsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            fldCharsetName := useCharsetName;
            pascalx.io.LittleEndianDataInput(fldBigEndianDataInputAsObject).fldCharsetRef := useCharsetRef;
            pascalx.io.LittleEndianDataInput(fldLittleEndianDataInputAsObject).fldCharsetRef := useCharsetRef;
        end;

        constructor DataInputStream.create(reader: ByteReader);
        begin
            create(reader, 'UTF-8');
        end;

        constructor DataInputStream.create(reader: ByteReader; const charsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
            useData: byte_Array1d;
            useLittleEndianDataInput: pascalx.io.LittleEndianDataInput;
            useBigEndianDataInput: pascalx.io.LittleEndianDataInput;
        begin
            inherited create();
            try
                useCharsetRef := Charset.get(charsetName);
                useCharsetName := charsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            useData := &Array.newByte1d(10);
            useLittleEndianDataInput := pascalx.io.LittleEndianDataInput.create(reader, useData);
            useLittleEndianDataInput.fldCharsetRef := useCharsetRef;
            useBigEndianDataInput := pascalx.io.BigEndianDataInput.create(reader, useData);
            useBigEndianDataInput.fldCharsetRef := useCharsetRef;
            fldCharsetName := useCharsetName;
            fldReader := reader;
            fldBigEndianDataInputAsInterface := useBigEndianDataInput;
            fldBigEndianDataInputAsObject := useBigEndianDataInput;
            fldLittleEndianDataInputAsInterface := useLittleEndianDataInput;
            fldLittleEndianDataInputAsObject := useLittleEndianDataInput;
        end;

        destructor DataInputStream.destroy;
        begin
            fldLittleEndianDataInputAsObject.free();
            fldBigEndianDataInputAsObject.free();
            inherited destroy;
        end;

        procedure DataInputStream.close();
        begin
            fldReader.close();
            destroy;
        end;

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

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

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

        function DataInputStream.skip(bytesQuantity: long): long;
        begin
            result := fldReader.skip(bytesQuantity);
        end;

        function DataInputStream.getExtensions(): Extension_Array1d;
        begin
            result := fldReader.getExtensions();
        end;

        function DataInputStream.getExtension(const typ: ShortString): Extension;
        begin
            result := fldReader.getExtension(typ);
        end;
    {%endregion}

    {%region  DataOutputStream }
        procedure DataOutputStream.setCharsetName(const newCharsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
        begin
            try
                useCharsetRef := Charset.get(newCharsetName);
                useCharsetName := newCharsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            fldCharsetName := useCharsetName;
            pascalx.io.LittleEndianDataOutput(fldBigEndianDataOutputAsObject).fldCharsetRef := useCharsetRef;
            pascalx.io.LittleEndianDataOutput(fldLittleEndianDataOutputAsObject).fldCharsetRef := useCharsetRef;
        end;

        constructor DataOutputStream.create(writer: ByteWriter);
        begin
            create(writer, 'UTF-8');
        end;

        constructor DataOutputStream.create(writer: ByteWriter; const charsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
            useData: byte_Array1d;
            useLittleEndianDataOutput: pascalx.io.LittleEndianDataOutput;
            useBigEndianDataOutput: pascalx.io.LittleEndianDataOutput;
        begin
            inherited create();
            try
                useCharsetRef := Charset.get(charsetName);
                useCharsetName := charsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            useData := &Array.newByte1d(10);
            useLittleEndianDataOutput := pascalx.io.LittleEndianDataOutput.create(writer, useData);
            useLittleEndianDataOutput.fldCharsetRef := useCharsetRef;
            useBigEndianDataOutput := pascalx.io.BigEndianDataOutput.create(writer, useData);
            useBigEndianDataOutput.fldCharsetRef := useCharsetRef;
            fldCharsetName := useCharsetName;
            fldWriter := writer;
            fldBigEndianDataOutputAsInterface := useBigEndianDataOutput;
            fldBigEndianDataOutputAsObject := useBigEndianDataOutput;
            fldLittleEndianDataOutputAsInterface := useLittleEndianDataOutput;
            fldLittleEndianDataOutputAsObject := useLittleEndianDataOutput;
        end;

        destructor DataOutputStream.destroy;
        begin
            fldLittleEndianDataOutputAsObject.free();
            fldBigEndianDataOutputAsObject.free();
            inherited destroy;
        end;

        procedure DataOutputStream.close();
        begin
            fldWriter.close();
            destroy;
        end;

        procedure DataOutputStream.write(byteData: int);
        begin
            fldWriter.write(byteData);
        end;

        procedure DataOutputStream.write(const src: byte_Array1d);
        begin
            fldWriter.write(src);
        end;

        procedure DataOutputStream.write(const src: byte_Array1d; offset, length: int);
        begin
            fldWriter.write(src, offset, length);
        end;

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

        function DataOutputStream.getExtensions(): Extension_Array1d;
        begin
            result := fldWriter.getExtensions();
        end;

        function DataOutputStream.getExtension(const typ: ShortString): Extension;
        begin
            result := fldWriter.getExtension(typ);
        end;
    {%endregion}

    {%region  UCharInputStream }
        procedure UCharInputStream.setCharsetName(const newCharsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
            oldDecoderRef: &Object;
        begin
            try
                useCharsetRef := Charset.get(newCharsetName);
                useCharsetName := newCharsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            oldDecoderRef := fldDecoderRef;
            fldCharsetName := useCharsetName;
            fldDecoderRef := useCharsetRef.newDecoder();
            oldDecoderRef.free();
        end;

        function UCharInputStream.readChars(length: int): &Object;
        var
            remained: int;
            readedOld: int;
            readedNew: int;
            src: ByteReader;
            dst: CharactersBuffer;
            ext: UCharInputStreamStreamedExtension;
            decoder: CharDecoder;
        begin
            decoder := CharDecoder(fldDecoderRef);
            ext := UCharInputStreamStreamedExtension(fldStreamedExtension);
            dst := ext.fldCharactersBuffer;
            src := fldReader;
            dst.discard(ext.fldCharactersReaded);
            ext.fldCharactersReaded := CoInt.MAX_VALUE;
            readedOld := -1;
            repeat
                readedNew := dst.length;
                if readedOld = readedNew then break;
                readedOld := readedNew;
                if readedOld >= length then break;
                remained := length - readedOld;
                if remained >= CoInt.MAX_VALUE - 6 then begin
                    remained := CoInt.MAX_VALUE;
                end else begin
                    remained := remained + 6;
                end;
                decoder.decodeToWriter(dst, src, remained);
            until false;
            ext.fldCharactersReaded := length;
            result := dst;
        end;

        constructor UCharInputStream.create(reader: ByteReader);
        begin
            create(reader, 'UTF-8');
        end;

        constructor UCharInputStream.create(reader: ByteReader; const charsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
            ext0: MarkExtension;
            ext1: LimitedSizeExtension;
        begin
            inherited create();
            try
                useCharsetRef := Charset.get(charsetName);
                useCharsetName := charsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            if reader = nil then begin
                ext0 := nil;
                ext1 := nil;
            end else begin
                ext0 := MarkExtension(reader.getExtension(MarkExtension));
                ext1 := LimitedSizeExtension(reader.getExtension(LimitedSizeExtension));
            end;
            fldCharsetName := useCharsetName;
            fldReader := reader;
            fldDecoderRef := useCharsetRef.newDecoder();
            if ext0 = nil then begin
                fldMarkExtension := nil;
            end else begin
                fldMarkExtension := UCharInputStreamMarkExtension.create(ext0);
            end;
            fldStreamedExtension := UCharInputStreamStreamedExtension.create(ext1);
        end;

        destructor UCharInputStream.destroy;
        begin
            fldDecoderRef.free();
            fldMarkExtension.free();
            fldStreamedExtension.free();
            inherited destroy;
        end;

        procedure UCharInputStream.close();
        begin
            fldReader.close();
            destroy;
        end;

        function UCharInputStream.read(): int;
        var
            stream: CharactersBuffer;
        begin
            stream := CharactersBuffer(readChars(1));
            if stream.length < 1 then begin
                result := -1;
                exit;
            end;
            result := int(stream[0]);
        end;

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

        function UCharInputStream.read(const dst: uchar_Array1d; offset, length: int): int;
        var
            readed: int;
            stream: CharactersBuffer;
        begin
            &Array.checkBounds(dst, offset, length);
            if length <= 0 then begin
                result := 0;
                exit;
            end;
            stream := CharactersBuffer(readChars(length));
            readed := CoInt.min(stream.length, length);
            if readed <= 0 then begin
                result := -1;
                exit;
            end;
            stream.getChars(0, readed, dst, offset);
            result := readed;
        end;

        function UCharInputStream.skip(ucharsQuantity: long): long;
        var
            total: long;
            skipped: long;
            buffer: uchar_Array1d;
        begin
            if ucharsQuantity <= 0 then begin
                result := 0;
                exit;
            end;
            total := 0;
            buffer := &Array.newUChar1d(224);
            while ucharsQuantity > 0 do begin
                skipped := read(buffer, 0, int(CoLong.min(ucharsQuantity, 224)));
                if skipped < 0 then break;
                dec(ucharsQuantity, skipped);
                inc(total, skipped);
            end;
            result := total;
        end;

        function UCharInputStream.getExtensions(): Extension_Array1d;
        var
            ext0: Extension;
            ext1: Extension;
        begin
            ext0 := fldStreamedExtension as Extension;
            ext1 := fldMarkExtension as Extension;
            if ext1 = nil then begin
                result := [ ext0 ];
                exit;
            end;
            result := [ ext0, ext1 ];
        end;

        function UCharInputStream.getExtension(const typ: ShortString): Extension;
        begin
            if Lang.isType(typ, Extension) then begin
                result := fldStreamedExtension as Extension;
                exit;
            end;
            if Lang.isType(typ, MarkExtension) then begin
                result := fldMarkExtension as MarkExtension;
                exit;
            end;
            if Lang.isType(typ, StreamedExtension) then begin
                result := fldStreamedExtension as StreamedExtension;
                exit;
            end;
            result := nil;
        end;
    {%endregion}

    {%region  UCharOutputStream }
        procedure UCharOutputStream.setCharsetName(const newCharsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
            oldEncoderRef: &Object;
        begin
            try
                useCharsetRef := Charset.get(newCharsetName);
                useCharsetName := newCharsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            oldEncoderRef := fldEncoderRef;
            fldCharsetName := useCharsetName;
            fldEncoderRef := useCharsetRef.newEncoder();
            oldEncoderRef.free();
        end;

        constructor UCharOutputStream.create(writer: ByteWriter);
        begin
            create(writer, 'UTF-8');
        end;

        constructor UCharOutputStream.create(writer: ByteWriter; const charsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
        begin
            inherited create();
            try
                useCharsetRef := Charset.get(charsetName);
                useCharsetName := charsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            fldCharsetName := useCharsetName;
            fldWriter := writer;
            fldEncoderRef := useCharsetRef.newEncoder();
        end;

        destructor UCharOutputStream.destroy;
        begin
            fldEncoderRef.free();
            inherited destroy;
        end;

        procedure UCharOutputStream.close();
        begin
            fldWriter.close();
            destroy;
        end;

        procedure UCharOutputStream.write(ucharData: int);
        begin
            CharEncoder(fldEncoderRef).encodeToWriter(fldWriter, [ uchar(ucharData) ]);
        end;

        procedure UCharOutputStream.write(const src: uchar_Array1d);
        begin
            CharEncoder(fldEncoderRef).encodeToWriter(fldWriter, src, 0, length(src));
        end;

        procedure UCharOutputStream.write(const src: uchar_Array1d; offset, length: int);
        begin
            CharEncoder(fldEncoderRef).encodeToWriter(fldWriter, src, offset, length);
        end;

        procedure UCharOutputStream.flush();
        begin
            fldWriter.flush();
        end;

        function UCharOutputStream.getExtensions(): Extension_Array1d;
        begin
            result := nil;
        end;

        function UCharOutputStream.getExtension(const typ: ShortString): Extension;
        begin
            result := nil;
        end;
    {%endregion}

    {%region  PrintStream }
        procedure PrintStream.write(const src: AnsiString);
        begin
            try
                CharEncoder(fldEncoderRef).encodeToWriter(fldWriter, src.toUTF16().toUCharArray());
            except
                error();
            end;
        end;

        procedure PrintStream.write(const src: UnicodeString);
        begin
            try
                CharEncoder(fldEncoderRef).encodeToWriter(fldWriter, src.toUCharArray());
            except
                error();
            end;
        end;

        procedure PrintStream.setCharsetName(const newCharsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
            oldEncoderRef: &Object;
        begin
            try
                useCharsetRef := Charset.get(newCharsetName);
                useCharsetName := newCharsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            oldEncoderRef := fldEncoderRef;
            fldCharsetName := useCharsetName;
            fldEncoderRef := useCharsetRef.newEncoder();
            oldEncoderRef.free();
        end;

        function PrintStream.setError(newError: boolean): boolean; assembler;
        asm
            {$IFDEF WINDOWS}
                        mov         al,     dl
            {$ELSE}
                        lea         rcx,    [rdi+$00]
                        mov         al,     sil
            {$ENDIF}
                        xchg        byte    [rcx+offset fldError], al
                        movsx       eax,    al
        end;

        procedure PrintStream.errorSet();
        begin
        end;

        procedure PrintStream.errorCleared();
        begin
        end;

        procedure PrintStream.error();
        begin
            if not setError(true) then errorSet();
        end;

        constructor PrintStream.create(writer: ByteWriter);
        begin
            create(writer, 'UTF-8');
        end;

        constructor PrintStream.create(writer: ByteWriter; const charsetName: AnsiString);
        var
            useCharsetName: AnsiString;
            useCharsetRef: Charset;
        begin
            inherited create();
            try
                useCharsetRef := Charset.get(charsetName);
                useCharsetName := charsetName;
            except
                useCharsetRef := Charset.getDefault();
                useCharsetName := useCharsetRef.name;
            end;
            fldCharsetName := useCharsetName;
            fldWriter := writer;
            fldEncoderRef := useCharsetRef.newEncoder();
        end;

        destructor PrintStream.destroy;
        begin
            fldEncoderRef.free();
            inherited destroy;
        end;

        procedure PrintStream.close();
        begin
            try
                fldWriter.close();
            except
                error();
            end;
            destroy;
        end;

        procedure PrintStream.write(byteData: int);
        begin
            try
                fldWriter.write(byteData);
            except
                error();
            end;
        end;

        procedure PrintStream.write(const src: byte_Array1d);
        begin
            try
                fldWriter.write(src);
            except
                error();
            end;
        end;

        procedure PrintStream.write(const src: byte_Array1d; offset, length: int);
        begin
            try
                fldWriter.write(src, offset, length);
            except
                error();
            end;
        end;

        procedure PrintStream.flush();
        begin
            try
                fldWriter.flush();
            except
                error();
            end;
        end;

        procedure PrintStream.print(const src: boolean);
        begin
            if src then begin
                write(UnicodeString('true'));
                exit;
            end;
            write(UnicodeString('false'));
        end;

        procedure PrintStream.print(const src: int);
        begin
            write(CoInt.toString(src));
        end;

        procedure PrintStream.print(const src: long);
        begin
            write(CoLong.toString(src));
        end;

        procedure PrintStream.print(const src: float);
        begin
            write(CoFloat.toString(src));
        end;

        procedure PrintStream.print(const src: double);
        begin
            write(CoDouble.toString(src));
        end;

        procedure PrintStream.print(const src: real);
        begin
            write(CoReal.toString(src));
        end;

        procedure PrintStream.print(const src: AnsiString);
        begin
            write(src);
        end;

        procedure PrintStream.print(const src: UnicodeString);
        begin
            write(src);
        end;

        procedure PrintStream.print(const src: TObject);
        begin
            if src = nil then begin
                write(UnicodeString('null'));
                exit;
            end;
            write(src.toString());
        end;

        procedure PrintStream.print(const src: uchar_Array1d; offset, length: int);
        begin
            try
                CharEncoder(fldEncoderRef).encodeToWriter(fldWriter, src, offset, length);
            except
                error();
            end;
        end;

        procedure PrintStream.printf(const form: AnsiString; const data: array of Value);
        begin
            write(AnsiString.format(form, data));
        end;

        procedure PrintStream.printf(const form: UnicodeString; const data: array of Value);
        begin
            write(UnicodeString.format(form, data));
        end;

        procedure PrintStream.println();
        begin
            write(CoUnicodeString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: boolean);
        begin
            if src then begin
                write(UnicodeString('true') + CoUnicodeString.LINE_ENDING);
                exit;
            end;
            write(UnicodeString('false') + CoUnicodeString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: int);
        begin
            write(CoInt.toString(src) + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: long);
        begin
            write(CoLong.toString(src) + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: float);
        begin
            write(CoFloat.toString(src) + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: double);
        begin
            write(CoDouble.toString(src) + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: real);
        begin
            write(CoReal.toString(src) + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: AnsiString);
        begin
            write(src + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: UnicodeString);
        begin
            write(src + CoUnicodeString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: TObject);
        begin
            if src = nil then begin
                write(UnicodeString('null') + CoUnicodeString.LINE_ENDING);
                exit;
            end;
            write(src.toString() + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.println(const src: uchar_Array1d; offset, length: int);
        begin
            write(UnicodeString.create(src, offset, length) + CoUnicodeString.LINE_ENDING);
        end;

        procedure PrintStream.printlnf(const form: AnsiString; const data: array of Value);
        begin
            write(AnsiString.format(form, data) + CoAnsiString.LINE_ENDING);
        end;

        procedure PrintStream.printlnf(const form: UnicodeString; const data: array of Value);
        begin
            write(UnicodeString.format(form, data) + CoUnicodeString.LINE_ENDING);
        end;

        function PrintStream.getExtensions(): Extension_Array1d;
        var
            locWriter: ByteWriter;
        begin
            locWriter := fldWriter;
            if locWriter = nil then begin
                result := nil;
                exit;
            end;
            result := locWriter.getExtensions();
        end;

        function PrintStream.getExtension(const typ: ShortString): Extension;
        var
            locWriter: ByteWriter;
        begin
            locWriter := fldWriter;
            if locWriter = nil then begin
                result := nil;
                exit;
            end;
            result := locWriter.getExtension(typ);
        end;

        function PrintStream.isError(): boolean;
        begin
            if setError(false) then begin
                errorCleared();
                result := true;
                exit;
            end;
            result := false;
        end;
    {%endregion}

    {%region  UResource }
        class function UResource.readResourceAsUnicodeString(const rsrcName: AnsiString): UnicodeString;
        begin
            result := readResourceAsUnicodeString(rsrcName, 'UTF-8');
        end;

        class function UResource.readResourceAsUnicodeString(const rsrcName, charsetName: AnsiString): UnicodeString;
        var
            size: int;
            inst: long;
            desc: long;
            handle: long;
            rstring: byte_Array1d;
            data: Pointer;
        begin
            inst := system.hInstance();
            desc := long(system.findResource(inst, rsrcName, system.PAnsiChar({$IFDEF WINDOWS}windows{$ELSE}system{$ENDIF}.RT_RCDATA)));
            if desc = 0 then begin
                result := '';
                exit;
            end;
            handle := long(system.loadResource(inst, desc));
            if handle = 0 then begin
                result := '';
                exit;
            end;
            size := int(system.sizeofResource(inst, desc));
            data := system.lockResource(handle);
            try
                rstring := &Array.newByte1d(size);
                &Array.copyRaw(data^, rstring[0], size);
                result := UnicodeString.create(Charset.get(charsetName).decode(rstring, 0, size));
            finally
                system.unlockResource(handle);
                system.freeResource(handle);
            end;
        end;

        class function UResource.readUnitResourceAsUnicodeString(const __unitName, rstrName: AnsiString): UnicodeString;
        begin
            result := readResourceAsUnicodeString(AResource.unitToTextResourceName(__unitName, rstrName), 'UTF-8');
        end;

        class function UResource.readUnitResourceAsUnicodeString(const __unitName, rstrName, charsetName: AnsiString): UnicodeString;
        begin
            result := readResourceAsUnicodeString(AResource.unitToTextResourceName(__unitName, rstrName), charsetName);
        end;
    {%endregion}

    {%region  SResource }
        class function SResource.isResourceExists(const rsrcName: AnsiString): boolean;
        begin
            result := long(system.findResource(system.hInstance(), rsrcName, system.PAnsiChar({$IFDEF WINDOWS}windows{$ELSE}system{$ENDIF}.RT_RCDATA))) <> 0;
        end;

        class function SResource.isUnitResourceExists(const __unitName, rsrcName: AnsiString): boolean;
        begin
            result := isResourceExists(AResource.unitToTextResourceName(__unitName, rsrcName));
        end;

        class function SResource.openResourceAsStream(const rsrcName: AnsiString): ByteReader;
        var
            size: int;
            inst: long;
            desc: long;
            handle: long;
            data: Pointer;
        begin
            inst := system.hInstance();
            desc := long(system.findResource(inst, rsrcName, system.PAnsiChar({$IFDEF WINDOWS}windows{$ELSE}system{$ENDIF}.RT_RCDATA)));
            if desc = 0 then begin
                result := nil;
                exit;
            end;
            handle := long(system.loadResource(inst, desc));
            if handle = 0 then begin
                result := nil;
                exit;
            end;
            size := int(system.sizeofResource(inst, desc));
            data := system.lockResource(handle);
            result := ResourceStream.create(handle, data, size);
        end;

        class function SResource.openUnitResourceAsStream(const __unitName, rsrcName: AnsiString): ByteReader;
        begin
            result := openResourceAsStream(AResource.unitToTextResourceName(__unitName, rsrcName));
        end;
    {%endregion}

    {%region  ResourceStream }
        constructor ResourceStream.create(handle: long; data: Pointer; length: int);
        begin
            inherited create();
            fldLength := length;
            fldHandle := handle;
            fldData := PByte(data);
        end;

        procedure ResourceStream.reset();
        begin
            fldPosition := fldMarked;
        end;

        procedure ResourceStream.mark(transferLimit: int);
        begin
            fldMarked := fldPosition;
        end;

        procedure ResourceStream.close();
        var
            handle: system.PtrUInt;
        begin
            handle := system.PtrUInt(fldHandle);
            destroy;
            system.unlockResource(handle);
            system.freeResource(handle);
        end;

        function ResourceStream.read(): int;
        var
            locPosition: int;
        begin
            locPosition := fldPosition;
            if locPosition >= fldLength then begin
                result := -1;
                exit;
            end;
            fldPosition := locPosition + 1;
            result := (fldData + locPosition)^ and $ff;
        end;

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

        function ResourceStream.read(const dst: byte_Array1d; offset, length: int): int;
        var
            locPosition: int;
            locRemainder: int;
        begin
            &Array.checkBounds(dst, offset, length);
            if length <= 0 then begin
                result := 0;
                exit;
            end;
            locPosition := fldPosition;
            locRemainder := fldLength - locPosition;
            if locRemainder <= 0 then begin
                result := -1;
                exit;
            end;
            if length > locRemainder then length := locRemainder;
            &Array.copyRaw((fldData + locPosition)^, dst[offset], length);
            fldPosition := locPosition + length;
            result := length;
        end;

        function ResourceStream.skip(bytesQuantity: long): long;
        var
            locLength: int;
            locPosition: int;
            locRemainder: int;
        begin
            if bytesQuantity <= 0 then begin
                result := 0;
                exit;
            end;
            if bytesQuantity < CoInt.MAX_VALUE then begin
                locLength := int(bytesQuantity);
            end else begin
                locLength := CoInt.MAX_VALUE;
            end;
            locPosition := fldPosition;
            locRemainder := fldLength - locPosition;
            if locLength > locRemainder then locLength := locRemainder;
            fldPosition := locPosition + locLength;
            result := locLength;
        end;

        function ResourceStream.seek(offset: long; from: SeekFrom): long;
        var
            locLength: long;
            locPosition: long;
        begin
            locLength := fldLength;
            case from of
            SeekFrom.sfBegin:
                locPosition := offset;
            SeekFrom.sfEnd:
                locPosition := offset + locLength;
            SeekFrom.sfCurrent:
                locPosition := offset + fldPosition;
            else
                raise IllegalArgumentException.create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.lang.UNIT_NAME, 'illegal-argument'), [ CoAnsiString.create('from') ]));
            end;
            if locPosition < 0 then locPosition := 0;
            if locPosition > locLength then locPosition := locLength;
            fldPosition := int(locPosition);
            result := locPosition;
        end;

        function ResourceStream.position(): long;
        begin
            result := fldPosition;
        end;

        function ResourceStream.size(): long;
        begin
            result := fldLength;
        end;

        function ResourceStream.available(): long;
        begin
            result := fldLength - fldPosition;
        end;

        function ResourceStream.getExtensions(): Extension_Array1d;
        begin
            result := [ self as Extension ];
        end;

        function ResourceStream.getExtension(const typ: ShortString): Extension;
        var
            ext: Extension;
        begin
            ext := self;
            if Lang.isInstance(ext, typ) then begin
                result := Extension(Lang.cast(ext, typ));
                exit;
            end;
            result := nil;
        end;
    {%endregion}

    {%region  LittleEndianDataInput }
        procedure LittleEndianDataInput.readData(length: int);
        var
            offset: int;
            readed: int;
            dst: byte_Array1d;
            reader: ByteReader;
        begin
            dst := fldData;
            offset := 0;
            reader := fldReader;
            while length > 0 do begin
                readed := reader.read(dst, offset, length);
                if readed <= 0 then begin
                    raise EndOfStreamException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io.read.end-of-stream'));
                end;
                inc(offset, readed);
                dec(length, readed);
            end;
        end;

        constructor LittleEndianDataInput.create(reader: ByteReader; const data: byte_Array1d);
        begin
            inherited create();
            fldData := data;
            fldReader := reader;
        end;

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

        procedure LittleEndianDataInput.readFully(const dst: byte_Array1d; offset, length: int);
        var
            readed: int;
            reader: ByteReader;
        begin
            &Array.checkBounds(dst, offset, length);
            reader := fldReader;
            while length > 0 do begin
                readed := reader.read(dst, offset, length);
                if readed <= 0 then begin
                    raise EndOfStreamException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io.read.end-of-stream'));
                end;
                inc(offset, readed);
                dec(length, readed);
            end;
        end;

        function LittleEndianDataInput.readBoolean(): boolean;
        var
            data: int;
        begin
            data := fldReader.read();
            if data < 0 then begin
                raise EndOfStreamException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io.read.end-of-stream'));
            end;
            result := (data and $ff) <> 0;
        end;

        function LittleEndianDataInput.readChar(): char;
        var
            data: int;
        begin
            data := fldReader.read();
            if data < 0 then begin
                raise EndOfStreamException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io.read.end-of-stream'));
            end;
            result := char(data);
        end;

        function LittleEndianDataInput.readUChar(): uchar;
        begin
            readData(2);
            result := uchar((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readUnsignedByte(): int;
        var
            data: int;
        begin
            data := fldReader.read();
            if data < 0 then begin
                raise EndOfStreamException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io.read.end-of-stream'));
            end;
            result := data and $ff;
        end;

        function LittleEndianDataInput.readUnsignedShort(): int;
        begin
            readData(2);
            result := short((@(fldData[0]))^) and $ffff;
        end;

        function LittleEndianDataInput.readByte(): int;
        var
            data: int;
        begin
            data := fldReader.read();
            if data < 0 then begin
                raise EndOfStreamException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'io.read.end-of-stream'));
            end;
            result := byte(data);
        end;

        function LittleEndianDataInput.readShort(): int;
        begin
            readData(2);
            result := short((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readInt(): int;
        begin
            readData(4);
            result := int((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readLong(): long;
        begin
            readData(8);
            result := long((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readFloat(): float;
        begin
            readData(4);
            result := float((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readDouble(): double;
        begin
            readData(8);
            result := double((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readReal(): real;
        begin
            readData(10);
            result := real((@(fldData[0]))^);
        end;

        function LittleEndianDataInput.readUTF(): UnicodeString;
        var
            b1: int;
            b2: int;
            b3: int;
            index: int;
            utfLength: int;
            resultLength: int;
            utfData: byte_Array1d;
            resultData: uchar_Array1d;
        begin
            utfLength := readUnsignedShort();
            if utfLength <= 0 then begin
                result := '';
                exit;
            end;
            utfData := &Array.newByte1d(utfLength);
            readFully(utfData, 0, utfLength);
            resultLength := 0;
            resultData := &Array.newUChar1d(utfLength);
            index := 0;
            while index < utfLength do begin
                b1 := utfData[index] and $ff;
                case b1 shr 4 of
                $00..$07: begin
                    inc(index);
                    resultData[resultLength] := uchar(b1);
                    inc(resultLength);
                end;
                $0c..$0d: begin
                    inc(index, 2);
                    if index > utfLength then begin
                        raise UTFDataFormatException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format'));
                    end;
                    b2 := utfData[index - 1];
                    if (b2 and $c0) <> $80 then begin
                        raise UTFDataFormatException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format'));
                    end;
                    resultData[resultLength] := uchar(((b1 and $1f) shl 6) or (b2 and $3f));
                    inc(resultLength);
                end;
                $0e: begin
                    inc(index, 3);
                    if index > utfLength then begin
                        raise UTFDataFormatException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format'));
                    end;
                    b2 := utfData[index - 2];
                    b3 := utfData[index - 1];
                    if ((b2 and $c0) <> $80) or ((b3 and $c0) <> $80) then begin
                        raise UTFDataFormatException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format'));
                    end;
                    resultData[resultLength] := uchar(((b1 and $0f) shl 12) or ((b2 and $3f) shl 6) or (b3 and $3f));
                    inc(resultLength);
                end;
                else
                    raise UTFDataFormatException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format'));
                end;
            end;
            result := UnicodeString.create(resultData, 0, resultLength);
        end;

        function LittleEndianDataInput.readln(): UnicodeString;
        var
            b0: int;
            index: int;
            endingStart: int;
            endingLength: int;
            resultLength: int;
            endingData: byte_Array1d;
            resultData: byte_Array1d;
            resultCopy: byte_Array1d;
            charsetRef: Charset;
            reader: ByteReader;
        begin
            resultLength := 0;
            resultData := &Array.newByte1d($7f);
            charsetRef := fldCharsetRef;
            endingData := charsetRef.encode(CoUnicodeString.LINE_ENDING.toUCharArray());
            endingStart := endingData[0] and $ff;
            endingLength := length(endingData);
            reader := fldReader;
            while resultLength < CoInt.MAX_VALUE do begin
                b0 := reader.read();
                if b0 = endingStart then begin
                    for index := 1 to endingLength - 1 do if reader.read() <> (endingData[index] and $ff) then begin
                        raise MalformedLineSeparatorException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format.malformed-line-separator'));
                    end;
                    break;
                end;
                if b0 < 0 then break;
                if resultLength = length(resultData) then begin
                    resultCopy := &Array.newByte1d((resultLength shl 1) or 1);
                    &Array.copyPrimitives(resultData, 0, resultCopy, 0, resultLength);
                    resultData := resultCopy;
                end;
                resultData[resultLength] := byte(b0);
                inc(resultLength);
            end;
            result := UnicodeString.create(charsetRef.decode(resultData, 0, resultLength));
        end;

        function LittleEndianDataInput.read(): UnicodeString;
        var
            b0: int;
            index: int;
            endingStart: int;
            endingLength: int;
            resultLength: int;
            endingData: byte_Array1d;
            resultData: byte_Array1d;
            resultCopy: byte_Array1d;
            charsetRef: Charset;
            reader: ByteReader;
        begin
            resultLength := 0;
            resultData := &Array.newByte1d($7f);
            charsetRef := fldCharsetRef;
            endingData := charsetRef.encode(CoUnicodeString.LINE_ENDING.toUCharArray());
            endingStart := endingData[0] and $ff;
            endingLength := length(endingData);
            reader := fldReader;
            repeat
                b0 := reader.read();
                if b0 < 0 then begin
                    result := '';
                    exit;
                end;
                if b0 > $20 then begin
                    resultData[0] := byte(b0);
                    resultLength := 1;
                    break;
                end;
            until false;
            while resultLength < CoInt.MAX_VALUE do begin
                b0 := reader.read();
                if b0 = endingStart then begin
                    for index := 1 to endingLength - 1 do if reader.read() <> (endingData[index] and $ff) then begin
                        raise MalformedLineSeparatorException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format.malformed-line-separator'));
                    end;
                    break;
                end;
                if b0 <= $20 then break;
                if resultLength = length(resultData) then begin
                    resultCopy := &Array.newByte1d((resultLength shl 1) or 1);
                    &Array.copyPrimitives(resultData, 0, resultCopy, 0, resultLength);
                    resultData := resultCopy;
                end;
                resultData[resultLength] := byte(b0);
                inc(resultLength);
            end;
            result := UnicodeString.create(charsetRef.decode(resultData, 0, resultLength));
        end;
    {%endregion}

    {%region  BigEndianDataInput }
        procedure BigEndianDataInput.readData(length: int); assembler;
        var
            storageLength: int;
            {$IFDEF WINDOWS}
            storageRSI: long;
            storageRDI: long;
            {$ENDIF}
            storageSelf: BigEndianDataInput;
        asm
            {$IFDEF WINDOWS}
                        mov         qword   [storageRSI], rsi
                        mov         qword   [storageRDI], rdi
                        mov         qword   [storageSelf], rcx
                        mov         dword   [storageLength], edx
            {$ELSE}
                        mov         qword   [storageSelf], rdi
                        mov         dword   [storageLength], esi
            {$ENDIF}
                        call        LittleEndianDataInput.readData
                        mov         ecx,    [storageLength]
                        shr         ecx,    $01
                        jz          @01
                        mov         rsi,    [storageSelf]
                        mov         rsi,    [rsi+offset fldData]
                        lea         rdi,    [rsi+rcx*2-$01]
                @00:    mov         al,     [rsi+$00]
                        mov         dl,     [rdi+$00]
                        mov         byte    [rsi+$00], dl
                        mov         byte    [rdi+$00], al
                        lea         rsi,    [rsi+$01]
                        lea         rdi,    [rdi-$01]
                        loop        @00
                @01:
            {$IFDEF WINDOWS}
                        mov         rdi,    [storageRDI]
                        mov         rsi,    [storageRSI]
            {$ENDIF}
        end;
    {%endregion}

    {%region  LittleEndianDataOutput }
        procedure LittleEndianDataOutput.writeData(length: int);
        begin
            fldWriter.write(fldData, 0, length);
        end;

        constructor LittleEndianDataOutput.create(writer: ByteWriter; const data: byte_Array1d);
        begin
            inherited create();
            fldData := data;
            fldWriter := writer;
        end;

        procedure LittleEndianDataOutput.write(const src: byte_Array1d);
        begin
            fldWriter.write(src, 0, length(src));
        end;

        procedure LittleEndianDataOutput.write(const src: byte_Array1d; offset, length: int);
        begin
            fldWriter.write(src, offset, length);
        end;

        procedure LittleEndianDataOutput.writeBoolean(const data: boolean);
        begin
            if data then begin
                fldWriter.write($01);
                exit;
            end;
            fldWriter.write($00);
        end;

        procedure LittleEndianDataOutput.writeChar(const data: char);
        begin
            fldWriter.write(int(data));
        end;

        procedure LittleEndianDataOutput.writeUChar(const data: uchar);
        begin
            uchar((@(fldData[0]))^) := data;
            writeData(2);
        end;

        procedure LittleEndianDataOutput.writeByte(const data: int);
        begin
            fldWriter.write(data);
        end;

        procedure LittleEndianDataOutput.writeShort(const data: int);
        begin
            short((@(fldData[0]))^) := data;
            writeData(2);
        end;

        procedure LittleEndianDataOutput.writeInt(const data: int);
        begin
            int((@(fldData[0]))^) := data;
            writeData(4);
        end;

        procedure LittleEndianDataOutput.writeLong(const data: long);
        begin
            long((@(fldData[0]))^) := data;
            writeData(8);
        end;

        procedure LittleEndianDataOutput.writeFloat(const data: float);
        begin
            float((@(fldData[0]))^) := data;
            writeData(4);
        end;

        procedure LittleEndianDataOutput.writeDouble(const data: double);
        begin
            double((@(fldData[0]))^) := data;
            writeData(8);
        end;

        procedure LittleEndianDataOutput.writeReal(const data: real);
        begin
            real((@(fldData[0]))^) := data;
            writeData(10);
        end;

        procedure LittleEndianDataOutput.writeUTF(const data: UnicodeString);
        var
            chr: int;
            index: int;
            resultLength: int;
            stringLength: int;
            resultData: byte_Array1d;
            stringData: uchar_Array1d;
        begin
            stringLength := data.length;
            if stringLength <= 0 then begin
                writeShort(0);
                exit;
            end;
            if stringLength > $00010000 then stringLength := $00010000;
            stringData := &Array.newUChar1d(stringLength);
            &Array.copyRaw(data[1], stringData[0], stringLength * sizeof(uchar));
            resultLength := 0;
            for index := stringLength - 1 downto 0 do begin
                chr := int(stringData[index]);
                if (chr > $0000) and (chr < $0080) then begin
                    inc(resultLength, 1);
                end else
                if chr < $0800 then begin
                    inc(resultLength, 2);
                end else begin
                    inc(resultLength, 3);
                end;
                if resultLength > $ffff then begin
                    raise UTFDataFormatException.create(AResource.readUnitResourceAsAnsiString(pascalx.io.UNIT_NAME, 'invalid-data-format.utf.too-long'));
                end;
            end;
            resultData := &Array.newByte1d(resultLength);
            resultLength := 0;
            for index := 0 to stringLength - 1 do begin
                chr := int(stringData[index]);
                if (chr > $0000) and (chr < $0080) then begin
                    resultData[resultLength + 0] := byte(chr);
                    inc(resultLength);
                    continue;
                end;
                if chr < $0800 then begin
                    resultData[resultLength + 0] := byte($c0 or (chr shr 6) and $1f);
                    resultData[resultLength + 1] := byte($80 or chr and $3f);
                    inc(resultLength, 2);
                    continue;
                end;
                resultData[resultLength + 0] := byte($e0 or (chr shr 12) and $0f);
                resultData[resultLength + 1] := byte($80 or (chr shr 6) and $3f);
                resultData[resultLength + 2] := byte($80 or chr and $3f);
                inc(resultLength, 3);
            end;
            writeShort(resultLength);
            write(resultData, 0, resultLength);
        end;

        procedure LittleEndianDataOutput.writeln(const data: UnicodeString);
        begin
            with fldWriter, fldCharsetRef do begin
                write(encode(data.toUCharArray()));
                write(encode(CoUnicodeString.LINE_ENDING.toUCharArray()));
            end;
        end;

        procedure LittleEndianDataOutput.write(const data: UnicodeString);
        begin
            with fldWriter, fldCharsetRef do begin
                write(encode(data.toUCharArray()));
                write(encode(UnicodeString(#$0020).toUCharArray()));
            end;
        end;
    {%endregion}

    {%region  BigEndianDataOutput }
        procedure BigEndianDataOutput.writeData(length: int); assembler;
        var
            storageLength: int;
            {$IFDEF WINDOWS}
            storageRSI: long;
            storageRDI: long;
            {$ENDIF}
            storageSelf: BigEndianDataOutput;
        asm
            {$IFDEF WINDOWS}
                        mov         qword   [storageRSI], rsi
                        mov         qword   [storageRDI], rdi
                        mov         qword   [storageSelf], rcx
                        mov         dword   [storageLength], edx
                        mov         ecx,    edx
            {$ELSE}
                        mov         qword   [storageSelf], rdi
                        mov         dword   [storageLength], esi
                        mov         ecx,    esi
            {$ENDIF}
                        shr         ecx,    $01
                        jz          @01
                        mov         rsi,    [storageSelf]
                        mov         rsi,    [rsi+offset fldData]
                        lea         rdi,    [rsi+rcx*2-$01]
                @00:    mov         al,     [rsi+$00]
                        mov         dl,     [rdi+$00]
                        mov         byte    [rsi+$00], dl
                        mov         byte    [rdi+$00], al
                        lea         rsi,    [rsi+$01]
                        lea         rdi,    [rdi-$01]
                        loop        @00
                @01:
            {$IFDEF WINDOWS}
                        mov         rcx,    [storageSelf]
                        mov         edx,    [storageLength]
            {$ELSE}
                        mov         rdi,    [storageSelf]
                        mov         esi,    [storageLength]
            {$ENDIF}
                        call        LittleEndianDataOutput.writeData
            {$IFDEF WINDOWS}
                        mov         rdi,    [storageRDI]
                        mov         rsi,    [storageRSI]
            {$ENDIF}
        end;
    {%endregion}

    {%region  UCharInputStreamMarkExtension }
        constructor UCharInputStreamMarkExtension.create(ext: MarkExtension);
        begin
            inherited create();
            fldMarkExtension := ext;
        end;

        procedure UCharInputStreamMarkExtension.reset();
        begin
            fldMarkExtension.reset();
        end;

        procedure UCharInputStreamMarkExtension.mark(transferLimit: int);
        begin
            fldMarkExtension.mark(transferLimit);
        end;
    {%endregion}

    {%region  UCharInputStreamStreamedExtension }
        constructor UCharInputStreamStreamedExtension.create(ext: LimitedSizeExtension);
        begin
            inherited create();
            fldCharactersBuffer := CharactersBuffer.create();
            fldLimitedSizeExtension := ext;
        end;

        function UCharInputStreamStreamedExtension.ready(): boolean;
        var
            ext: LimitedSizeExtension;
        begin
            if fldCharactersBuffer.length > fldCharactersReaded then begin
                result := true;
                exit;
            end;
            ext := fldLimitedSizeExtension;
            result := (ext <> nil) and (ext.available() > 0);
        end;
    {%endregion}

    {%region} initialization
        Lang.registerInterfaces([
            typeInfo(Extension),
            typeInfo(Extendable),
            typeInfo(ByteReader),
            typeInfo(ByteWriter),
            typeInfo(UCharReader),
            typeInfo(UCharWriter),
            typeInfo(DataInput),
            typeInfo(DataOutput)
        ]);
    {%endregion}

end.