Conversor Unicode UTF

17

O objetivo é criar um conversor totalmente compatível entre as codificações Unicode oficiais, conforme indicado nas Perguntas frequentes da UTF . Dado que isso é centrado no Unicode, aceitarei a resposta com a menor contagem de bytes, usando o melhor possível das codificações envolvidas (que provavelmente será UTF-8, a menos que você possa programá-lo no APL). Peço desculpas pelo longo post, mas muito disso explica as codificações que também podem ser acessadas na especificação oficial (pdf, seção 3.9 D90 - D92) , ou Wikipedia .

Especificações

Se, a qualquer momento, o idioma de sua escolha não puder atender exatamente a um requisito, substitua-o por algo que atenda ao espírito das regras fornecidas. Por exemplo. nem todo idioma possui matrizes, funções etc. integradas

  • Não é possível usar bibliotecas / funções de cadeias ou bibliotecas / funções de codificação. O objetivo desse código de golfe é implementar o conversor usando manipulação de bits / bytes. No entanto, é possível usar as próprias strings em sua capacidade como uma matriz de caracteres ou bytes. Ah, e nenhuma chamada do SO que realize a conversão também.

  • O conversor é uma função que aceita três parâmetros: uma matriz de bytes que representa a cadeia de entrada codificada e as codificações "entrada" e "saída" representadas como números. Arbitrariamente, atribuiremos UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, and UTF32LEnúmeros de 0 a 6 nessa ordem. Não há necessidade de verificar se o número é < 0ou > 6, assumiremos que esses parâmetros estão corretos. O conversor retornará uma matriz de bytes válida na codificação de saída desejada.

  • Usaremos o caractere nulo ( U+0000) como um terminador de string. Qualquer coisa depois disso não importa. Vamos supor que a matriz de entrada tenha o caractere nulo em algum lugar, para que você não precise fazer uma verificação de limites.

  • Conforme a FAQ , se a matriz de bytes de entrada for inválida para sua codificação declarada, devemos sinalizar um erro. Faremos isso de uma das seguintes maneiras: travar o programa, lançar uma exceção, retornar nulo ou retornar uma matriz cujos quatro primeiros bytes sejam todos 0 (para que possa ser reconhecido como U+0000em todas as codificações).

As codificações

As especificações oficiais devem ser seguidas, mas a Wikipedia fornece uma explicação boa (e até onde eu acredito correta) das codificações, e eu as resumirei aqui para garantir a integridade. Observe que UTF-16 e UTF-32 têm variantes para endianness .

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

A codificação mais simples, cada ponto de código é simplesmente codificado em 4 bytes igual ao seu valor numérico. LE / BE representa endianness (little endian / big endian).

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

Os pontos de código de U+0000 - U+FFFFsão codificados em 2 bytes iguais ao seu valor numérico. Valores maiores são codificados usando um par de substitutos dos quais são valores reservados U+D800 - U+DFFF. Portanto, para codificar pontos maiores que U+FFFF, o seguinte algoritmo pode ser usado (copiado descaradamente da Wikipedia ):

  • 0x010000 é subtraído do ponto de código, deixando um número de 20 bits no intervalo 0..0x0FFFFF.
  • Os dez principais bits (um número no intervalo 0..0x03FF) são adicionados ao 0xD800 para fornecer a primeira unidade de código ou substituto principal, que estará no intervalo 0xD800..0xDBFF [...].
  • Os dez bits baixos (também no intervalo 0..0x03FF) são adicionados a 0xDC00 para fornecer a segunda unidade de código ou substituto de trilha, que estará no intervalo 0xDC00..0xDFFF [...].

UTF-8

Os pontos de código de U+0000 - U+007Fsão codificados como 1 byte igual ao seu valor numérico. Desde que U+0080 - U+07FFeles são codificados como 110xxxxx 10xxxxxx, U+0800 - U+FFFFé 1110xxxx 10xxxxxx 10xxxxxx, são os valores mais altos 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. Os xsão os bits do valor numérico do ponto de código.

BOM

A marca de ordem de byte (BOM U+FEFF) é usada como o primeiro ponto de código para indicar endianness. Seguindo as diretrizes de perguntas frequentes sobre listas técnicas , a lista técnica será usada da seguinte maneira: Por UTF-8, UTF-16 and UTF-32ser opcional. Se a lista técnica estiver ausente em UTF-16ou UTF-32, será considerado grande endian. A lista técnica não deve aparecer UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE.

Armadilhas comuns que causam UTF inválido

