2011-07-28 19 views
7

Necesito analizar y codificar a un formato de mensaje binario heredado en Java. Empecé utilizando DataOutputStream para leer/escribir tipos primitivos, pero el problema que tengo es que el formato del mensaje no se alinea muy bien con las compensaciones de bytes e incluye indicadores de bits.Cómo analizar/codificar formatos de mensajes binarios?

Por ejemplo, tengo que lidiar con mensajes como este:

+----------+---+---+----------+---------+--------------+ 
+uint32 +b +b + uint32 +4bit enum+32 byte string+ 
+----------+---+---+----------+---------+--------------+ 

Dónde (b) es una bandera de un bit. El problema es que los tipos de primitiva Java no se alinean con los límites de bytes, por lo que no podría usar DataOutputStream para codificar esto, ya que el tipo de nivel más bajo que puedo escribir es un byte.

¿Hay bibliotecas, estándar o de terceros, para manejar formatos de mensajes de nivel de bit arbitrario?

Edit: Gracias a @Software Monkey por obligarme a mirar mis especificaciones más de cerca. La especificación que estoy usando se alinea realmente en los límites de bytes para que DataOutputStream sea apropiado. Sin embargo, dada mi pregunta original, me habría ido con la solución propuesta por @emboss.

Editar: Aunque el formato del mensaje para esta pregunta se descubrió que estaba en los límites de bytes me encontré con otro formato de mensaje que es aplicable a la pregunta original. Este formato define una asignación de caracteres de 6 bits donde cada carácter realmente solo ocupa 6 bits, no el byte completo, por lo que las cadenas de caracteres no se alinean en los límites de bytes. He descubierto varios flujos de salida binarios que abordan este problema. Como este: http://introcs.cs.princeton.edu/java/stdlib/BinaryOut.java.html

+4

Esta es una muy inusual especulación; con mis años de experiencia en comunicaciones por cable, me gustaría cuestionar si tienes ese diseño correcto. En particular, su segundo uint32 y la cadena de 32 bytes comienzan a medio camino a través de un byte, ¡seguro que no! No es inusual para empaquetar banderas en bits en un byte, palabra o dword para un protocolo de cable, pero para haber desalineado bytes completos, palabras y dwords es evidentemente idiota. –

+0

Ack, tienes razón. Tenía una imagen pobre del formato del mensaje para salir y ahora veo 'bits de reserva' enredados entre los indicadores de bits y la siguiente primitiva alineándolos en un límite de bytes. ¡Gracias! – kenen

+0

Debe actualizar su pregunta con la especificación real (y una nota para que la respuesta del relieve no parezca fuera de lugar). –

Respuesta

5

No es una orden interna del tipo byte en Java, y se puede leer en byte[] tampones bien utilizando InputStream#read(byte[]) y escribir en un OutputStream usando OutputStream#write(byte[], int, int), así que no hay problema en eso.

En cuanto a sus mensajes - como usted señaló correctamente, el poco más pequeño de información que se obtiene a la vez es un byte, por lo que tendrá para descomponer el formato de mensaje en 8 trozos poco primero:

Supongamos que su mensaje está en un byte [] datos nombrados. También asumo poca endianidad.

