GoFrame 二進制編解碼-gbinary

2022-04-12 10:26 更新

?goframe?框架提供了獨立的二進制數(shù)據(jù)操作包?gbinary?,主要用于各種數(shù)據(jù)類型與?[ ]byte?二進制類型之間的相互轉(zhuǎn)換;以及針對于整型數(shù)據(jù)進行精準按位處理的功能。常用于網(wǎng)絡(luò)通信時數(shù)據(jù)編碼/解碼,以及數(shù)據(jù)文件操作時的編碼/解碼。

使用方式:

import "github.com/gogf/gf/v2/encoding/gbinary"

接口文檔:

https://pkg.go.dev/github.com/gogf/gf/v2/encoding/gbinary

用于二進制數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換處理的接口文檔如下:

func Encode(vs ...interface{}) ([]byte, error)
func EncodeInt(i int) []byte
func EncodeInt8(i int8) []byte
func EncodeInt16(i int16) []byte
func EncodeInt32(i int32) []byte
func EncodeInt64(i int64) []byte
func EncodeUint(i uint) []byte
func EncodeUint8(i uint8) []byte
func EncodeUint16(i uint16) []byte
func EncodeUint32(i uint32) []byte
func EncodeUint64(i uint64) []byte
func EncodeBool(b bool) []byte
func EncodeFloat32(f float32) []byte
func EncodeFloat64(f float64) []byte
func EncodeString(s string) []byte

func Decode(b []byte, vs ...interface{}) error
func DecodeToInt(b []byte) int
func DecodeToInt8(b []byte) int8
func DecodeToInt16(b []byte) int16
func DecodeToInt32(b []byte) int32
func DecodeToInt64(b []byte) int64
func DecodeToUint(b []byte) uint
func DecodeToUint8(b []byte) uint8
func DecodeToUint16(b []byte) uint16
func DecodeToUint32(b []byte) uint32
func DecodeToUint64(b []byte) uint64
func DecodeToBool(b []byte) bool
func DecodeToFloat32(b []byte) float32
func DecodeToFloat64(b []byte) float64
func DecodeToString(b []byte) string

支持按位處理的接口文檔如下:

func EncodeBits(bits []Bit, i int, l int) []Bit
func EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bit
func EncodeBitsToBytes(bits []Bit) []byte
func DecodeBits(bits []Bit) uint
func DecodeBitsToUint(bits []Bit) uint
func DecodeBytesToBits(bs []byte) []Bit

其中的?Bit?類型表示一個二進制數(shù)字(0或1),其定義如下:

type Bit int8

二進制操作示例

我們來看一個比較完整的二進制操作示例,基本演示了絕大部分的二進制轉(zhuǎn)換操作。

github.com/gogf/gf/v2/blob/master/.example/encoding/gbinary/binary.go

package main

import (
    "fmt"
    "github.com/gogf/gf/v2/os/glog"
    "github.com/gogf/gf/v2/encoding/gbinary"
)

func main() {
    // 使用gbinary.Encoded對基本數(shù)據(jù)類型進行二進制打包
    if buffer := gbinary.Encode(18, 300, 1.01); buffer != nil {
       // glog.Error(err)
    } else {
        fmt.Println(buffer)
    }

    // 使用gbinary.Decode對整形二進制解包,注意第二個及其后參數(shù)為字長確定的整形變量的指針地址,字長確定的類型,
    // 例如:int8/16/32/64、uint8/16/32/64、float32/64
    // 這里的1.01默認為float64類型(64位系統(tǒng)下)
    if buffer := gbinary.Encode(18, 300, 1.01); buffer != nil {
        //glog.Error(err)
    } else {
        var i1 int8
        var i2 int16
        var f3 float64
        if err := gbinary.Decode(buffer, &i1, &i2, &f3); err != nil {
            glog.Error(err)
        } else {
            fmt.Println(i1, i2, f3)
        }
    }

    // 編碼/解析 int,自動識別變量長度
    fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(1)))
    fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(300)))
    fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(70000)))
    fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(2000000000)))
    fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(500000000000)))

    // 編碼/解析 uint,自動識別變量長度
    fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(1)))
    fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(300)))
    fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(70000)))
    fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(2000000000)))
    fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(500000000000)))

    // 編碼/解析 int8/16/32/64
    fmt.Println(gbinary.DecodeToInt8(gbinary.EncodeInt8(int8(100))))
    fmt.Println(gbinary.DecodeToInt16(gbinary.EncodeInt16(int16(100))))
    fmt.Println(gbinary.DecodeToInt32(gbinary.EncodeInt32(int32(100))))
    fmt.Println(gbinary.DecodeToInt64(gbinary.EncodeInt64(int64(100))))

    // 編碼/解析 uint8/16/32/64
    fmt.Println(gbinary.DecodeToUint8(gbinary.EncodeUint8(uint8(100))))
    fmt.Println(gbinary.DecodeToUint16(gbinary.EncodeUint16(uint16(100))))
    fmt.Println(gbinary.DecodeToUint32(gbinary.EncodeUint32(uint32(100))))
    fmt.Println(gbinary.DecodeToUint64(gbinary.EncodeUint64(uint64(100))))

    // 編碼/解析 string
    fmt.Println(gbinary.DecodeToString(gbinary.EncodeString("I'm string!")))
}

