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好了!

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