在 Go 中使用结构更新 mmap 文件

Updating mmap file with struct in Go

类似于Writing struct to mapped memory file (mmap),如何在Go中将struct写入mmap文件或使用struct更新mmap文件?

假设我的二进制文件以

的二进制文件头开头
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
}

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

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

您可以使用 encoding/binary 到 read/write 固定大小的结构。这种方法是可移植的,不依赖于内存布局、编译器或 CPU 体系结构。例如:

// 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, &header); err != nil {
        return nil, err
    }
    return &header, nil
}      

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

或者,您可以手动解码:

func decode2(buf []byte) (*MVHD, error) {
    if len(buf) < 24 {
        return nil, errors.New("not enough data")
    }  
    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.ByteOrder Put 调用就地更新数据:

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