Codificación del buffer de protocolo
Artículos de referencia
protobuf-encode-varint-and-zigzag
Formato e implementación de protobuf
Código fuente
Documentación oficial
El siguiente es el contenido de la documentación oficial:
Los buffers de protocolo son un método para codificar datos estructurados en un formato eficiente y escalable.
Se puede utilizar para serializar datos estructurados y es ideal para almacenamiento de datos o formatos de intercambio de datos RPC. Es un formato de datos estructurados serializados extensibles, independientes del idioma y de la plataforma que se puede utilizar en protocolos de comunicación, almacenamiento de datos y otros campos.
Ventajas:
Almacenado en forma de flujo binario, con campos definidos adyacentes en secuencia. Cada campo tiene datos de valor-clave al lado, con la clave calculada a partir de número_campo y tipo_cable y el valor que consiste en el valor definido por el campo (posiblemente incluyendo la longitud del valor).
Por ejemplo, definamos un ID de campo:
Crear una instancia de Test y asignar el valor 1 a test.id
El formato de clave: valor es como siguiente:
Antes de explicar lo anterior, primero introduzcamos el método de codificación muy inteligente Varint utilizado por Protobuf.
Varint es una representación compacta de números.
Representa un número en uno o más bytes, siendo los números más pequeños los que utilizan menos bytes. Esto reduce la cantidad de bytes utilizados para representar el número. Por ejemplo, para un número de tipo int32, normalmente se requieren 4 bytes para representarlo. Pero con Varint, un pequeño número int32 se puede representar en 1 byte. Por supuesto, todo tiene sus cualidades buenas y malas, y cuando se utiliza Varint, los números grandes requieren 5 bytes para representarse. Desde una perspectiva estadística, no todos los números en una pieza de información son grandes, por lo que en la mayoría de los casos el uso de Varint le permite representar un número en menos bytes.
En Varint, el bit más alto de cada byte tiene un significado especial; si es 1, significa que los siguientes bytes también son parte del número; si es 0, significa el final; Los otros 7 dígitos se utilizan para representar números.
Entonces entendamos la imagen de arriba:
La parte clave del valor: "10011000" "00000110", el significado es el siguiente
Lo anterior es Solo una breve introducción al método de representación de datos int32. ¿Cómo representan los protobúferes otros tipos de datos?
La idea central es usar números sin signo para representar números con signo, intercalarlos para representar números positivos y negativos, usar codificación en zigzag y usar menos bytes para representar números con valores absolutos más pequeños, incluidos números positivos y negativos. aprovechando al máximo la tecnología Varint.
Para números enteros positivos, la compresión de datos se puede lograr si eliminamos el exceso de ceros (o tantos ceros sin sentido como sea posible) y comenzamos con un 1 significativo al transmitir los datos. ¿Qué pasa con la compresión de 0 sin sentido?
La respuesta también es muy simple, por ejemplo: 00000000_000000_000000_000000_00000001 Este número, si solo podemos enviar un bit de 1 o un byte de 00000001, se comprimirán muchos datos adicionales.
¿Cómo son los números negativos?
¿Qué pasa cuando todos los anteriores son 1?
El zigzag nos da una solución inteligente: como mencionamos antes, el primer bit del complemento es el bit de signo, lo que nos impide comprimir los ceros a la izquierda, por lo que colocamos el bit de signo en el complemento al final. del código y mueve los otros bits hacia adelante uno:
Pero incluso esto es difícil de comprimir, porque cuanto más pequeño es el número, más unos iniciales contiene.
Por lo tanto, este algoritmo invierte todos los bits de datos de números negativos poco a poco, dejando el bit de signo sin cambios, lo que da como resultado un número entero como este:
Para números enteros no negativos, el bit de signo se mueve al mismo extremo , y los otros bits avanzan un bit y los datos permanecen sin cambios.
De esta forma, los números positivos, el 0, y los números negativos tienen la misma representación. De esta forma podemos comprimir números enteros pequeños. Estas dos situaciones combinadas se pueden escribir como el siguiente algoritmo:
Convertimos 1 a (00000000_00000000_00000000_00000000_0000000010) en zigzag Después de esto, si solo necesitamos enviar 2 bits (10), u 8 bits (0000000010) a If. omita el 0 inicial, es mejor omitirlos todos. Dado que la transferencia de datos se realiza en bytes, será mejor que la mantengamos en unidades de 8 bits. En la transmisión real, ¿cómo deberíamos codificarlo?
Zigzag introduce una representación en bytes.
Corte el valor en cada grupo de 7 bits de menor a mayor. Si todavía hay información válida en los bits altos, agregue 1 bit 1 (0x80) a estos 7 bits. Esto se repite hasta que aparecen todos los ceros a la izquierda y el algoritmo finaliza.
Ejemplo:
Primero usemos un conjunto de 7 bits para separar los números anteriores:
A, usó (~0x7f) para hacer la operación , la operación El resultado es que todavía hay información en los bits altos, así que sacamos los 7 bits de los bits bajos y sumamos 1 (0x80) al 8º bit desde abajo: 11001111
B Mueve el número siete dígitos hacia la derecha: (0000-0000000 -0000000-0000000-0001111) Zigzag
C. Luego saca los últimos siete bits y haz la operación con (~0x7f). descubrimos que no hay información en los bits altos (todos 0), luego tomaremos los últimos ocho bits. Eliminamos todos: 00001111, salimos del bucle y terminamos el algoritmo
D. Finalmente obtenga dos bytes de datos [11001111, 00001111]
A través de los pasos anteriores, obtendremos un número de secciones de transformación en zigzag de 4 bytes que se convierte en datos de 2 bytes.
El algoritmo es el siguiente
Todo el proceso es inverso a la compresión: para cada byte, primero mira si hay un 1 (0x80) en el bit más alto. Si lo hay, significa que no es el último paquete de bytes de datos, y luego se toman los últimos 7 bits del byte para ensamblar. De lo contrario, significa que se ha alcanzado el último byte, luego salte del bucle directamente después del ensamblaje y finalice el algoritmo. Finalmente, obtenemos un entero de 4 bytes.
La razón fundamental para utilizar este algoritmo es que creemos que en la mayoría de los casos los números que utilizamos no serán grandes. Luego también podemos calcular que por cada más de 7 bits de información, el número de bytes transmitidos aumentará en 1, de modo que si el número es grande, el número original de 4 bytes pasará a ser de 5 bytes:
Creación de instancias
test2=new Test2();
test2.b="testing";
Luego los bytes codificados se expresan como:
p>
12 07 74 65 73 74 69 6e 67
Los dos primeros bytes son la clave:
El primer byte 0x12 → número de campo = 2, escriba = 2.
El segundo byte 0x07 indica que el siguiente número de 7 dígitos es el valor VALOR de este tipo.
El tercer byte comienza a probar
Creación de instancias y asignación
Codificación de resultados