Convertisseur UTF Unicode

17

L'objectif est de créer un convertisseur entièrement conforme entre les encodages Unicode officiels comme indiqué dans la FAQ UTF . Étant donné que cela est centré sur Unicode, j'accepterai la réponse avec le nombre d'octets le plus bas en utilisant le meilleur possible des codages impliqués (qui sera probablement UTF-8, à moins que vous ne le programmiez en APL). Je m'excuse pour le long post, mais une grande partie explique les encodages qui peuvent également être consultés dans les spécifications officielles (pdf, section 3.9 D90 - D92) , ou Wikipedia .

Caractéristiques

Si à tout moment votre langue de choix ne peut pas répondre exactement à une exigence, remplacez-la par quelque chose qui colle à l'esprit des règles données. Par exemple. tous les langages n'ont pas de tableaux, de fonctions, etc.

  • Pas d'utilisation de bibliothèques / fonctions de chaîne, ni d'encodage de bibliothèques / fonctions. Le but de ce code golf est d'implémenter le convertisseur en utilisant une manipulation bit / octet. Cependant, l'utilisation de chaînes elles-mêmes en tant que tableau de caractères ou d'octets est autorisée. Oh, et aucun appel OS qui effectue la conversion non plus.

  • Le convertisseur est une fonction qui prendra trois paramètres: un tableau d'octets représentant la chaîne d'entrée codée et les codages "d'entrée" et "de sortie" représentés sous forme de nombres. Arbitrairement, nous attribuerons des UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, and UTF32LEnombres de 0 à 6 dans cet ordre. Il n'est pas nécessaire de vérifier si le nombre est < 0ou > 6, nous supposerons que ces paramètres sont corrects. Le convertisseur renverra un tableau d'octets valide dans le codage de sortie souhaité.

  • Nous utiliserons le caractère nul ( U+0000) comme terminateur de chaîne. Rien après cela n'a pas d'importance. Nous supposerons que le tableau d'entrée a le caractère nul quelque part, vous n'avez donc pas besoin de vérifier les limites.

  • Selon la FAQ , si le tableau d'octets d'entrée n'est pas valide pour son encodage déclaré, nous devons signaler une erreur. Nous allons le faire de l'une des manières suivantes: planter le programme, lever une exception, retourner null ou retourner un tableau dont les quatre premiers octets sont tous à 0 (afin qu'il puisse être reconnu comme U+0000dans chaque encodage).

Les encodages

Les spécifications officielles doivent être suivies, mais Wikipédia fournit une bonne (et pour autant que je pense correcte) explication des encodages, et je les résumerai ici pour être complet. Notez que UTF-16 et UTF-32 ont des variantes pour l' endianité .

UTF-32, UTF-32LE, UTF-32BE

Le codage le plus simple, chaque point de code est simplement codé en 4 octets égal à sa valeur numérique. LE / BE représente l'endianité (petit endian / gros endian).

UTF-16, UTF-16LE, UTF-16BE

Les points de code de U+0000 - U+FFFFsont codés sur 2 octets égaux à sa valeur numérique. Les valeurs plus grandes sont codées à l'aide d'une paire de substituts qui sont des valeurs réservées de U+D800 - U+DFFF. Donc, pour encoder des points supérieurs à U+FFFF, l'algorithme suivant peut être utilisé (copié sans vergogne à partir de Wikipedia ):

  • 0x010000 est soustrait du point de code, laissant un nombre de 20 bits dans la plage 0..0x0FFFFF.
  • Les dix premiers bits (un nombre dans la plage 0..0x03FF) sont ajoutés à 0xD800 pour donner la première unité de code ou substitut de tête, qui sera dans la plage 0xD800..0xDBFF [...].
  • Les dix bits les plus faibles (également dans la plage 0..0x03FF) sont ajoutés à 0xDC00 pour donner la deuxième unité de code ou substitut de piste, qui sera dans la plage 0xDC00..0xDFFF [...].

UTF-8

Les points de code de U+0000 - U+007Fsont codés sur 1 octet égal à sa valeur numérique. De, U+0080 - U+07FFils sont codés comme 110xxxxx 10xxxxxx, U+0800 - U+FFFFest 1110xxxx 10xxxxxx 10xxxxxx, les valeurs plus élevées sont 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. Les x'sont les bits de la valeur numérique du point de code.

Nomenclature

La marque d'ordre des octets (BOM, U+FEFF) est utilisée comme premier point de code pour indiquer l'endianité. En suivant les directives de la FAQ sur les nomenclatures , la nomenclature sera utilisée comme suit: car UTF-8, UTF-16 and UTF-32elle est facultative. Si la nomenclature est absente dans UTF-16ou UTF-32, elle est supposée être big endian. La nomenclature ne doit pas apparaître dans UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE.

Pièges courants provoquant un UTF non valide

Diverses choses peuvent rendre une séquence d'octets non valide UTF.

  • UTF-8 et UTF-32: encodage direct des points de code de substitution ( U+D800 - U+DFFF) ou des points de code supérieurs à U+10FFFF.
  • UTF-8: nombreuses séquences d'octets invalides.
  • UTF-16: substituts non appariés ou mal appariés.
  • BOM: Doit être utilisé comme spécifié dans la section de codage. Notez que lors de la sortie UTF-16ou UTF-32(aucune endianité inhérente spécifiée) vous pouvez choisir, mais avec peu d'endian, vous devez inclure la nomenclature.

Notez que les non-caractères et les points de code non attribués (tous deux distincts des substituts) doivent être traités comme des caractères normaux.

DPenner1
la source
"Pas d'utilisation de bibliothèques / fonctions de chaîne, ni d'encodage de bibliothèques / fonctions." Et les vrais intégrés. Dans l' APL, ''⎕R''⍠'InEnc' 'UTF16BE' 'OutEnc' 'UTF8-BOM'.
Adám
2
@NBZ Ceux-ci ne seraient pas autorisés car le but de ce défi est d'implémenter le comportement que ceux-ci fournissent.
DPenner1
Note aux répondeurs: j'avais plus ou moins abandonné cette question, mais compte tenu du regain d'intérêt récent, dans les prochains jours, je vais prendre un peu de temps pour parcourir les réponses.
DPenner1

Réponses:

3

C ++, (UTF-8) 971 octets

#include<cstdint>
using u=uint8_t;using U=uint32_t;U i,o,x,b,m;U R(u*&p){x=*p++;if(!i){m=0;while(128>>m&x)++m;if(m>1)for(x&=127>>m;--m;)x=x<<6|((*p&192)-128?~0:*p++&63);return m?x=~0:x;}else if(i<3){x<<=8;x+=*p++;}else if(i<4){x+=*p++<<8;}else if(i<6){x<<=24;x+=*p++<<16;x+=*p++<<8;x+=*p++;}else{x+=*p++<<8;x+=*p++<<16;x+=*p++<<24;}return x;}U r(u*&p){U x0=R(p);if(i&&i<4&&x>>10==54)x=R(p)>>10==55?(x0<<10)+x-56613888:~0;if(!b++){if(x==65279)if(!i||i%3==1)r(p);else x=~0;else if(x==65534&&i==1)i=3,r(p);else if(x==4294836224&&i==4)i=6,r(p);}return x>1114111||x>>11==27?x=~0:x;}void w(U x,u*&p){if(!o){if(x<128)*p++=x;else{for(m=0;~63<<m&x;m+=6);for(*p++=~127>>m/6|x>>m;m;)*p++=128|x>>(m-=6)&63;}}else if(o<4&&x>65535)x-=65536,w(55296|x>>10,p),w(56320|x&1023,p);else if(o<3)*p++=x>>8,*p++=x;else if(o<4)*p++=x,*p++=x>>8;else if(o<6)*p++=x>>24,*p++=x>>16,*p++=x>>8,*p++=x;else*p++=x,*p++=x>>8,*p++=x>>16,*p++=x>>24;}int t(u*&p,u*&q){for(b=0,x=1;U(x+x);)w(r(p),q);return x;}

Le programme lisible ci-dessous peut être condensé à la forme ci-dessus en le filtrant via la commande Perl suivante:

perl -p0 -e 's!//.*!!g;s/\s+/ /g;s/ \B|\B //g;s/0x[\da-f]+/hex($&)/ige;s/#include<[^<>]+>/\n$&\n/g;s/^\n+//mg'

La commande ci-dessus

  • supprime les commentaires
  • supprime les espaces inutiles
  • convertit les littéraux hexadécimaux en décimaux
  • rétablit les nouvelles #includelignes autour des lignes

Code lisible

#include <cstdint>
using u = uint8_t;
using U = uint32_t;

U   i,                          // input encoding
    o,                          // output encoding
    x,                          // last read value
    b,                          // char count(BOM only valid when b==0)
    m;                          // temporary variable for measuring UTF-8

//   Encodings:
// 0 UTF-8
// 1 UTF-16
// 2 UTF-16BE
// 3 UTF-16LE
// 4 UTF-32
// 5 UTF-32BE
// 6 UTF-32LE

// Read a character or UTF-16 surrogate
U R(u*& p) {
    x = *p++;
    if (!i) { // UTF-8
        m=0; while (128>>m&x) ++m; // how many bytes?
        if (m>1) for (x&=127>>m; --m; ) x = x<<6 | ((*p&192)-128?~0:*p++&63);
        return m ? x=~0 : x;
    } else if (i<3) { // UTF-16, UTF-16BE
        x<<=8; x+=*p++;
    } else if (i<4) { // UTF-16LE
        x+=*p++<<8;
    } else if (i<6) { // UTF-32, UTF-32BE
        x<<=24; x+=*p++<<16; x+=*p++<<8; x+=*p++;
    } else { // UTF-32LE
        x+=*p++<<8; x+=*p++<<16; x+=*p++<<24;
    }
    return x;
}

// Read a character, combining surrogates, processing BOM, and checking range
U r(u*& p) {
    U x0 = R(p);
    if (i && i<4 && x>>10==54)
        x = R(p)>>10==55 ? (x0<<10)+x-56613888: ~0; // 56613888 == 0xd800<<10 + 0xdc00 - 0x10000
    if (!b++) {                 // first char - is it BOM?
        if (x==0xFEFF)
            if (!i || i%3==1)
                r(p); // BOM in UTF-8 or UTF-16 or UTF-32 - ignore, and read next char
            else
                x = ~0; // not allowed in these modes
        else if (x==0xFFFE && i==1)
            i=3,r(p); // reversed BOM in UTF-16 - change to little-endian, and read next char
        else if (x==0xFFFE0000 && i==4)
            i=6,r(p); // reversed BOM in UTF-32 - change to little-endian, and read next char
    }
    return x>0x10ffff || x>>11==27 ? x=~0 : x;
}


// Write character(assumed in-range)
void w(U x, u*& p) {
    if (!o) { // UTF-8
        if (x<128) *p++=x;        // ASCII
        else {
            for (m=0; ~63<<m&x; m+=6); // how many bits?
            for (*p++=~127>>m/6|x>>m; m; ) *p++ = 128|x>>(m-=6)&63;
        }
    } else if (o<4 && x>65535)  // UTF-16 surrogate
        x-=65536, w(0xD800|x>>10,p), w(0xDC00|x&0x3FF,p);
    else if (o<3)  // UTF-16, UTF-16BE
        *p++=x>>8, *p++=x;
    else if (o<4)  // UTF-16LE
        *p++=x, *p++=x>>8;
    else if (o<6)  // UTF-32, UTF-32BE
        *p++=x>>24, *p++=x>>16, *p++=x>>8, *p++=x;
    else  // UTF-32LE
        *p++=x, *p++=x>>8, *p++=x>>16, *p++=x>>24;
}

// Transcode
int t(u*& p, u*& q)                  // input, output
{
    for (b=0,x=1;U(x+x);)    // exit condition is true only for x==-x, i.e. 0 and ~0
        w(r(p),q);
    return x;
}

La fonction à appeler est t(), avec des encodages d'entrée et de sortie passés dans les variables globales iet orespectivement, et ppointant vers les octets d'entrée, qui doivent être terminés par des valeurs nulles. qpointe vers le tampon de sortie, qui sera écrasé, et doit être suffisamment grand pour le résultat - il n'y a aucune tentative pour éviter un dépassement de tampon.

J'espère que les commentaires du code sont suffisamment explicatifs - demandez ci-dessous si l'un d'eux est trop cryptique (mais faites d'abord un effort!).

