Home > Software design >  Updating mmap file with struct in Go
Updating mmap file with struct in Go

Time:09-24

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
}
  • Related