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  }