file.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * Copyright 2020 Dgraph Labs, Inc. and Contributors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package z
  17. import (
  18. "encoding/binary"
  19. "fmt"
  20. "io"
  21. "os"
  22. "path/filepath"
  23. "github.com/pkg/errors"
  24. )
  25. // MmapFile represents an mmapd file and includes both the buffer to the data
  26. // and the file descriptor.
  27. type MmapFile struct {
  28. Data []byte
  29. Fd *os.File
  30. }
  31. var NewFile = errors.New("Create a new file")
  32. func OpenMmapFileUsing(fd *os.File, sz int, writable bool) (*MmapFile, error) {
  33. filename := fd.Name()
  34. fi, err := fd.Stat()
  35. if err != nil {
  36. return nil, errors.Wrapf(err, "cannot stat file: %s", filename)
  37. }
  38. var rerr error
  39. fileSize := fi.Size()
  40. if sz > 0 && fileSize == 0 {
  41. // If file is empty, truncate it to sz.
  42. if err := fd.Truncate(int64(sz)); err != nil {
  43. return nil, errors.Wrapf(err, "error while truncation")
  44. }
  45. fileSize = int64(sz)
  46. rerr = NewFile
  47. }
  48. // fmt.Printf("Mmaping file: %s with writable: %v filesize: %d\n", fd.Name(), writable, fileSize)
  49. buf, err := Mmap(fd, writable, fileSize) // Mmap up to file size.
  50. if err != nil {
  51. return nil, errors.Wrapf(err, "while mmapping %s with size: %d", fd.Name(), fileSize)
  52. }
  53. if fileSize == 0 {
  54. dir, _ := filepath.Split(filename)
  55. if err := SyncDir(dir); err != nil {
  56. return nil, err
  57. }
  58. }
  59. return &MmapFile{
  60. Data: buf,
  61. Fd: fd,
  62. }, rerr
  63. }
  64. // OpenMmapFile opens an existing file or creates a new file. If the file is
  65. // created, it would truncate the file to maxSz. In both cases, it would mmap
  66. // the file to maxSz and returned it. In case the file is created, z.NewFile is
  67. // returned.
  68. func OpenMmapFile(filename string, flag int, maxSz int) (*MmapFile, error) {
  69. // fmt.Printf("opening file %s with flag: %v\n", filename, flag)
  70. fd, err := os.OpenFile(filename, flag, 0666)
  71. if err != nil {
  72. return nil, errors.Wrapf(err, "unable to open: %s", filename)
  73. }
  74. writable := true
  75. if flag == os.O_RDONLY {
  76. writable = false
  77. }
  78. return OpenMmapFileUsing(fd, maxSz, writable)
  79. }
  80. type mmapReader struct {
  81. Data []byte
  82. offset int
  83. }
  84. func (mr *mmapReader) Read(buf []byte) (int, error) {
  85. if mr.offset > len(mr.Data) {
  86. return 0, io.EOF
  87. }
  88. n := copy(buf, mr.Data[mr.offset:])
  89. mr.offset += n
  90. if n < len(buf) {
  91. return n, io.EOF
  92. }
  93. return n, nil
  94. }
  95. func (m *MmapFile) NewReader(offset int) io.Reader {
  96. return &mmapReader{
  97. Data: m.Data,
  98. offset: offset,
  99. }
  100. }
  101. // Bytes returns data starting from offset off of size sz. If there's not enough data, it would
  102. // return nil slice and io.EOF.
  103. func (m *MmapFile) Bytes(off, sz int) ([]byte, error) {
  104. if len(m.Data[off:]) < sz {
  105. return nil, io.EOF
  106. }
  107. return m.Data[off : off+sz], nil
  108. }
  109. // Slice returns the slice at the given offset.
  110. func (m *MmapFile) Slice(offset int) []byte {
  111. sz := binary.BigEndian.Uint32(m.Data[offset:])
  112. start := offset + 4
  113. next := start + int(sz)
  114. if next > len(m.Data) {
  115. return []byte{}
  116. }
  117. res := m.Data[start:next]
  118. return res
  119. }
  120. // AllocateSlice allocates a slice of the given size at the given offset.
  121. func (m *MmapFile) AllocateSlice(sz, offset int) ([]byte, int, error) {
  122. start := offset + 4
  123. // If the file is too small, double its size or increase it by 1GB, whichever is smaller.
  124. if start+sz > len(m.Data) {
  125. const oneGB = 1 << 30
  126. growBy := len(m.Data)
  127. if growBy > oneGB {
  128. growBy = oneGB
  129. }
  130. if growBy < sz+4 {
  131. growBy = sz + 4
  132. }
  133. if err := m.Truncate(int64(len(m.Data) + growBy)); err != nil {
  134. return nil, 0, err
  135. }
  136. }
  137. binary.BigEndian.PutUint32(m.Data[offset:], uint32(sz))
  138. return m.Data[start : start+sz], start + sz, nil
  139. }
  140. func (m *MmapFile) Sync() error {
  141. if m == nil {
  142. return nil
  143. }
  144. return Msync(m.Data)
  145. }
  146. func (m *MmapFile) Delete() error {
  147. // Badger can set the m.Data directly, without setting any Fd. In that case, this should be a
  148. // NOOP.
  149. if m.Fd == nil {
  150. return nil
  151. }
  152. if err := Munmap(m.Data); err != nil {
  153. return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err)
  154. }
  155. m.Data = nil
  156. if err := m.Fd.Truncate(0); err != nil {
  157. return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err)
  158. }
  159. if err := m.Fd.Close(); err != nil {
  160. return fmt.Errorf("while close file: %s, error: %v\n", m.Fd.Name(), err)
  161. }
  162. return os.Remove(m.Fd.Name())
  163. }
  164. // Close would close the file. It would also truncate the file if maxSz >= 0.
  165. func (m *MmapFile) Close(maxSz int64) error {
  166. // Badger can set the m.Data directly, without setting any Fd. In that case, this should be a
  167. // NOOP.
  168. if m.Fd == nil {
  169. return nil
  170. }
  171. if err := m.Sync(); err != nil {
  172. return fmt.Errorf("while sync file: %s, error: %v\n", m.Fd.Name(), err)
  173. }
  174. if err := Munmap(m.Data); err != nil {
  175. return fmt.Errorf("while munmap file: %s, error: %v\n", m.Fd.Name(), err)
  176. }
  177. if maxSz >= 0 {
  178. if err := m.Fd.Truncate(maxSz); err != nil {
  179. return fmt.Errorf("while truncate file: %s, error: %v\n", m.Fd.Name(), err)
  180. }
  181. }
  182. return m.Fd.Close()
  183. }
  184. func SyncDir(dir string) error {
  185. df, err := os.Open(dir)
  186. if err != nil {
  187. return errors.Wrapf(err, "while opening %s", dir)
  188. }
  189. if err := df.Sync(); err != nil {
  190. return errors.Wrapf(err, "while syncing %s", dir)
  191. }
  192. if err := df.Close(); err != nil {
  193. return errors.Wrapf(err, "while closing %s", dir)
  194. }
  195. return nil
  196. }