J'ai compilé une suite de tests substantielle tout en développant cette réponse; Je l'inclus ci-dessous pour le bénéfice des autres participants et pour documenter mon interprétation des exigences:

Fonctions de test

#include <vector>
#include <iostream>

std::ostream& operator<<(std::ostream& out, const std::vector<u>& v)
{
    out << "{ ";
    for (int i: v) out << i << " ";
    out << "}";
    return out;
}

int test_read(int encoding, std::vector<u> input, U expected)
{
    b = 0;
    i = encoding;
    auto d = input.data();
    U actual = r(d);
    if (actual == expected) return 0;
    std::cerr << std::hex << "Decoding " << encoding << "; " << input << " gave " << actual
              << " instead of " << expected << std::endl;
    return 1;
}

int test_write(int encoding, U input, std::vector<u> expected)
{
    o = encoding;
    u buf[20], *p = buf;
    w(input, p);
    std::vector<u> actual(buf,p);
    if (expected == actual) return 0;
    std::cerr << std::hex << "Encoding " << encoding << "; " << input << " gave " << actual
              << " instead of " << expected << std::endl;
    return 1;
}

int test_transcode(int ienc, std::vector<u> input, int oenc, std::vector<u> expected)
{
    b = 0;
    i = ienc; o = oenc;
    u buf[200], *p = buf, *d = input.data();
    int result = t(d, p);
    std::vector<u> actual(buf,p);
    if (result ? expected.empty() : expected == actual) return 0;
    std::cerr << std::hex << "Encoding " << ienc << " to " << oenc << "; " << input << " gave " << actual
              << " instead of " << expected << std::endl;
    return 1;
}

