更新Go中的mmap文件与结构体

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

Updating mmap file with struct in Go

问题

https://stackoverflow.com/questions/14337517/writing-struct-to-mapped-memory-file-mmap 类似,如何在Go中将结构体写入mmap文件或使用结构体更新mmap文件?

假设我的二进制文件以二进制头开始:

type MVHD struct {
	Version      byte
	Flags        [3]byte
	DateCreated  time.Time
	DateModified time.Time

	TimeUnit        uint32 // 每秒的时间单位(默认为600)
	DurationInUnits uint64 // 时间长度(以时间单位计算)

	Raw []byte // 解码后的位上方的未解码数据
}

假设我想将其映射为内存文件并更新DateModified字段,是否可能?

(我对Go中的mmap的有限阅读是它只能通过字节数组访问,但我确信有一种通过结构体访问它的方法。我在这里找到了一个示例,它使用了reflect,但对我来说太复杂了,无法掌握基本思想)

英文:

Similar to https://stackoverflow.com/questions/14337517/writing-struct-to-mapped-memory-file-mmap, how to write a struct to mmap file or update mmap file with struct in Go?

Suppose my binary file begins with a binary header of

type MVHD struct {
	Version      byte
	Flags        [3]byte
	DateCreated  time.Time
	DateModified time.Time

	TimeUnit        uint32 // time unit per second (default = 600)
	DurationInUnits uint64 // time length (in time units)

	Raw []byte // undecoded data after decoded bits above
}

Say I want to map it as memory file and update the DateModified field, is that possible?

(my limited reading on mmap in Go is that it can only be accessed via byte array, but I'm sure there is a way to access it via struct. I found one here using reflect but it is too complicated for me to grasp the basic idea)

答案1

得分: 2

你可以使用encoding/binary来读取/写入固定大小的结构体。这种方法是可移植的,不依赖于内存布局、编译器或CPU架构。例如:

// 注意:解码时使用uint32而不是time.Time。
// 如果需要,之后再转换为time.Time。
type MVHD struct {
    Version          byte
    Flags            [3]byte
    DateCreatedSecs  uint32
    DateModifiedSecs uint32

    TimeUnit        uint32 // 每秒的时间单位(默认为600)
    DurationInUnits uint64 // 时间长度(以时间单位计算)
}

// ..或者使用binary.BigEndian - 根据你的数据选择正确的字节序。
var endian = binary.LittleEndian

func decode(rd io.Reader) (*MVHD, error) {
    var header MVHD 
    if err := binary.Read(rd, endian, &header); err != nil {
        return nil, err
    }
    return &header, nil
}      

使用bytes.NewReader[]byte转换为io.Reader。这将允许你将mmap数据与decode一起使用。

或者,你可以手动解码:

func decode2(buf []byte) (*MVHD, error) {
    if len(buf) < 24 {
        return nil, errors.New("数据不足")
    }  
    return &MVHD{
        Version:          buf[0],
        Flags:            [3]byte{buf[1], buf[2], buf[3]},
        DateCreatedSecs:  binary.LittleEndian.Uint32(buf[4:8]),
        DateModifiedSecs: binary.LittleEndian.Uint32(buf[8:12]),
        TimeUnit:         binary.LittleEndian.Uint32(buf[12:16]),
        DurationInUnits:  binary.LittleEndian.Uint64(buf[16:24]),
    }, nil
}

类似地,你可以使用binary.ByteOrderPut方法来原地更新数据:

func updateDateModified(buf []byte, t uint32) error {
    if len(buf) < 12 {
        return errors.New("数据不足")
    }
    binary.LittleEndian.PutUint32(buf[8:12], t)
    return nil
}
英文:

You can use encoding/binary to read/write fixed size structs. This approach is portable and doesn't depend on the memory layout, compiler, or CPU architecture. Eg:

// Note: using uint32 instead of time.Time for decoding.
// Convert to time.Time afterwards if needed.
type MVHD struct {
    Version          byte
    Flags            [3]byte
    DateCreatedSecs  uint32
    DateModifiedSecs uint32

    TimeUnit        uint32 // time unit per second (default = 600)
    DurationInUnits uint64 // time length (in time units)
}

// ..or use binary.BigEndian - whichever is correct for your data.
var endian = binary.LittleEndian

func decode(rd io.Reader) (*MVHD, error) {
    var header MVHD 
    if err := binary.Read(rd, endian, &amp;header); err != nil {
        return nil, err
    }
    return &amp;header, nil
}      

Use bytes.NewReader to convert a []byte into an io.Reader. This will allow you to use decode with mmap data.

Alternatively, you can decode it manually:

func decode2(buf []byte) (*MVHD, error) {
    if len(buf) &lt; 24 {
        return nil, errors.New(&quot;not enough data&quot;)
    }  
    return &amp;MVHD{
        Version:          buf[0],
        Flags:            [3]byte{buf[1], buf[2], buf[3]},
        DateCreatedSecs:  binary.LittleEndian.Uint32(buf[4:8]),
        DateModifiedSecs: binary.LittleEndian.Uint32(buf[8:12]),
        TimeUnit:         binary.LittleEndian.Uint32(buf[12:16]),
        DurationInUnits:  binary.LittleEndian.Uint64(buf[16:24]),
    }, nil
}

Similarly, you can update data in place with binary.ByteOrder Put calls:

func updateDateModified(buf []byte, t uint32) error {
    if len(buf) &lt; 12 {
        return errors.New(&quot;not enough data&quot;)
    }
    binary.LittleEndian.PutUint32(buf[8:12], t)
    return nil
}

huangapple
  • 本文由 发表于 2021年9月20日 05:04:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/69247065.html
匿名

发表评论

匿名网友

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

确定