file_fadvise.go 2.87 KB
Newer Older
“李磊”'s avatar
“李磊” committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package file

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"path/filepath"

	"linkfog.com/public/lib/fadvise"
	"linkfog.com/public/lib/l"
	"linkfog.com/public/option"
)

func FadviseSwitch(f *os.File) {
	if err := fadvise.Switch(f); err != nil {
		l.Error(err)
	}
}

func PurgeSwitch(path string) {
	if !option.Fadvise {
		return
	}
	if err := purge(path); err != nil {
		l.Error(err)
	}
}

func purge(path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	err = fadvise.Switch(f)
	if err != nil {
		return err
	}
	return nil
}

func ReadFileFadvise(name string) ([]byte, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	defer FadviseSwitch(f)
	var size int
	if info, err := f.Stat(); err == nil {
		size64 := info.Size()
		if int64(int(size64)) == size64 {
			size = int(size64)
		}
	}
	size++ // one byte for final read at EOF

	// If a file claims a small size, read at least 512 bytes.
	// In particular, files in Linux's /proc claim size 0 but
	// then do not work right if read in small pieces,
	// so an initial read of 1 byte would not work correctly.
	if size < 512 {
		size = 512
	}

	data := make([]byte, 0, size)
	for {
		if len(data) >= cap(data) {
			d := append(data[:cap(data)], 0)
			data = d[:len(data)]
		}
		n, err := f.Read(data[len(data):cap(data)])
		data = data[:len(data)+n]
		if err != nil {
			if err == io.EOF {
				err = nil
			}
			return data, err
		}
	}
}

// ReadFileIntoBuffer reads an arbitrary file into a buffer.
func ReadFileIntoBufferFadvise(filename string, buf *bytes.Buffer) (int64, error) {
	f, err := os.Open(filename)
	if err != nil {
		return -1, err
	}
	defer f.Close()
	defer FadviseSwitch(f)
	return buf.ReadFrom(f)
}

func ReadFileLineFadvise(filePath string) (line string, err error) {
	if !exist(filePath) {
		return line, l.WrapError("file not exist.")
	}

	fi, err := os.Open(filePath)
	if err != nil {
		return line, l.WrapError("open file err:", err, "filePath:", filePath)
	}
	defer fi.Close()
	defer FadviseSwitch(fi)
	br := bufio.NewReader(fi)
	for {
		a, _, c := br.ReadLine()
		if c == io.EOF {
			break
		}
		return string(a), err
	}
	return line, err
}

func exist(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil || !os.IsNotExist(err)
}

// walk folder to purge file page cache
func WalkPurgeSwitch(path string) {
	if !option.Fadvise {
		return
	}
	if err := walkPurge(path); err != nil {
		l.Error(err)
	}
}

func walkPurge(path string) error {
	return filepath.Walk(path,
		func(path string, info os.FileInfo, err error) error {
			if err != nil {
				l.Error(err)
				return nil
			}
			if !info.Mode().IsRegular() {
				return nil
			}
			err = purge(path)
			if err != nil {
				l.Error(err)
			}
			return nil
		})
}