pascalx.io.charset.eightbit.pas

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

{
    pascalx.io.charset.eightbit — модуль для разработки восьмибитных кодировок
    текста.

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

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

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

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

unit pascalx.io.charset.eightbit;

    {$MODE DELPHI}

interface

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

    {$WARN 3018 OFF} { позволить конструкторы с любой видимостью }

    {$TYPEINFO ON}
    {$CALLING REGISTER}

    const UNIT_NAME = 'pascalx.io.charset.eightbit';

    {%region} type
        EightBitCharset                           = class;
        EightBitCharsetCharacterDecodingException = class;
        EightBitCharsetCharacterEncodingException = class;

        EightBitCharset = class abstract(Charset)
        private
            fldCharacters: uchar_Array1d;
            function getCharacters(): UnicodeString;
        protected
            constructor create(const name: AnsiString; const aliases: AnsiString_Array1d; const characters: UnicodeString);
        public
            function newDecoder(): CharDecoder; override; final;
            function newEncoder(): CharEncoder; override; final;
        published
            property characters: UnicodeString read getCharacters;
        end;

        EightBitCharsetCharacterDecodingException = class(CharacterDecodingException)
        public
            constructor create(const charsetName: AnsiString; byteData: int; helpContext: int = 0);
        end;

        EightBitCharsetCharacterEncodingException = class(CharacterEncodingException)
        public
            constructor create(const charsetName: AnsiString; ucharData: int; helpContext: int = 0);
        end;
    {%endregion}

implementation

    {$R *.res}

    {$TYPEINFO OFF}
    {$CALLING REGISTER}

    {%region} type
        EightBitCharDecoder = class;
        EightBitCharEncoder = class;

        EightBitCharDecoder = class sealed(CharDecoder)
        private
            fldCharset: EightBitCharset;
            function decode(src: int): int; overload;
        protected
            procedure decode(src: ByteReader; length: int; dst: UCharWriter); override; overload;
        public
            constructor create(charset: EightBitCharset);
        end;

        EightBitCharEncoder = class sealed(CharEncoder)
        private
            fldCharset: EightBitCharset;
            function encode(src: int): int; overload;
        protected
            procedure encode(src: UCharReader; length: int; dst: ByteWriter); override; overload;
        public
            constructor create(charset: EightBitCharset);
            function canEncode(src: uchar): boolean; override;
        end;
    {%endregion}

    {%region  EightBitCharset }
        function EightBitCharset.getCharacters(): UnicodeString;
        begin
            result := UnicodeString.create(fldCharacters);
        end;

        constructor EightBitCharset.create(const name: AnsiString; const aliases: AnsiString_Array1d; const characters: UnicodeString);
        begin
            inherited create(name, aliases);
            fldCharacters := characters.toUCharArray();
        end;

        function EightBitCharset.newDecoder(): CharDecoder;
        begin
            result := EightBitCharDecoder.create(self);
        end;

        function EightBitCharset.newEncoder(): CharEncoder;
        begin
            result := EightBitCharEncoder.create(self);
        end;
    {%endregion}

    {%region  EightBitCharsetCharacterDecodingException }
        constructor EightBitCharsetCharacterDecodingException.create(const charsetName: AnsiString; byteData, helpContext: int);
        begin
            inherited create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.io.charset.eightbit.UNIT_NAME, 'character-decoding.eight-bit-charset'), [
                CoAnsiString.create(CoInt.toLeadZeroString(byteData and $ff, 2, 16)), CoAnsiString.create(charsetName)
            ]), helpContext);
        end;
    {%endregion}

    {%region  EightBitCharsetCharacterEncodingException }
        constructor EightBitCharsetCharacterEncodingException.create(const charsetName: AnsiString; ucharData, helpContext: int);
        begin
            inherited create(AnsiString.format(AResource.readUnitResourceAsAnsiString(pascalx.io.charset.eightbit.UNIT_NAME, 'character-encoding.eight-bit-charset'), [
                CoAnsiString.create(CoInt.toLeadZeroString(ucharData and $ffff, 4, 16)), CoAnsiString.create(charsetName)
            ]), helpContext);
        end;
    {%endregion}

    {%region  EightBitCharDecoder }
        function EightBitCharDecoder.decode(src: int): int;
        var
            code: int;
            charset: EightBitCharset;
        begin
            if src < $80 then begin
                result := src;
                exit;
            end;
            charset := fldCharset;
            code := int(charset.fldCharacters[src - $80]);
            if code > 0 then begin
                result := code;
                exit;
            end;
            case errorAction of
            eaException: begin
                raise EightBitCharsetCharacterDecodingException.create(charset.name, src);
            end;
            eaReport: begin
                report();
                result := -1;
            end;
            eaIgnore: begin
                result := -2;
            end;
            else
                result := $003f;
            end;
        end;

        procedure EightBitCharDecoder.decode(src: ByteReader; length: int; dst: UCharWriter);
        var
            byteData: int;
            ucharData: int;
        begin
            while length > 0 do begin
                dec(length);
                byteData := src.read();
                if (byteData < $00) or (byteData > $ff) then break;
                ucharData := decode(byteData);
                if ucharData = -1 then break;
                if ucharData >= 0 then dst.write(ucharData);
            end;
        end;

        constructor EightBitCharDecoder.create(charset: EightBitCharset);
        begin
            inherited create(charset);
            fldCharset := charset;
        end;
    {%endregion}

    {%region  EightBitCharEncoder }
        function EightBitCharEncoder.encode(src: int): int;
        var
            code: int;
            charset: EightBitCharset;
        begin
            if src < $0080 then begin
                result := src;
                exit;
            end;
            charset := fldCharset;
            code := &Array.indexOf(uchar(src), charset.fldCharacters, 0, $80);
            if code >= 0 then begin
                result := code + $80;
                exit;
            end;
            case errorAction of
            eaException: begin
                raise EightBitCharsetCharacterEncodingException.create(charset.name, src);
            end;
            eaReport: begin
                report();
                result := -1;
            end;
            eaIgnore: begin
                result := -2;
            end;
            else
                result := $3f;
            end;
        end;

        procedure EightBitCharEncoder.encode(src: UCharReader; length: int; dst: ByteWriter);
        var
            byteData: int;
            ucharData: int;
        begin
            while length > 0 do begin
                dec(length);
                ucharData := src.read();
                if (ucharData < $0000) or (ucharData > $ffff) then break;
                byteData := encode(ucharData);
                if byteData = -1 then break;
                if byteData >= 0 then dst.write(byteData);
            end;
        end;

        constructor EightBitCharEncoder.create(charset: EightBitCharset);
        begin
            inherited create(charset);
            fldCharset := charset;
        end;

        function EightBitCharEncoder.canEncode(src: uchar): boolean;
        begin
            result := (src < #$0080) or (&Array.indexOf(src, fldCharset.fldCharacters, 0, $80) >= 0);
        end;
    {%endregion}

end.