Similar to 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)
CodePudding user response:
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, &header); err != nil {
return nil, err
}
return &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) < 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
}
Similarly, you can update data in place with binary.ByteOrder
Put
calls:
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
}