這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
文章來源:http://gf.johng.cn/500342
gf架構提供了獨立的位元據操作包gbinary,主要用於各種資料類型與[ ]byte二進位類型之間的相互轉換;以及針對於整型資料進行精準按位處理的功能。常用於網路通訊時資料編碼/解碼,以及資料檔案操作時的編碼/解碼。
用於位元據結構轉換處理的方法列表如下:
func Encode(vs ...interface{}) ([]byte, error)func EncodeInt(i int) []bytefunc EncodeInt8(i int8) []bytefunc EncodeInt16(i int16) []bytefunc EncodeInt32(i int32) []bytefunc EncodeInt64(i int64) []bytefunc EncodeUint(i uint) []bytefunc EncodeUint8(i uint8) []bytefunc EncodeUint16(i uint16) []bytefunc EncodeUint32(i uint32) []bytefunc EncodeUint64(i uint64) []bytefunc EncodeBool(b bool) []bytefunc EncodeFloat32(f float32) []bytefunc EncodeFloat64(f float64) []bytefunc EncodeString(s string) []bytefunc Decode(b []byte, vs ...interface{}) errorfunc DecodeToInt(b []byte) intfunc DecodeToInt8(b []byte) int8func DecodeToInt16(b []byte) int16func DecodeToInt32(b []byte) int32func DecodeToInt64(b []byte) int64func DecodeToUint(b []byte) uintfunc DecodeToUint8(b []byte) uint8func DecodeToUint16(b []byte) uint16func DecodeToUint32(b []byte) uint32func DecodeToUint64(b []byte) uint64func DecodeToBool(b []byte) boolfunc DecodeToFloat32(b []byte) float32func DecodeToFloat64(b []byte) float64func DecodeToString(b []byte) string
支援按位處理的方法列表如下:
func EncodeBits(bits []Bit, i int, l int) []Bitfunc EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bitfunc EncodeBitsToBytes(bits []Bit) []bytefunc DecodeBits(bits []Bit) uintfunc DecodeBitsToUint(bits []Bit) uintfunc DecodeBytesToBits(bs []byte) []Bit
其中的Bit類型表示一個位元字(0或1),其定義如下:
type Bit int8
二進位操作樣本
我們來看一個比較完整的二進位操作樣本,基本示範了絕大部分的二進位轉換操作。
gitee.com/johng/gf/blob/master/geg/encoding/gbinary/binary.go
package mainimport ( "fmt" "gitee.com/johng/gf/g/os/glog" "gitee.com/johng/gf/g/encoding/gbinary")func main() { // 使用gbinary.Encoded對基礎資料型別 (Elementary Data Type)進行二進位打包 if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil { glog.Error(err) } else { fmt.Println(buffer) } // 使用gbinary.Decode對整形二進位解包,注意第二個及其後參數為字長確定的整形變數的指標地址,字長確定的類型, // 例如:int8/16/32/64、uint8/16/32/64、float32/64 // 這裡的1.01預設為float64類型(64位系統下) if buffer, err := gbinary.Encode(18, 300, 1.01); err != 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!")))}
以上程式執行結果為:
[18 44 1 41 92 143 194 245 40 240 63]18 300 1.0113007000020000000005000000000001300700002000000000500000000000100100100100100100100100I'm string!
- 編碼
gbinary.Encode方法是一個非常強大靈活的方法,可以將所有的基本類型轉換為二進位類型([ ]byte)。在gbinary.Encode方法內部,會自動對變數進行長度計算,採用最小二進位長度來存放該變數的二進位值。例如,針對int類型值為1的變數,gbinary.Encode將只會用1個byte來儲存,而int類型值為300的變數,將會使用2個byte來儲存,盡量減少二進位結果的儲存空間。因此,在解析的時候要非常注意[ ]byte的長度,建議能夠確定變數長度的地方,在進行二進位編碼/解碼時,盡量採用形如int8/16/32/64的定長基本類型來儲存變數,這樣解析的時候也能夠採用對應的變數形式進行解析,不易產生錯誤。
gbinary包也提供了一系列gbinary.Encode*的方法,用於將基礎資料型別 (Elementary Data Type)轉換為二進位。其中,gbinary.EncodeInt/gbinary.EncodeUint也是會在內部自動識別變數值大小,返回不定長度的[ ]byte值,長度範圍1/2/4/8。
- 解碼
在二進位類型的解析操作中,二進位的長度([ ]byte的長度)是非常重要的,只有給定正確的長度才能執行正確的解析,因此gbinary.Decode方法給定的變數長度必須為確定長度類型的變數,例如:int8/16/32/64、uint8/16/32/64、float32/64,而如果給定的第二個變數地址對應的變數類型為int/uint,無法確定長度,因此解析會失敗。
此外,gbinary包也提供了一系列gbinary.DecodeTo*的方法,用於將二進位轉換為特定的資料類型。其中,gbinary.DecodeToInt/gbinary.DecodeToUint方法會對二進位長度進行自動識別解析,支援的二進位參數長度範圍1-8。
按位操作處理樣本
gbinary的Bits相關操作簡化了底層二進位位操作的複雜度,為精準的資料按位處理提供了可能。
批量感應器狀態資料上報樣本
例如,針對於物聯網項目而言,感應器裝置是比較常見的硬體裝置,我們以下的樣本展示了網關向平台上報其下管理的感應器的狀態資訊。由於感應器狀態只有4種(0:已下線,1:開啟, 2:關閉, 3:待機),正好對應了2個二進位位(2 bit),因此1byte(8 bit)便可以表示出4個感應器裝置的狀態。看以下的例子,用以上報平台100個感應器已開啟,上報的狀態順序便是感應器在網關下連接埠順序(索引從0開始):
gitee.com/johng/gf/blob/master/geg/encoding/gbinary/bits1.go
package mainimport ( "fmt" "gitee.com/johng/gf/g/encoding/gbinary")func main() { // 感應器狀態,0:已下線, 1:開啟, 2:關閉, 3:待機 count := 100 status := 1 // 網關編碼 bits := make([]gbinary.Bit, 0) for i := 0; i < count; i++ { bits = gbinary.EncodeBits(bits, uint(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)}
執行後輸出結果為:
buffer length: 25alived sensor: 100
可以看到,上報100個感應器的狀態資料只需要25byte即可,該樣本中我們在平台上解碼後統計開啟的感應器數量有100台。
gkvdb資料庫META資料結構操作樣本
我們再看看一個實戰的例子。gkvdb是gf架構相同作者開發的基於DRH演算法的高效能Key-Value嵌入式資料庫,其中中繼資料的儲存資料結構如下:
[鍵名雜湊64(64bit,8byte) 鍵名長度(8bit,1byte) 索引值長度(24bit,3byte) 資料檔案位移量(40bit,5byte)](變長)
我們使用一條中繼資料來示範編碼/解碼操作。
gitee.com/johng/gf/blob/master/geg/encoding/gbinary/bits2.go
package mainimport ( "fmt" "gitee.com/johng/gf/g/encoding/gbinary")func main() { // Meta中繼資料檔案資料結構:[鍵名雜湊64(64bit,8byte) 鍵名長度(8bit,1byte) 索引值長度(24bit,3byte) 資料檔案位移量(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)) /* 檔案儲存體及資料查詢過程忽略,這裡只展示中繼資料編碼/解碼樣本 */ // 解碼 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]))}
運行後,輸出結果為:
meta length: 17hash : 521369841259754125klen : 12vlen : 35535offset: 80000000