Decodificar uma quantidade de tamanho variável


16

Uma quantidade de tamanho variável (também chamada de VLQ ou uintvar) é uma maneira de codificar um valor inteiro de 28 bits usando apenas o número de bytes necessário. Isso foi usado no formato de arquivo MIDI como uma maneira de minimizar o tamanho de determinados dados do evento.

O modo como funciona é bastante simples. Como uma série big-endian de bytes, o bit mais significativo (MSB) de cada byte é a 1para indicar que outro byte VLQ segue. Os 7 bits restantes de cada byte compõem o valor decodificado.

Exemplo (da Wikipedia):

[ 0x86, 0xc3, 0x17 ] => 106903

insira a descrição da imagem aqui Referências adicionais: Wikipedia , Some Guy .

Desafio:

Dada uma quantidade de comprimento variável, converta-a no seu valor inteiro.

Entrada:

Uma lista de um a quatro bytes ou um tipo de valor de 32 bits representando um VLQ válido de um número inteiro.

Resultado:

O valor inteiro da entrada VLQ.

Regras e pontuação:

  • Isso é código-golfe, então a resposta mais curta em bytes para cada idioma vence.
  • Regras padrão e regras de E / S padrão se aplicam.
  • Lacunas proibidas (é claro).
  • Forneça o link com um teste para o seu código ( TIO.run , etc).
  • Uma explicação clara para sua resposta é altamente recomendada.
  • Os built-ins que lidam com essa conversão não são banidos, mas não usá-los é muito mais interessante.

Casos de teste:

Input (VLQ)                   Output (int)

[                   0x00 ] => 0
[                   0x07 ] => 7
[                   0x7f ] => 127
[             0x81, 0x00 ] => 128
[             0xC0, 0x00 ] => 8192
[             0xff, 0x7f ] => 16383
[       0x81, 0x80, 0x00 ] => 16384
[       0x86, 0xc3, 0x17 ] => 106903
[       0xbd, 0x84, 0x40 ] => 1000000
[       0xff, 0xff, 0x7f ] => 2097151 
[ 0xC0, 0x80, 0x80, 0x00 ] => 134217728  
[ 0xFF, 0xFF, 0xFF, 0x7F ] => 268435455  

Nota: você não precisa usar literais hexadecimais para representar um byte como sua entrada ou saída. Você pode usar literal decimal ( [ 129, 128, 0 ]), inteiro ( 0x80818000) ou qualquer outra representação razoável de bytes / octetos, se for mais adequada à sua plataforma. O formato é flexível desde que represente 1 a 4 bytes / octetos.

Golf away!


O último byte da entrada é garantido como <128? Ou precisamos apoiar casos como [0x01, 0x80, 0x02] => 1?
Arnauld

5
@ Arnauld Agora vejo melhor o que você estava dizendo e, em retrospecto, o caso que você mencionou teria sido bom ter exigido. No MIDI, por exemplo, você estará lendo um fluxo de dados e não necessariamente saberá qual byte é o último. A forma como está escrito, garantindo que o último byte da lista seja o último byte no VLQ, torna o MSB irrelevante e, de fato, meio que derrota o objetivo do VLQ. Por exemplo, você não seria capaz de usar essas rotinas (válidas para o desafio) para decodificação de arquivos MIDI. Totalmente ruim! Deveria ter mantido isso na caixa de areia por muito mais tempo ...
640KB

5
Isso também é parcialmente ruim, porque acho que vi o desafio na caixa de areia e não prestei atenção suficiente. Mas sim, era isso que eu tinha em mente: dada uma lista de bytes, produza o primeiro valor do VLQ ou produza todos os valores do VLQ .
Arnauld

1
@KevinCruijssen, supõe-se que uma quantidade de tamanho variável seja um fluxo de bytes, onde tecnicamente você só sabe quando termina alcançando o marcador MSB = 0. Eu sei que as regras não captaram isso, mas pela definição de VLQ eu vou ter que dizer não.
640KB