以上程序執(zhí)行結(jié)果為:

[18 44 1 41 92 143 194 245 40 240 63]
18 300 1.01
1
300
70000
2000000000
500000000000
1
300
70000
2000000000
500000000000
100
100
100
100
100
100
100
100
I'm string!

編碼

?gbinary.Encode?方法是一個非常強大靈活的方法,可以將所有的基本類型轉(zhuǎn)換為二進制類型(?[ ]byte?)。在?gbinary.Encode?方法內(nèi)部,會自動對變量進行長度計算,采用最小二進制長度來存放該變量的二進制值。例如,針對int類型值為1的變量,?gbinary.Encode?將只會用1個byte來存儲,而int類型值為300的變量,將會使用2個byte來存儲,盡量減少二進制結(jié)果的存儲空間。因此,在解析的時候要非常注意?[ ]byte?的長度,建議能夠確定變量長度的地方,在進行二進制編碼/解碼時,盡量采用形如int8/16/32/64的定長基本類型來存儲變量,這樣解析的時候也能夠采用對應(yīng)的變量形式進行解析,不易產(chǎn)生錯誤。

?gbinary?包也提供了一系列?gbinary.Encode*?的方法,用于將基本數(shù)據(jù)類型轉(zhuǎn)換為二進制。其中,?gbinary.EncodeInt/gbinary.EncodeUint?也是會在內(nèi)部自動識別變量值大小,返回不定長度的?[ ]byte?值,長度范圍1/2/4/8。

解碼

在二進制類型的解析操作中,二進制的長度(?[ ]byte?的長度)是非常重要的,只有給定正確的長度才能執(zhí)行正確的解析,因此?gbinary.Decode?方法給定的變量長度必須為確定長度類型的變量,例如:int8/16/32/64、uint8/16/32/64、float32/64,而如果給定的第二個變量地址對應(yīng)的變量類型為int/uint,無法確定長度,因此解析會失敗。

此外,?gbinary?包也提供了一系列?gbinary.DecodeTo*?的方法,用于將二進制轉(zhuǎn)換為特定的數(shù)據(jù)類型。其中,?gbinary.DecodeToInt/gbinary.DecodeToUint?方法會對二進制長度進行自動識別解析,支持的二進制參數(shù)長度范圍1-8。

按位操作處理示例

?gbinary?的Bits相關(guān)操作簡化了底層二進制位操作的復(fù)雜度,為精準的數(shù)據(jù)按位處理提供了可能。

批量傳感器狀態(tài)數(shù)據(jù)上報示例

例如,針對于物聯(lián)網(wǎng)項目而言,傳感器設(shè)備是比較常見的硬件設(shè)備,我們以下的示例展示了網(wǎng)關(guān)向平臺上報其下管理的傳感器的狀態(tài)信息。由于傳感器狀態(tài)只有4種(0:已下線,1:開啟, 2:關(guān)閉, 3:待機),正好對應(yīng)了2個二進制位(2 bit),因此1byte(8 bit)便可以表示出4個傳感器設(shè)備的狀態(tài)。

