// Open a data file that grows by the specified size. func OpenDataFile(path string, growth int) (file *DataFile, err error) { file = &DataFile{Path: path, Growth: growth} if file.Fh, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600); err != nil { return } var size int64 if size, err = file.Fh.Seek(0, os.SEEK_END); err != nil { return } // Ensure the file is not smaller than file growth if file.Size = int(size); file.Size < file.Growth { if err = file.EnsureSize(growth); err != nil { return } } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } for i := file.Size - 1; i >= 0; i-- { if file.Buf[i] != 0 { file.Used = i + 1 break } } tdlog.Infof("%s opened: %d of %d bytes in-use", path, file.Used, file.Size) return }
// Clear the entire file and resize it to initial size. func (file *DataFile) Clear() (err error) { if err = file.Buf.Unmap(); err != nil { return } else if err = os.Truncate(file.Path, 0); err != nil { return } else if err = os.Truncate(file.Path, int64(file.Growth)); err != nil { return } else if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } file.Used, file.Size = 0, file.Growth tdlog.Infof("%s cleared: %d of %d bytes in-use", file.Path, file.Used, file.Size) return }
// Clear the entire file and resize it to initial size. func (file *DataFile) Clear() (err error) { if err = file.Close(); err != nil { return } if err = os.Truncate(file.Path, 0); err != nil { return } if err = os.Truncate(file.Path, int64(file.Growth)); err != nil { return } if err = file.Reopen(); err != nil { return } tdlog.Infof("%s cleared: %d of %d bytes in-use", file.Path, file.Used, file.Size) return }
// Clear the entire file and resize it to initial size. func (file *DataFile) Clear() (err error) { if err = file.Close(); err != nil { return } else if err = os.Truncate(file.Path, 0); err != nil { return } else if file.Fh, err = os.OpenFile(file.Path, os.O_CREATE|os.O_RDWR, 0600); err != nil { return } else if err = file.overwriteWithZero(0, file.Growth); err != nil { return } else if file.Buf, err = gommap.Map(file.Fh); err != nil { return } file.Used, file.Size = 0, file.Growth tdlog.Infof("%s cleared: %d of %d bytes in-use", file.Path, file.Used, file.Size) return }
// Ensure there is enough room for that many bytes of data. func (file *DataFile) EnsureSize(more int) (err error) { if file.Used+more <= file.Size { return } else if file.Buf != nil { if err = file.Buf.Unmap(); err != nil { return } } if err = file.overwriteWithZero(file.Size, file.Growth); err != nil { return } else if file.Buf, err = gommap.Map(file.Fh); err != nil { return } file.Size += file.Growth tdlog.Infof("%s grown: %d -> %d bytes (%d bytes in-use)", file.Path, file.Size-file.Growth, file.Size, file.Used) return file.EnsureSize(more) }
// Follow the longest bucket chain to calculate total number of buckets, hence the "used size" of hash table file. func (ht *HashTable) calculateNumBuckets() { ht.numBuckets = ht.Size / BUCKET_SIZE largestBucketNum := INITIAL_BUCKETS - 1 for i := 0; i < INITIAL_BUCKETS; i++ { lastBucket := ht.lastBucket(i) if lastBucket > largestBucketNum && lastBucket < ht.numBuckets { largestBucketNum = lastBucket } } ht.numBuckets = largestBucketNum + 1 usedSize := ht.numBuckets * BUCKET_SIZE if usedSize > ht.Size { ht.Used = ht.Size ht.EnsureSize(usedSize - ht.Used) } ht.Used = usedSize tdlog.Infof("%s: calculated used size is %d", ht.Path, usedSize) }
// Ensure there is enough room for that many bytes of data. func (file *DataFile) EnsureSize(more int) (err error) { if file.Used+more <= file.Size { return } if file.Buf != nil { if err = file.Buf.Unmap(); err != nil { return } } if err = os.Truncate(file.Path, int64(file.Size+file.Growth)); err != nil { return } else if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } file.Size += file.Growth tdlog.Infof("%s grown: %d -> %d bytes (%d bytes in-use)", file.Path, file.Size-file.Growth, file.Size, file.Used) return file.EnsureSize(more) }
// Open a data file that grows by the specified size. func OpenDataFile(path string, growth int) (file *DataFile, err error) { file = &DataFile{Path: path, Growth: growth} if file.Fh, err = os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600); err != nil { return } var size int64 if size, err = file.Fh.Seek(0, os.SEEK_END); err != nil { return } // Ensure the file is not smaller than file growth if file.Size = int(size); file.Size < file.Growth { if err = file.EnsureSize(growth); err != nil { return } } if file.Buf, err = gommap.Map(file.Fh, gommap.RDWR, 0); err != nil { return } // Bi-sect file buffer to find out how much space is in-use for low, mid, high := 0, file.Size/2, file.Size; ; { switch { case high-mid == 1: if LooksEmpty(file.Buf[mid:]) { if mid > 0 && LooksEmpty(file.Buf[mid-1:]) { file.Used = mid - 1 } else { file.Used = mid } return } file.Used = high return case LooksEmpty(file.Buf[mid:]): high = mid mid = low + (mid-low)/2 default: low = mid mid = mid + (high-mid)/2 } } tdlog.Infof("%s opened: %d of %d bytes in-use", path, file.Used, file.Size) return }