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
}