-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfiles.go
More file actions
192 lines (162 loc) · 4.2 KB
/
files.go
File metadata and controls
192 lines (162 loc) · 4.2 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package env
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"sync"
)
// Optimization: Buffer pool for file operations
var (
bufferPool = sync.Pool{
New: func() interface{} {
buf := make([]byte, 0, 4096) // 4KB initial capacity
return &buf
},
}
)
// Load provides optimized .env file loading with reduced allocations
func Load(filename ...string) error {
var fileToLoad string
if len(filename) > 0 && filename[0] != "" {
fileToLoad = filename[0]
} else {
fileToLoad = ".env"
}
file, err := os.Open(fileToLoad)
if err != nil {
if os.IsNotExist(err) && fileToLoad == ".env" && (len(filename) == 0 || filename[0] == "") {
// If .env doesn't exist and it was the default, don't return an error.
return nil
}
return err
}
defer func() { _ = file.Close() }()
// Get file size for optimal buffer allocation
stat, err := file.Stat()
if err != nil {
return err
}
// Use buffered reading with optimal buffer size
bufSize := int(stat.Size())
if bufSize > 64*1024 { // Cap at 64KB
bufSize = 64 * 1024
} else if bufSize < 1024 { // Minimum 1KB
bufSize = 1024
}
reader := bufio.NewReaderSize(file, bufSize)
return parseEnvContent(reader)
}
// parseEnvContentOptimized efficiently parses .env content with minimal allocations
func parseEnvContent(reader *bufio.Reader) error {
// Get buffer from pool
bufPtr := bufferPool.Get().(*[]byte)
buf := *bufPtr
defer func() {
if cap(buf) <= 8192 { // Only reuse if not too large
*bufPtr = buf[:0] // Reset length but keep capacity
bufferPool.Put(bufPtr)
}
}()
for {
line, err := reader.ReadBytes('\n')
if err != nil && err != io.EOF {
return err
}
// Process line efficiently
if len(line) > 0 {
// Remove trailing newline
if line[len(line)-1] == '\n' {
line = line[:len(line)-1]
}
// Remove trailing carriage return (Windows)
if len(line) > 0 && line[len(line)-1] == '\r' {
line = line[:len(line)-1]
}
if err := processLine(line); err != nil {
return err
}
}
if err == io.EOF {
break
}
}
return nil
}
// processLineOptimized processes a single line with minimal allocations
func processLine(line []byte) error {
// Trim leading and trailing whitespace in-place
line = bytes.TrimSpace(line)
// Skip empty lines and comments
if len(line) == 0 || line[0] == '#' {
return nil
}
// Find equals sign efficiently
equalsIndex := bytes.IndexByte(line, '=')
if equalsIndex == -1 {
return nil // Skip invalid lines
}
// Extract key and value with minimal allocations
keyBytes := bytes.TrimSpace(line[:equalsIndex])
valueBytes := bytes.TrimSpace(line[equalsIndex+1:])
if len(keyBytes) == 0 {
return nil // Skip if no key
}
// Convert to strings only when necessary
key := string(keyBytes)
value := string(valueBytes)
// Remove quotes efficiently
if len(value) >= 2 {
if (value[0] == '"' && value[len(value)-1] == '"') ||
(value[0] == '\'' && value[len(value)-1] == '\'') {
value = value[1 : len(value)-1]
}
}
// Only set if not already set (preserves environment precedence)
if os.Getenv(key) == "" {
return os.Setenv(key, value)
}
return nil
}
// SaveOptimized provides optimized environment variable saving
func Save(filename string, keys []string) error {
if len(keys) == 0 {
return errors.New("no keys specified to save")
}
// Pre-allocate buffer for writing
var buf strings.Builder
buf.Grow(len(keys) * 50) // Estimate 50 chars per line
for _, key := range keys {
if value, exists := os.LookupEnv(key); exists {
// Escape value if it contains spaces or special characters
if needsQuoting(value) {
value = fmt.Sprintf(`"%s"`, strings.ReplaceAll(value, `"`, `\"`))
}
buf.WriteString(key)
buf.WriteByte('=')
buf.WriteString(value)
buf.WriteByte('\n')
}
}
// Write atomically
return os.WriteFile(filename, []byte(buf.String()), 0644)
}
// needsQuoting efficiently checks if a value needs quoting
func needsQuoting(value string) bool {
for i := 0; i < len(value); i++ {
switch value[i] {
case ' ', '\t', '\n', '\r', '"', '\'':
return true
}
}
return false
}
// MustLoadOptimized is the optimized version of MustLoad
func MustLoad(filename ...string) {
if err := Load(filename...); err != nil {
panic(err)
}
}