我的Golang课程"GPB"使用体验分享

我的Golang课程"GPB"使用体验分享 大家好! 我叫奥列格,今年37岁。我想提供一个关于Go语言的课程。问题是,我的英语水平相当有限,而且我对Git/GitHub等技术工具完全不熟悉。我最复杂的操作也就是git clone了。 我请求大家帮助我正确地将其发布到互联网上,并撰写相关文档。

核心内容: 有一个名为GPB的类,它可以收集任何二进制包,并预先计算包所需的内存大小。 你需要按顺序执行诸如W8W16W32等命令(有很多这样的命令)。第一次执行仅计算包的大小。 第二次执行时,会传入内存地址,并根据包的大小来收集并构建实际的包。 读取包的方式类似,只是使用R*形式的函数。不允许超出包的边界。

你们能提供帮助吗?如果你们感兴趣,我可以把包含该类的tar文件发给你们。

奥列格。

(由俄语机器翻译)


更多关于我的Golang课程"GPB"使用体验分享的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你好,Oleg,

你需要帮助来编写和/或将你的课程材料上传到Github吗?

OlegPuchinin: 你能帮忙吗?如果你感兴趣,我可以把课程的tar文件发给你。

我也不确定你所说的“GPB课程”是什么意思。这个代码是否“安全”(没有知识产权问题,没有密码/密钥等)?如果可以的话,你能把它上传到Github吗?这样我们可以更好地了解你目前的情况以及你遇到的任何问题。


半人/半机翻译:

你需要帮助来编写你的课程材料和/或将文件上传到Github吗?

我也不确定“GPB课程”是什么意思。这个代码可以安全地上传到Github吗(没有知识产权或密码/密钥问题)?如果你能上传,我们就可以阅读代码并更好地理解任何问题。

更多关于我的Golang课程"GPB"使用体验分享的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个非常有趣的序列化/反序列化库设计。让我来分析一下这个GPB类的实现思路,并提供一些代码示例。

从描述来看,GPB应该是一个二进制包构建器,支持两阶段操作:先计算大小,再实际写入。以下是基于你描述的实现示例:

package gpb

import (
	"encoding/binary"
	"errors"
)

type GPB struct {
	buffer     []byte
	offset     int
	sizeOnly   bool
	totalSize  int
	byteOrder  binary.ByteOrder
}

// NewGPB 创建新的GPB实例
func NewGPB() *GPB {
	return &GPB{
		byteOrder: binary.LittleEndian, // 默认使用小端序
		sizeOnly:  true,                // 第一阶段:仅计算大小
	}
}

// BeginWrite 开始实际写入阶段
func (g *GPB) BeginWrite(buffer []byte) {
	g.buffer = buffer
	g.offset = 0
	g.sizeOnly = false
}

// W8 写入8位整数
func (g *GPB) W8(value uint8) {
	if g.sizeOnly {
		g.totalSize += 1
		return
	}
	
	if g.offset+1 > len(g.buffer) {
		panic("buffer overflow")
	}
	
	g.buffer[g.offset] = value
	g.offset += 1
}

// W16 写入16位整数
func (g *GPB) W16(value uint16) {
	if g.sizeOnly {
		g.totalSize += 2
		return
	}
	
	if g.offset+2 > len(g.buffer) {
		panic("buffer overflow")
	}
	
	g.byteOrder.PutUint16(g.buffer[g.offset:], value)
	g.offset += 2
}

// W32 写入32位整数
func (g *GPB) W32(value uint32) {
	if g.sizeOnly {
		g.totalSize += 4
		return
	}
	
	if g.offset+4 > len(g.buffer) {
		panic("buffer overflow")
	}
	
	g.byteOrder.PutUint32(g.buffer[g.offset:], value)
	g.offset += 4
}

// W64 写入64位整数
func (g *GPB) W64(value uint64) {
	if g.sizeOnly {
		g.totalSize += 8
		return
	}
	
	if g.offset+8 > len(g.buffer) {
		panic("buffer overflow")
	}
	
	g.byteOrder.PutUint64(g.buffer[g.offset:], value)
	g.offset += 8
}

// WBytes 写入字节切片
func (g *GPB) WBytes(data []byte) {
	length := len(data)
	
	// 先写入长度
	g.W32(uint32(length))
	
	if g.sizeOnly {
		g.totalSize += length
		return
	}
	
	if g.offset+length > len(g.buffer) {
		panic("buffer overflow")
	}
	
	copy(g.buffer[g.offset:], data)
	g.offset += length
}

// TotalSize 获取包的总大小
func (g *GPB) TotalSize() int {
	return g.totalSize
}

// 读取部分的实现
type Reader struct {
	buffer    []byte
	offset    int
	byteOrder binary.ByteOrder
}

func NewReader(data []byte) *Reader {
	return &Reader{
		buffer:    data,
		byteOrder: binary.LittleEndian,
	}
}

// R8 读取8位整数
func (r *Reader) R8() (uint8, error) {
	if r.offset+1 > len(r.buffer) {
		return 0, errors.New("buffer underflow")
	}
	
	value := r.buffer[r.offset]
	r.offset += 1
	return value, nil
}

// R16 读取16位整数
func (r *Reader) R16() (uint16, error) {
	if r.offset+2 > len(r.buffer) {
		return 0, errors.New("buffer underflow")
	}
	
	value := r.byteOrder.Uint16(r.buffer[r.offset:])
	r.offset += 2
	return value, nil
}

// R32 读取32位整数
func (r *Reader) R32() (uint32, error) {
	if r.offset+4 > len(r.buffer) {
		return 0, errors.New("buffer underflow")
	}
	
	value := r.byteOrder.Uint32(r.buffer[r.offset:])
	r.offset += 4
	return value, nil
}

// RBytes 读取字节切片
func (r *Reader) RBytes() ([]byte, error) {
	length, err := r.R32()
	if err != nil {
		return nil, err
	}
	
	if r.offset+int(length) > len(r.buffer) {
		return nil, errors.New("buffer underflow")
	}
	
	data := r.buffer[r.offset : r.offset+int(length)]
	r.offset += int(length)
	return data, nil
}

使用示例:

package main

import (
	"fmt"
	"log"
)

func main() {
	// 第一阶段:计算包大小
	builder := gpb.NewGPB()
	
	// 构建包
	builder.W32(0x12345678)
	builder.W16(0xABCD)
	builder.W8(0xFF)
	builder.WBytes([]byte("Hello, World!"))
	
	// 获取所需缓冲区大小
	requiredSize := builder.TotalSize()
	fmt.Printf("Required buffer size: %d bytes\n", requiredSize)
	
	// 第二阶段:实际写入
	buffer := make([]byte, requiredSize)
	builder.BeginWrite(buffer)
	
	// 重新执行相同的写入操作
	builder.W32(0x12345678)
	builder.W16(0xABCD)
	builder.W8(0xFF)
	builder.WBytes([]byte("Hello, World!"))
	
	// 读取包
	reader := gpb.NewReader(buffer)
	
	val32, err := reader.R32()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("R32: 0x%X\n", val32)
	
	val16, err := reader.R16()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("R16: 0x%X\n", val16)
	
	val8, err := reader.R8()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("R8: 0x%X\n", val8)
	
	data, err := reader.RBytes()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("RBytes: %s\n", string(data))
}

这个设计模式在需要预分配缓冲区的场景中很有用,特别是在嵌入式系统或网络协议中。如果你有tar文件,我可以帮你分析具体的实现并提供更详细的优化建议。

回到顶部