// Ensure the file ahs room for more data. func (file *File) Ensure(more uint64) (err error) { if file.Append+more <= file.Size { return } if file.Buf != nil { if err = file.Buf.Unmap(); err != nil { return } } if _, err = file.Fh.Seek(0, os.SEEK_END); err != nil { return } if _, err = file.Fh.Write(make([]byte, file.Growth)); err != nil { return } if err = file.Fh.Sync(); err != nil { return } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } file.Size += file.Growth log.Printf("File %s has grown %d bytes\n", file.Name, file.Growth) return file.Ensure(more) }
// Open (create if non-exist) the file. func Open(name string, growth uint64) (file *File, err error) { if growth < 1 { err = errors.New(fmt.Sprintf("Opening %s, file growth (%d) is too small", name, growth)) } file = &File{Name: name, Growth: growth} if file.Fh, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0600); err != nil { return } fsize, err := file.Fh.Seek(0, os.SEEK_END) if err != nil { return } file.Size = uint64(fsize) if file.Size == 0 { file.CheckSizeAndEnsure(file.Growth) return } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } // find append position for pos := file.Size - 1; pos > 0; pos-- { if file.Buf[pos] != 0 { file.Append = pos + 1 break } } return }
// Open (create if non-exist) the file. func Open(name string, growth uint64) (file *File, err error) { if growth < 1 { err = errors.New(fmt.Sprintf("Opening %s, file growth (%d) is too small", name, growth)) } file = &File{Name: name, Growth: growth, Sync: new(sync.RWMutex)} if file.Fh, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0600); err != nil { return } fsize, err := file.Fh.Seek(0, os.SEEK_END) if err != nil { return } if int(fsize) < 0 { log.Panicf("File %s is too large to mmap", name) } file.Size = uint64(fsize) if file.Size == 0 { return file, file.Ensure(file.Growth) } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } // find append position for low, mid, high := uint64(0), file.Size/2, file.Size; ; { switch { case high-mid == 1: if file.Buf[mid] == 0 { if file.Buf[mid-1] == 0 { file.Append = mid - 1 } else { file.Append = mid } return } file.Append = high return case file.Buf[mid] == 0: high = mid mid = low + (mid-low)/2 default: low = mid mid = mid + (high-mid)/2 } } return }
// Open (create if non-exist) the file. func Open(name string, growth uint64) (file *File, err error) { if growth < 1 { err = errors.New(fmt.Sprintf("Growth size (%d) is too small (opening %s)", growth, name)) } file = &File{Name: name, Growth: growth} if file.Fh, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0600); err != nil { return } fsize, err := file.Fh.Seek(0, os.SEEK_END) if err != nil { return } file.Size = uint64(fsize) if file.Size == 0 { file.CheckSizeAndEnsure(file.Growth) return } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } // find used size for low, mid, high := uint64(0), file.Size/2, file.Size; ; { switch { case high-mid == 1: if file.Buf[mid] == 0 { if file.Buf[mid-1] == 0 { file.UsedSize = mid - 1 } else { file.UsedSize = mid } return } file.UsedSize = high return case file.Buf[mid] == 0: high = mid mid = low + (mid-low)/2 default: low = mid mid = mid + (high-mid)/2 } } log.Printf("%s has %d bytes out of %d bytes in-use", name, file.UsedSize, file.Size) return }
// Ensure the file ahs room for more data. func (file *File) CheckSizeAndEnsure(more uint64) { if file.UsedSize+more <= file.Size { return } var err error if file.Buf != nil { if err = file.Buf.Unmap(); err != nil { panic(err) } } if _, err = file.Fh.Seek(0, os.SEEK_END); err != nil { panic(err) } // grow the file incrementally zeroBuf := make([]byte, FILE_GROWTH_INCREMENTAL) for i := uint64(0); i < file.Growth; i += FILE_GROWTH_INCREMENTAL { var slice []byte if i+FILE_GROWTH_INCREMENTAL > file.Growth { slice = zeroBuf[0 : i+FILE_GROWTH_INCREMENTAL-file.Growth] } else { slice = zeroBuf } if _, err = file.Fh.Write(slice); err != nil { panic(err) } } if err = file.Fh.Sync(); err != nil { panic(err) } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { panic(err) } file.Size += file.Growth log.Printf("File %s has grown %d bytes\n", file.Name, file.Growth) file.CheckSizeAndEnsure(more) }