1
@ gwaugh Ok, isso faz sentido e eu posso entender o raciocínio. Modificarei minha resposta do MathGolf de acordo. :)
Kevin Cruijssen 08/08/19

Respostas:





4

J , 10 bytes

128#.128|]

Experimente online!

Inspirando-se na resposta de J Salle no APL.

  • 128|] Restante dos números de entrada dividido por 128
  • 128#. Interpretado como os dígitos de um número 128 base


3

05AB1E , 6 bytes

žy%žyβ

Experimente online!

12827


Sim, isso é inútil hoje em dia. Na versão herdada, eu pude ver alguma utilidade para isso, apenas quando você tem um dígito antes dele em seu programa. Caso contrário, você poderia simplesmente usar7o . Hoje em dia você pode compactar certos números inteiros de 3 bytes (intervalo [101,355]) em 2 bytes, portanto, 128 podem ser ƵRassim. Também me perguntei a mesma coisa sobre os 2 bytes incorporados para 16. Normalmente, você usaria o literal ou, caso contrário, teríamos 4o/ 4n/ se um dígito estiver por trás dele no programa. Somente quando um dígito é antes do 16, que eu não acho que iria acontecer, o embutida é útil ..
Kevin Cruijssen

3

Stax , 8 bytes

å→♂á╣>nt

Execute e depure

Algoritmo:

  • Converta cada byte em uma matriz de 7 bits de largura fixa.
  • Achatar matrizes de bits em um.
  • Converta a matriz de bits novamente em número.

2

JavaScript (ES6), 29 bytes

-2 bytes graças a @Shaggy

Recebe a entrada como uma matriz de bytes.

a=>a.map(p=c=>p=p<<7|c&127)|p

Experimente online!


2

APL + WIN, 22 bytes

Solicita um vetor de números inteiros:

2⊥¯32↑,0 1↓⍉(8⍴2)⊤¯4↑⎕

Experimente online! Cortesia de Dyalog Classic

Explicação:

¯4↑⎕ Pad the vector to 4 integers from the right.

⍉(8⍴2)⊤ Convert to a matrix of 8 bit values.

,0 1↓ drop the MSBs and flatten to a vector.

2⊥¯32↑ pad bit vector to 32 bits starting at LSB and convert back to integer.

2

Stax , 12 bytes

ü╫ôà¡k2Wù}a☺

Execute e depure-o em staxlang.xyz!

Descompactado (14 bytes) e explicação:

rk128%128i^#*+
r                 Reverse 'cuz big-endian
 k                Fold from the left using block:
  128%              Modulize by 128
      128i^#        Push 128 to the power of (one plus the iteration index)
            *       Multiply
             +      Add to the total
                  Implicit print

O Stax possui conversão básica embutida, mas funciona apenas em strings. É quase funciona em listas de números inteiros, embora; o problema está no manuseio de Stax 0.

Uma string é uma lista de números inteiros. Quando você usa uma lista como essa, qualquer zero é automaticamente convertido em 32 como uma abreviação para espaços. Como o interno |bpara conversão de base trata seu operando como uma cadeia de caracteres e não como uma lista bruta de números inteiros, qualquer caso com zero falhará.

10 bytes, falha nos zeros