Suite de tests

static const U FAIL = ~0;
int main() {
    int e = 0;                        // error count
    // UTF-8
    e += test_read(0, { 128 }, FAIL); // unexpected continuation
    e += test_read(0, { 128, 1 }, FAIL);
    e += test_read(0, { 128, 128 }, FAIL);
    e += test_read(0, { 192, 192 }, FAIL); // start without continuation
    e += test_read(0, { 192, 0 }, FAIL);
    e += test_read(0, { 224, 0 }, FAIL);
    e += test_read(0, { 224, 192 }, FAIL);
    e += test_read(0, { 0xf4, 0x90, 128, 128 }, FAIL); // Unicode maximum+1

    e += test_read(0, { 127 }, 127);
    e += test_read(0, { 192, 129 }, 1); // We accept overlong UTF-8
    e += test_read(0, { 0xc2, 128 }, 128);
    e += test_read(0, { 224, 128, 129 }, 1);
    e += test_read(0, { 0xef, 128, 128 }, 0xF000);
    e += test_read(0, { 0xef, 191, 191 }, 0xFFFF);
    e += test_read(0, { 0xf4, 128, 128, 128 }, 0x100000);
    e += test_read(0, { 0xf4, 0x8f, 191, 191 }, 0x10FFFF); // Unicode maximum

    e += test_read(0, { 0xEF, 0xBB, 0xBF, 127 }, 127); // byte-order mark

    e += test_write(0, 0, { 0 });
    e += test_write(0, 127, { 127 });
    e += test_write(0, 128, { 0xc2, 128 });
    e += test_write(0, 255, { 0xc3, 191 });
    e += test_write(0, 0xFFFF, { 0xef, 191, 191 });
    e += test_write(0, 0x10FFFF, { 0xf4, 0x8f, 191, 191 });

    // UTF-16
    e += test_read(1, { 0, 1 }, 1);
    e += test_read(1, { 0xd8, 0, 0xdc, 1 }, 0x10001);
    e += test_read(1, { 0xdb, 0xff, 0xdf, 0xff }, 0x10ffff);

    e += test_read(1, { 0xd8, 0, 0xd8, 1 }, FAIL); // mismatched surrogate
    e += test_read(1, { 0xd8, 0, 0, 1 }, FAIL); // mismatched surrogate
    e += test_read(1, { 0xdc, 0 }, FAIL);

    e += test_write(1, 1, { 0, 1 });
    e += test_write(1, 256, { 1, 0 });
    e += test_write(1, 0xffff, { 255, 255 });
    e += test_write(1, 0x10001, { 0xd8, 0, 0xdc, 1 });
    e += test_write(1, 0x10ffff, { 0xdb, 0xff, 0xdf, 0xff });

    // UTF-16LE
    e += test_write(3, 1, { 1, 0 });
    e += test_write(3, 256, { 0, 1 });
    e += test_write(3, 0x10001, { 0, 0xd8, 1, 0xdc });
    e += test_write(3, 0x10fffe, { 0xff, 0xdb, 0xfe, 0xdf });

    // UTF-16 byte-order mark
    e += test_read(1, { 0xFE, 0xFF, 0x0, 1 }, 1); // byte-order mark
    e += test_read(1, { 0xFF, 0xFE, 1, 0x0 }, 1); // reversed byte-order mark
    // disallowed byte-order marks
    e += test_read(2, { 0xFE, 0xFF }, FAIL);
    e += test_read(3, { 0xFF, 0xFE }, FAIL);
    // reversed byte-order mark is an unassigned character - to be treated like regular character, according to question
    e += test_read(2, { 0xFF, 0xFE }, 0xfffe);
    e += test_read(3, { 0xFE, 0xFF }, 0xfffe);

    // UTF-32
    e += test_read(4, { 0, 0, 0, 1 }, 1);
    e += test_read(4, { 1, 0, 0, 0 }, FAIL);
    e += test_write(4, 1, { 0, 0, 0, 1 });
    e += test_write(4, 0x10203, { 0, 1, 2, 3 });

    // UTF-32LE
    e += test_read(6, { 0, 0, 0, 1 }, FAIL);
    e += test_read(6, { 1, 0, 0, 0 }, 1);

    // UTF-32 byte-order mark
    e += test_read(4, { 0, 0, 0xFE, 0xFF,  0, 0, 0, 1 }, 1); // byte-order mark
    e += test_read(4, { 0xFF, 0xFE, 0, 0,  1, 0, 0, 0 }, 1); // reversed byte-order mark
    // disallowed byte-order marks
    e += test_read(5, { 0, 0, 0xFE, 0xFF }, FAIL);
    e += test_read(5, { 0xFF, 0xFE, 0, 0 }, FAIL);
    e += test_read(6, { 0, 0, 0xFE, 0xFF }, FAIL);
    e += test_read(6, { 0xFF, 0xFE, 0, 0 }, FAIL);

    e += test_transcode(1, { 1, 2, 0xFE, 0xFF, 0, 0 }, // That's not a BOM; it's a zwnj when not the first char
                        1, { 1, 2, 0xFE, 0xFF, 0, 0 });
    e += test_transcode(1, { 0xFF, 0xFE, 1, 2, 0, 0 }, // reversed byte-order mark implies little-endian
                        1, { 2, 1, 0, 0 });
    e += test_transcode(4, { 0xFF, 0xFE, 0, 0, 1, 2, 0, 0, 0, 0 }, // reversed BOM means little-endian
                        4, { 0, 0, 2, 1, 0, 0, 0, 0 });
    e += test_transcode(1, { 0xdb, 0xff, 0xdf, 0xff, 0, 0 }, // U+10ffff UTF-16 to UTF-8
                        0, { 0xf4, 0x8f, 191, 191, 0 });

    return e;
}
Toby Speight
la source
Dang .. C ++ a battu Python.
TickTock
5

