2012-09-22 8 views
6

Ho un problema che sono un po 'bloccato e sono stato informato da un collega che questo sarebbe un buon posto per chiedere aiuto.Implementazione di un bitfield in stile C in Java

Sto cercando di implementare un bitfield in stile C in Java. Ecco un esempio approssimativo (non ho il codice effettivo di fronte a me in questo 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; 

Ho un bel po 'di bitfield stile simile dal codice legacy. La ragione per cui ho bisogno di trovare un metodo equivalente per Java è che sto lavorando sul codice che userà Java per comunicare con altre applicazioni legacy usando UDP.

Non ho la possibilità di riscrivere il codice. Sono consapevole che questo approccio non è portabile, ha problemi di endianness (e padding/alignment, ect) e potrebbe essere fatto in un modo migliore se potessi riscrivere il codice. Purtroppo ho bisogno di una risposta a questo problema molto specifico. Il sistema è chiuso e quindi non devo preoccuparmi di ogni singola combinazione possibile di compilatori/sistemi operativi/ect.

L'approccio dell'utilizzo di un EnumSet Java non funzionerà perché credo che consentirà solo che ogni valore sia un bit. Devo essere in grado di impacchettare i valori con, ad esempio, il valore di d che occupa 10 bit.

Conosco il Java Bitset ma ha delle limitazioni. Sto usando una versione precedente di Java e quindi non ho alcuni dei più recenti metodi Java Bitset (Vale a dire i metodi valueOf che probabilmente sarebbero sicuramente d'aiuto).

Qualcuno ha qualche idea su come renderlo il più gestibile possibile? Ho oltre 10 bitfield che devo implementare per le mie comunicazioni.

Grazie per l'aiuto che puoi fornire!

+1

Nota che il vostro esempio originale è in realtà un comportamento indefinito. – oldrinb

+0

Come hai una versione vecchia e limitata di Java, puoi dirci di cosa si tratta? –

+0

È Java SE 6. Tecnicamente i bitfield vengono compilati con un compilatore C++. Credo che C++ abbia aggiunto il supporto per l'utilizzo di tipi diversi dagli interi. Se è indefinito, posso accettare che ... Non ho la possibilità di correggerlo, e qualsiasi comportamento che sta facendo è ciò che devo emulare. – shadowisadog

risposta

4

Poiché UDP accetta solo array di byte, è possibile dichiarare java classe in qualsiasi modo idoneo e l'unico passo fondamentale è quello di definire di serializzazione e deserializzazione metodi:

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ì, buon punto e grazie per il feedback. Sto ancora sperando in una soluzione più generale perché ho così tanti bitfield da affrontare. Fare ciascuno manualmente sembra come sarebbe un'esperienza molto dolorosa. – shadowisadog

+0

Fare attenzione a non impostare nessuno dei valori dei membri all'esterno del loro intervallo valido o aggiungere alcune maschere sulla serializzazione; in questo momento, serializzare 'a = 2' risulterebbe in' a = 0', 'b = 1'. – willglynn

2

qualche ricerca superficiale non ha rivelato le biblioteche per rendere questo facile, ma si può sempre fare e disfare le cose a mano con operazioni bit per 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

Grazie willglynn. Ero consapevole che questo è possibile, ma dato il numero di bitfield che devo gestire, temevo che questo approccio sarebbe stato difficile da gestire e potrebbe essere difficile eseguire il debug. Ho trovato questo [collegamento] (http://stackoverflow.com/questions/7604653/questo-è-il-molto-efficiente-invia-in-java-to-pacchetto-informazioni-informa-di-e- read-it-back) Che sembra simile al tuo approccio ma forse un po 'più generico. – shadowisadog

3

ho finito per usare un approccio simile qui presentata: What is the most efficent way in Java to pack bits

E quindi ho creato una classe wrapper che utilizza LinkedHashMap per memorizzare le singole voci dei campi di bit.

Ogni campo è stato implementato come una classe che memorizza il numero di bit e il valore del campo. Il nome del campo è la chiave di LinkedHashMap.

Ho aggiunto i metodi per l'avvio e la fine di una struttura, un metodo per aggiungere un campo di bit alla struttura e metodi per ottenere e impostare valori in base alle chiavi.

Il mio metodo pack esegue l'iterazione su LinkedHashMap e inserisce i bit tenendo traccia dell'offset di bit (ho appena usato un intero per questo scopo).

Il metodo unpack esegue anche l'iterazione di LinkedHashMap, ottiene i bit, tiene traccia dell'offset di bit e memorizza i valori in LinkedHashMap.

Per comodità, ho scritto i metodi per impacchettare i campi di bit in numeri interi, corti, lunghi e un byte. Per convertire tra l'array di byte e i valori ho usato un ByteBuffer e ho chiamato il metodo wrap.

Ho anche scritto i metodi per decomprimere un numero intero compresso, corto, lungo o byte allocando inizialmente ByteBuffer per il numero di byte del tipo di dati (4 per intero, 2 per breve, ect) e quindi chiamando il vari metodi put del ByteBuffer. Una volta avuto un array di byte, sono riuscito a passarlo al metodo di decompressione.

Sono andato con questo approccio perché avevo bisogno di qualcosa di autonomo, che fosse facile da lavorare, e che fosse abbastanza facile da seguire per altre persone ... So che probabilmente ci sono modi più eleganti che includono annotazioni o altre cose (Ho trovato JavaStruct ma non incorpora campi di bit.)

L'imballaggio e il disimballaggio da vari tipi di dati primitivi mi consentono di leggere e scrivere i risultati da un DataInputStream/DataOutputStream più semplice.

Mi dispiace di non essere in grado di inviare il codice a beneficio di tutti, quindi la spiegazione di cui sopra dovrà essere sufficiente. Speriamo che possa aiutare qualcuno in una situazione simile :).

2

Classe Struct dalla libreria Javolution fa ciò che è necessario (http://www.javolution.org/apidocs/index.html?javolution/io/Struct.html) Vedere "Orologio" Esempio:

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(); 
}