UTF-8 é uma maneira relativamente simples de codificar pontos de código Unicode em um formato de largura variável, para que não confunda facilmente código que não é compatível com Unicode.
Visão geral do UTF-8
- Bytes no intervalo de 1-0x7F, inclusive, são normalmente válidos
- Os bytes com o padrão de bits
10XX XXXX
são considerados bytes de continuação, com os seis bits menos significativos sendo usados para codificar parte de um ponto de código. Eles não devem aparecer, a menos que sejam esperados por um byte anterior. - Os bytes com o padrão
110X XXXX
esperam um byte de continuação depois - Os bytes com o padrão
1110 XXXX
esperam dois bytes de continuação depois - Os bytes com o padrão
1111 0XXX
esperam três bytes de continuação depois - Todos os outros bytes são inválidos e não devem aparecer em nenhum lugar de um fluxo UTF-8. Clusters de 5, 6 e 7 bytes são possíveis em teoria, mas não serão permitidos para os propósitos deste desafio.
Codificações excessivas
O UTF-8 também exige que um ponto de código seja representado com o número mínimo de bytes. Qualquer sequência de bytes que possa ser representada com menos bytes não é válida. O UTF-8 modificado adiciona uma exceção a isso para caracteres nulos (U + 0000), que devem ser representados como C0 80
(representação hexadecimal)) e, em vez disso, não permite que bytes nulos apareçam em qualquer lugar do fluxo. (Isso o torna compatível com cadeias terminadas em nulo)
Desafio
Você deve criar um programa que, ao receber uma sequência de bytes, determine se essa sequência representa UTF-8 modificado válido e retornará um valor verdadeiro, se válido, e caso contrário, um valor falso. Observe que você deve verificar se há codificações longas e bytes nulos (já que este é UTF-8 modificado). Você não precisa decodificar os valores UTF-8.
Exemplos
41 42 43 ==> yes (all bytes are in the 0-0x7F range)
00 01 02 ==> no (there is a null byte in the stream)
80 7F 41 ==> no (there is a continuation byte without a starter byte)
D9 84 10 ==> yes (the correct number of continuation bytes follow a starter byte)
F0 81 82 41 ==> no (there are not enough continuation bytes after F0)
EF 8A A7 91 ==> no (too many continuation bytes)
E1 E1 01 ==> no (starter byte where a continuation byte is expected)
E0 80 87 ==> no (overlong encoding)
41 C0 80 ==> yes (null byte encoded with the only legal overlong encoding)
F8 42 43 ==> no (invalid byte 'F8')
Regras
- Aplicam-se regras e brechas padrão
- A entrada e a saída podem estar em qualquer formato conveniente, desde que todos os valores no intervalo de bytes não assinados (0-255) possam ser lidos.
- Pode ser necessário usar uma matriz ou arquivo em vez de uma sequência terminada por nulo. Você precisa ler bytes nulos.
- O menor código vence!
- Observe que não é garantido que o uso de built-in para decodificar o UTF-8 esteja em conformidade com os requisitos fornecidos aqui. Pode ser necessário contorná-lo e criar casos especiais.
EDIT: bônus adicional por não usar builtins que decodificam UTF-8
EDIT2: bônus removido, já que apenas a resposta Rust foi qualificada e é difícil de definir.