Python - 1367 caractères UTF-8

Bien! C'était une question extrêmement difficile en raison de la quantité de travail qu'il a fallu pour comprendre et mettre en œuvre toutes les spécifications, mais je pense que j'ai une mise en œuvre correcte.

O,P,Q,R=65536,128,b'\xff\xfe\x00\x00',63
def A(x,y):assert x;return y
def B(x):
    o,c=[],0
    for b in x:
        if c:c,v=c-1,A(127<b<192,v<<6)|(b-P)
        else:
            c,v=(b>127)+(b>223)+(b>239),b
            if b>127:v=A(191<b<248,b&(R>>c))
        o+=[v][c:]
    return o[o[0]in(65279,O-2):]
def C(k):
    def o(x,s=None):
        for a,b in zip(x[k::2],x[1-k::2]):
            d=a|(b<<8)
            if s!=None:yield(A(56319<d<57344,d-56320)|(s<<10))+O;s=None
            elif 55295<d<57344:s=A(s<1024,d-55296)
            else:yield d
    return o
def D(x):n=(2,3,1)[[Q[:2],Q[1::-1],x[:2]].index(x[:2])];return C(n&1)(x[n&2:])
E=lambda a,b,c,d:lambda x:[L|(l<<8)|(m<<16) for L,l,m in zip(x[a::4],x[b::4],x[c::4])]
def F(x):n,m=((1,4),(-1,4),(-1,0))[[Q,Q[::-1],x[:4]].index(x[:4])];return E(*range(4)[::n])(x[m:])
S=lambda x,s=0,a=255:(x>>s)&a
G=lambda e:(e,)if e<P else(192|S(e,6),P|(e&R))if e<2048 else(224|S(e,12),P|S(e,6,R),P|(e&R))if e<O else(240|S(e,18),P|S(e,12,R),P|S(e,6,R),P|(e&R))
H=lambda e:(S(e,8),S(e))if e<O else(216|S(e-O,18),S(e-O,10),220+S((e-O)&1023,8),S(e-O))
I=lambda e:(S(e),S(e,8))if e<O else(S(e-O,10),216|S(e-O,18),S(e-O),220+S((e-O)&1023,8))
J=lambda e:(S(e,24),S(e,16),S(e,8),S(e))
K=lambda e:(S(e),S(e,8),S(e,16),S(e,24))
convert=lambda d,i,o:bytes(sum(map(L[o],N(list(M[i](d)))),()))if d else d
L,M=[G,H,H,I,J,J,K],[B,D,C(1),C(0),F,E(3,2,1,0),E(0,1,2,3)]
N=lambda d:[A(-1<x<1114112 and x&~2047!=55296,x)for x in d]

