Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion dotenv/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ func (p *parser) locateKeyName(src string) (string, string, bool, error) {
var key string
var inherited bool
// trim "export" and space at beginning
src = strings.TrimLeftFunc(exportRegex.ReplaceAllString(src, ""), isSpace)
if exportRegex.MatchString(src) {
// we use a `strings.trim` to preserve the pointer to the same underlying memory.
// a regexp replace would copy the string.
src = strings.TrimLeftFunc(strings.TrimPrefix(src, "export"), isSpace)
}

// locate key name end and validate it in single loop
offset := 0
Expand Down
24 changes: 24 additions & 0 deletions dotenv/parser_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dotenv

import (
"fmt"
"runtime"
"strings"
"testing"

"gotest.tools/v3/assert"
Expand Down Expand Up @@ -38,3 +41,24 @@ func TestParseVariable(t *testing.T) {
assert.Error(t, err, "line 1: unexpected character \"%\" in variable name \"%!(EXTRA string)=foo\"")

}

func TestMemoryExplosion(t *testing.T) {
p := newParser()
var startMemStats runtime.MemStats
var endMemStats runtime.MemStats
runtime.ReadMemStats(&startMemStats)

size := 1000
input := []string{}
for i := 0; i < size; i++ {
input = append(input, fmt.Sprintf("KEY%d=VALUE%d", i, i))
}
out := map[string]string{}
err := p.parse(strings.Join(input, "\n"), out, nil)
assert.NilError(t, err)
assert.Equal(t, size, len(out))
runtime.ReadMemStats(&endMemStats)
assert.Assert(t, endMemStats.Alloc-startMemStats.Alloc < uint64(size)*1000, /* assume 1K per line */
"memory usage should be linear with input size. Memory grew by: %d",
endMemStats.Alloc-startMemStats.Alloc)
}