9

我有一个问题,我有点卡住了,一位同事告诉我,这将是一个寻求帮助的好地方。

我正在尝试在 Java 中实现 C 风格的位域。这是一个粗略的示例(此时我面前没有实际的代码)。

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;

我有很多来自遗留代码的类似风格的位域。我需要为 Java 提出一种等效方法的原因是,我正在编写将使用 Java 与使用 UDP 的其他遗留应用程序进行通信的代码。

我没有重写代码的选项。我知道这种方法不可移植,存在字节顺序问题(以及填充/对齐等),如果我能够重写代码,可以做得更好。不幸的是,我需要这个非常具体的问题的答案。系统是封闭的,所以我不需要担心编译器/操作系统/等的每一个可能的组合。

使用 Java EnumSet 的方法将不起作用,因为我相信这只会允许每个值是一位。我需要能够使用例如 d 占用 10 位的值来打包值。

我知道 Java Bitset,但它有局限性。我使用的是较旧版本的 Java,因此我没有一些较新的 Java Bitset 方法(即 valueOf 方法可能肯定会有所帮助)。

有没有人有任何想法如何使其尽可能易于管理?我有超过 10 个位域需要为我的通信实现。

感谢您提供任何帮助!

4

4 回答 4

7

由于 UDP 只接受字节数组,您可以以任何合适的方式声明 Java 类,唯一关键的步骤是定义其序列化和反序列化方法:

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;
  }
}
于 2012-09-22T18:37:57.153 回答
5

Javolution 库中的类 Struct 可以满足您的需求(http://www.javolution.org/apidocs/index.html?javolution/io/Struct.html)请参阅“时钟”示例:

 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();
 }
于 2013-08-21T11:15:00.163 回答
3

我最终使用了这里介绍的类似方法:Java 中最有效的打包方式是什么

然后我创建了一个包装类,它使用LinkedHashMap来存储各个位字段条目。

每个字段都被实现为一个存储位数和字段值的类。字段的名称是 LinkedHashMap 的键。

我添加了用于开始和结束结构的方法、向结构添加位字段的方法以及基于键获取和设置值的方法。

我的 pack 方法遍历 LinkedHashMap 并放置位,同时跟踪位偏移量(为此我只使用了一个整数)。

unpack 方法还迭代 LinkedHashMap 并获取位,跟踪位偏移,并将值存储在 LinkedHashMap 中。

为方便起见,我编写了将位字段打包为整数、短整型、长整型和字节的方法。为了在字节数组和值之间进行转换,我使用了 ByteBuffer 并调用了 wrap 方法。

我还编写了解压缩压缩整数、短整数、长整数或字节的方法,方法是首先为数据类型所具有的字节数分配 ByteBuffer(整数为 4,短整数为 2,等等),然后调用各种 put 方法字节缓冲区。一旦我有了一个字节数组,我就可以将它传递给 unpack 方法。

我采用这种方法是因为我需要一些自包含的东西,易于使用,并且其他人也很容易遵循......我知道可能有更优雅的方式涉及注释或其他东西(我发现 JavaStruct 但它没有包含位字段。)

从各种原始数据类型打包和解包使我能够更轻松地从 DataInputStream/DataOutputStream 读取和写入结果。

很抱歉我无法发布代码让大家受益,所以上面的解释就足够了。希望它会帮助处于类似情况的人:)。

于 2012-09-26T02:20:04.740 回答
1

一些粗略的搜索并没有显示任何库来简化此操作,但您始终可以使用按位操作手动打包和解包:

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

    // ...
}
于 2012-09-22T18:20:22.233 回答