Un uint32 tiene 32 bits de largo -> eso es cuatro bytes. (Tenga cuidado al analizar esto en Java, los enteros y largos de Java están firmados, debe manejar eso. Una manera fácil de evitar problemas sería tomar largos para eso. Los datos [0] llenan los bits 31 - 24, datos [1] 23 - 16, los datos [2] los bits 15 - 8 y de datos [3] Bits 7 a 0. Por lo que necesita para cambiar de manera apropiada a la izquierda y pegarlas junto con lógica OR:

long uint32 = ((data[0]&0xFF) << 24) | 
       ((data[1]&0xFF) << 16) | 
       ((data[2]&0xFF) << 8) | 
       (data[3]&0xFF); 

Siguiente, hay dos bits individuales. Supongo que debe verificar si están "encendidos" (1) o "desactivados" (0). Para hacer esto, usa máscaras de bits y compara su byte con AND lógico.

primer bit: (máscara binaria | 1 0 0 0 0 0 0 0 | = 128 = 0x80)

if ((data[4] & 0x80) == 0x80) // on 

segundo bit: (máscara binaria | 0 1 0 0 0 0 0 0 | = 64 = 0x40)

if ((data[4] & 0x40) == 0x40) // on 

para componer la siguiente uint32, tendrá que componer bytes a través de límites de bytes de los datos subyacentes. P.ej. para el primer byte tome los 6 bits restantes de datos [4], muévalos dos hacia la izquierda (serán el bit 8 a 2 del uint32) y "agregue" los primeros dos (los más altos) de los datos [5] cambiando ellos 6 bits a la derecha (tomarán el 1 y 0 de la ranura restante de uint32). "Agregar" significa lógicamente OR'ing:

byte uint32Byte1 = (byte)((data[4]&0xFF) << 2 | (data[5]&&0xFF) >> 6); 

La construcción de su uint32 es entonces el mismo procedimiento que en el primer ejemplo. Y así sucesivamente y así sucesivamente.

+0

El código de extracción uint32 anterior está roto para valores de bytes <0; necesita usar '((data [x] & 0xFF) << n)'; de lo contrario 'data [x]' se convierte en el equivalente firmado int, *** antes de que *** se cambie, causando un relleno de alto bit. La excepción es '<< 24' para un bit de 32 bits, y' << 56L' para un valor de 64 bit, los cuales desplazarán cualquier extensión de signo. –

+0

@Software Monkey: ¡Tienes razón, gracias! Actualizaré la publicación. – emboss

2

Debe aplicar bit arithmetics (operadores AND, OR, NOT) para cambiar o leer bits individuales dentro de un byte en Java. Los operadores aritméticos son &, | y ~

+0

Eso funciona para agarrar las primeras banderas de bits al comienzo de un byte. Eso todavía dejaría una lógica extraña para tratar con un uint32 que comience 4 bits en un byte y continúe por 3.5 bytes más. – kenen

4

He escuchado cosas buenas sobre Preon.

+0

El sitio web parece estar bastante roto; No se puede obtener ninguno de los ejemplos que aparecen, solo muestra una publicidad y un cuerpo de contenido en blanco. –

+0

No estoy seguro de qué sitio web ha marcado, pero para mí, http://preon.codehaus.org/ no parece estar roto. Es posible que desee consultar http://www.slideshare.net/springerw/oopsla-talk-on-preon para las diapositivas en Preon, y http://www.scribd.com/doc/8128172/Preon-Introduction y relacionadas para más documentación. (Sin embargo, sí estoy de acuerdo en que el código de ejemplo debería incluirse realmente en la documentación generada por Maven). –

4

Sólo para añadir a la respuesta de pholser, creo que la versión Preon sería algo como esto:

class DataStructure { 
    @BoundNumber(size="32") long  first; // uint32 
    @Bound     boolean second; // boolean 
    @Bound     boolean third; // boolean 
    @BoundNumber(size="32") long  fourth; // uint32 
    @BoundNumber(size="4") int  fifth; // enum 
    @BoundString(size="32") String  sixth; // string 
} 

... pero en realidad, usted puede hacer su vida más fácil mediante el uso de apoyo de Preon for dealing with enumerations directly.

Creación de una Codec por ella y usarla para descifrar algunos datos sería algo como esto:

Codec<DataStructure> codec = Codecs.create(DataStructure.class) 
DataStructure data = Codecs.decode(codec, ....) 
4

con Java Binary Block Parser la secuencia de comandos para analizar el mensaje será

class Parsed { 
    @Bin int field1; 
    @Bin (type = BinType.BIT) boolean field2; 
    @Bin(type = BinType.BIT) boolean field3; 
    @Bin int field4; 
    @Bin(type = BinType.BIT) int enums; 
    @Bin(type = BinType.UBYTE_ARRAY) String str; 
    } 

    Parsed parsed = JBBPParser.prepare("int field1; bit field2; bit field3; int field4; bit:4 enums; ubyte [32] str;").parse(STREAM).mapTo(Parsed.class); 
Cuestiones relacionadas