- 自己动手写Java虚拟机
- 张秀宏
- 463字
- 2023-01-30 16:12:51
3.2.1 读取数据
解析class文件的第一步是从里面读取数据。虽然可以把class文件当成字节流来处理,但是直接操作字节很不方便,所以先定义一个结构体来帮助读取数据。在ch03\classfile目录下创建class_reader.go文件,在其中定义ClassReader结构体和数据读取方法,代码如下:
package classfile import "encoding/binary" type ClassReader struct { data []byte }
ClassReader只是[]byte类型的包装而已。readUint8()读取u1类型数据,代码如下:
func (self *ClassReader) readUint8() uint8 {...} // u1 func (self *ClassReader) readUint16() uint16 {...} // u2 func (self *ClassReader) readUint32() uint32 {...} // u4 func (self *ClassReader) readUint64() uint64 {...} func (self *ClassReader) readUint16s() []uint16 {...} func (self *ClassReader) readBytes(length uint32) []byte {...} func (self *ClassReader) readUint8() uint8 { val := self.data[0] self.data = self.data[1:] return val }
注意,ClassReader并没有使用索引记录数据位置,而是使用Go语言的reslice语法跳过已经读取的数据。readUint16()读取u2类型数据,代码如下:
func (self *ClassReader) readUint16() uint16 { val := binary.BigEndian.Uint16(self.data) self.data = self.data[2:] return val }
Go标准库encoding/binary包中定义了一个变量BigEndian,正好可以从[]byte中解码多字节数据。readUint32()读取u4类型数据,代码如下:
func (self *ClassReader) readUint32() uint32 { val := binary.BigEndian.Uint32(self.data) self.data = self.data[4:] return val }
readUint64()读取uint64(Java虚拟机规范并没有定义u8)类型数据,代码如下:
func (self *ClassReader) readUint64() uint64 { val := binary.BigEndian.Uint64(self.data) self.data = self.data[8:] return val }
readUint16s()读取uint16表,表的大小由开头的uint16数据指出,代码如下:
func (self *ClassReader) readUint16s() []uint16 { n := self.readUint16() s := make([]uint16, n) for i := range s { s[i] = self.readUint16() } return s }
最后一个方法是readBytes(),用于读取指定数量的字节,代码如下:
func (self *ClassReader) readBytes(n uint32) []byte { bytes := self.data[:n] self.data = self.data[n:] return bytes }