如何在Golang中解包字节缓冲区中的各种整数形式?

huangapple go评论74阅读模式
英文:

How do I unpack various form of integers in a byte buffer in Golang?

问题

我需要从字节缓冲区中提取各种字段。我想出了以下解决方案:

func (fs *FileSystem) readSB() {
 	// fs.f 是一个 *os.File
 	buf := make([]byte, 1024)
 	fs.f.ReadAt(buf, 1024)
 
 	// 偏移量: 类型
 	var p *bytes.Buffer
 
 	// 0: uint32
 	p = bytes.NewBuffer(buf[0:])
 	binary.Read(p, binary.LittleEndian, &fs.sb.inodeCount)
 	// 4: uint32
 	p = bytes.NewBuffer(buf[4:])
 	binary.Read(p, binary.LittleEndian, &fs.sb.blockCount)
 	// 20: uint32
 	p = bytes.NewBuffer(buf[20:])
 	binary.Read(p, binary.LittleEndian, &fs.sb.firstDataBlock)
 	// 24: uint32
 	p = bytes.NewBuffer(buf[24:])
 	binary.Read(p, binary.LittleEndian, &fs.sb.blockSize)
 	fs.sb.blockSize = 1024 << fs.sb.blockSize
 	// 32: uint32
 	p = bytes.NewBuffer(buf[32:])
 	binary.Read(p, binary.LittleEndian, &fs.sb.blockPerGroup)
 	// 40: uint32
 	p = bytes.NewBuffer(buf[40:])
 	binary.Read(p, binary.LittleEndian, &fs.sb.inodePerBlock)
}

有没有更好/更符合惯用方式/更直接的方法来做到这一点?

  • 我希望保持偏移量明确
  • 我希望尽可能从字节缓冲区中读取,而不是从文件中寻找和读取。
英文:

I need to extract various fields in a byte buffer. I came up with this solution:

func (fs *FileSystem) readSB() {
 	// fs.f is a *os.File
 	buf := make([]byte, 1024)
 	fs.f.ReadAt(buf, 1024)
 
 	// Offset: type
 	var p *bytes.Buffer
 
 	// 0: uint32
 	p = bytes.NewBuffer(buf[0:])
 	binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodeCount)
 	// 4: uint32
 	p = bytes.NewBuffer(buf[4:])
 	binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockCount)
 	// 20: uint32
 	p = bytes.NewBuffer(buf[20:])
 	binary.Read(p, binary.LittleEndian, &amp;fs.sb.firstDataBlock)
 	// 24: uint32
 	p = bytes.NewBuffer(buf[24:])
 	binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockSize)
 	fs.sb.blockSize = 1024 &lt;&lt; fs.sb.blockSize
 	// 32: uint32
 	p = bytes.NewBuffer(buf[32:])
 	binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockPerGroup)
 	// 40: uint32
 	p = bytes.NewBuffer(buf[40:])
 	binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodePerBlock)
}

Is there a more better/idiomatic/straightforward way of doing this?

  • I want to keep offsets explicit
  • I want to read from the byte buffer, not seeking and reading from the file when possible.

答案1

得分: 33

你可以通过使用.Next()来跳过不想读取的字节,从而避免每次创建新的缓冲区:

{
	// 偏移量: 类型
	p := bytes.NewBuffer(buf)

	// 0: uint32
	binary.Read(p, binary.LittleEndian, &fs.sb.inodeCount)

	// 4: uint32
	binary.Read(p, binary.LittleEndian, &fs.sb.blockCount)

	// 跳过 [8:20)
	p.Next(12)

	// 20: uint32
	binary.Read(p, binary.LittleEndian, &fs.sb.firstDataBlock)

	// 24: uint32
	binary.Read(p, binary.LittleEndian, &fs.sb.blockSize)
	fs.sb.blockSize = 1024 << fs.sb.blockSize

	// 跳过 [28:32)
	p.Next(4)

	// 32: uint32
	binary.Read(p, binary.LittleEndian, &fs.sb.blockPerGroup)

	// 跳过 [36:40)
	p.Next(4)

	// 40: uint32
	binary.Read(p, binary.LittleEndian, &fs.sb.inodePerBlock)
}

