2012-09-22 6 views
6

Tengo un problema al que me atasco un poco y un colega me informó de que este sería un buen lugar para buscar ayuda.Implementación de un campo de bits de estilo C en Java

Estoy tratando de implementar un campo de bits estilo C en Java. Aquí hay un ejemplo aproximado (no tengo el código actual frente a mí en este momento).

typedef union 
{ 
    typedef struct 
    { 
    unsigned short a :1; 
    unsigned short b :1; 
    unsigned short c :2; 
    unsigned short d :10; 
    } bitfield; 

    unsigned short bitmap; 
}example_bitfield; 

Tengo un buen bit de bitfields de estilo similar de código heredado. La razón por la que necesito encontrar un método equivalente para Java es que estoy trabajando en un código que usará Java para comunicarse con otras aplicaciones heredadas que usan UDP.

No tengo la opción de volver a escribir el código. Soy consciente de que este enfoque no es portátil, tiene problemas de endianness (y relleno/alineación, ect), y podría hacerse de una mejor manera si pudiera reescribir el código. Lamentablemente, necesito una respuesta a este problema tan específico. El sistema está cerrado y no tengo que preocuparme por todas las combinaciones posibles de compiladores/sistemas operativos/ect.

El enfoque de usar un EnumSet de Java no funcionará porque creo que solo permitirá que cada valor sea de un bit. Necesito poder empacar valores con, por ejemplo, el valor de d ocupando 10 bits.

Conozco el Java Bitset pero tiene limitaciones. Estoy usando una versión anterior de Java, por lo que no tengo algunos de los métodos más nuevos de Java Bitset (a saber, el valor de los métodos que seguramente ayudaría).

¿Alguien tiene alguna idea de cómo hacer que esto sea lo más manejable posible? Tengo más de 10 bitfields que necesito implementar para mis comunicaciones.

¡Gracias por cualquier ayuda que pueda proporcionar!

+1

en cuenta que su ejemplo original es en realidad un comportamiento indefinido. – oldrinb

+0

Como tiene una versión antigua y limitada de Java, ¿puede decirnos qué es? –

+0

Es Java SE 6. Técnicamente, los bitfields se están compilando con un compilador de C++. Creo que C++ agregó soporte para usar tipos que no sean enteros. Si no está definido, puedo aceptarlo ... No tengo la opción de corregirlo, y cualquier comportamiento que esté haciendo actualmente es lo que tengo que emular. – shadowisadog

Respuesta

4

Desde UDP sólo acepta matrices de bytes, se puede declarar java la clase de cualquier modo adecuado y el único paso crítico es definir serialización y deserialización métodos:

class example_bitfield { 
    byte a; 
    byte b; 
    byte c; 
    short d; 

    public void fromArray(byte[] m) { 
    byte b0=m[0]; 
    byte b1=m[1]; 
    a=b0>>>7; 
    b=(b0>>6)&1; 
    c=(b0>>4)&3; 
    d=(b0&0xF<<6)|(b1>>>2); 
    } 
    public void toArray(byte[] m) { 
    m[0]=(a<<7)|(b<<6)|(c<<4)|(d>>>6); 
    m[1]=(d&0x3F)<<2; 
    } 
} 
+0

Sí, buen punto y gracias por los comentarios. Todavía estoy esperando una solución más general porque tengo tantos bitfields para tratar. Hacer cada uno de forma manual parece que sería una experiencia muy dolorosa. – shadowisadog

+0

Tenga cuidado de no establecer ninguno de los valores de miembros fuera de su rango válido, o agregue algunas máscaras en la serialización; en este momento, serializar 'a = 2' daría como resultado' a = 0', 'b = 1'. – willglynn

2

algunas búsquedas superficial no reveló ninguna biblioteca para hacer esto fácil, pero siempre se podía hacer y deshacer las cosas a mano con operaciones bit a bit:

