Implementing a C style bitfield in Java

混江龙づ霸主 提交于 2019-12-04 01:09:40

问题


I have a problem that I am a bit stuck on and I was informed by a colleague that this would be a good place to seek help.

I am trying to implement a C style bitfield in Java. Here is a rough example (I do not have the actual code in front of me at this moment).

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;

I have a good bit of similar style bitfields from legacy code. The reason that I need to come up with an equivalent method for Java is that I am working on code that will use Java to communicate with other legacy applications using UDP.

I do not have the option of rewriting the code. I am aware that this approach is not portable, has endianness issues (and padding/alignment, ect), and could be done a better way if I were able to rewrite the code. Unfortunately I need an answer to this very specific problem. The system is closed and so I do not need to worry about every single possible combination of compilers/operating systems/ect.

The approach of using a Java EnumSet will not work because I believe that will only allow for each value to be one bit. I need to be able to pack values with for instance the value of d occupying 10 bits.

I know about the Java Bitset but it has limitations. I am using an older version of Java, and so I do not have some of the newer Java Bitset methods (Namely the valueOf methods which would probably surely help).

Does anyone have any ideas of how to make this as manageable as possible? I have over 10 bitfields that I need to implement for my communications.

Thank you for any help you can provide!


回答1:


Since UDP accepts only byte arrays, you can declare java class in any suitable way and the only critical step is to define serialization and deserialization methods:

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



回答2:


Class Struct from Javolution library makes what you need (http://www.javolution.org/apidocs/index.html?javolution/io/Struct.html) See "Clock" example:

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



回答3:


I ended up using a similar approach presented here: What is the most efficent way in Java to pack bits

And then I made a wrapper class that uses LinkedHashMap to store the individual bit field entries.

Each field was implemented as a class that stores the number of bits and the value of the field. The name of the field is the key to the LinkedHashMap.

I added methods for starting and ending a structure, a method to add a bit field to the structure, and methods for getting and setting values based on keys.

My pack method iterates through the LinkedHashMap and puts the bits while keeping track of the bit offset (I just used an integer for this purpose).

The unpack method also iterates the LinkedHashMap and gets the bits, keeping track of the bit offset, and storing the values in the LinkedHashMap.

For convenience I wrote methods for packing the bit fields to integers, shorts, longs, and a byte. To convert between the byte array and the values I used a ByteBuffer and called the wrap method.

I also wrote methods for unpacking a packed integer, short, long, or byte by first allocating the ByteBuffer for the number of bytes that the data type has (4 for integer, 2 for short, ect) and then calling the various put methods of the ByteBuffer. Once I had a byte array I was able to pass that to the unpack method.

I went with this approach because I needed something self contained, that was easy to work with, and that was fairly easy for other people to follow... I know there are probably more elegant ways involving annotations or other things (I found JavaStruct but it didn't incorporate bit fields.)

Packing and unpacking from various primitive data types enable me to read and write the results from a DataInputStream/DataOutputStream easier.

I am sorry that I am unable to post the code for everyone to benefit from, so the above explanation will have to suffice. Hopefully it will help someone in a similar situation :).




回答4:


Some cursory searching didn't reveal any libraries to make this easy, but you could always pack and unpack things by hand with bitwise operations:

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

    // ...
}


来源:https://stackoverflow.com/questions/12546106/implementing-a-c-style-bitfield-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!