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

就这样乍一看它的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();
        }

这样子的话读写结构体里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;
    }

写入结构体时的函数:

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

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

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

struct Packet_t{
short len;
char content[0];
}

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

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

但这样的定义出来的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;
        }
        }

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章