Евгений Темиргалеев писал(а):
1) Александр, запишите постусловие для state (как-то не очень сочетаются OUT state: BOOLEAN и d.st := -1; RETURN из середины (не зря Вирт это дело в О-7 убрал))
Записано в EncCodecs.
Код:
post:
fLen = 0: ok (не ошибка),
state: декодер в состоянии ожидания продолжения незаконченной последовательности
fLen > 0: ошибка при обработке символа f[fR]
state: не определено
Если убрать RETURN, то надо вставлять 3 проверки: в условии WHILE, после CASE, после WHILE.
Цитата:
2) Что выдает Ваш декодер для C0H 80H ? (см.
http://www.ietf.org/rfc/rfc3629.txt, 10 Security Considerations)
Ошибки исправил.
Код:
PROCEDURE (d: Decoder) Decode (IN f: ARRAY OF SHORTCHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF CHAR; VAR tW: INTEGER; OUT state: BOOLEAN);
VAR x: INTEGER;
BEGIN
WHILE fLen > 0 DO
x := ORD(f[fR]);
(* http://www.lemoda.net/c/utf8-to-ucs2/index.html *)
(* http://tools.ietf.org/html/rfc3629 *)
CASE d.st OF 0:
CASE x OF 0..127:
t[tW] := CHR(x); INC(tW)
| 194 (* !!! *)..223:
d.b := x MOD 32;
d.st := 1
| 224..239:
d.b := x MOD 16;
d.st := 2
ELSE (* unexpected shortchar *)
d.st := -1;
RETURN
END
| 1:
CASE x OF 128..191:
t[tW] := CHR(d.b * 64 + x MOD 64); INC(tW);
d.st := 0
ELSE (* unexpected shortchar *)
d.st := -1;
RETURN
END
| 2:
CASE x OF 128..159:
IF d.b = 0 THEN (* !!! *) (* unexpected shortchar *)
d.st := -1;
RETURN
ELSE
d.b := d.b * 64 + x MOD 64;
d.st := 1
END
| 160..191:
d.b := d.b * 64 + x MOD 64;
d.st := 1
ELSE (* unexpected shortchar *)
d.st := -1;
RETURN
END
END;
INC(fR); DEC(fLen)
END;
CASE d.st OF 0: state := FALSE
| 1,2: state := TRUE
END
END Decode;
Тесты:
Код:
MODULE TestEnc3;
(* Test UTF-8 encoder/decoder *)
IMPORT Codecs := EncCodecs, EncStdCodecs, Log;
(* encode and decode all possible characters *)
PROCEDURE Do1*;
VAR s: ARRAY 65536 OF CHAR;
ss: ARRAY 65536 * 3 OF SHORTCHAR;
i: INTEGER;
fR, fLen, tW: INTEGER;
st: BOOLEAN;
e: Codecs.Encoder;
d: Codecs.Decoder;
BEGIN
i := 0; WHILE i < 65536 DO s[i] := CHR(i); INC(i) END;
e := Codecs.dir.NewEncoder("UTF-8");
ASSERT(e # NIL, 100);
d := Codecs.dir.NewDecoder("UTF-8");
ASSERT(e # NIL, 101);
fR := 0; fLen := 65536; tW := 0;
e.Encode(s, fR, fLen, ss, tW);
ASSERT(fLen = 0, 102);
Log.Int(tW); Log.Ln;
i := 0; WHILE i < 65536 DO s[i] := 0X; INC(i) END;
fR := 0; fLen := tW; tW := 0;
d.Decode(ss, fR, fLen, s, tW, st);
ASSERT(fLen = 0, 103);
ASSERT(~st, 104);
ASSERT(tW = 65536, 105);
i := 0; WHILE i < 65536 DO ASSERT(s[i] = CHR(i), 106); INC(i) END
END Do1;
(* decode and encode all possible 2 B and 3 B shortchar sequences *)
PROCEDURE Do2*;
VAR s: ARRAY 1 OF CHAR;
ss, ss1: ARRAY 3 OF SHORTCHAR;
i: INTEGER;
fR, fLen, tW: INTEGER;
st: BOOLEAN;
e: Codecs.Encoder;
d: Codecs.Decoder;
c0, c1, c2: INTEGER;
BEGIN
e := Codecs.dir.NewEncoder("UTF-8");
ASSERT(e # NIL, 100);
d := Codecs.dir.NewDecoder("UTF-8");
ASSERT(e # NIL, 101);
(* check 2 B sequences *)
i := 0;
WHILE i < 65536 DO
IF i MOD 256 > 127 THEN
ss[0] := SHORT(CHR(i MOD 256));
ss[1] := SHORT(CHR(i DIV 256));
d.Reset;
fR := 0; fLen := 2; tW := 0;
d.Decode(ss, fR, fLen, s, tW, st);
IF (fLen = 0) & ~st THEN
ASSERT(tW = 1, 102);
fR := 0; fLen := 1; tW := 0;
e.Encode(s, fR, fLen, ss1, tW);
ASSERT(fLen = 0, 103);
ASSERT(tW = 2, 104);
ASSERT(ORD(ss1[0]) + 256 * ORD(ss1[1]) = i, 105)
END
END;
INC(i)
END;
(* check 3 B sequences *)
i := 0;
WHILE i < 1000000H DO
c0 := i MOD 256;
c1 := i DIV 256 MOD 256;
c2 := i DIV 65536;
IF (c0 > 127) & (c1 > 127) & (c2 > 127) THEN
ss[0] := SHORT(CHR(c0));
ss[1] := SHORT(CHR(c1));
ss[2] := SHORT(CHR(c2));
d.Reset;
fR := 0; fLen := 3; tW := 0;
d.Decode(ss, fR, fLen, s, tW, st);
IF (fLen = 0) & ~st THEN
ASSERT(tW = 1, 106);
fR := 0; fLen := 1; tW := 0;
e.Encode(s, fR, fLen, ss1, tW);
ASSERT(fLen = 0, 107);
ASSERT(tW = 3, 108);
ASSERT(ORD(ss1[0]) + 256 * ORD(ss1[1]) + 65536 * ORD(ss1[2]) = i, 109)
END
END;
INC(i)
END;
END Do2;
(* http://tools.ietf.org/html/rfc3629, section 7 *)
PROCEDURE Do3*;
VAR s: ARRAY 100 OF CHAR;
ss: ARRAY 100 OF SHORTCHAR;
e: Codecs.Encoder;
d: Codecs.Decoder;
PROCEDURE T (IN s: ARRAY OF CHAR; sLen: INTEGER; IN ss: ARRAY OF SHORTCHAR; ssLen: INTEGER);
VAR s1: ARRAY 100 OF CHAR;
ss1: ARRAY 100 OF SHORTCHAR;
i: INTEGER;
fR, fLen, tW: INTEGER;
st: BOOLEAN;
BEGIN
fR := 0; fLen := sLen; tW := 0;
e.Encode(s, fR, fLen, ss1, tW);
ASSERT(fLen = 0, 102);
ASSERT(tW = ssLen, 103);
i := 0; WHILE i < tW DO ASSERT(ss[i] = ss1[i], 104); INC(i) END;
fR := 0; fLen := ssLen; tW := 0;
d.Decode(ss, fR, fLen, s1, tW, st);
ASSERT(fLen = 0, 105);
ASSERT(~st, 106);
ASSERT(tW = sLen, 107);
i := 0; WHILE i < tW DO ASSERT(s[i] = s1[i], 108); INC(i) END
END T;
BEGIN
e := Codecs.dir.NewEncoder("UTF-8");
ASSERT(e # NIL, 100);
d := Codecs.dir.NewDecoder("UTF-8");
ASSERT(e # NIL, 101);
s[0] := 0041X; s[1] := 2262X; s[2] := 0391X; s[3] := 002EX;
ss[0] := 41X; ss[1] := 0E2X; ss[2] := 89X; ss[3] := 0A2X; ss[4] := 0CEX; ss[5] := 91X; ss[6] := 2EX;
T(s, 4, ss, 7);
s[0] := 0D55CX; s[1] := 0AD6DX; s[2] := 0C5B4X;
ss[0] := 0EDX; ss[1] := 95X; ss[2] := 9CX; ss[3] := 0EAX; ss[4] := 0B5X; ss[5] := 0ADX; ss[6] := 0ECX; ss[7] := 96X; ss[8] := 0B4X;
T(s, 3, ss, 9);
s[0] := 65E5X; s[1] := 672CX; s[2] := 8A9EX;
ss[0] := 0E6X; ss[1] := 97X; ss[2] := 0A5X; ss[3] := 0E6X; ss[4] := 9CX; ss[5] := 0ACX; ss[6] := 0E8X; ss[7] := 0AAX; ss[8] := 9EX;
T(s, 3, ss, 9)
END Do3;
END TestEnc3.
(!)TestEnc3.Do1
(!)TestEnc3.Do2
(!)TestEnc3.Do3
Цитата:
3) Проверки через SET эффективнее, чем арифметические (хотя преобразования могут выглядеть громоздко).
BITS(x) * {6, 7} = {7} --> MOV reg, x ; AND reg, 192 ; CMP reg, 128 ; усл. переход
(x >= 128) & (x < 192) --> MOV reg, x ; CMP x, 128 ; усл. переход1 ; CMP x, 192 ; усл. переход 2
Верно.
Цитата:
И всякие x MOD 32 в идеале надо писать тоже через SET. Потому что set * по определению есть AND, а для MOD 2^n это хотя и ожидаемая, но оптимизация. Аналогично x * 2^n и x DIV 2^n в идеале писать через ASH.
DIV/MOD 2^n оптимизирует любой нормальный компилятор. DIV/MOD нагляднее.
Ещё улучшил генератор EncStdMaps.
Пример:
Код:
MODULE EncStdMap_cp1251;
(* This file was generated automatically *)
(* Source: http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT *)
IMPORT Codecs := EncCodecs;
TYPE
Encoder = POINTER TO RECORD (Codecs.Encoder) END;
Decoder = POINTER TO RECORD (Codecs.Decoder) END;
(* Encoder *)
PROCEDURE (e: Encoder) Encode (IN f: ARRAY OF CHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF SHORTCHAR; VAR tW: INTEGER);
VAR x, y: INTEGER;
BEGIN
WHILE fLen > 0 DO
x := ORD(f[fR]);
CASE x OF
| 00H..7FH,0A0H,0A4H,0A6H,0A7H,0A9H,0ABH..0AEH,0B0H,0B1H,0B5H..0B7H,0BBH: y := x
| 0409H,040CH: y := -037FH + x
| 040AH: y := 8CH
| 040BH: y := 8EH
| 040EH: y := 0A1H
| 0490H: y := 0A5H
| 0408H: y := 0A3H
| 0410H..044FH: y := -0350H + x
| 0491H: y := 0B4H
| 0404H: y := 0AAH
| 0401H: y := 0A8H
| 0407H: y := 0AFH
| 0406H: y := 0B2H
| 0453H: y := 83H
| 0405H: y := 0BDH
| 0452H: y := 90H
| 045FH: y := 9FH
| 0459H,045CH: y := -03BFH + x
| 045AH: y := 9CH
| 045BH: y := 9EH
| 045EH: y := 0A2H
| 2122H: y := 99H
| 2039H: y := 8BH
| 2116H: y := 0B9H
| 2030H: y := 89H
| 20ACH: y := 88H
| 0456H: y := 0B3H
| 2026H: y := 85H
| 203AH: y := 9BH
| 0458H: y := 0BCH
| 201EH,2020H,2021H: y := -1F9AH + x
| 0454H: y := 0BAH
| 0451H: y := 0B8H
| 0457H: y := 0BFH
| 0455H: y := 0BEH
| 040FH: y := 8FH
| 2013H,2014H: y := -1F7DH + x
| 201AH: y := 82H
| 2022H: y := 95H
| 201CH,201DH: y := -1F89H + x
| 2018H,2019H: y := -1F87H + x
| 0402H,0403H: y := -0382H + x
ELSE
RETURN
END;
t[tW] := SHORT(CHR(y)); INC(tW);
INC(fR); DEC(fLen)
END
END Encode;
PROCEDURE NewEncoder* (): Codecs.Encoder;
VAR e: Encoder;
BEGIN
NEW(e); RETURN e
END NewEncoder;
(* Decoder *)
PROCEDURE (d: Decoder) Decode (IN f: ARRAY OF SHORTCHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF CHAR; VAR tW: INTEGER; OUT state: BOOLEAN);
VAR x, y: INTEGER;
BEGIN
WHILE fLen > 0 DO
x := ORD(f[fR]);
CASE x OF
| 00H..7FH,0A0H,0A4H,0A6H,0A7H,0A9H,0ABH..0AEH,0B0H,0B1H,0B5H..0B7H,0BBH: y := x
| 8FH: y := 040FH
| 80H,81H: y := 0382H + x
| 91H,92H: y := 1F87H + x
| 93H,94H: y := 1F89H + x
| 95H: y := 2022H
| 0BFH: y := 0457H
| 0BEH: y := 0455H
| 82H: y := 201AH
| 0B8H: y := 0451H
| 84H,86H,87H: y := 1F9AH + x
| 0BCH: y := 0458H
| 0BAH: y := 0454H
| 9BH: y := 203AH
| 85H: y := 2026H
| 0B3H: y := 0456H
| 88H: y := 20ACH
| 89H: y := 2030H
| 8BH: y := 2039H
| 0B9H: y := 2116H
| 99H: y := 2122H
| 0A2H: y := 045EH
| 9EH: y := 045BH
| 9CH: y := 045AH
| 9AH,9DH: y := 03BFH + x
| 9FH: y := 045FH
| 90H: y := 0452H
| 0BDH: y := 0405H
| 83H: y := 0453H
| 0B2H: y := 0406H
| 0AFH: y := 0407H
| 0A8H: y := 0401H
| 0AAH: y := 0404H
| 0B4H: y := 0491H
| 0C0H..0FFH: y := 0350H + x
| 0A3H: y := 0408H
| 0A5H: y := 0490H
| 0A1H: y := 040EH
| 8EH: y := 040BH
| 96H,97H: y := 1F7DH + x
| 8CH: y := 040AH
| 8AH,8DH: y := 037FH + x
ELSE
RETURN
END;
t[tW] := CHR(y); INC(tW);
INC(fR); DEC(fLen)
END;
state := FALSE
END Decode;
PROCEDURE (d: Decoder) Reset, EMPTY;
PROCEDURE NewDecoder* (): Codecs.Decoder;
VAR d: Decoder;
BEGIN
NEW(d); RETURN d
END NewDecoder;
END EncStdMap_cp1251.