pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/directio/directio.go (about) 1 //go:build !windows 2 // +build !windows 3 4 // Package directio provides methods for reading/writing files with direct io 5 package directio 6 7 // ////////////////////////////////////////////////////////////////////////////////// // 8 // // 9 // Copyright (c) 2022 ESSENTIAL KAOS // 10 // Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> // 11 // // 12 // ////////////////////////////////////////////////////////////////////////////////// // 13 14 import ( 15 "io" 16 "os" 17 "sync" 18 "unsafe" 19 ) 20 21 // ////////////////////////////////////////////////////////////////////////////////// // 22 23 var blockPool = sync.Pool{ 24 New: func() interface{} { 25 return make([]byte, BLOCK_SIZE+ALIGN_SIZE) 26 }, 27 } 28 29 // ////////////////////////////////////////////////////////////////////////////////// // 30 31 // ReadFile read file with Direct IO without buffering data in page cache 32 func ReadFile(file string) ([]byte, error) { 33 fd, err := openFile(file, os.O_RDONLY, 0) 34 35 if err != nil { 36 return nil, err 37 } 38 39 defer fd.Close() 40 41 info, err := fd.Stat() 42 43 if err != nil { 44 return nil, err 45 } 46 47 return readData(fd, info) 48 } 49 50 // WriteFile write file with Direct IO without buffering data in page cache 51 func WriteFile(file string, data []byte, perms os.FileMode) error { 52 fd, err := openFile(file, os.O_CREATE|os.O_WRONLY, perms) 53 54 if err != nil { 55 return err 56 } 57 58 defer fd.Close() 59 60 return writeData(fd, data) 61 } 62 63 // ////////////////////////////////////////////////////////////////////////////////// // 64 65 func readData(fd *os.File, info os.FileInfo) ([]byte, error) { 66 var buf []byte 67 68 block := allocateBlock() 69 blockSize := len(block) 70 chunks := (int(info.Size()) / blockSize) + 1 71 72 for i := 0; i < chunks; i++ { 73 n, err := fd.ReadAt(block, int64(i*blockSize)) 74 75 if err != nil && err != io.EOF { 76 return nil, err 77 } 78 79 buf = append(buf, block[:n]...) 80 } 81 82 freeBlock(block) 83 84 return buf, nil 85 } 86 87 func writeData(fd *os.File, data []byte) error { 88 block := allocateBlock() 89 blockSize := len(block) 90 dataSize := len(data) 91 pointer := 0 92 93 for { 94 if pointer+blockSize >= dataSize { 95 copy(block, data[pointer:]) 96 } else { 97 copy(block, data[pointer:pointer+blockSize]) 98 } 99 100 _, err := fd.Write(block) 101 102 if err != nil { 103 return err 104 } 105 106 pointer += blockSize 107 108 if pointer >= dataSize { 109 break 110 } 111 } 112 113 freeBlock(block) 114 115 return fd.Truncate(int64(dataSize)) 116 } 117 118 func allocateBlock() []byte { 119 block := blockPool.Get().([]byte) 120 121 if ALIGN_SIZE == 0 { 122 return block 123 } 124 125 var offset int 126 127 alg := alignment(block, ALIGN_SIZE) 128 129 if alg != 0 { 130 offset = ALIGN_SIZE - alg 131 } 132 133 return block[offset : offset+BLOCK_SIZE] 134 } 135 136 func freeBlock(block []byte) { 137 blockPool.Put(block) 138 } 139 140 func alignment(block []byte, alignment int) int { 141 return int(uintptr(unsafe.Pointer(&block[0])) & uintptr(alignment-1)) 142 }