java解析c语言之Javolution的坑

Java解析C语言的结构体有好多种方案,经过对比之后还是选择了Javolution,主要是它的用法和平常c结构体的用法看起来差不多,而且一开始测试用的时候效果还可以,但是后来随着使用越多,那些坑就慢慢暴露出来了。 
坑一: 
对齐方式会变。

    public static class UserInfo_t extends Struct {
        public final Unsigned32 id = new Unsigned32();
        public final Unsigned32 age = new Unsigned32();
        public final Unsigned32 weight = new Unsigned32();
        public final Signed64 coin = new Signed64();
        public final Unsigned32 reserve = new Unsigned32();
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

就这样乍一看它的size应该是4+4+4+8+4=24,但是实际运行的结果却是28,无缘无故多了4个字节,一旦把数据填充到这结构体里面,coin字段开始后面的数据全部都不对了,后来把原始数据和解析后的数据对比一下才发现,这个结构体在遇到64位的数据类型的时候,就自动把前面的数据按8字节对齐了,这样子的话coin数据本应该是由数据流的第12字节的位置开始解析变成了第16个字节开始解析,后面的数据就必然会跟着移位。 
解决方案: 
由于我们的另一端是已经固定写好的,一旦改动,就牵涉到整个项目,所以只好从这一端想办法解决了,我们这里的解决方案是把8字节的数据拆成两个4字节的,然后读写的数据的时候需要重新封装。重新定义后的结构体如下:

    public static class UserInfo_t extends Struct {
        public final Unsigned32 id = new Unsigned32();
        public final Unsigned32 age = new Unsigned32();
        public final Unsigned32 weight = new Unsigned32();
        //public final Signed64 coin = new Signed64();
        public final Unsigned32[] coin = (Unsigned32[]) array(new Unsigned32[2]);
        public final Unsigned32 reserve = new Unsigned32();
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这样子的话读写结构体里64的数据的时候就要重新封装一下了,这里给出我们读的函数:

    public static long StructTransArgToLong(Unsigned32[] value) {
        long tmp;
        tmp = ((long) ((int) (value[0].get() & 0x0FFFFFFFFL)) & 0x0FFFFFFFFL)
                | ((long)((int) (value[1].get() & 0x0FFFFFFFFL)) << 32);
        return tmp;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

写入结构体时的函数:

    public static void StructTransLongToArg(long src, Unsigned32[] value) {
        value[0].set((int) (src & 0xFFFFFFFF)));
        value[1].set((int) ((src >> 32) & 0xFFFFFFFF));
        return ;
    }   
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

这里注意一下,有时候可能接收到的数据可能大小端不一致,这封装的读写函数里的数组0和1可能要对调一下的。

坑二: 
对齐方式固定4个字节。 
在c里面定义的结构体是这样的

struct Packet_t{
short len;
char content[0];
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

这种一般是用在变长数据,content数据存储的是可变数据的开始地址,在c的结构体里是不占用大小的,所以这个结构体的大小应该为2.在java里就定义不出来的,所以只好把content字段去掉定义如下:

    public static class Packet_t extends Struct {
        public final Unsigned16 len = new Unsigned16();
        }
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

但这样的定义出来的size却是4,由于用的时候都是直接从size()函数返回的地址开始读后面可变长度的数据流,这样就会导致读出来的数据少了前面两个字节。 
解决方案: 
一开始想到的办法就是重载size(),但父类的size()定义成final了,所以没法重载,只有重新定义个带参数的size()函数了:

    public static class Packet_t extends Struct {
        public final Unsigned16 len = new Unsigned16();
            public int size(int nouse){
            return super.size() - 2;
        }
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

总结: 
这只是暂时遇到的问题,不知道后面还有什么坑,用的时候还是小心点,先个每一个结构体的size输出来校对一下,下次项目在遇到java解析c结构体的时候,还是用回jni好了!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值