convertest la fonction qui prend l'objet «octets» de données, l'ID d'entrée et l'ID de sortie. Cela semble fonctionner - bien que python semble avoir une utilisation légèrement cassée des nomenclatures lorsqu'il n'est pas spécifié dans l'encodage, donc utiliser l'encodage intégré de python pour tester les modes 1 et 4 ne fonctionnera pas.

Fait amusant: la taille est également de 555 16 ou 10101010101 2 .

773 caractères pour le décodage, 452 pour le codage, 59 pour la vérification et 83 pour les pièces diverses.

Cel Skeggs
la source
@TrangOul: Généralement, les modifications triviales (comme le balisage de langue) sont mal vues.
Zach Gates
Cette question / réponse ne montre pas un consensus communautaire. À travers le réseau, des modifications triviales comme celles-ci sont mal vues. Ni les utilisateurs <1000 ni> 1000 représentants ne doivent effectuer ces modifications, sauf s'ils améliorent clairement le contenu ou le format. Il est préférable de ne pas modifier des éléments comme les balises de langue, les corrections / modifications d'un seul mot, etc. @cat
Zach Gates
Je pense que la taille n'est plus 0x555 :-(. Mais vous pourriez vous rapprocher en utilisant la pointe Python-golf standard d'utiliser un espace pour les retraits.
Toby Speight
@TobySpeight c'est 0x557 en ce moment, vous avez raison. Et en fait, j'ai utilisé des tabulations, qui ont dû être converties en espaces pour la publication, mais comptent toujours comme un caractère. Je vais voir si je peux raser quelques personnages d'une autre manière quand j'en aurai l'occasion.
Cel Skeggs
4

