我的Golang课程"GPB"使用体验分享
我的Golang课程"GPB"使用体验分享
大家好!
我叫奥列格,今年37岁。我想提供一个关于Go语言的课程。问题是,我的英语水平相当有限,而且我对Git/GitHub等技术工具完全不熟悉。我最复杂的操作也就是git clone了。
我请求大家帮助我正确地将其发布到互联网上,并撰写相关文档。
核心内容:
有一个名为GPB的类,它可以收集任何二进制包,并预先计算包所需的内存大小。
你需要按顺序执行诸如W8、W16、W32等命令(有很多这样的命令)。第一次执行仅计算包的大小。
第二次执行时,会传入内存地址,并根据包的大小来收集并构建实际的包。
读取包的方式类似,只是使用R*形式的函数。不允许超出包的边界。
你们能提供帮助吗?如果你们感兴趣,我可以把包含该类的tar文件发给你们。
奥列格。
(由俄语机器翻译)
更多关于我的Golang课程"GPB"使用体验分享的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好,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文件,我可以帮你分析具体的实现并提供更详细的优化建议。

