官术网_书友最值得收藏!

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反編譯器。

主站蜘蛛池模板: 若羌县| 磴口县| 双辽市| 土默特左旗| 临夏市| 敦化市| 德令哈市| 泰和县| 莒南县| 来凤县| 健康| 托克托县| 张掖市| 扶风县| 永平县| 北京市| 桦川县| 桓台县| 响水县| 信丰县| 华安县| 抚宁县| 江达县| 南丹县| 项城市| 通化市| 溆浦县| 湘西| 会昌县| 贺州市| 化德县| 望谟县| 藁城市| 苗栗县| 苏尼特左旗| 色达县| 大宁县| 洪湖市| 马山县| 泾阳县| 贵州省|