看以下的例子,用以上報平臺100個傳感器已開啟,上報的狀態(tài)順序便是傳感器在網(wǎng)關(guān)下端口順序(索引從0開始):

https://github.com/gogf/gf/v2/blob/master/.example/encoding/gbinary/bits1.go

package main


import (
    "fmt"
    "github.com/gogf/gf/v2/encoding/gbinary"
)


func main() {
    // 傳感器狀態(tài),0:已下線, 1:開啟, 2:關(guān)閉, 3:待機
    count  := 100
    status := 1


    // 網(wǎng)關(guān)編碼
    bits := make([]gbinary.Bit, 0)
    for i := 0; i < count; i++ {
        bits = gbinary.EncodeBits(bits, int(status), 2)
    }
    buffer := gbinary.EncodeBitsToBytes(bits)
    fmt.Println("buffer length:", len(buffer))


    /* 上報過程忽略,這里只展示編碼/解碼示例 */


    // 平臺解碼
    alivecount := 0
    sensorbits := gbinary.DecodeBytesToBits(buffer)
    for i := 0; i < len(sensorbits); i += 2 {
        if gbinary.DecodeBits(sensorbits[i:i+2]) == 1 {
            alivecount++
        }
    }
    fmt.Println("alived sensor:", alivecount)
}

執(zhí)行后輸出結(jié)果為:

buffer length: 25
alived sensor: 100

可以看到,上報100個傳感器的狀態(tài)數(shù)據(jù)只需要25byte即可,該示例中我們在平臺上解碼后統(tǒng)計開啟的傳感器數(shù)量有100臺。

gkvdb數(shù)據(jù)庫META數(shù)據(jù)結(jié)構(gòu)操作示例

我們再看看一個實戰(zhàn)的例子。?gkvdb?是?gf?框架相同作者開發(fā)的基于?DRH?算法的高性能?Key-Value?嵌入式數(shù)據(jù)庫,其中元數(shù)據(jù)的存儲數(shù)據(jù)結(jié)構(gòu)如下:

[鍵名哈希64(64bit,8byte) 鍵名長度(8bit,1byte) 鍵值長度(24bit,3byte) 數(shù)據(jù)文件偏移量(40bit,5byte)](變長)

我們使用一條元數(shù)據(jù)來演示編碼/解碼操作。

github.com/gogf/gf/v2/blob/master/.example/encoding/gbinary/bits2.go

package main


import (
    "fmt"
    "github.com/gogf/gf/v2/encoding/gbinary"
)


func main() {
    // Meta元數(shù)據(jù)文件數(shù)據(jù)結(jié)構(gòu):[鍵名哈希64(64bit,8byte) 鍵名長度(8bit,1byte) 鍵值長度(24bit,3byte) 數(shù)據(jù)文件偏移量(40bit,5byte)](變長)
    hash   := 521369841259754125
    klen   := 12
    vlen   := 35535
    offset := 80000000


    // 編碼
    bits   := make([]gbinary.Bit, 0)
    bits    = gbinary.EncodeBits(bits, hash,   64)
    bits    = gbinary.EncodeBits(bits, klen,    8)
    bits    = gbinary.EncodeBits(bits, vlen,   24)
    bits    = gbinary.EncodeBits(bits, offset, 40)
    buffer := gbinary.EncodeBitsToBytes(bits)
    fmt.Println("meta length:", len(buffer))


	/* 文件存儲及數(shù)據(jù)查詢過程忽略,這里只展示元數(shù)據(jù)編碼/解碼示例 */


    // 解碼
    metabits := gbinary.DecodeBytesToBits(buffer)
    fmt.Println("hash  :", gbinary.DecodeBits(metabits[0  : 64]))
    fmt.Println("klen  :", gbinary.DecodeBits(metabits[64 : 72]))
    fmt.Println("vlen  :", gbinary.DecodeBits(metabits[72 : 96]))
    fmt.Println("offset:", gbinary.DecodeBits(metabits[96 : 136]))
}

運行后,輸出結(jié)果為:

meta length: 17
hash  : 521369841259754125
klen  : 12
vlen  : 35535
offset: 80000000


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號