Várias coisas podem fazer com que uma sequência de bytes seja UTF inválida.

  • UTF-8 e UTF-32: codificação direta de pontos de código substitutos ( U+D800 - U+DFFF) ou pontos de código maiores que U+10FFFF.
  • UTF-8: Muitas seqüências de bytes inválidas.
  • UTF-16: Substitutos emparelhados ou emparelhados incorretamente.
  • BOM: deve ser usado conforme especificado na seção de codificação. Observe que ao emitir UTF-16ou UTF-32(nenhuma endianidade inerente especificada) você pode selecionar, mas com pouca endian, você deve incluir a BOM.

Observe que não-caracteres e pontos de código não atribuídos (ambos distintos dos substitutos) devem ser tratados como caracteres regulares.

DPenner1
fonte
"Não é possível usar bibliotecas / funções de string ou codificar bibliotecas / funções." E quanto aos embutidos reais. No APL ''⎕R''⍠'InEnc' 'UTF16BE' 'OutEnc' 'UTF8-BOM',.
Adám
2
@NBZ Isso não seria permitido, pois o objetivo desse desafio é implementar o comportamento que eles fornecem.
precisa saber é o seguinte
Nota para os respondentes: Abandonei mais ou menos essa pergunta, mas, devido ao recente interesse renovado, nos próximos dias, levarei algum tempo para analisar as respostas.
precisa saber é o seguinte

Respostas:

3

C ++, (UTF-8) 971 bytes

#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;}

O programa legível abaixo pode ser condensado na forma acima, filtrando-o através do seguinte comando Perl:

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'

O comando acima

  • remove comentários
  • remove espaços em branco desnecessários
  • converte literais hexadecimais em decimal
  • restabelece novas #includelinhas em torno das linhas

Código legível

#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;
}

A função a ser chamada é t(), com as codificações de entrada e saída passadas nas variáveis ​​globais ie o, respectivamente, e papontando para os bytes de entrada, que devem ter terminação nula. qaponta para o buffer de saída, que será substituído e deve ser grande o suficiente para o resultado - não há tentativa de evitar a saturação do buffer.

Espero que os comentários do código sejam suficientemente explicativos - pergunte abaixo se um deles é muito enigmático (mas faça um esforço primeiro!).

Compilei uma suíte de testes substancial ao desenvolver esta resposta; Eu o incluo abaixo para o benefício de outros participantes e para documentar minha interpretação dos requisitos:

Funções de teste

#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;
}

Suíte de teste

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
fonte
Dang .. C ++ venceu Python.
precisa saber é o seguinte
5

Python - 1367 caracteres UTF-8

Bem! Essa foi uma pergunta extremamente difícil devido à enorme quantidade de trabalho necessário para entender e implementar todas as especificações, mas acho que tenho uma implementação correta.

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]

converté a função que pega o objeto 'bytes' dos dados, o ID de entrada e o ID de saída. Parece funcionar - embora o python pareça ter um uso ligeiramente quebrado das BOMs quando não especificado na codificação, portanto, o uso da codificação interna do python para testar os modos 1 e 4 não funcionará.

Curiosidade: o tamanho também é 555 16 ou 10101010101 2 .

773 caracteres para decodificação, 452 para codificação, 59 para verificação e 83 para peças diversas.

Cel Skeggs
fonte
@TrangOul: Geralmente, edições triviais (como marcação de idioma) são desaprovadas.
Zach Gates
Essa pergunta / resposta não mostra um consenso da comunidade. Em toda a rede, edições triviais como essas são desaprovadas. Nem os usuários <1000 nem> 1000 representantes devem fazer essas edições, a menos que melhorem claramente o conteúdo ou o formato. É melhor adiar a edição de tags de idioma, correções / alterações em uma única palavra, etc. @cat
Zach Gates
Eu acho que o tamanho não é mais 0x555 :-(. Mas você pode se aproximar usando a dica padrão do Python-golf de usar um espaço para recuos.
Toby Speight
@TobySpeight está 0x557 agora, você está certo. Na verdade, usei guias, que precisavam ser convertidas em espaços para postagem, mas ainda contavam como um caractere. Vou ver se consigo barbear alguns caracteres de outra maneira quando tiver uma chance.
Cel Skeggs
4

Python 3, 1138 bytes (UTF-8)

Acontece que 14 horas de viagens internacionais são uma oportunidade fantástica para terminar um desafio de golfe ...

A função de conversão é C(). Isto exige u(), v()e w()para decodificar, e U(), V()e,W() para codificar, UTF-8, -16 e -32, respectivamente. Nenhum dos codificadores produzirá uma BOM, mas todos os decodificadores manipularão corretamente um. As condições de erro resultam em uma exceção (geralmente a ZeroDivisionError, cortesia da função "morrer repentinamente" 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
fonte