Ç┘_A♥∙QZ►╣
{128%m128|b    Unpacked
{128%m         Modulize each by 128
      128|b    Convert from base 128

Execute e depure-o em staxlang.xyz!


1
Eu vejo de onde veio esse problema. {:B7)m$:bchega a 8 e parece funcionar também, embora seja uma espécie de uso exótico $.
recursivo

@recursive Isso é inteligente. Publique como uma resposta separada.
Khuldraeseth na'Barya 08/08/19


2

C (gcc) , 48 bytes

Pega um número inteiro na ordem big-endian como entrada, que é a mesma ordem que uma matriz de bytes.

f(i,j){for(j=0;j|=i&127,i&128;j<<=7,i>>=8);i=j;}

Experimente online!

C (gcc) , 53 bytes

Se uma matriz de bytes for necessária:

f(c,j)char*c;{for(j=0;j|=*c&127,*c++&128;j<<=7);c=j;}

Experimente online!


Quando eu compilo seu código de teste TIO com -Os, ele lida apenas com os dois últimos casos. Eu não sei C o suficiente para dizer o porquê.
QWR

O @qwr TIO é padronizado como -O0, o que permite (geralmente) armazenar um valor de retorno no primeiro parâmetro. Essa é uma característica peculiar do golfe de código, mas não funciona com níveis mais altos de otimização.
ErikF 9/08/19

Você pode cortar um byte substituindo &128por >>7.
G. Sliepen 17/08/19

2

MathGolf , 14 bytes

ê♣%hrx♣▬^mÅε*Σ

Entrada como números inteiros.

Experimente online.

Tenho a sensação de que isso pode ser mais curto. É um pouco chato que o MathGolf tenha um byte incorporado para a constante 128, mas sem conversão de base (exceto para binário / hexadecimal).

Explicação:

ê              # Take the inputs as integer-array
 ♣%            # Take modulo-128 on each value in this array
   h           # Push the length of the array (without popping the array itself)
    r          # Pop and push a list in the range [0, length)
     x         # Reverse the array to (length, 0]
      ♣▬       # Take 128 to the power of each value in this array
        ^      # Zip the two lists together to create pairs
         mÅ    # Map each pair to (using the following two commands):
           ε*  #  Reduce by multiplication (so basically the product of the pair)
             Σ # After multiplying each pair, take the total sum
               # (after which the entire stack joined together is output implicitly)

A conversão de base está em cima da mesa desde o dia 1, mas há outras revisões em relação à conversão de base que desejo abordar ao mesmo tempo. Por exemplo, não quero operadores diferentes para converter de / para seqüências hexadecimais.
maxb

2

Python 3 , 58 49 bytes

-9 bytes graças a @Chas e @ ar4093

lambda x:int("".join(bin(a|128)[3:]for a in x),2)

Experimente online!

ou

lambda x:int("".join(f"{a%128:07b}"for a in x),2)

Experimente online!

Entrada via lista de números inteiros.

A binfunção do Python adiciona "0b" ao início da string, para que sejam retiradas antes que possam ser concatenadas. Ele também não mantém zeros à esquerda, portanto, se não houver nenhum (também conhecido como último byte), eles deverão ser adicionados novamente. bem.Obrigado ao @Chas por descobrir que, sempre definindo o primeiro bit, posso remover os três primeiros caracteres e pronto.

Aparentemente (de acordo com @ ar4093), a formatfunção permite não apenas não ter o prefixo '0b', mas também remover o primeiro bit e preencher 7 caracteres, tudo ao mesmo tempo.


Bem-vindo ao CGCC, que o tornará um programador muito pior! :). Eu tive o mesmo pensamento que você no começo. Você pode salvar 9 bytes bin(a|128)[3:], pois não precisará do zfill.
Chas Brown

Como você disse, com a função de formato você pode salvar 9 bytes: bin(a)[2:].zfill(8)[1:]->f"{a%128:07b}"
ar4093



1

Carvão , 11 bytes

I↨﹪θ¹²⁸¦¹²⁸

Experimente online! Link é a versão detalhada do código. Recebe entrada como uma matriz. Explicação:

   θ        Input array
  ﹪ ¹²⁸    Elementwise modulo 128
 ↨      ¹²⁸ Convert from base 128
I          Cast to string for implicit print


1

Lote do Windows, 76 bytes

:a
@set/ax="%1&127",y=y*128+x
@if %2. neq . @shift&goto:a
@echo %y%

Passe parâmetros prefixados com "0x" e espaço entre (por exemplo, 0xC0 0x80 0x80 0x00).


Bem feito. Obviamente, para qualquer outra pessoa que estiver testando, você precisará fazer uma @set y=,ax=execução entre as execuções.
640KB

1
apenas o "conjunto y =" é suficiente.
peter ferrie
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.