- 自己動(dòng)手實(shí)現(xiàn)Lua:虛擬機(jī)、編譯器和標(biāo)準(zhǔn)庫(kù)
- 張秀宏
- 1459字
- 2019-01-03 15:00:09
2.4 解析二進(jìn)制chunk
如前所述,具體的二進(jìn)制chunk解析工作由reader結(jié)構(gòu)體來完成。請(qǐng)讀者在binchunk目錄下面創(chuàng)建reader.go文件,在里面定義reader結(jié)構(gòu)體,代碼如下所示。
package binchunk import "encoding/binary" import "math" type reader struct { data []byte }
reader結(jié)構(gòu)體只有一個(gè)data字段,存放將要被解析的二進(jìn)制chunk數(shù)據(jù)。下面我們來看看由reader結(jié)構(gòu)體解析二進(jìn)制chunk都有哪些方法。
2.4.1 讀取基本數(shù)據(jù)類型
讀取基本數(shù)據(jù)類型的方法一共有7種,其他方法通過調(diào)用這7種方法來從二進(jìn)制chunk里提取數(shù)據(jù)。最簡(jiǎn)單的是“readByte()”方法,即從字節(jié)流里讀取一個(gè)字節(jié),代碼如下所示。
func (self *reader) readByte() byte { b := self.data[0] self.data = self.data[1:] return b }
readUint32()方法使用小端方式從字節(jié)流里讀取一個(gè)cint存儲(chǔ)類型(占4個(gè)字節(jié),映射為Go語(yǔ)言u(píng)int32類型)的整數(shù),代碼如下所示。
func (self *reader) readUint32() uint32 { i := binary.LittleEndian.Uint32(self.data) self.data = self.data[4:] return i }
readUint64()方法使用小端方式從字節(jié)流里讀取一個(gè)size_t存儲(chǔ)類型(占8個(gè)字節(jié),映射為Go語(yǔ)言u(píng)int64類型)的整數(shù),代碼如下所示。
func (self *reader) readUint64() uint64 { i := binary.LittleEndian.Uint64(self.data) self.data = self.data[8:] return i }
readLuaInteger()方法借助readUint64()方法從字節(jié)流里讀取一個(gè)Lua整數(shù)(占8個(gè)字節(jié),映射為Go語(yǔ)言int64類型),代碼如下所示。
func (self *reader) readLuaInteger() int64 { return int64(self.readUint64()) }
readLuaNumber()方法借助readUint64()方法從字節(jié)流里讀取一個(gè)Lua浮點(diǎn)數(shù)(占8個(gè)字節(jié),映射為Go語(yǔ)言float64類型),代碼如下所示。
func (self *reader) readLuaNumber() float64 { return math.Float64frombits(self.readUint64()) }
readString()方法從字節(jié)流里讀取字符串(映射為Go語(yǔ)言string類型),代碼如下所示:
func (self *reader) readString() string { size := uint(self.readByte()) // 短字符串? if size == 0 { // NULL字符串 return "" } if size == 0xFF { // 長(zhǎng)字符串 size = uint(self.readUint64()) } bytes := self.readBytes(size -1) return string(bytes) }
readBytes()方法從字節(jié)流里讀取n個(gè)字節(jié),代碼如下所示。
func (self *reader) readBytes(n uint) []byte { bytes := self.data[:n] self.data = self.data[n:] return bytes }
2.4.2 檢查頭部
checkHeader()方法從字節(jié)流里讀取并檢查二進(jìn)制chunk頭部的各個(gè)字段,如果發(fā)現(xiàn)某個(gè)字段和期望不符,則調(diào)用panic函數(shù)終止加載,代碼如下所示。
func (self *reader) checkHeader() { if string(self.readBytes(4)) ! = LUA_SIGNATURE { panic("not a precompiled chunk! ") } else if self.readByte() ! = LUAC_VERSION { panic("version mismatch! ") } else if self.readByte() ! = LUAC_FORMAT { panic("format mismatch! ") } else if string(self.readBytes(6)) ! = LUAC_DATA { panic("corrupted! ") } else if self.readByte() ! = CINT_SIZE { panic("int size mismatch! ") } else if self.readByte() ! = CSZIET_SIZE { panic("size_t size mismatch! ") } else if self.readByte() ! = INSTRUCTION_SIZE { panic("instruction size mismatch! ") } else if self.readByte() ! = LUA_INTEGER_SIZE { panic("lua_Integer size mismatch! ") } else if self.readByte() ! = LUA_NUMBER_SIZE { panic("lua_Number size mismatch! ") } else if self.readLuaInteger() ! = LUAC_INT { panic("endianness mismatch! ") } else if self.readLuaNumber() ! = LUAC_NUM { panic("float format mismatch! ") } }
2.4.3 讀取函數(shù)原型
readProto()方法從字節(jié)流里讀取函數(shù)原型,代碼如下所示。
func (self *reader) readProto(parentSource string) *Prototype { source := self.readString() if source == "" { source = parentSource } return &Prototype{ Source: source, LineDefined: self.readUint32(), LastLineDefined: self.readUint32(), NumParams: self.readByte(), IsVararg: self.readByte(), MaxStackSize: self.readByte(), Code: self.readCode(), Constants: self.readConstants(), Upvalues: self.readUpvalues(), Protos: self.readProtos(source), LineInfo: self.readLineInfo(), LocVars: self.readLocVars(), UpvalueNames: self.readUpvalueNames(), } }
讀取函數(shù)基本信息的部分比較簡(jiǎn)單,只有Source字段的處理稍微有點(diǎn)麻煩,這是因?yàn)長(zhǎng)ua編譯器只給主函數(shù)設(shè)置了源文件名以減少冗余數(shù)據(jù),所以子函數(shù)原型需要從自己的父函數(shù)原型那里獲取源文件名。下面我們來看一下指令表等列表的讀取方法。
readCode()方法從字節(jié)流里讀取指令表,代碼如下所示。
func (self *reader) readCode() []uint32 { code := make([]uint32, self.readUint32()) for i := range code { code[i] = self.readUint32() } return code } readConstants()方法從字節(jié)流里讀取常量表,代碼如下所示。 func (self *reader) readConstants() []interface{} { constants := make([]interface{}, self.readUint32()) for i := range constants { constants[i] = self.readConstant() } return constants }
readConstant()方法從字節(jié)流里讀取一個(gè)常量,代碼如下所示。
func (self *reader) readConstant() interface{} { switch self.readByte() { // tag case TAG_NIL: return nil case TAG_BOOLEAN: return self.readByte() ! = 0 case TAG_INTEGER: return self.readLuaInteger() case TAG_NUMBER: return self.readLuaNumber() case TAG_SHORT_STR: return self.readString() case TAG_LONG_STR: return self.readString() default: panic("corrupted! ") } }
readUpvalues()方法從字節(jié)流里讀取Upvalue表,代碼如下所示。
func (self *reader) readUpvalues() []Upvalue { upvalues := make([]Upvalue, self.readUint32()) for i := range upvalues { upvalues[i] = Upvalue{ Instack: self.readByte(), Idx: self.readByte(), } } return upvalues }
由于函數(shù)原型本身就是遞歸數(shù)據(jù)結(jié)構(gòu),所以readProto()方法也會(huì)遞歸調(diào)用自己去讀取子函數(shù)原型。
readProtos()方法從字節(jié)流里讀取子函數(shù)原型表,代碼如下所示。
func (self *reader) readProtos(parentSource string) []*Prototype { protos := make([]*Prototype, self.readUint32()) for i := range protos { protos[i] = self.readProto(parentSource) } return protos } readLineInfo()方法從字節(jié)流里讀取行號(hào)表,代碼如下所示。 func (self *reader) readLineInfo() []uint32 { lineInfo := make([]uint32, self.readUint32()) for i := range lineInfo { lineInfo[i] = self.readUint32() } return lineInfo }
readLocVars()方法從字節(jié)流里讀取局部變量表,代碼如下所示。
func (self *reader) readLocVars() []LocVar { locVars := make([]LocVar, self.readUint32()) for i := range locVars { locVars[i] = LocVar{ VarName: self.readString(), StartPC: self.readUint32(), EndPC: self.readUint32(), } } return locVars }
readUpvalueNames()方法從字節(jié)流里讀取Upvalue名列表,代碼如下所示。
func (self *reader) readUpvalueNames() []string { names := make([]string, self.readUint32()) for i := range names { names[i] = self.readString() } return names }
到此為止,二進(jìn)制chunk解析代碼也都介紹完畢了。在2.5節(jié),我們會(huì)實(shí)現(xiàn)一個(gè)簡(jiǎn)化版的Lua反編譯器。
- Node.js+Webpack開發(fā)實(shí)戰(zhàn)
- Developing Middleware in Java EE 8
- Processing互動(dòng)編程藝術(shù)
- Linux命令行與shell腳本編程大全(第4版)
- 自然語(yǔ)言處理Python進(jìn)階
- PHP 7+MySQL 8動(dòng)態(tài)網(wǎng)站開發(fā)從入門到精通(視頻教學(xué)版)
- SQL Server與JSP動(dòng)態(tài)網(wǎng)站開發(fā)
- Serverless computing in Azure with .NET
- 大話Java:程序設(shè)計(jì)從入門到精通
- Kubernetes進(jìn)階實(shí)戰(zhàn)
- Learning Nessus for Penetration Testing
- 監(jiān)控的藝術(shù):云原生時(shí)代的監(jiān)控框架
- 基于GPU加速的計(jì)算機(jī)視覺編程:使用OpenCV和CUDA實(shí)時(shí)處理復(fù)雜圖像數(shù)據(jù)
- Less Web Development Cookbook
- Clojure for Finance