class ExampleBitfield { 
    int bitfield;  // actually 16 bits 

    public int getBitfield() { 
     return bitfield; 
    } 
    public void setBitfield(int bitfield) { 
     this.bitfield = bitfield & 0xffff; 
    } 

    // lowest bit 
    public int getA() { 
     return (bitfield >> 0) & 0x01; 
    } 
    public int setA(int a) { 
     return (bitfield & ~0x01) | ((a & 0x01) << 0); 
    } 

    // second lowest bit 
    public int getB() { 
     return (bitfield >> 1) & 0x01; 
    } 
    public int setB(int b) { 
     return (bitfield & ~0x02) | ((b & 0x01) << 1); 
    } 

    // ... 
} 
+0

Gracias willglynn. Sabía que esto es posible, sin embargo, dado el número de bitfields que tengo que manejar, temía que este enfoque fuera difícil de manejar y pudiera ser difícil de depurar. Encontré este [enlace] (http://stackoverflow.com/questions/7604653/what-is-the-most-efficient-way-in-java-to-pack-bits-into-byte-and- read-it-back) Lo que parece similar a su enfoque, pero tal vez algo más genérico. – shadowisadog

3

terminé usando un enfoque similar se presenta aquí: What is the most efficent way in Java to pack bits

Y luego hice una clase contenedora que usa LinkedHashMap para almacenar las entradas de campo de bit individuales.

Cada campo se implementó como una clase que almacena el número de bits y el valor del campo. El nombre del campo es la clave de LinkedHashMap.

Agregué métodos para iniciar y finalizar una estructura, un método para agregar un campo de bit a la estructura y métodos para obtener y establecer valores basados ​​en claves.

Mi método de paquete itera a través de LinkedHashMap y coloca los bits mientras realiza un seguimiento del desplazamiento de bits (solo he usado un número entero para este fin).

El método de desempaquetado también itera el LinkedHashMap y obtiene los bits, haciendo un seguimiento del desplazamiento del bit y almacenando los valores en el LinkedHashMap.

Para mayor comodidad, escribí métodos para empaquetar los campos de bits en enteros, cortos, largos y un byte. Para convertir entre la matriz de bytes y los valores, utilicé un ByteBuffer y llamé al método de ajuste.

También escribí métodos para desempaquetar un entero empacado, corto, largo o byte asignando primero el ByteBuffer para la cantidad de bytes que tiene el tipo de datos (4 para entero, 2 para abreviar, ect) y luego llamando al varios métodos de poner el ByteBuffer. Una vez que tuve una matriz de bytes, pude pasar eso al método de desempaquetar.

Fui con este enfoque porque necesitaba algo independiente, con el que era fácil trabajar, y eso era bastante fácil de seguir para otras personas ... Sé que probablemente haya formas más elegantes que involucren anotaciones u otras cosas (Encontré JavaStruct pero no incorporaba campos de bits.)

El embalaje y desembalaje de varios tipos de datos primitivos me permite leer y escribir los resultados de un DataInputStream/DataOutputStream más fácilmente.

Lamento que no pueda publicar el código para que todos se beneficien, por lo que la explicación anterior deberá ser suficiente. Espero que ayude a alguien en una situación similar :).

2

Clase Struct de la biblioteca Javolution hace lo que necesita (http://www.javolution.org/apidocs/index.html?javolution/io/Struct.html) Ver "Reloj" ejemplo:

import java.nio.ByteBuffer; 
class Clock extends Struct { // Hardware clock mapped to memory. 
    Unsigned16 seconds = new Unsigned16(5); // unsigned short seconds:5 
    Unsigned16 minutes = new Unsigned16(5); // unsigned short minutes:5 
    Unsigned16 hours = new Unsigned16(4); // unsigned short hours:4 
    Clock() { 
     setByteBuffer(Clock.nativeBuffer(), 0); 
    } 
    private static native ByteBuffer nativeBuffer(); 
}