Python 3, 1138 octets (UTF-8)

Il s'avère donc que 14 heures de voyages internationaux sont une opportunité fantastique pour terminer un défi de golf ...

La fonction de conversion est C(). Cela demande u(), v()et w()à décoder, et U(), V()et W()à coder, UTF-8, -16 et -32 respectivement. Aucun des encodeurs ne sortira une nomenclature, mais tous les décodeurs en géreront correctement un. Les conditions d'erreur entraînent une exception (généralement une ZeroDivisionError, grâce à la fonction "mourir subitement" E()).

from struct import*
l=len
j=''.join
b=lambda c:[*bin(c)[2:]]
P,Q,i,o,z,Z='HI10><'
B=65279
O,F,H,L,X=1024,65536,55296,56320,57344
E=lambda:1/0
R=lambda y,e,c,n:unpack(([[z,Z][y[:n]==pack(Z+c,B)],e][l(e)])+c*(l(y)//n),y)
S=lambda d,e:B!=d[0]and d or e and E()or d[1:]
def u(y,d=(),p=0):
 while p<l(y):
  q=b(y[p])
  if l(q)>7:
   x=q.index(o);C=1<x<5and q[x+1:]or E();X=x+p;X>l(y)>E();p+=1
   while p<X:q=b(y[p]);C=l(q)>7and(i,o==q[:2])and(*C,*q[2:])or E();p+=1
   d=*d,int(j(C),2)
  else:d=*d,y[p];p+=1
 return S(d,0)
def T(p):
 q=b(p);C=()
 while l(q)not in(7,11,16,21):q=o,*q
 while l(q)>6:C=int(i+o+j(q[-6:]),2),*C;q=q[:-6]
 return bytes(p<128and[p]or[int(i*(7-l(q))+o+j(q),2),*C])
U=lambda c:b''.join(map(T,c))
def v(y,e=''):
 c=R(y,e,P,2);d=[];n=0
 while n<l(c)-1:h,a=c[n:n+2];D=[(h,a),(F+(h-H)*O+a-L,)][H<=h<L<=a<X];M=3-l(D);n+=M;d+=D[:M]
 if n<l(c):d=*d,c[n]
 return S(d,e)
V=lambda c,e=z:W(sum(map(lambda p:([H+(p-F)//O,L+(p-F)%O],[p])[p<F],c),[]),e,P)
w=lambda y,e='':S(R(y,e,Q,4),e)
W=lambda c,e=z,C=Q:pack(e+C*l(c),*c)
K=(u,U),(v,V),(v,V,z),(v,V,Z),(w,W),(w,W,z),(w,W,Z)
def C(y,f,t):f,_,*a=K[f];_,t,*b=K[t];return t(f(y,*a),*b)
Tim Pederick
la source