或者你可以避免逐块读取并创建一个头部结构,直接使用binary.Read进行读取:

type Head struct {
	InodeCount		uint32	//  0:4
	BlockCount		uint32	//  4:8
	Unknown1		uint32	//  8:12
	Unknown2 		uint32	// 12:16
	Unknown3 		uint32	// 16:20
	FirstBlock		uint32	// 20:24
	BlockSize		uint32	// 24:28
	Unknown4		uint32	// 28:32
	BlocksPerGroup	uint32	// 32:36
	Unknown5		uint32	// 36:40
	InodesPerBlock	uint32	// 40:44
}

func main() {
	var header Head

	err = binary.Read(file, binary.LittleEndian, &header)

	if err != nil {
		log.Fatal(err)
	}

	log.Printf("%#v\n", header)
}
英文:

You could avoid creating a new buffer every time by using .Next() to skip the bytes you don't want to read:

{
	// Offset: type
	p := bytes.NewBuffer(buf)

	// 0: uint32
	binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodeCount)

	// 4: uint32
	binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockCount)

	// Skip [8:20)
	p.Next(12)

	// 20: uint32
	binary.Read(p, binary.LittleEndian, &amp;fs.sb.firstDataBlock)

	// 24: uint32
	binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockSize)
	fs.sb.blockSize = 1024 &lt;&lt; fs.sb.blockSize

	// Skip [28:32)
	p.Next(4)

	// 32: uint32
	binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockPerGroup)

	// Skip [36:40)
	p.Next(4)

	// 40: uint32
	binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodePerBlock)
}

Or you could avoid reading chunk by chunk and create a header structure which you read directly using binary.Read:

type Head struct {
	InodeCount		uint32	//  0:4
	BlockCount		uint32	//  4:8
	Unknown1		uint32	//  8:12
	Unknown2 		uint32	// 12:16
	Unknown3 		uint32	// 16:20
	FirstBlock		uint32	// 20:24
	BlockSize		uint32	// 24:28
	Unknown4		uint32	// 28:32
	BlocksPerGroup	uint32	// 32:36
	Unknown5		uint32	// 36:40
	InodesPerBlock	uint32	// 40:44
}

func main() {
	var header Head

	err = binary.Read(file, binary.LittleEndian, &amp;header)

	if err != nil {
		log.Fatal(err)
	}

	log.Printf(&quot;%#v\n&quot;, header)
}

答案2

得分: 1

我有一个名为binpacker的包来处理这些情况

示例

示例数据:

buffer := new(bytes.Buffer)
packer := binpacker.NewPacker(buffer)
unpacker := binpacker.NewUnpacker(buffer)
packer.PushByte(0x01)
packer.PushUint16(math.MaxUint16)

解包:

var val1 byte
var val2 uint16
var err error
val1, err = unpacker.ShiftByte()
val2, err = unpacker.ShiftUint16()

或者

var val1 byte
var val2 uint16
var err error
unpacker.FetchByte(&amp;val1).FetchUint16(&amp;val2)
unpacker.Error() // 确保错误为nil
英文:

I have a package binpacker to handle those situations

example

example data:

buffer := new(bytes.Buffer)
packer := binpacker.NewPacker(buffer)
unpacker := binpacker.NewUnpacker(buffer)
packer.PushByte(0x01)
packer.PushUint16(math.MaxUint16)

unpack it:

var val1 byte
var val2 uint16
var err error
val1, err = unpacker.ShiftByte()
val2, err = unpacker.ShiftUint16()

Or

var val1 byte
var val2 uint16
var err error
unpacker.FetchByte(&amp;val1).FetchUint16(&amp;val2)
unpacker.Error() // Make sure error is nil

huangapple
  • 本文由 发表于 2012年9月11日 03:02:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/12357865.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定