diff --git a/.benchspotter/.gitignore b/.benchspotter/.gitignore new file mode 100644 index 0000000..371b39b --- /dev/null +++ b/.benchspotter/.gitignore @@ -0,0 +1 @@ +/recall/ \ No newline at end of file diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkConfig.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkConfig.profile" new file mode 100644 index 0000000..0901a3a Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkConfig.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" new file mode 100644 index 0000000..eb1016b Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" new file mode 100644 index 0000000..4121ab7 Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" new file mode 100644 index 0000000..b9f641e Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" new file mode 100644 index 0000000..78d1b1d Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkFile.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkFile.profile" new file mode 100644 index 0000000..86bbd5d Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkFile.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkParse.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkParse.profile" new file mode 100644 index 0000000..1b9a9be Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/cpu/\341\232\227\341\232\227BenchmarkParse.profile" differ diff --git a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/escape.txt b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/escape.txt new file mode 100644 index 0000000..affdbeb --- /dev/null +++ b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/escape.txt @@ -0,0 +1,4511 @@ +# github.com/git-bug/gitconfig +:1: cannot inline type..eq.SI: marked go:noinline +:1: cannot inline type..eq.SM8S: marked go:noinline +:1: cannot inline type..eq.SSM1K7SSM1K7M8SM8SI: marked go:noinline +:1: cannot inline type..eq.SSM1K7SSM1K7M8SM8: marked go:noinline +:1: cannot inline type..eq.SSM1K7S: marked go:noinline +:1: cannot inline type..eq.SSM1: marked go:noinline +:1: cannot inline type..eq.M8SM8: marked go:noinline +:1: inlining call to Key.String +:1: inlining call to Key.sectionKey +:1: inlining call to Entry.error +:1: inlining call to osEnviron.Lookup +:1: inlining call to Env.UpdateFile +:1: inlining call to (*parsed).entries +:1: inlining call to (*parsed).nameOf +:1: inlining call to (*parsed).secOf +:1: inlining call to (*parsed).valueOf +:1: inlining call to flatEntry.is +:1: inlining call to Key.sectionKey +:1: inlining call to (*parsed).entries +:1: inlining call to (*parsed).nameOf +:1: inlining call to (*parsed).secOf +:1: inlining call to (*parsed).valueOf +./config.go:39:26: append(c.index[e.Key], i) escapes to heap in newConfig: +./config.go:39:26: flow: {heap} ← &{storage for append(c.index[e.Key], i)}: +./config.go:39:26: from append(c.index[e.Key], i) (spill) at ./config.go:39:26 +./config.go:39:26: from c.index[e.Key] = append(c.index[e.Key], i) (assign) at ./config.go:39:18 +./config.go:42:38: append(c.subsections[sk.section], sk.subsection) escapes to heap in newConfig: +./config.go:42:38: flow: {heap} ← &{storage for append(c.subsections[sk.section], sk.subsection)}: +./config.go:42:38: from append(c.subsections[sk.section], sk.subsection) (spill) at ./config.go:42:38 +./config.go:42:38: from c.subsections[sk.section] = append(c.subsections[sk.section], sk.subsection) (assign) at ./config.go:42:30 +./config.go:31:16: parameter entries leaks to {heap} for newConfig with derefs=1: +./config.go:31:16: flow: {temp} ← entries: +./config.go:31:16: flow: e ← *{temp}: +./config.go:31:16: from for loop (range-deref) at ./config.go:38:14 +./config.go:31:16: flow: {heap} ← e: +./config.go:31:16: from e.Key (dot) at ./config.go:39:12 +./config.go:31:16: from c.index[e.Key] (key of map put) at ./config.go:39:10 +./config.go:32:7: &Config{...} escapes to heap in newConfig: +./config.go:32:7: flow: c ← &{storage for &Config{...}}: +./config.go:32:7: from &Config{...} (spill) at ./config.go:32:7 +./config.go:32:7: from c := &Config{...} (assign) at ./config.go:32:4 +./config.go:32:7: flow: ~r0 ← c: +./config.go:32:7: from return c (return) at ./config.go:45:2 +./config.go:31:16: parameter entries leaks to {storage for &Config{...}} for newConfig with derefs=0: +./config.go:31:16: flow: {storage for &Config{...}} ← entries: +./config.go:31:16: from Config{...} (struct literal element) at ./config.go:32:14 +./config.go:34:20: make(map[Key][]int) escapes to heap in newConfig: +./config.go:34:20: flow: {storage for &Config{...}} ← &{storage for make(map[Key][]int)}: +./config.go:34:20: from make(map[Key][]int) (spill) at ./config.go:34:20 +./config.go:34:20: from Config{...} (struct literal element) at ./config.go:32:14 +./config.go:35:20: make(map[string][]string) escapes to heap in newConfig: +./config.go:35:20: flow: {storage for &Config{...}} ← &{storage for make(map[string][]string)}: +./config.go:35:20: from make(map[string][]string) (spill) at ./config.go:35:20 +./config.go:35:20: from Config{...} (struct literal element) at ./config.go:32:14 +./config.go:31:16: leaking param: entries +./config.go:32:7: &Config{...} escapes to heap +./config.go:34:20: make(map[Key][]int) escapes to heap +./config.go:35:20: make(map[string][]string) escapes to heap +./config.go:37:14: make(map[sectionKey]bool) does not escape +./config.go:39:26: append escapes to heap +./config.go:42:38: append escapes to heap +./key.go:46:24: string(b) escapes to heap in parseKey: +./key.go:46:24: flow: ~r0 ← &{storage for string(b)}: +./key.go:46:24: from string(b) (spill) at ./key.go:46:24 +./key.go:46:24: from ~r0 = string(b) (assign-pair) at ./key.go:46:24 +./key.go:40:25: string(b) escapes to heap in parseKey: +./key.go:40:25: flow: ~r0 ← &{storage for string(b)}: +./key.go:40:25: from string(b) (spill) at ./key.go:40:25 +./key.go:40:25: from ~r0 = string(b) (assign-pair) at ./key.go:40:25 +./key.go:35:27: string(b) escapes to heap in parseKey: +./key.go:35:27: flow: ~r0 ← &{storage for string(b)}: +./key.go:35:27: from string(b) (spill) at ./key.go:35:27 +./key.go:35:27: from ~r0 = string(b) (assign-pair) at ./key.go:35:27 +./key.go:25:15: parameter s leaks to ~r0 for parseKey with derefs=0: +./key.go:25:15: flow: k ← s: +./key.go:25:15: from s[first + 1:last] (slice) at ./key.go:47:18 +./key.go:25:15: from k.Subsection = s[first + 1:last] (assign) at ./key.go:47:15 +./key.go:25:15: flow: ~r0 ← k: +./key.go:25:15: from return k, true (return) at ./key.go:41:3 +./key.go:25:15: leaking param: s to result ~r0 level=0 +./key.go:35:27: ([]byte)(s) does not escape +./key.go:35:27: string(b) escapes to heap +./key.go:40:25: ([]byte)(s) does not escape +./key.go:40:25: string(b) escapes to heap +./key.go:46:24: ([]byte)(s) does not escape +./key.go:46:24: string(b) escapes to heap +./config.go:49:7: c does not escape +./config.go:49:25: key does not escape +./config.go:61:7: (*Config).filter capturing by value: c (addr=false assign=false width=8) +./config.go:61:25: (*Config).filter capturing by value: sk (addr=false assign=false width=40) +./config.go:61:7: parameter c leaks to {heap} for (*Config).filter with derefs=2: +./config.go:61:7: flow: {temp} ← *c: +./config.go:61:7: from c.entries (dot of pointer) at ./config.go:66:22 +./config.go:61:7: flow: e ← *{temp}: +./config.go:61:7: from for loop (range-deref) at ./config.go:66:15 +./config.go:61:7: flow: {heap} ← e: +./config.go:61:7: from yield(e) (call parameter) at ./config.go:67:41 +./config.go:62:9: func literal escapes to heap in (*Config).filter: +./config.go:62:9: flow: ~r0 ← &{storage for func literal}: +./config.go:62:9: from func literal (spill) at ./config.go:62:9 +./config.go:62:9: from return func literal (return) at ./config.go:62:2 +./config.go:61:7: parameter c leaks to {storage for func literal} for (*Config).filter with derefs=0: +./config.go:61:7: flow: {storage for func literal} ← c: +./config.go:61:7: from c (captured by a closure) at ./config.go:63:6 +./config.go:61:25: parameter sk leaks to {storage for func literal} for (*Config).filter with derefs=0: +./config.go:61:25: flow: {storage for func literal} ← sk: +./config.go:61:25: from sk (captured by a closure) at ./config.go:67:29 +./config.go:61:7: leaking param: c +./config.go:61:25: leaking param: sk +./config.go:62:14: yield does not escape +./config.go:62:9: func literal escapes to heap +./config.go:75:7: parameter c leaks to ~r0 for (*Config).Get with derefs=2: +./config.go:75:7: flow: ~r0 ← **c: +./config.go:75:7: from c.entries (dot of pointer) at ./config.go:80:10 +./config.go:75:7: from c.entries[idx[len(idx) - 1]] (dot of pointer) at ./config.go:80:18 +./config.go:75:7: from return c.entries[idx[len(idx) - 1]], true (return) at ./config.go:80:2 +./config.go:75:7: leaking param: c to result ~r0 level=2 +./config.go:75:22: key does not escape +./config.go:85:7: parameter c leaks to {heap} for (*Config).GetAll with derefs=2: +./config.go:85:7: flow: {heap} ← **c: +./config.go:85:7: from c.entries (dot of pointer) at ./config.go:92:13 +./config.go:85:7: from c.entries[j] (dot of pointer) at ./config.go:92:21 +./config.go:85:7: from out[i] = c.entries[j] (assign) at ./config.go:92:10 +./config.go:90:13: make([]Entry, len(idx)) escapes to heap in (*Config).GetAll: +./config.go:90:13: flow: out ← &{storage for make([]Entry, len(idx))}: +./config.go:90:13: from make([]Entry, len(idx)) (spill) at ./config.go:90:13 +./config.go:90:13: from out := make([]Entry, len(idx)) (assign) at ./config.go:90:6 +./config.go:90:13: flow: ~r0 ← out: +./config.go:90:13: from return out (return) at ./config.go:94:2 +./config.go:85:7: leaking param content: c +./config.go:85:25: key does not escape +./config.go:90:13: make([]Entry, len(idx)) escapes to heap +./config.go:98:7: (*Config).All capturing by value: c (addr=false assign=false width=8) +./config.go:98:7: parameter c leaks to {heap} for (*Config).All with derefs=2: +./config.go:98:7: flow: {temp} ← *c: +./config.go:98:7: from c.entries (dot of pointer) at ./config.go:103:22 +./config.go:98:7: flow: e ← *{temp}: +./config.go:98:7: from for loop (range-deref) at ./config.go:103:15 +./config.go:98:7: flow: {heap} ← e: +./config.go:98:7: from yield(e) (call parameter) at ./config.go:104:13 +./config.go:99:9: func literal escapes to heap in (*Config).All: +./config.go:99:9: flow: ~r0 ← &{storage for func literal}: +./config.go:99:9: from func literal (spill) at ./config.go:99:9 +./config.go:99:9: from return func literal (return) at ./config.go:99:2 +./config.go:98:7: parameter c leaks to {storage for func literal} for (*Config).All with derefs=0: +./config.go:98:7: flow: {storage for func literal} ← c: +./config.go:98:7: from c (captured by a closure) at ./config.go:100:6 +./config.go:98:7: leaking param: c +./config.go:99:14: yield does not escape +./config.go:99:9: func literal escapes to heap +./key.go:69:7: parameter k leaks to ~r0 for Key.sectionKey with derefs=0: +./key.go:69:7: flow: ~r0 ← k: +./key.go:69:7: from k.Section (dot) at ./key.go:70:21 +./key.go:69:7: from sectionKey{...} (struct literal element) at ./key.go:70:19 +./key.go:69:7: from return sectionKey{...} (return) at ./key.go:70:2 +./key.go:69:7: leaking param: k to result ~r0 level=0 +./config.go:114:17: (*Config).Section capturing by value: c (addr=false assign=false width=8) +./config.go:114:17: (*Config).Section capturing by value: sk (addr=false assign=false width=40) +./config.go:113:7: parameter c leaks to {heap} for (*Config).Section with derefs=2: +./config.go:113:7: flow: c ← c: +./config.go:113:7: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:114:17 +./config.go:113:7: flow: {temp} ← *c: +./config.go:113:7: from c.entries (dot of pointer) at ./config.go:66:22 +./config.go:113:7: flow: e ← *{temp}: +./config.go:113:7: from for loop (range-deref) at ./config.go:66:15 +./config.go:113:7: flow: {heap} ← e: +./config.go:113:7: from yield(e) (call parameter) at ./config.go:67:41 +./config.go:114:48: string(b) escapes to heap in (*Config).Section: +./config.go:114:48: flow: ~r0 ← &{storage for string(b)}: +./config.go:114:48: from string(b) (spill) at ./config.go:114:48 +./config.go:114:48: from ~r0 = string(b) (assign-pair) at ./config.go:114:48 +./config.go:114:48: flow: sk ← ~r0: +./config.go:114:48: from sectionKey{...} (struct literal element) at ./config.go:114:28 +./config.go:114:48: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:114:17 +./config.go:114:48: flow: {storage for func literal} ← sk: +./config.go:114:48: from sk (captured by a closure) at ./config.go:114:17 +./config.go:114:17: func literal escapes to heap in (*Config).Section: +./config.go:114:17: flow: ~r0 ← &{storage for func literal}: +./config.go:114:17: from func literal (spill) at ./config.go:114:17 +./config.go:114:17: from ~r0 = func literal (assign-pair) at ./config.go:114:17 +./config.go:114:17: flow: ~r0 ← ~r0: +./config.go:114:17: from return ~r0 (return) at ./config.go:114:2 +./config.go:113:7: parameter c leaks to {storage for func literal} for (*Config).Section with derefs=0: +./config.go:113:7: flow: c ← c: +./config.go:113:7: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:114:17 +./config.go:113:7: flow: {storage for func literal} ← c: +./config.go:113:7: from c (captured by a closure) at ./config.go:114:17 +./config.go:113:26: parameter section leaks to {storage for func literal} for (*Config).Section with derefs=0: +./config.go:113:26: flow: s ← section: +./config.go:113:26: from s := section (assign-pair) at ./config.go:114:48 +./config.go:113:26: flow: ~r0 ← s: +./config.go:113:26: from ~r0 = s (assign-pair) at ./config.go:114:48 +./config.go:113:26: flow: sk ← ~r0: +./config.go:113:26: from sectionKey{...} (struct literal element) at ./config.go:114:28 +./config.go:113:26: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:114:17 +./config.go:113:26: flow: {storage for func literal} ← sk: +./config.go:113:26: from sk (captured by a closure) at ./config.go:114:17 +./config.go:113:7: leaking param: c +./config.go:113:26: leaking param: section +./config.go:62:14: yield does not escape +./config.go:114:48: ([]byte)(s) does not escape +./config.go:114:48: string(b) escapes to heap +./config.go:114:17: func literal escapes to heap +./config.go:121:17: (*Config).Subsection capturing by value: c (addr=false assign=false width=8) +./config.go:121:17: (*Config).Subsection capturing by value: sk (addr=false assign=false width=40) +./config.go:120:7: parameter c leaks to {heap} for (*Config).Subsection with derefs=2: +./config.go:120:7: flow: c ← c: +./config.go:120:7: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:121:17 +./config.go:120:7: flow: {temp} ← *c: +./config.go:120:7: from c.entries (dot of pointer) at ./config.go:66:22 +./config.go:120:7: flow: e ← *{temp}: +./config.go:120:7: from for loop (range-deref) at ./config.go:66:15 +./config.go:120:7: flow: {heap} ← e: +./config.go:120:7: from yield(e) (call parameter) at ./config.go:67:41 +./config.go:121:39: string(b) escapes to heap in (*Config).Subsection: +./config.go:121:39: flow: ~r0 ← &{storage for string(b)}: +./config.go:121:39: from string(b) (spill) at ./config.go:121:39 +./config.go:121:39: from ~r0 = string(b) (assign-pair) at ./config.go:121:39 +./config.go:121:39: flow: sk ← ~r0: +./config.go:121:39: from sectionKey{...} (struct literal element) at ./config.go:121:28 +./config.go:121:39: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:121:17 +./config.go:121:39: flow: {storage for func literal} ← sk: +./config.go:121:39: from sk (captured by a closure) at ./config.go:121:17 +./config.go:121:17: func literal escapes to heap in (*Config).Subsection: +./config.go:121:17: flow: ~r0 ← &{storage for func literal}: +./config.go:121:17: from func literal (spill) at ./config.go:121:17 +./config.go:121:17: from ~r0 = func literal (assign-pair) at ./config.go:121:17 +./config.go:121:17: flow: ~r0 ← ~r0: +./config.go:121:17: from return ~r0 (return) at ./config.go:121:2 +./config.go:120:7: parameter c leaks to {storage for func literal} for (*Config).Subsection with derefs=0: +./config.go:120:7: flow: c ← c: +./config.go:120:7: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:121:17 +./config.go:120:7: flow: {storage for func literal} ← c: +./config.go:120:7: from c (captured by a closure) at ./config.go:121:17 +./config.go:120:38: parameter sub leaks to {storage for func literal} for (*Config).Subsection with derefs=0: +./config.go:120:38: flow: sk ← sub: +./config.go:120:38: from sectionKey{...} (struct literal element) at ./config.go:121:28 +./config.go:120:38: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:121:17 +./config.go:120:38: flow: {storage for func literal} ← sk: +./config.go:120:38: from sk (captured by a closure) at ./config.go:121:17 +./config.go:120:29: parameter section leaks to {storage for func literal} for (*Config).Subsection with derefs=0: +./config.go:120:29: flow: s ← section: +./config.go:120:29: from s := section (assign-pair) at ./config.go:121:39 +./config.go:120:29: flow: ~r0 ← s: +./config.go:120:29: from ~r0 = s (assign-pair) at ./config.go:121:39 +./config.go:120:29: flow: sk ← ~r0: +./config.go:120:29: from sectionKey{...} (struct literal element) at ./config.go:121:28 +./config.go:120:29: from c, sk := c, sectionKey{...} (assign-pair) at ./config.go:121:17 +./config.go:120:29: flow: {storage for func literal} ← sk: +./config.go:120:29: from sk (captured by a closure) at ./config.go:121:17 +./config.go:120:7: leaking param: c +./config.go:120:29: leaking param: section +./config.go:120:38: leaking param: sub +./config.go:62:14: yield does not escape +./config.go:121:39: ([]byte)(s) does not escape +./config.go:121:39: string(b) escapes to heap +./config.go:121:17: func literal escapes to heap +./config.go:132:46: string(b) escapes to heap in (*Config).Subsections: +./config.go:132:46: flow: ~r0 ← &{storage for string(b)}: +./config.go:132:46: from string(b) (spill) at ./config.go:132:46 +./config.go:132:46: from ~r0 = string(b) (assign-pair) at ./config.go:132:46 +./config.go:132:21: append(go.shape.[]string{}, slices.s...) escapes to heap in (*Config).Subsections: +./config.go:132:21: flow: ~r0 ← &{storage for append(go.shape.[]string{}, slices.s...)}: +./config.go:132:21: from append(go.shape.[]string{}, slices.s...) (spill) at ./config.go:132:21 +./config.go:132:21: from ~r0 = append(go.shape.[]string{}, slices.s...) (assign-pair) at ./config.go:132:21 +./config.go:132:21: flow: ~r0 ← ~r0: +./config.go:132:21: from return ~r0 (return) at ./config.go:132:2 +./config.go:132:21: go.shape.[]string{} escapes to heap in (*Config).Subsections: +./config.go:132:21: flow: {temp} ← &{storage for go.shape.[]string{}}: +./config.go:132:21: from go.shape.[]string{} (spill) at ./config.go:132:21 +./config.go:132:21: from append(go.shape.[]string{}, slices.s...) (call parameter) at ./config.go:132:21 +./config.go:132:21: flow: {temp} ← {temp}: +./config.go:132:21: flow: ~r0 ← {temp}: +./config.go:132:21: from ~r0 = append(go.shape.[]string{}, slices.s...) (assign-pair) at ./config.go:132:21 +./config.go:132:21: flow: ~r0 ← ~r0: +./config.go:132:21: from return ~r0 (return) at ./config.go:132:2 +./config.go:128:7: c does not escape +./config.go:128:30: section does not escape +./config.go:132:46: ([]byte)(s) does not escape +./config.go:132:46: string(b) escapes to heap +./config.go:132:21: go.shape.[]string{} escapes to heap +./config.go:132:21: append escapes to heap +./config.go:136:36: parameter def leaks to ~r0 for (*Config).Value with derefs=0: +./config.go:136:36: flow: ~r0 ← def: +./config.go:136:36: from return def (return) at ./config.go:140:2 +./config.go:136:7: parameter c leaks to ~r0 for (*Config).Value with derefs=2: +./config.go:136:7: flow: .autotmp_6 ← **c: +./config.go:136:7: from (*Config).Get(c, key) (call parameter) at ./config.go:137:19 +./config.go:136:7: flow: e ← .autotmp_6: +./config.go:136:7: from e, ok := Entry(.autotmp_6), .autotmp_7 (assign-pair) at ./config.go:137:11 +./config.go:136:7: flow: ~r0 ← e: +./config.go:136:7: from e.Value (dot) at ./config.go:138:11 +./config.go:136:7: from return e.Value (return) at ./config.go:138:3 +./config.go:136:7: leaking param: c to result ~r0 level=2 +./config.go:136:24: key does not escape +./config.go:136:36: leaking param: def to result ~r0 level=0 +./value.go:35:15: v does not escape +./value.go:15:16: v does not escape +./config.go:240:24: &ValueError{...} escapes to heap in Entry.Bool: +./config.go:240:24: flow: ~r0 ← &{storage for &ValueError{...}}: +./config.go:240:24: from &ValueError{...} (spill) at ./config.go:240:24 +./config.go:240:24: from &ValueError{...} (interface-converted) at ./config.go:240:24 +./config.go:240:24: from ~r0 = &ValueError{...} (assign-pair) at ./config.go:240:24 +./config.go:240:24: flow: ~r1 ← ~r0: +./config.go:240:24: from return false, ~r0 (return) at ./config.go:240:3 +./config.go:234:7: parameter e leaks to {storage for &ValueError{...}} for Entry.Bool with derefs=0: +./config.go:234:7: flow: e ← e: +./config.go:234:7: from e, typ, err := e, "bool", err (assign-pair) at ./config.go:240:24 +./config.go:234:7: flow: {storage for &ValueError{...}} ← e: +./config.go:234:7: from ValueError{...} (struct literal element) at ./config.go:240:24 +./config.go:234:7: leaking param: e +./config.go:240:24: &ValueError{...} escapes to heap +./config.go:146:7: parameter c leaks to {heap} for (*Config).Bool with derefs=2: +./config.go:146:7: flow: .autotmp_7 ← **c: +./config.go:146:7: from (*Config).Get(c, key) (call parameter) at ./config.go:147:16 +./config.go:146:7: flow: e ← .autotmp_7: +./config.go:146:7: from e, ok := Entry(.autotmp_7), .autotmp_8 (assign-pair) at ./config.go:147:8 +./config.go:146:7: flow: {heap} ← e: +./config.go:146:7: from Entry.Bool(e) (call parameter) at ./config.go:151:18 +./config.go:146:7: leaking param content: c +./config.go:146:23: key does not escape +./config.go:250:20: &ValueError{...} escapes to heap in Entry.Int: +./config.go:250:20: flow: ~r0 ← &{storage for &ValueError{...}}: +./config.go:250:20: from &ValueError{...} (spill) at ./config.go:250:20 +./config.go:250:20: from &ValueError{...} (interface-converted) at ./config.go:250:20 +./config.go:250:20: from ~r0 = &ValueError{...} (assign-pair) at ./config.go:250:20 +./config.go:250:20: flow: ~r1 ← ~r0: +./config.go:250:20: from return int64(0), ~r0 (return) at ./config.go:250:3 +./config.go:254:20: &ValueError{...} escapes to heap in Entry.Int: +./config.go:254:20: flow: ~r0 ← &{storage for &ValueError{...}}: +./config.go:254:20: from &ValueError{...} (spill) at ./config.go:254:20 +./config.go:254:20: from &ValueError{...} (interface-converted) at ./config.go:254:20 +./config.go:254:20: from ~r0 = &ValueError{...} (assign-pair) at ./config.go:254:20 +./config.go:254:20: flow: ~r1 ← ~r0: +./config.go:254:20: from return int64(0), ~r0 (return) at ./config.go:254:3 +./config.go:248:7: parameter e leaks to {storage for &ValueError{...}} for Entry.Int with derefs=0: +./config.go:248:7: flow: e ← e: +./config.go:248:7: from e, typ, err := e, "int", err (assign-pair) at ./config.go:254:20 +./config.go:248:7: flow: {storage for &ValueError{...}} ← e: +./config.go:248:7: from ValueError{...} (struct literal element) at ./config.go:254:20 +./config.go:248:7: parameter e leaks to {storage for &ValueError{...}} for Entry.Int with derefs=0: +./config.go:248:7: flow: e ← e: +./config.go:248:7: from e, typ, err := e, "int", nil (assign-pair) at ./config.go:250:20 +./config.go:248:7: flow: {storage for &ValueError{...}} ← e: +./config.go:248:7: from ValueError{...} (struct literal element) at ./config.go:250:20 +./config.go:248:7: leaking param: e +./config.go:250:20: &ValueError{...} escapes to heap +./config.go:254:20: &ValueError{...} escapes to heap +./config.go:161:7: parameter c leaks to {heap} for (*Config).Int with derefs=2: +./config.go:161:7: flow: .autotmp_7 ← **c: +./config.go:161:7: from (*Config).Get(c, key) (call parameter) at ./config.go:162:16 +./config.go:161:7: flow: e ← .autotmp_7: +./config.go:161:7: from e, ok := Entry(.autotmp_7), .autotmp_8 (assign-pair) at ./config.go:162:8 +./config.go:161:7: flow: {heap} ← e: +./config.go:161:7: from Entry.Int(e) (call parameter) at ./config.go:166:17 +./config.go:161:7: leaking param content: c +./config.go:161:22: key does not escape +./value.go:128:15: home + v[1:] escapes to heap in expandPath: +./value.go:128:15: flow: ~r0 ← &{storage for home + v[1:]}: +./value.go:128:15: from home + v[1:] (spill) at ./value.go:128:15 +./value.go:128:15: from return home + v[1:], nil (return) at ./value.go:128:3 +./value.go:120:17: parameter v leaks to ~r0 for expandPath with derefs=0: +./value.go:120:17: flow: ~r0 ← v: +./value.go:120:17: from return v, nil (return) at ./value.go:132:2 +./value.go:120:17: leaking param: v to result ~r0 level=0 +./value.go:120:20: home does not escape +./value.go:128:15: home + v[1:] escapes to heap +./config.go:267:21: &ValueError{...} escapes to heap in Entry.Path: +./config.go:267:21: flow: ~r0 ← &{storage for &ValueError{...}}: +./config.go:267:21: from &ValueError{...} (spill) at ./config.go:267:21 +./config.go:267:21: from &ValueError{...} (interface-converted) at ./config.go:267:21 +./config.go:267:21: from ~r0 = &ValueError{...} (assign-pair) at ./config.go:267:21 +./config.go:267:21: flow: ~r1 ← ~r0: +./config.go:267:21: from return "", ~r0 (return) at ./config.go:267:3 +./config.go:271:21: &ValueError{...} escapes to heap in Entry.Path: +./config.go:271:21: flow: ~r0 ← &{storage for &ValueError{...}}: +./config.go:271:21: from &ValueError{...} (spill) at ./config.go:271:21 +./config.go:271:21: from &ValueError{...} (interface-converted) at ./config.go:271:21 +./config.go:271:21: from ~r0 = &ValueError{...} (assign-pair) at ./config.go:271:21 +./config.go:271:21: flow: ~r1 ← ~r0: +./config.go:271:21: from return "", ~r0 (return) at ./config.go:271:3 +./config.go:265:7: parameter e leaks to ~r0 for Entry.Path with derefs=0: +./config.go:265:7: flow: .autotmp_6 ← e: +./config.go:265:7: from e.Value (dot) at ./config.go:269:24 +./config.go:265:7: from expandPath(e.Value, home) (call parameter) at ./config.go:269:22 +./config.go:265:7: flow: p ← .autotmp_6: +./config.go:265:7: from p, err := string(.autotmp_6), .autotmp_7 (assign-pair) at ./config.go:269:9 +./config.go:265:7: flow: ~r0 ← p: +./config.go:265:7: from return p, nil (return) at ./config.go:273:2 +./config.go:265:7: parameter e leaks to {storage for &ValueError{...}} for Entry.Path with derefs=0: +./config.go:265:7: flow: e ← e: +./config.go:265:7: from e, typ, err := e, "path", err (assign-pair) at ./config.go:271:21 +./config.go:265:7: flow: {storage for &ValueError{...}} ← e: +./config.go:265:7: from ValueError{...} (struct literal element) at ./config.go:271:21 +./config.go:265:7: parameter e leaks to {storage for &ValueError{...}} for Entry.Path with derefs=0: +./config.go:265:7: flow: e ← e: +./config.go:265:7: from e, typ, err := e, "path", nil (assign-pair) at ./config.go:267:21 +./config.go:265:7: flow: {storage for &ValueError{...}} ← e: +./config.go:265:7: from ValueError{...} (struct literal element) at ./config.go:267:21 +./config.go:265:7: leaking param: e +./config.go:265:21: home does not escape +./config.go:267:21: &ValueError{...} escapes to heap +./config.go:271:21: &ValueError{...} escapes to heap +./config.go:177:34: parameter def leaks to {heap} for (*Config).Path with derefs=0: +./config.go:177:34: flow: e ← def: +./config.go:177:34: from Entry{...} (struct literal element) at ./config.go:181:12 +./config.go:177:34: from e = Entry{...} (assign) at ./config.go:181:5 +./config.go:177:34: flow: {heap} ← e: +./config.go:177:34: from Entry.Path(e, home) (call parameter) at ./config.go:183:18 +./config.go:177:7: parameter c leaks to {heap} for (*Config).Path with derefs=2: +./config.go:177:7: flow: .autotmp_8 ← **c: +./config.go:177:7: from (*Config).Get(c, key) (call parameter) at ./config.go:178:16 +./config.go:177:7: flow: e ← .autotmp_8: +./config.go:177:7: from e, ok := Entry(.autotmp_8), .autotmp_9 (assign-pair) at ./config.go:178:8 +./config.go:177:7: flow: {heap} ← e: +./config.go:177:7: from Entry.Path(e, home) (call parameter) at ./config.go:183:18 +./config.go:177:23: parameter key leaks to {heap} for (*Config).Path with derefs=0: +./config.go:177:23: flow: {temp} ← key: +./config.go:177:23: from parseKey(key) (call parameter) at ./config.go:180:19 +./config.go:177:23: flow: .autotmp_11 ← {temp}: +./config.go:177:23: flow: k ← .autotmp_11: +./config.go:177:23: from k, _ := Key(.autotmp_11), 0 (assign-pair) at ./config.go:180:8 +./config.go:177:23: flow: e ← k: +./config.go:177:23: from Entry{...} (struct literal element) at ./config.go:181:12 +./config.go:177:23: from e = Entry{...} (assign) at ./config.go:181:5 +./config.go:177:23: flow: {heap} ← e: +./config.go:177:23: from Entry.Path(e, home) (call parameter) at ./config.go:183:18 +./config.go:177:34: parameter def leaks to ~r0 for (*Config).Path with derefs=0: +./config.go:177:34: flow: ~r0 ← def: +./config.go:177:34: from return def, err (return) at ./config.go:185:3 +./config.go:177:7: leaking param content: c +./config.go:177:23: leaking param: key +./config.go:177:28: home does not escape +./config.go:177:34: leaking param: def +./config.go:192:9: &ValueError{...} escapes to heap in Entry.error: +./config.go:192:9: flow: ~r0 ← &{storage for &ValueError{...}}: +./config.go:192:9: from &ValueError{...} (spill) at ./config.go:192:9 +./config.go:192:9: from &ValueError{...} (interface-converted) at ./config.go:192:9 +./config.go:192:9: from return &ValueError{...} (return) at ./config.go:192:2 +./config.go:191:7: parameter e leaks to {storage for &ValueError{...}} for Entry.error with derefs=0: +./config.go:191:7: flow: {storage for &ValueError{...}} ← e: +./config.go:191:7: from ValueError{...} (struct literal element) at ./config.go:192:20 +./config.go:191:22: parameter typ leaks to {storage for &ValueError{...}} for Entry.error with derefs=0: +./config.go:191:22: flow: {storage for &ValueError{...}} ← typ: +./config.go:191:22: from ValueError{...} (struct literal element) at ./config.go:192:20 +./config.go:191:34: parameter err leaks to {storage for &ValueError{...}} for Entry.error with derefs=0: +./config.go:191:34: flow: {storage for &ValueError{...}} ← err: +./config.go:191:34: from ValueError{...} (struct literal element) at ./config.go:192:20 +./config.go:191:7: leaking param: e +./config.go:191:22: leaking param: typ +./config.go:191:34: leaking param: err +./config.go:192:9: &ValueError{...} escapes to heap +./discover.go:20:25: parameter key leaks to {heap} for osEnviron.Lookup with derefs=0: +./discover.go:20:25: flow: {heap} ← key: +./discover.go:20:25: from os.LookupEnv(key) (call parameter) at ./discover.go:20:73 +./discover.go:20:25: leaking param: key +./discover.go:22:10: parameter env leaks to {heap} for get with derefs=0: +./discover.go:22:10: flow: {heap} ← env: +./discover.go:22:10: from env.Lookup(key) (call parameter) at ./discover.go:23:20 +./discover.go:22:23: parameter key leaks to {heap} for get with derefs=0: +./discover.go:22:23: flow: {heap} ← key: +./discover.go:22:23: from env.Lookup(key) (call parameter) at ./discover.go:23:20 +./discover.go:22:10: leaking param: env +./discover.go:22:23: leaking param: key +./discover.go:123:43: "GIT_CONFIG_KEY_" + ~r0 escapes to heap in countEntries: +./discover.go:123:43: flow: {heap} ← &{storage for "GIT_CONFIG_KEY_" + ~r0}: +./discover.go:123:43: from "GIT_CONFIG_KEY_" + ~r0 (spill) at ./discover.go:123:43 +./discover.go:123:43: from env.Lookup("GIT_CONFIG_KEY_" + ~r0) (call parameter) at ./discover.go:123:24 +./discover.go:127:47: "GIT_CONFIG_VALUE_" + ~r0 escapes to heap in countEntries: +./discover.go:127:47: flow: {heap} ← &{storage for "GIT_CONFIG_VALUE_" + ~r0}: +./discover.go:127:47: from "GIT_CONFIG_VALUE_" + ~r0 (spill) at ./discover.go:127:47 +./discover.go:127:47: from env.Lookup("GIT_CONFIG_VALUE_" + ~r0) (call parameter) at ./discover.go:127:26 +./discover.go:112:19: parameter env leaks to {heap} for countEntries with derefs=0: +./discover.go:112:19: flow: {heap} ← env: +./discover.go:112:19: from env.Lookup("GIT_CONFIG_KEY_" + ~r0) (call parameter) at ./discover.go:123:24 +./discover.go:119:76: count escapes to heap in countEntries: +./discover.go:119:76: flow: {storage for ... argument} ← &{storage for count}: +./discover.go:119:76: from count (spill) at ./discover.go:119:76 +./discover.go:119:76: from ... argument (slice-literal-element) at ./discover.go:119:25 +./discover.go:119:76: flow: fmt.a ← &{storage for ... argument}: +./discover.go:119:76: from ... argument (spill) at ./discover.go:119:25 +./discover.go:119:76: from fmt.format, fmt.a := "gitconfig: bogus count in GIT_CONFIG_COUNT: %q", ... argument (assign-pair) at ./discover.go:119:25 +./discover.go:119:76: flow: {heap} ← *fmt.a: +./discover.go:119:76: from fmt.errorf(fmt.format, fmt.a...) (call parameter) at ./discover.go:119:25 +./discover.go:125:78: i escapes to heap in countEntries: +./discover.go:125:78: flow: {storage for ... argument} ← &{storage for i}: +./discover.go:125:78: from i (spill) at ./discover.go:125:78 +./discover.go:125:78: from ... argument (slice-literal-element) at ./discover.go:125:26 +./discover.go:125:78: flow: fmt.a ← &{storage for ... argument}: +./discover.go:125:78: from ... argument (spill) at ./discover.go:125:26 +./discover.go:125:78: from fmt.format, fmt.a := "gitconfig: missing config key GIT_CONFIG_KEY_%d", ... argument (assign-pair) at ./discover.go:125:26 +./discover.go:125:78: flow: {heap} ← *fmt.a: +./discover.go:125:78: from fmt.errorf(fmt.format, fmt.a...) (call parameter) at ./discover.go:125:26 +./discover.go:129:82: i escapes to heap in countEntries: +./discover.go:129:82: flow: {storage for ... argument} ← &{storage for i}: +./discover.go:129:82: from i (spill) at ./discover.go:129:82 +./discover.go:129:82: from ... argument (slice-literal-element) at ./discover.go:129:26 +./discover.go:129:82: flow: fmt.a ← &{storage for ... argument}: +./discover.go:129:82: from ... argument (spill) at ./discover.go:129:26 +./discover.go:129:82: from fmt.format, fmt.a := "gitconfig: missing config value GIT_CONFIG_VALUE_%d", ... argument (assign-pair) at ./discover.go:129:26 +./discover.go:129:82: flow: {heap} ← *fmt.a: +./discover.go:129:82: from fmt.errorf(fmt.format, fmt.a...) (call parameter) at ./discover.go:129:26 +./discover.go:119:25: &errors.errorString{...} escapes to heap in countEntries: +./discover.go:119:25: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./discover.go:119:25: from &errors.errorString{...} (spill) at ./discover.go:119:25 +./discover.go:119:25: from &errors.errorString{...} (interface-converted) at ./discover.go:119:25 +./discover.go:119:25: from ~r0 = &errors.errorString{...} (assign-pair) at ./discover.go:119:25 +./discover.go:119:25: flow: fmt.err ← ~r0: +./discover.go:119:25: from fmt.err = ~r0 (assign-pair) at ./discover.go:119:25 +./discover.go:119:25: flow: ~r1 ← fmt.err: +./discover.go:119:25: from return nil, fmt.err (return) at ./discover.go:119:3 +./discover.go:125:26: &errors.errorString{...} escapes to heap in countEntries: +./discover.go:125:26: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./discover.go:125:26: from &errors.errorString{...} (spill) at ./discover.go:125:26 +./discover.go:125:26: from &errors.errorString{...} (interface-converted) at ./discover.go:125:26 +./discover.go:125:26: from ~r0 = &errors.errorString{...} (assign-pair) at ./discover.go:125:26 +./discover.go:125:26: flow: fmt.err ← ~r0: +./discover.go:125:26: from fmt.err = ~r0 (assign-pair) at ./discover.go:125:26 +./discover.go:125:26: flow: ~r1 ← fmt.err: +./discover.go:125:26: from return nil, fmt.err (return) at ./discover.go:125:4 +./discover.go:129:26: &errors.errorString{...} escapes to heap in countEntries: +./discover.go:129:26: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./discover.go:129:26: from &errors.errorString{...} (spill) at ./discover.go:129:26 +./discover.go:129:26: from &errors.errorString{...} (interface-converted) at ./discover.go:129:26 +./discover.go:129:26: from ~r0 = &errors.errorString{...} (assign-pair) at ./discover.go:129:26 +./discover.go:129:26: flow: fmt.err ← ~r0: +./discover.go:129:26: from fmt.err = ~r0 (assign-pair) at ./discover.go:129:26 +./discover.go:129:26: flow: ~r1 ← fmt.err: +./discover.go:129:26: from return nil, fmt.err (return) at ./discover.go:129:4 +./discover.go:131:19: append(entries, CommandEntry{...}) escapes to heap in countEntries: +./discover.go:131:19: flow: entries ← &{storage for append(entries, CommandEntry{...})}: +./discover.go:131:19: from append(entries, CommandEntry{...}) (spill) at ./discover.go:131:19 +./discover.go:131:19: from entries = append(entries, CommandEntry{...}) (assign) at ./discover.go:131:11 +./discover.go:131:19: flow: ~r0 ← entries: +./discover.go:131:19: from return entries, nil (return) at ./discover.go:133:2 +./discover.go:112:19: leaking param: env +./discover.go:119:25: ... argument does not escape +./discover.go:119:76: count escapes to heap +./discover.go:119:25: &errors.errorString{...} escapes to heap +./discover.go:123:43: "GIT_CONFIG_KEY_" + ~r0 escapes to heap +./discover.go:125:26: ... argument does not escape +./discover.go:125:78: i escapes to heap +./discover.go:125:26: &errors.errorString{...} escapes to heap +./discover.go:127:47: "GIT_CONFIG_VALUE_" + ~r0 escapes to heap +./discover.go:129:26: ... argument does not escape +./discover.go:129:82: i escapes to heap +./discover.go:129:26: &errors.errorString{...} escapes to heap +./discover.go:131:19: append escapes to heap +./discover.go:197:17: "strings: illegal use of non-zero Builder copied by value" escapes to heap in unquote: +./discover.go:197:17: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./discover.go:197:17: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./discover.go:197:17 +./discover.go:197:17: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./discover.go:197:17 +./discover.go:197:17: append(strings.b.buf, strings.s...) escapes to heap in unquote: +./discover.go:197:17: flow: {heap} ← &{storage for append(strings.b.buf, strings.s...)}: +./discover.go:197:17: from append(strings.b.buf, strings.s...) (spill) at ./discover.go:197:17 +./discover.go:197:17: from strings.b.buf = append(strings.b.buf, strings.s...) (assign) at ./discover.go:197:17 +./discover.go:200:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap in unquote: +./discover.go:200:16: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./discover.go:200:16: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./discover.go:200:16 +./discover.go:200:16: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./discover.go:200:16 +./discover.go:200:16: append(strings.b.buf, strings.c) escapes to heap in unquote: +./discover.go:200:16: flow: {heap} ← &{storage for append(strings.b.buf, strings.c)}: +./discover.go:200:16: from append(strings.b.buf, strings.c) (spill) at ./discover.go:200:16 +./discover.go:200:16: from strings.b.buf = append(strings.b.buf, strings.c) (assign) at ./discover.go:200:16 +./discover.go:186:14: parameter s leaks to rest for unquote with derefs=0: +./discover.go:186:14: flow: rest ← s: +./discover.go:186:14: from return ~r0, s, true (return) at ./discover.go:204:3 +./discover.go:186:14: leaking param: s to result rest level=0 +./discover.go:197:17: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./discover.go:197:17: append escapes to heap +./discover.go:200:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./discover.go:200:16: append escapes to heap +./discover.go:175:19: append(entries, c) escapes to heap in parseParameters: +./discover.go:175:19: flow: entries ← &{storage for append(entries, c)}: +./discover.go:175:19: from append(entries, c) (spill) at ./discover.go:175:19 +./discover.go:175:19: from entries = append(entries, c) (assign) at ./discover.go:175:11 +./discover.go:175:19: flow: ~r0 ← entries: +./discover.go:175:19: from return entries, nil (return) at ./discover.go:178:2 +./discover.go:148:22: s does not escape +./discover.go:175:19: append escapes to heap +./platform.go:37:19: syscall.Errno(5) escapes to heap in transient: +./platform.go:37:19: flow: {heap} ← &{storage for syscall.Errno(5)}: +./platform.go:37:19: from syscall.Errno(5) (spill) at ./platform.go:37:19 +./platform.go:37:19: from errors.Is(err, syscall.Errno(5)) (call parameter) at ./platform.go:37:13 +./platform.go:37:56: syscall.Errno(32) escapes to heap in transient: +./platform.go:37:56: flow: {heap} ← &{storage for syscall.Errno(32)}: +./platform.go:37:56: from syscall.Errno(32) (spill) at ./platform.go:37:56 +./platform.go:37:56: from errors.Is(err, syscall.Errno(32)) (call parameter) at ./platform.go:37:50 +./platform.go:37:97: syscall.Errno(33) escapes to heap in transient: +./platform.go:37:97: flow: {heap} ← &{storage for syscall.Errno(33)}: +./platform.go:37:97: from syscall.Errno(33) (spill) at ./platform.go:37:97 +./platform.go:37:97: from errors.Is(err, syscall.Errno(33)) (call parameter) at ./platform.go:37:91 +./platform.go:35:16: parameter err leaks to {heap} for transient with derefs=0: +./platform.go:35:16: flow: {heap} ← err: +./platform.go:35:16: from errors.Is(err, syscall.Errno(5)) (call parameter) at ./platform.go:37:13 +./platform.go:35:16: leaking param: err +./platform.go:37:19: syscall.Errno(5) escapes to heap +./platform.go:37:56: syscall.Errno(32) escapes to heap +./platform.go:37:97: syscall.Errno(33) escapes to heap +./platform.go:43:21: fn does not escape +./load.go:130:21: readConfigFile capturing by value: path (addr=false assign=false width=16) +./load.go:130:34: readConfigFile capturing by value: limit (addr=false assign=false width=8) +./load.go:134:6: readConfigFile capturing by ref: data (addr=false assign=true width=24) +./load.go:140:3: readConfigFile.func1 capturing by value: .autotmp_5 (addr=false assign=false width=8) +./load.go:130:21: parameter path leaks to {heap} for readConfigFile with derefs=0: +./load.go:130:21: flow: path ← path: +./load.go:130:21: from path := path (assign-pair) at ./load.go:136:24 +./load.go:130:21: flow: {heap} ← path: +./load.go:130:21: from os.OpenFile(path, flag, fs.FileMode(0)) (call parameter) at ./load.go:136:24 +./load.go:151:40: &io.LimitedReader{...} escapes to heap in readConfigFile.func1: +./load.go:151:40: flow: ~r0 ← &{storage for &io.LimitedReader{...}}: +./load.go:151:40: from &io.LimitedReader{...} (spill) at ./load.go:151:40 +./load.go:151:40: from &io.LimitedReader{...} (interface-converted) at ./load.go:151:40 +./load.go:151:40: from ~r0 = &io.LimitedReader{...} (assign-pair) at ./load.go:151:40 +./load.go:151:40: flow: {heap} ← ~r0: +./load.go:151:40: from io.ReadAll(~r0) (call parameter) at ./load.go:151:25 +./load.go:130:21: leaking param: path +./load.go:135:24: func literal does not escape +./load.go:151:40: &io.LimitedReader{...} escapes to heap +./discover.go:86:20: parameter gitDir leaks to {heap} for resolveGitDir with derefs=0: +./discover.go:86:20: flow: {heap} ← gitDir: +./discover.go:86:20: from os.Stat(gitDir) (call parameter) at ./discover.go:91:20 +./discover.go:97:14: &FileError{...} escapes to heap in resolveGitDir: +./discover.go:97:14: flow: ~r1 ← &{storage for &FileError{...}}: +./discover.go:97:14: from &FileError{...} (spill) at ./discover.go:97:14 +./discover.go:97:14: from &FileError{...} (interface-converted) at ./discover.go:97:14 +./discover.go:97:14: from return "", &FileError{...} (return) at ./discover.go:97:3 +./discover.go:101:14: &FileError{...} escapes to heap in resolveGitDir: +./discover.go:101:14: flow: ~r1 ← &{storage for &FileError{...}}: +./discover.go:101:14: from &FileError{...} (spill) at ./discover.go:101:14 +./discover.go:101:14: from &FileError{...} (interface-converted) at ./discover.go:101:14 +./discover.go:101:14: from return "", &FileError{...} (return) at ./discover.go:101:3 +./discover.go:86:20: parameter gitDir leaks to {storage for &FileError{...}} for resolveGitDir with derefs=0: +./discover.go:86:20: flow: {storage for &FileError{...}} ← gitDir: +./discover.go:86:20: from FileError{...} (struct literal element) at ./discover.go:101:24 +./discover.go:101:54: &errors.errorString{...} escapes to heap in resolveGitDir: +./discover.go:101:54: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./discover.go:101:54: from &errors.errorString{...} (spill) at ./discover.go:101:54 +./discover.go:101:54: from &errors.errorString{...} (interface-converted) at ./discover.go:101:54 +./discover.go:101:54: from ~r0 = &errors.errorString{...} (assign-pair) at ./discover.go:101:54 +./discover.go:101:54: flow: {storage for &FileError{...}} ← ~r0: +./discover.go:101:54: from FileError{...} (struct literal element) at ./discover.go:101:24 +./discover.go:86:20: parameter gitDir leaks to {storage for &FileError{...}} for resolveGitDir with derefs=0: +./discover.go:86:20: flow: {storage for &FileError{...}} ← gitDir: +./discover.go:86:20: from FileError{...} (struct literal element) at ./discover.go:97:24 +./discover.go:86:20: parameter gitDir leaks to ~r0 for resolveGitDir with derefs=0: +./discover.go:86:20: flow: ~r0 ← gitDir: +./discover.go:86:20: from return gitDir, nil (return) at ./discover.go:93:3 +./discover.go:99:57: string(data) escapes to heap in resolveGitDir: +./discover.go:99:57: flow: strings.s ← &{storage for string(data)}: +./discover.go:99:57: from string(data) (spill) at ./discover.go:99:57 +./discover.go:99:57: from strings.TrimRight(string(data), "\r\n") (call parameter) at ./discover.go:99:49 +./discover.go:99:57: from strings.s, strings.prefix := strings.TrimRight(string(data), "\r\n"), "gitdir: " (assign-pair) at ./discover.go:99:31 +./discover.go:99:57: flow: stringslite.s ← strings.s: +./discover.go:99:57: from stringslite.s, stringslite.prefix := strings.s, strings.prefix (assign-pair) at ./discover.go:99:31 +./discover.go:99:57: flow: stringslite.after ← stringslite.s: +./discover.go:99:57: from stringslite.after, stringslite.found = stringslite.s, false (assign-pair) at ./discover.go:99:31 +./discover.go:99:57: flow: .autotmp_4 ← stringslite.after: +./discover.go:99:57: flow: strings.after ← .autotmp_4: +./discover.go:99:57: from strings.after, strings.found = string(.autotmp_4), .autotmp_5 (assign-pair) at ./discover.go:99:31 +./discover.go:99:57: flow: .autotmp_14 ← strings.after: +./discover.go:99:57: flow: path ← .autotmp_14: +./discover.go:99:57: from path, ok := string(.autotmp_14), .autotmp_15 (assign-pair) at ./discover.go:99:11 +./discover.go:99:57: flow: ~r0 ← path: +./discover.go:99:57: from return path, nil (return) at ./discover.go:106:2 +./discover.go:86:20: leaking param: gitDir +./discover.go:97:14: &FileError{...} escapes to heap +./discover.go:99:57: string(data) escapes to heap +./discover.go:101:14: &FileError{...} escapes to heap +./discover.go:101:54: &errors.errorString{...} escapes to heap +./discover.go:104:23: ... argument does not escape +./discover.go:27:30: parameter env leaks to {heap} for discover with derefs=0: +./discover.go:27:30: flow: {heap} ← env: +./discover.go:27:30: from env.Lookup("GIT_CONFIG_GLOBAL") (call parameter) at ./discover.go:35:24 +./discover.go:27:15: parameter gitDir leaks to {heap} for discover with derefs=0: +./discover.go:27:15: flow: {heap} ← gitDir: +./discover.go:27:15: from resolveGitDir(gitDir) (call parameter) at ./discover.go:77:32 +./discover.go:54:100: v escapes to heap in discover: +./discover.go:54:100: flow: {storage for ... argument} ← &{storage for v}: +./discover.go:54:100: from v (spill) at ./discover.go:54:100 +./discover.go:54:100: from ... argument (slice-literal-element) at ./discover.go:54:28 +./discover.go:54:100: flow: fmt.a ← &{storage for ... argument}: +./discover.go:54:100: from ... argument (spill) at ./discover.go:54:28 +./discover.go:54:100: from fmt.format, fmt.a := "gitconfig: bad boolean environment value %q for GIT_CONFIG_NOSYSTEM", ... argument (assign-pair) at ./discover.go:54:28 +./discover.go:54:100: flow: {heap} ← *fmt.a: +./discover.go:54:100: from fmt.errorf(fmt.format, fmt.a...) (call parameter) at ./discover.go:54:28 +./discover.go:37:29: []string{...} escapes to heap in discover: +./discover.go:37:29: flow: e ← &{storage for []string{...}}: +./discover.go:37:29: from []string{...} (spill) at ./discover.go:37:29 +./discover.go:37:29: from e.GlobalConfig = []string{...} (assign) at ./discover.go:37:19 +./discover.go:37:29: flow: {storage for ... argument} ← e: +./discover.go:37:29: from e.Home (dot) at ./discover.go:43:59 +./discover.go:37:29: from ... argument (slice-literal-element) at ./discover.go:43:57 +./discover.go:37:29: flow: filepath.elem ← &{storage for ... argument}: +./discover.go:37:29: from ... argument (spill) at ./discover.go:43:57 +./discover.go:37:29: from filepath.elem := ... argument (assign-pair) at ./discover.go:43:57 +./discover.go:37:29: flow: {temp} ← filepath.elem: +./discover.go:37:29: from filepath.join(filepath.elem) (call parameter) at ./discover.go:43:57 +./discover.go:37:29: flow: ~r0 ← *{temp}: +./discover.go:37:29: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./discover.go:43:57 +./discover.go:37:29: flow: {heap} ← ~r0: +./discover.go:37:29: from append(e.GlobalConfig, ~r0) (call parameter) at ./discover.go:43:27 +./discover.go:41:27: append(e.GlobalConfig, ~r0) escapes to heap in discover: +./discover.go:41:27: flow: e ← &{storage for append(e.GlobalConfig, ~r0)}: +./discover.go:41:27: from append(e.GlobalConfig, ~r0) (spill) at ./discover.go:41:27 +./discover.go:41:27: from e.GlobalConfig = append(e.GlobalConfig, ~r0) (assign) at ./discover.go:41:19 +./discover.go:41:27: flow: {storage for ... argument} ← e: +./discover.go:41:27: from e.Home (dot) at ./discover.go:43:59 +./discover.go:41:27: from ... argument (slice-literal-element) at ./discover.go:43:57 +./discover.go:41:27: flow: filepath.elem ← &{storage for ... argument}: +./discover.go:41:27: from ... argument (spill) at ./discover.go:43:57 +./discover.go:41:27: from filepath.elem := ... argument (assign-pair) at ./discover.go:43:57 +./discover.go:41:27: flow: {temp} ← filepath.elem: +./discover.go:41:27: from filepath.join(filepath.elem) (call parameter) at ./discover.go:43:57 +./discover.go:41:27: flow: ~r0 ← *{temp}: +./discover.go:41:27: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./discover.go:43:57 +./discover.go:41:27: flow: {heap} ← ~r0: +./discover.go:41:27: from append(e.GlobalConfig, ~r0) (call parameter) at ./discover.go:43:27 +./discover.go:43:27: append(e.GlobalConfig, ~r0) escapes to heap in discover: +./discover.go:43:27: flow: e ← &{storage for append(e.GlobalConfig, ~r0)}: +./discover.go:43:27: from append(e.GlobalConfig, ~r0) (spill) at ./discover.go:43:27 +./discover.go:43:27: from e.GlobalConfig = append(e.GlobalConfig, ~r0) (assign) at ./discover.go:43:19 +./discover.go:43:27: flow: {storage for ... argument} ← e: +./discover.go:43:27: from e.Home (dot) at ./discover.go:43:59 +./discover.go:43:27: from ... argument (slice-literal-element) at ./discover.go:43:57 +./discover.go:43:27: flow: filepath.elem ← &{storage for ... argument}: +./discover.go:43:27: from ... argument (spill) at ./discover.go:43:57 +./discover.go:43:27: from filepath.elem := ... argument (assign-pair) at ./discover.go:43:57 +./discover.go:43:27: flow: {temp} ← filepath.elem: +./discover.go:43:27: from filepath.join(filepath.elem) (call parameter) at ./discover.go:43:57 +./discover.go:43:27: flow: ~r0 ← *{temp}: +./discover.go:43:27: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./discover.go:43:57 +./discover.go:43:27: flow: {heap} ← ~r0: +./discover.go:43:27: from append(e.GlobalConfig, ~r0) (call parameter) at ./discover.go:43:27 +./discover.go:46:27: append(e.GlobalConfig, ~r0) escapes to heap in discover: +./discover.go:46:27: flow: e ← &{storage for append(e.GlobalConfig, ~r0)}: +./discover.go:46:27: from append(e.GlobalConfig, ~r0) (spill) at ./discover.go:46:27 +./discover.go:46:27: from e.GlobalConfig = append(e.GlobalConfig, ~r0) (assign) at ./discover.go:46:19 +./discover.go:46:27: flow: {storage for ... argument} ← e: +./discover.go:46:27: from e.Home (dot) at ./discover.go:43:59 +./discover.go:46:27: from ... argument (slice-literal-element) at ./discover.go:43:57 +./discover.go:46:27: flow: filepath.elem ← &{storage for ... argument}: +./discover.go:46:27: from ... argument (spill) at ./discover.go:43:57 +./discover.go:46:27: from filepath.elem := ... argument (assign-pair) at ./discover.go:43:57 +./discover.go:46:27: flow: {temp} ← filepath.elem: +./discover.go:46:27: from filepath.join(filepath.elem) (call parameter) at ./discover.go:43:57 +./discover.go:46:27: flow: ~r0 ← *{temp}: +./discover.go:46:27: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./discover.go:43:57 +./discover.go:46:27: flow: {heap} ← ~r0: +./discover.go:46:27: from append(e.GlobalConfig, ~r0) (call parameter) at ./discover.go:43:27 +./discover.go:74:20: append(cmd, params...) escapes to heap in discover: +./discover.go:74:20: flow: e ← &{storage for append(cmd, params...)}: +./discover.go:74:20: from append(cmd, params...) (spill) at ./discover.go:74:20 +./discover.go:74:20: from e.Command = append(cmd, params...) (assign) at ./discover.go:74:12 +./discover.go:74:20: flow: {storage for ... argument} ← e: +./discover.go:74:20: from e.Home (dot) at ./discover.go:43:59 +./discover.go:74:20: from ... argument (slice-literal-element) at ./discover.go:43:57 +./discover.go:74:20: flow: filepath.elem ← &{storage for ... argument}: +./discover.go:74:20: from ... argument (spill) at ./discover.go:43:57 +./discover.go:74:20: from filepath.elem := ... argument (assign-pair) at ./discover.go:43:57 +./discover.go:74:20: flow: {temp} ← filepath.elem: +./discover.go:74:20: from filepath.join(filepath.elem) (call parameter) at ./discover.go:43:57 +./discover.go:74:20: flow: ~r0 ← *{temp}: +./discover.go:74:20: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./discover.go:43:57 +./discover.go:74:20: flow: {heap} ← ~r0: +./discover.go:74:20: from append(e.GlobalConfig, ~r0) (call parameter) at ./discover.go:43:27 +./discover.go:54:28: &errors.errorString{...} escapes to heap in discover: +./discover.go:54:28: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./discover.go:54:28: from &errors.errorString{...} (spill) at ./discover.go:54:28 +./discover.go:54:28: from &errors.errorString{...} (interface-converted) at ./discover.go:54:28 +./discover.go:54:28: from ~r0 = &errors.errorString{...} (assign-pair) at ./discover.go:54:28 +./discover.go:54:28: flow: fmt.err ← ~r0: +./discover.go:54:28: from fmt.err = ~r0 (assign-pair) at ./discover.go:54:28 +./discover.go:54:28: flow: ~r1 ← fmt.err: +./discover.go:54:28: from return Env{}, fmt.err (return) at ./discover.go:54:4 +./discover.go:27:15: leaking param: gitDir +./discover.go:27:30: leaking param: env +./discover.go:37:29: []string{...} escapes to heap +./discover.go:41:57: ... argument does not escape +./discover.go:41:27: append escapes to heap +./discover.go:43:57: ... argument does not escape +./discover.go:43:27: append escapes to heap +./discover.go:46:57: ... argument does not escape +./discover.go:46:27: append escapes to heap +./discover.go:54:28: ... argument does not escape +./discover.go:54:100: v escapes to heap +./discover.go:54:28: &errors.errorString{...} escapes to heap +./discover.go:74:20: append escapes to heap +./edit.go:31:29: &KeyError{...} escapes to heap in editKey: +./edit.go:31:29: flow: err ← &{storage for &KeyError{...}}: +./edit.go:31:29: from &KeyError{...} (spill) at ./edit.go:31:29 +./edit.go:31:29: from &KeyError{...} (interface-converted) at ./edit.go:31:29 +./edit.go:31:29: from return Key{}, "", "", "", &KeyError{...} (return) at ./edit.go:31:3 +./edit.go:28:14: parameter key leaks to {storage for &KeyError{...}} for editKey with derefs=0: +./edit.go:28:14: flow: {storage for &KeyError{...}} ← key: +./edit.go:28:14: from KeyError{...} (struct literal element) at ./edit.go:31:38 +./edit.go:28:14: parameter key leaks to name for editKey with derefs=0: +./edit.go:28:14: flow: name ← key: +./edit.go:28:14: from key[last + 1:] (slice) at ./edit.go:35:34 +./edit.go:28:14: from section, name = key[:first], key[last + 1:] (assign-pair) at ./edit.go:35:16 +./edit.go:28:14: parameter key leaks to sub for editKey with derefs=0: +./edit.go:28:14: flow: sub ← key: +./edit.go:28:14: from key[first + 1:last] (slice) at ./edit.go:37:12 +./edit.go:28:14: from sub = key[first + 1:last] (assign) at ./edit.go:37:7 +./edit.go:28:14: parameter key leaks to section for editKey with derefs=0: +./edit.go:28:14: flow: section ← key: +./edit.go:28:14: from key[:first] (slice) at ./edit.go:35:21 +./edit.go:28:14: from section, name = key[:first], key[last + 1:] (assign-pair) at ./edit.go:35:16 +./edit.go:28:14: parameter key leaks to k for editKey with derefs=0: +./edit.go:28:14: flow: {temp} ← key: +./edit.go:28:14: from parseKey(key) (call parameter) at ./edit.go:29:19 +./edit.go:28:14: flow: .autotmp_7 ← {temp}: +./edit.go:28:14: flow: k ← .autotmp_7: +./edit.go:28:14: from k, ok := Key(.autotmp_7), .autotmp_8 (assign-pair) at ./edit.go:29:8 +./edit.go:28:14: leaking param: key +./edit.go:31:29: &KeyError{...} escapes to heap +./edit.go:61:10: &KeyError{...} escapes to heap in checkValue: +./edit.go:61:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./edit.go:61:10: from &KeyError{...} (spill) at ./edit.go:61:10 +./edit.go:61:10: from &KeyError{...} (interface-converted) at ./edit.go:61:10 +./edit.go:61:10: from return &KeyError{...} (return) at ./edit.go:61:3 +./edit.go:59:17: parameter key leaks to {storage for &KeyError{...}} for checkValue with derefs=0: +./edit.go:59:17: flow: {storage for &KeyError{...}} ← key: +./edit.go:59:17: from KeyError{...} (struct literal element) at ./edit.go:61:19 +./edit.go:59:17: leaking param: key +./edit.go:59:22: value does not escape +./edit.go:61:10: &KeyError{...} escapes to heap +./edit.go:43:24: parameter key leaks to {heap} for (*File).prepare with derefs=0: +./edit.go:43:24: flow: {heap} ← key: +./edit.go:43:24: from editKey(key) (call parameter) at ./edit.go:47:38 +./edit.go:44:23: &KeyError{...} escapes to heap in (*File).prepare: +./edit.go:44:23: flow: ~r0 ← &{storage for &KeyError{...}}: +./edit.go:44:23: from &KeyError{...} (spill) at ./edit.go:44:23 +./edit.go:44:23: from &KeyError{...} (interface-converted) at ./edit.go:44:23 +./edit.go:44:23: from ~r0 = &KeyError{...} (assign-pair) at ./edit.go:44:23 +./edit.go:44:23: flow: err ← ~r0: +./edit.go:44:23: from err := ~r0 (assign) at ./edit.go:44:9 +./edit.go:44:23: flow: err ← err: +./edit.go:44:23: from return Key{}, "", "", "", err (return) at ./edit.go:45:3 +./edit.go:43:24: parameter key leaks to {storage for &KeyError{...}} for (*File).prepare with derefs=0: +./edit.go:43:24: flow: key ← key: +./edit.go:43:24: from f, key := f, key (assign-pair) at ./edit.go:44:23 +./edit.go:43:24: flow: {storage for &KeyError{...}} ← key: +./edit.go:43:24: from KeyError{...} (struct literal element) at ./edit.go:44:23 +./edit.go:43:7: f does not escape +./edit.go:43:24: leaking param: key +./edit.go:43:36: value does not escape +./edit.go:44:23: &KeyError{...} escapes to heap +./edit.go:68:10: &KeyError{...} escapes to heap in (*File).checkOpen: +./edit.go:68:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./edit.go:68:10: from &KeyError{...} (spill) at ./edit.go:68:10 +./edit.go:68:10: from &KeyError{...} (interface-converted) at ./edit.go:68:10 +./edit.go:68:10: from return &KeyError{...} (return) at ./edit.go:68:3 +./edit.go:66:26: parameter key leaks to {storage for &KeyError{...}} for (*File).checkOpen with derefs=0: +./edit.go:66:26: flow: {storage for &KeyError{...}} ← key: +./edit.go:66:26: from KeyError{...} (struct literal element) at ./edit.go:68:19 +./edit.go:66:7: f does not escape +./edit.go:66:26: leaking param: key +./edit.go:68:10: &KeyError{...} escapes to heap +./edit.go:79:16: append(idx, i) escapes to heap in (*File).entriesOf: +./edit.go:79:16: flow: idx ← &{storage for append(idx, i)}: +./edit.go:79:16: from append(idx, i) (spill) at ./edit.go:79:16 +./edit.go:79:16: from idx = append(idx, i) (assign) at ./edit.go:79:8 +./edit.go:79:16: flow: ~r0 ← idx: +./edit.go:79:16: from return idx (return) at ./edit.go:82:2 +./edit.go:74:7: f does not escape +./edit.go:74:26: k does not escape +./edit.go:79:16: append escapes to heap +./edit.go:95:14: parameter p leaks to {heap} for flatten with derefs=1: +./edit.go:95:14: flow: p ← &p: +./edit.go:95:14: from p (address-of) at ./edit.go:99:47 +./edit.go:95:14: from p, it := p, it (assign-pair) at ./edit.go:99:53 +./edit.go:95:14: flow: ~r0 ← **p: +./edit.go:95:14: from p.secs (dot of pointer) at ./edit.go:99:53 +./edit.go:95:14: from p.secs[it.sec] (dot of pointer) at ./edit.go:99:53 +./edit.go:95:14: from ~r0 = p.secs[it.sec] (assign-pair) at ./edit.go:99:53 +./edit.go:95:14: flow: {heap} ← ~r0: +./edit.go:95:14: from flatEntry{...} (struct literal element) at ./edit.go:99:31 +./edit.go:95:14: from append(out, flatEntry{...}) (call parameter) at ./edit.go:99:16 +./edit.go:99:16: append(out, flatEntry{...}) escapes to heap in flatten: +./edit.go:99:16: flow: out ← &{storage for append(out, flatEntry{...})}: +./edit.go:99:16: from append(out, flatEntry{...}) (spill) at ./edit.go:99:16 +./edit.go:99:16: from out = append(out, flatEntry{...}) (assign) at ./edit.go:99:8 +./edit.go:99:16: flow: ~r0 ← out: +./edit.go:99:16: from return out (return) at ./edit.go:102:2 +./edit.go:95:14: leaking param content: p +./edit.go:99:16: append escapes to heap +./edit.go:105:7: e does not escape +./edit.go:105:23: k does not escape +./edit.go:112:32: a does not escape +./edit.go:112:35: b does not escape +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:33:39: slices.siftDownCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.first + slices.root], slices.data[slices.first + slices.child] = slices.data[slices.first + slices.child], slices.data[slices.first + slices.root] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:33:39: slices.siftDownCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.first + slices.root], slices.data[slices.first + slices.child] = slices.data[slices.first + slices.child], slices.data[slices.first + slices.root] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:20:29: parameter slices.data leaks to {heap} for siftDownCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:20:29: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:20:29: from slices.data[slices.first + slices.child] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:27:31 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:20:29: from slices.cmp(slices.data[slices.first + slices.child], slices.data[slices.first + slices.child + 1]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:27:26 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:50:30: slices.heapSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.first], slices.data[slices.first + slices.i] = slices.data[slices.first + slices.i], slices.data[slices.first] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:50:30: slices.heapSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.first], slices.data[slices.first + slices.i] = slices.data[slices.first + slices.i], slices.data[slices.first] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:38:29: parameter slices.data leaks to {heap} for heapSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:38:29: flow: {temp} ← slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:38:29: from slices.siftDownCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[2]uintptr)(slices..dict[0]), slices.data, slices.i, slices.hi, slices.first, slices.cmp) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:45:18 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:38:29: flow: {heap} ← *{temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:251:29: slices.breakPatternsCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.idx], slices.data[slices.a + slices.other] = slices.data[slices.a + slices.other], slices.data[slices.idx] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:251:29: slices.breakPatternsCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.idx], slices.data[slices.a + slices.other] = slices.data[slices.a + slices.other], slices.data[slices.idx] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:27: parameter slices.data leaks to {heap} for medianCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:27: flow: slices.data ← slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:27: from slices..dict, slices.data, slices.a, slices.b, slices.swaps, slices.cmp := (*[2]uintptr)(slices..dict[0]), slices.data, slices.a, slices.b, slices.swaps, slices.cmp (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:308:22 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:27: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:27: from slices.data[slices.b] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:308:22 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:27: from slices.cmp(slices.data[slices.b], slices.data[slices.a]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:308:22 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:261:32: parameter slices.data leaks to {heap} for choosePivotCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:261:32: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:261:32: from slices.medianCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[3]uintptr)(slices..dict[1]), slices.data, slices.i, slices.j, slices.k, &slices.swaps, slices.cmp) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:284:20 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:214:22: slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.i - 1] = slices.data[slices.i - 1], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:214:22: slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.i - 1] = slices.data[slices.i - 1], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:222:24: slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:222:24: slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:231:24: slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:231:24: slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:195:41: parameter slices.data leaks to {heap} for partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:195:41: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:195:41: from slices.data[slices.i] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:202:26 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:195:41: from slices.cmp(slices.data[slices.i], slices.data[slices.i - 1]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:202:21 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:174:23: slices.partitionEqualCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.a], slices.data[slices.pivot] = slices.data[slices.pivot], slices.data[slices.a] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:174:23: slices.partitionEqualCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.a], slices.data[slices.pivot] = slices.data[slices.pivot], slices.data[slices.a] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:187:20: slices.partitionEqualCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:187:20: slices.partitionEqualCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:173:35: parameter slices.data leaks to {heap} for partitionEqualCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:173:35: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:173:35: from slices.data[slices.a] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:178:27 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:173:35: from slices.cmp(slices.data[slices.a], slices.data[slices.i]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:178:22 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:136:23: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.a], slices.data[slices.pivot] = slices.data[slices.pivot], slices.data[slices.a] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:136:23: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.a], slices.data[slices.pivot] = slices.data[slices.pivot], slices.data[slices.a] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:146:20: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.a] = slices.data[slices.a], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:146:20: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.a] = slices.data[slices.a], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:149:19: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:149:19: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:163:20: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:163:20: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:167:19: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.a] = slices.data[slices.a], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:167:19: slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.a] = slices.data[slices.a], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:135:30: parameter slices.data leaks to {heap} for partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:135:30: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:135:30: from slices.data[slices.i] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:139:25 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:135:30: from slices.cmp(slices.data[slices.i], slices.data[slices.a]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:139:20 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:73:24: slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:73:24: slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:91:23: slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:91:23: slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:61:28: parameter slices.data leaks to {heap} for pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:61:28: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:61:28: from slices.choosePivotCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[4]uintptr)(slices..dict[3]), slices.data, slices.a, slices.b, slices.cmp) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:89:36 +./parse.go:290:14: append(p.parsed.secs, sec) escapes to heap in (*parser).header: +./parse.go:290:14: flow: {heap} ← &{storage for append(p.parsed.secs, sec)}: +./parse.go:290:14: from append(p.parsed.secs, sec) (spill) at ./parse.go:290:14 +./parse.go:290:14: from p.parsed.secs = append(p.parsed.secs, sec) (assign) at ./parse.go:290:14 +./parse.go:292:7: append(p.parsed.items, item{...}) escapes to heap in (*parser).header: +./parse.go:292:7: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:292:7: from append(p.parsed.items, item{...}) (spill) at ./parse.go:292:7 +./parse.go:292:7: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:292:7 +./parse.go:215:7: parameter p leaks to {heap} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, sec := p, sec (assign-pair) at ./parse.go:290:14 +./parse.go:215:7: flow: {temp} ← *p: +./parse.go:215:7: from p.parsed (dot of pointer) at ./parse.go:290:14 +./parse.go:215:7: from p.parsed.secs (dot) at ./parse.go:290:14 +./parse.go:215:7: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:290:14 +./parse.go:215:7: flow: {temp} ← {temp}: +./parse.go:215:7: flow: {heap} ← {temp}: +./parse.go:215:7: from p.parsed.secs = append(p.parsed.secs, sec) (assign) at ./parse.go:290:14 +./parse.go:280:33: string(sub) escapes to heap in (*parser).header: +./parse.go:280:33: flow: sec ← &{storage for string(sub)}: +./parse.go:280:33: from string(sub) (spill) at ./parse.go:280:33 +./parse.go:280:33: from sectionKey{...} (struct literal element) at ./parse.go:280:19 +./parse.go:280:33: from sec = sectionKey{...} (assign) at ./parse.go:280:7 +./parse.go:280:33: flow: sec ← sec: +./parse.go:280:33: from p, sec := p, sec (assign-pair) at ./parse.go:290:14 +./parse.go:280:33: flow: {heap} ← sec: +./parse.go:280:33: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:290:14 +./parse.go:283:52: name[dot + 1:] + "." + string(sub) escapes to heap in (*parser).header: +./parse.go:283:52: flow: sec ← &{storage for name[dot + 1:] + "." + string(sub)}: +./parse.go:283:52: from name[dot + 1:] + "." + string(sub) (spill) at ./parse.go:283:52 +./parse.go:283:52: from sectionKey{...} (struct literal element) at ./parse.go:283:20 +./parse.go:283:52: from sec = sectionKey{...} (assign) at ./parse.go:283:8 +./parse.go:283:52: flow: sec ← sec: +./parse.go:283:52: from p, sec := p, sec (assign-pair) at ./parse.go:290:14 +./parse.go:283:52: flow: {heap} ← sec: +./parse.go:283:52: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:290:14 +./parse.go:222:20: string(b) escapes to heap in (*parser).header: +./parse.go:222:20: flow: ~r0 ← &{storage for string(b)}: +./parse.go:222:20: from string(b) (spill) at ./parse.go:222:20 +./parse.go:222:20: from ~r0 = string(b) (assign-pair) at ./parse.go:222:20 +./parse.go:222:20: flow: name ← ~r0: +./parse.go:222:20: from name := ~r0 (assign) at ./parse.go:222:7 +./parse.go:222:20: flow: sec ← name: +./parse.go:222:20: from sec.section = name (assign) at ./parse.go:233:15 +./parse.go:222:20: flow: sec ← sec: +./parse.go:222:20: from p, sec := p, sec (assign-pair) at ./parse.go:290:14 +./parse.go:222:20: flow: {heap} ← sec: +./parse.go:222:20: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:290:14 +./parse.go:222:29: string(d[start + 1:i]) escapes to heap in (*parser).header: +./parse.go:222:29: flow: s ← &{storage for string(d[start + 1:i])}: +./parse.go:222:29: from string(d[start + 1:i]) (spill) at ./parse.go:222:29 +./parse.go:222:29: from s := string(d[start + 1:i]) (assign-pair) at ./parse.go:222:20 +./parse.go:222:29: flow: ~r0 ← s: +./parse.go:222:29: from ~r0 = s (assign-pair) at ./parse.go:222:20 +./parse.go:222:29: flow: name ← ~r0: +./parse.go:222:29: from name := ~r0 (assign) at ./parse.go:222:7 +./parse.go:222:29: flow: sec ← name: +./parse.go:222:29: from sec.section = name (assign) at ./parse.go:233:15 +./parse.go:222:29: flow: sec ← sec: +./parse.go:222:29: from p, sec := p, sec (assign-pair) at ./parse.go:290:14 +./parse.go:222:29: flow: {heap} ← sec: +./parse.go:222:29: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:290:14 +./parse.go:224:19: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:224:19: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:224:19: from &SyntaxError{...} (spill) at ./parse.go:224:19 +./parse.go:224:19: from &SyntaxError{...} (interface-converted) at ./parse.go:224:19 +./parse.go:224:19: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:224:19 +./parse.go:224:19: flow: ~r0 ← ~r0: +./parse.go:224:19: from return ~r0 (return) at ./parse.go:224:3 +./parse.go:231:20: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:231:20: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:231:20: from &SyntaxError{...} (spill) at ./parse.go:231:20 +./parse.go:231:20: from &SyntaxError{...} (interface-converted) at ./parse.go:231:20 +./parse.go:231:20: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:231:20 +./parse.go:231:20: flow: ~r0 ← ~r0: +./parse.go:231:20: from return ~r0 (return) at ./parse.go:231:4 +./parse.go:245:20: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:245:20: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:245:20: from &SyntaxError{...} (spill) at ./parse.go:245:20 +./parse.go:245:20: from &SyntaxError{...} (interface-converted) at ./parse.go:245:20 +./parse.go:245:20: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:245:20 +./parse.go:245:20: flow: ~r0 ← ~r0: +./parse.go:245:20: from return ~r0 (return) at ./parse.go:245:4 +./parse.go:251:21: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:251:21: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:251:21: from &SyntaxError{...} (spill) at ./parse.go:251:21 +./parse.go:251:21: from &SyntaxError{...} (interface-converted) at ./parse.go:251:21 +./parse.go:251:21: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:251:21 +./parse.go:251:21: flow: ~r0 ← ~r0: +./parse.go:251:21: from return ~r0 (return) at ./parse.go:251:5 +./parse.go:260:22: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:260:22: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:260:22: from &SyntaxError{...} (spill) at ./parse.go:260:22 +./parse.go:260:22: from &SyntaxError{...} (interface-converted) at ./parse.go:260:22 +./parse.go:260:22: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:260:22 +./parse.go:260:22: flow: ~r0 ← ~r0: +./parse.go:260:22: from return ~r0 (return) at ./parse.go:260:6 +./parse.go:267:21: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:267:21: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:267:21: from &SyntaxError{...} (spill) at ./parse.go:267:21 +./parse.go:267:21: from &SyntaxError{...} (interface-converted) at ./parse.go:267:21 +./parse.go:267:21: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:267:21 +./parse.go:267:21: flow: ~r0 ← ~r0: +./parse.go:267:21: from return ~r0 (return) at ./parse.go:267:5 +./parse.go:274:20: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:274:20: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:274:20: from &SyntaxError{...} (spill) at ./parse.go:274:20 +./parse.go:274:20: from &SyntaxError{...} (interface-converted) at ./parse.go:274:20 +./parse.go:274:20: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:274:20 +./parse.go:274:20: flow: ~r0 ← ~r0: +./parse.go:274:20: from return ~r0 (return) at ./parse.go:274:4 +./parse.go:277:20: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:277:20: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:277:20: from &SyntaxError{...} (spill) at ./parse.go:277:20 +./parse.go:277:20: from &SyntaxError{...} (interface-converted) at ./parse.go:277:20 +./parse.go:277:20: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:277:20 +./parse.go:277:20: flow: ~r0 ← ~r0: +./parse.go:277:20: from return ~r0 (return) at ./parse.go:277:4 +./parse.go:287:19: &SyntaxError{...} escapes to heap in (*parser).header: +./parse.go:287:19: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:287:19: from &SyntaxError{...} (spill) at ./parse.go:287:19 +./parse.go:287:19: from &SyntaxError{...} (interface-converted) at ./parse.go:287:19 +./parse.go:287:19: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:287:19 +./parse.go:287:19: flow: ~r0 ← ~r0: +./parse.go:287:19: from return ~r0 (return) at ./parse.go:287:3 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "invalid section header" (assign-pair) at ./parse.go:287:19 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:287:19 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:287:19 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "invalid section header" (assign-pair) at ./parse.go:277:20 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:277:20 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:277:20 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line + 1, "unterminated section header" (assign-pair) at ./parse.go:274:20 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:274:20 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:274:20 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "NUL byte in subsection" (assign-pair) at ./parse.go:267:21 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:267:21 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:267:21 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "unterminated subsection" (assign-pair) at ./parse.go:260:22 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:260:22 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:260:22 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "unterminated subsection" (assign-pair) at ./parse.go:251:21 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:251:21 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:251:21 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "invalid section header" (assign-pair) at ./parse.go:245:20 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:245:20 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:245:20 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line, "empty section name" (assign-pair) at ./parse.go:231:20 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:231:20 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:231:20 +./parse.go:215:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).header with derefs=1: +./parse.go:215:7: flow: p ← p: +./parse.go:215:7: from p, line, msg := p, p.line + 1, "unterminated section header" (assign-pair) at ./parse.go:224:19 +./parse.go:215:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:215:7: from p.name (dot of pointer) at ./parse.go:224:19 +./parse.go:215:7: from SyntaxError{...} (struct literal element) at ./parse.go:224:19 +./parse.go:215:7: leaking param content: p +./parse.go:222:29: string(d[start + 1:i]) escapes to heap +./parse.go:222:20: ([]byte)(s) does not escape +./parse.go:222:20: string(b) escapes to heap +./parse.go:224:19: &SyntaxError{...} escapes to heap +./parse.go:231:20: &SyntaxError{...} escapes to heap +./parse.go:245:20: &SyntaxError{...} escapes to heap +./parse.go:251:21: &SyntaxError{...} escapes to heap +./parse.go:260:22: &SyntaxError{...} escapes to heap +./parse.go:267:21: &SyntaxError{...} escapes to heap +./parse.go:269:16: append does not escape +./parse.go:274:20: &SyntaxError{...} escapes to heap +./parse.go:277:20: &SyntaxError{...} escapes to heap +./parse.go:280:33: string(sub) escapes to heap +./parse.go:283:52: name[dot + 1:] + "." + string(sub) escapes to heap +./parse.go:283:61: string(sub) does not escape +./parse.go:287:19: &SyntaxError{...} escapes to heap +./parse.go:290:14: append escapes to heap +./parse.go:292:7: append escapes to heap +./parse.go:123:13: append(p.parsed.items, item{...}) escapes to heap in (*parser).addEntry: +./parse.go:123:13: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:123:13: from append(p.parsed.items, item{...}) (spill) at ./parse.go:123:13 +./parse.go:123:13: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:123:13 +./parse.go:127:18: string(name) escapes to heap in (*parser).addEntry: +./parse.go:127:18: flow: {heap} ← &{storage for string(name)}: +./parse.go:127:18: from string(name) (spill) at ./parse.go:127:18 +./parse.go:127:18: from p.names[string(name)] (key of map put) at ./parse.go:127:10 +./parse.go:130:18: append(p.parsed.texts, entryText{...}) escapes to heap in (*parser).addEntry: +./parse.go:130:18: flow: {heap} ← &{storage for append(p.parsed.texts, entryText{...})}: +./parse.go:130:18: from append(p.parsed.texts, entryText{...}) (spill) at ./parse.go:130:18 +./parse.go:130:18: from p.parsed.texts = append(p.parsed.texts, entryText{...}) (assign) at ./parse.go:130:10 +./parse.go:122:56: parameter value leaks to {heap} for (*parser).addEntry with derefs=0: +./parse.go:122:56: flow: {heap} ← value: +./parse.go:122:56: from entryText{...} (struct literal element) at ./parse.go:130:37 +./parse.go:122:56: from append(p.parsed.texts, entryText{...}) (call parameter) at ./parse.go:130:18 +./parse.go:122:7: parameter p leaks to {heap} for (*parser).addEntry with derefs=1: +./parse.go:122:7: flow: {temp} ← *p: +./parse.go:122:7: from p.parsed (dot of pointer) at ./parse.go:130:20 +./parse.go:122:7: from p.parsed.texts (dot) at ./parse.go:130:20 +./parse.go:122:7: from append(p.parsed.texts, entryText{...}) (call parameter) at ./parse.go:130:18 +./parse.go:122:7: flow: {temp} ← {temp}: +./parse.go:122:7: flow: {heap} ← {temp}: +./parse.go:122:7: from p.parsed.texts = append(p.parsed.texts, entryText{...}) (assign) at ./parse.go:130:10 +./parse.go:126:17: string(b) escapes to heap in (*parser).addEntry: +./parse.go:126:17: flow: ~r0 ← &{storage for string(b)}: +./parse.go:126:17: from string(b) (spill) at ./parse.go:126:17 +./parse.go:126:17: from ~r0 = string(b) (assign-pair) at ./parse.go:126:17 +./parse.go:126:17: flow: n ← ~r0: +./parse.go:126:17: from n = ~r0 (assign) at ./parse.go:126:5 +./parse.go:126:17: flow: {heap} ← n: +./parse.go:126:17: from p.names[string(name)] = n (assign) at ./parse.go:127:25 +./parse.go:126:25: string(name) escapes to heap in (*parser).addEntry: +./parse.go:126:25: flow: s ← &{storage for string(name)}: +./parse.go:126:25: from string(name) (spill) at ./parse.go:126:25 +./parse.go:126:25: from s := string(name) (assign-pair) at ./parse.go:126:17 +./parse.go:126:25: flow: ~r0 ← s: +./parse.go:126:25: from ~r0 = s (assign-pair) at ./parse.go:126:17 +./parse.go:126:25: flow: n ← ~r0: +./parse.go:126:25: from n = ~r0 (assign) at ./parse.go:126:5 +./parse.go:126:25: flow: {heap} ← n: +./parse.go:126:25: from p.names[string(name)] = n (assign) at ./parse.go:127:25 +./parse.go:122:7: parameter p leaks to ~r0 for (*parser).addEntry with derefs=1: +./parse.go:122:7: flow: p ← p: +./parse.go:122:7: from p, kind, start, end := p, itemKind(5), start, end (assign-pair) at ./parse.go:123:13 +./parse.go:122:7: flow: ~r0 ← *p: +./parse.go:122:7: from p.parsed (dot of pointer) at ./parse.go:123:13 +./parse.go:122:7: from p.parsed.items (dot) at ./parse.go:123:13 +./parse.go:122:7: from p.parsed.items[len(p.parsed.items) - 1] (dot of pointer) at ./parse.go:123:13 +./parse.go:122:7: from &p.parsed.items[len(p.parsed.items) - 1] (address-of) at ./parse.go:123:13 +./parse.go:122:7: from ~r0 = &p.parsed.items[len(p.parsed.items) - 1] (assign-pair) at ./parse.go:123:13 +./parse.go:122:7: flow: it ← ~r0: +./parse.go:122:7: from it := ~r0 (assign) at ./parse.go:123:5 +./parse.go:122:7: flow: ~r0 ← it: +./parse.go:122:7: from return it (return) at ./parse.go:131:2 +./parse.go:122:7: leaking param content: p +./parse.go:122:43: name does not escape +./parse.go:122:56: leaking param: value +./parse.go:123:13: append escapes to heap +./parse.go:124:26: string(name) does not escape +./parse.go:126:25: string(name) escapes to heap +./parse.go:126:17: ([]byte)(s) does not escape +./parse.go:126:17: string(b) escapes to heap +./parse.go:127:18: string(name) escapes to heap +./parse.go:130:18: append escapes to heap +./parse.go:334:7: (*parser).value capturing by value: p (addr=false assign=false width=8) +./parse.go:336:2: (*parser).value capturing by ref: buf (addr=false assign=true width=24) +./parse.go:334:7: parameter p leaks to {heap} for (*parser).value with derefs=1: +./parse.go:334:7: flow: buf ← *p: +./parse.go:334:7: from p.buf (dot of pointer) at ./parse.go:336:10 +./parse.go:334:7: from p.buf[:0] (slice) at ./parse.go:336:14 +./parse.go:334:7: from buf := p.buf[:0] (assign) at ./parse.go:336:6 +./parse.go:334:7: flow: {heap} ← buf: +./parse.go:334:7: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:334:7: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:362:10: append(buf, d[pending:at]...) escapes to heap in (*parser).value: +./parse.go:362:10: flow: buf ← &{storage for append(buf, d[pending:at]...)}: +./parse.go:362:10: from append(buf, d[pending:at]...) (spill) at ./parse.go:362:10 +./parse.go:362:10: from buf = append(buf, d[pending:at]...) (assign) at ./parse.go:362:10 +./parse.go:362:10: flow: {heap} ← buf: +./parse.go:362:10: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:362:10: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:367:9: append(buf, d[pending:at]...) escapes to heap in (*parser).value: +./parse.go:367:9: flow: buf ← &{storage for append(buf, d[pending:at]...)}: +./parse.go:367:9: from append(buf, d[pending:at]...) (spill) at ./parse.go:367:9 +./parse.go:367:9: from buf = append(buf, d[pending:at]...) (assign) at ./parse.go:367:9 +./parse.go:367:9: flow: {heap} ← buf: +./parse.go:367:9: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:367:9: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:378:17: append(buf, byte(9)) escapes to heap in (*parser).value: +./parse.go:378:17: flow: buf ← &{storage for append(buf, byte(9))}: +./parse.go:378:17: from append(buf, byte(9)) (spill) at ./parse.go:378:17 +./parse.go:378:17: from buf = append(buf, byte(9)) (assign) at ./parse.go:378:9 +./parse.go:378:17: flow: {heap} ← buf: +./parse.go:378:17: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:378:17: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:380:17: append(buf, byte(10)) escapes to heap in (*parser).value: +./parse.go:380:17: flow: buf ← &{storage for append(buf, byte(10))}: +./parse.go:380:17: from append(buf, byte(10)) (spill) at ./parse.go:380:17 +./parse.go:380:17: from buf = append(buf, byte(10)) (assign) at ./parse.go:380:9 +./parse.go:380:17: flow: {heap} ← buf: +./parse.go:380:17: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:380:17: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:382:17: append(buf, byte(8)) escapes to heap in (*parser).value: +./parse.go:382:17: flow: buf ← &{storage for append(buf, byte(8))}: +./parse.go:382:17: from append(buf, byte(8)) (spill) at ./parse.go:382:17 +./parse.go:382:17: from buf = append(buf, byte(8)) (assign) at ./parse.go:382:9 +./parse.go:382:17: flow: {heap} ← buf: +./parse.go:382:17: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:382:17: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:384:17: append(buf, d[i + 1]) escapes to heap in (*parser).value: +./parse.go:384:17: flow: buf ← &{storage for append(buf, d[i + 1])}: +./parse.go:384:17: from append(buf, d[i + 1]) (spill) at ./parse.go:384:17 +./parse.go:384:17: from buf = append(buf, d[i + 1]) (assign) at ./parse.go:384:9 +./parse.go:384:17: flow: {heap} ← buf: +./parse.go:384:17: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:384:17: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:390:9: append(buf, d[pending:at]...) escapes to heap in (*parser).value: +./parse.go:390:9: flow: buf ← &{storage for append(buf, d[pending:at]...)}: +./parse.go:390:9: from append(buf, d[pending:at]...) (spill) at ./parse.go:390:9 +./parse.go:390:9: from buf = append(buf, d[pending:at]...) (assign) at ./parse.go:390:9 +./parse.go:390:9: flow: {heap} ← buf: +./parse.go:390:9: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:390:9: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:394:16: append(buf, c) escapes to heap in (*parser).value: +./parse.go:394:16: flow: buf ← &{storage for append(buf, c)}: +./parse.go:394:16: from append(buf, c) (spill) at ./parse.go:394:16 +./parse.go:394:16: from buf = append(buf, c) (assign) at ./parse.go:394:8 +./parse.go:394:16: flow: {heap} ← buf: +./parse.go:394:16: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:394:16: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:404:9: append(buf, d[pending:at]...) escapes to heap in (*parser).value: +./parse.go:404:9: flow: buf ← &{storage for append(buf, d[pending:at]...)}: +./parse.go:404:9: from append(buf, d[pending:at]...) (spill) at ./parse.go:404:9 +./parse.go:404:9: from buf = append(buf, d[pending:at]...) (assign) at ./parse.go:404:9 +./parse.go:404:9: flow: {heap} ← buf: +./parse.go:404:9: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:404:9: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:405:16: append(buf, c) escapes to heap in (*parser).value: +./parse.go:405:16: flow: buf ← &{storage for append(buf, c)}: +./parse.go:405:16: from append(buf, c) (spill) at ./parse.go:405:16 +./parse.go:405:16: from buf = append(buf, c) (assign) at ./parse.go:405:8 +./parse.go:405:16: flow: {heap} ← buf: +./parse.go:405:16: from buf[:0] (slice) at ./parse.go:337:28 +./parse.go:405:16: from p.buf = buf[:0] (assign) at ./parse.go:337:23 +./parse.go:352:34: &SyntaxError{...} escapes to heap in (*parser).value: +./parse.go:352:34: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:352:34: from &SyntaxError{...} (spill) at ./parse.go:352:34 +./parse.go:352:34: from &SyntaxError{...} (interface-converted) at ./parse.go:352:34 +./parse.go:352:34: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:352:34 +./parse.go:352:34: flow: ~r4 ← ~r0: +./parse.go:352:34: from return "", 0, 0, openEnd(0), ~r0 (return) at ./parse.go:352:5 +./parse.go:360:35: &SyntaxError{...} escapes to heap in (*parser).value: +./parse.go:360:35: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:360:35: from &SyntaxError{...} (spill) at ./parse.go:360:35 +./parse.go:360:35: from &SyntaxError{...} (interface-converted) at ./parse.go:360:35 +./parse.go:360:35: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:360:35 +./parse.go:360:35: flow: ~r4 ← ~r0: +./parse.go:360:35: from return "", 0, 0, openEnd(0), ~r0 (return) at ./parse.go:360:6 +./parse.go:386:34: &SyntaxError{...} escapes to heap in (*parser).value: +./parse.go:386:34: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:386:34: from &SyntaxError{...} (spill) at ./parse.go:386:34 +./parse.go:386:34: from &SyntaxError{...} (interface-converted) at ./parse.go:386:34 +./parse.go:386:34: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:386:34 +./parse.go:386:34: flow: ~r4 ← ~r0: +./parse.go:386:34: from return "", 0, 0, openEnd(0), ~r0 (return) at ./parse.go:386:5 +./parse.go:410:32: &SyntaxError{...} escapes to heap in (*parser).value: +./parse.go:410:32: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:410:32: from &SyntaxError{...} (spill) at ./parse.go:410:32 +./parse.go:410:32: from &SyntaxError{...} (interface-converted) at ./parse.go:410:32 +./parse.go:410:32: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:410:32 +./parse.go:410:32: flow: ~r4 ← ~r0: +./parse.go:410:32: from return "", 0, 0, openEnd(0), ~r0 (return) at ./parse.go:410:3 +./parse.go:417:16: string(buf) escapes to heap in (*parser).value: +./parse.go:417:16: flow: ~r0 ← &{storage for string(buf)}: +./parse.go:417:16: from string(buf) (spill) at ./parse.go:417:16 +./parse.go:417:16: from return string(buf), i, lines, open, nil (return) at ./parse.go:417:2 +./parse.go:334:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).value with derefs=1: +./parse.go:334:7: flow: p ← p: +./parse.go:334:7: from p, line, msg := p, p.line + lines, "unterminated quote" (assign-pair) at ./parse.go:410:32 +./parse.go:334:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:334:7: from p.name (dot of pointer) at ./parse.go:410:32 +./parse.go:334:7: from SyntaxError{...} (struct literal element) at ./parse.go:410:32 +./parse.go:334:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).value with derefs=1: +./parse.go:334:7: flow: p ← p: +./parse.go:334:7: from p, line, msg := p, p.line + lines, "invalid escape" (assign-pair) at ./parse.go:386:34 +./parse.go:334:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:334:7: from p.name (dot of pointer) at ./parse.go:386:34 +./parse.go:334:7: from SyntaxError{...} (struct literal element) at ./parse.go:386:34 +./parse.go:334:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).value with derefs=1: +./parse.go:334:7: flow: p ← p: +./parse.go:334:7: from p, line, msg := p, p.line + lines + 1, "unterminated quote" (assign-pair) at ./parse.go:360:35 +./parse.go:334:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:334:7: from p.name (dot of pointer) at ./parse.go:360:35 +./parse.go:334:7: from SyntaxError{...} (struct literal element) at ./parse.go:360:35 +./parse.go:334:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).value with derefs=1: +./parse.go:334:7: flow: p ← p: +./parse.go:334:7: from p, line, msg := p, p.line + lines, "unterminated quote" (assign-pair) at ./parse.go:352:34 +./parse.go:334:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:334:7: from p.name (dot of pointer) at ./parse.go:352:34 +./parse.go:334:7: from SyntaxError{...} (struct literal element) at ./parse.go:352:34 +./parse.go:334:7: leaking param content: p +./parse.go:337:8: func literal does not escape +./parse.go:352:34: &SyntaxError{...} escapes to heap +./parse.go:360:35: &SyntaxError{...} escapes to heap +./parse.go:362:10: append escapes to heap +./parse.go:367:9: append escapes to heap +./parse.go:378:17: append escapes to heap +./parse.go:380:17: append escapes to heap +./parse.go:382:17: append escapes to heap +./parse.go:384:17: append escapes to heap +./parse.go:386:34: &SyntaxError{...} escapes to heap +./parse.go:390:9: append escapes to heap +./parse.go:394:16: append escapes to heap +./parse.go:404:9: append escapes to heap +./parse.go:405:16: append escapes to heap +./parse.go:410:32: &SyntaxError{...} escapes to heap +./parse.go:417:16: string(buf) escapes to heap +./parse.go:296:7: parameter p leaks to {heap} for (*parser).entry with derefs=1: +./parse.go:296:7: flow: {temp} ← p: +./parse.go:296:7: from (*parser).addEntry(p, start, i, name, "") (call parameter) at ./parse.go:310:13 +./parse.go:296:7: flow: {heap} ← *{temp}: +./parse.go:314:19: &SyntaxError{...} escapes to heap in (*parser).entry: +./parse.go:314:19: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:314:19: from &SyntaxError{...} (spill) at ./parse.go:314:19 +./parse.go:314:19: from &SyntaxError{...} (interface-converted) at ./parse.go:314:19 +./parse.go:314:19: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:314:19 +./parse.go:314:19: flow: ~r0 ← ~r0: +./parse.go:314:19: from return ~r0 (return) at ./parse.go:314:3 +./parse.go:296:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).entry with derefs=1: +./parse.go:296:7: flow: p ← p: +./parse.go:296:7: from p, line, msg := p, p.line, "invalid key" (assign-pair) at ./parse.go:314:19 +./parse.go:296:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:296:7: from p.name (dot of pointer) at ./parse.go:314:19 +./parse.go:296:7: from SyntaxError{...} (struct literal element) at ./parse.go:314:19 +./parse.go:296:7: leaking param content: p +./parse.go:314:19: &SyntaxError{...} escapes to heap +./parse.go:174:8: append(p.parsed.items, item{...}) escapes to heap in (*parser).run: +./parse.go:174:8: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:174:8: from append(p.parsed.items, item{...}) (spill) at ./parse.go:174:8 +./parse.go:174:8: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:174:8 +./parse.go:185:9: append(p.parsed.items, item{...}) escapes to heap in (*parser).run: +./parse.go:185:9: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:185:9: from append(p.parsed.items, item{...}) (spill) at ./parse.go:185:9 +./parse.go:185:9: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:185:9 +./parse.go:188:9: append(p.parsed.items, item{...}) escapes to heap in (*parser).run: +./parse.go:188:9: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:188:9: from append(p.parsed.items, item{...}) (spill) at ./parse.go:188:9 +./parse.go:188:9: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:188:9 +./parse.go:199:9: append(p.parsed.items, item{...}) escapes to heap in (*parser).run: +./parse.go:199:9: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:199:9: from append(p.parsed.items, item{...}) (spill) at ./parse.go:199:9 +./parse.go:199:9: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:199:9 +./parse.go:171:7: parameter p leaks to {heap} for (*parser).run with derefs=1: +./parse.go:171:7: flow: {temp} ← p: +./parse.go:171:7: from (*parser).header(p) (call parameter) at ./parse.go:201:22 +./parse.go:171:7: flow: {heap} ← *{temp}: +./parse.go:209:20: &SyntaxError{...} escapes to heap in (*parser).run: +./parse.go:209:20: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:209:20: from &SyntaxError{...} (spill) at ./parse.go:209:20 +./parse.go:209:20: from &SyntaxError{...} (interface-converted) at ./parse.go:209:20 +./parse.go:209:20: from ~r0 = &SyntaxError{...} (assign-pair) at ./parse.go:209:20 +./parse.go:209:20: flow: ~r0 ← ~r0: +./parse.go:209:20: from return ~r0 (return) at ./parse.go:209:4 +./parse.go:171:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).run with derefs=1: +./parse.go:171:7: flow: p ← p: +./parse.go:171:7: from p, line, msg := p, p.line, "invalid line" (assign-pair) at ./parse.go:209:20 +./parse.go:171:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:171:7: from p.name (dot of pointer) at ./parse.go:209:20 +./parse.go:171:7: from SyntaxError{...} (struct literal element) at ./parse.go:209:20 +./parse.go:171:7: leaking param content: p +./parse.go:173:20: string(bytes.a) does not escape +./parse.go:173:20: string(bytes.b) does not escape +./parse.go:174:8: append escapes to heap +./parse.go:185:9: append escapes to heap +./parse.go:188:9: append escapes to heap +./parse.go:199:9: append escapes to heap +./parse.go:209:20: &SyntaxError{...} escapes to heap +./parse.go:104:16: make([]item, 0, 2 * bytes.Count(data, []byte{...}) + 4) escapes to heap in parse: +./parse.go:104:16: flow: {heap} ← &{storage for make([]item, 0, 2 * bytes.Count(data, []byte{...}) + 4)}: +./parse.go:104:16: from make([]item, 0, 2 * bytes.Count(data, []byte{...}) + 4) (spill) at ./parse.go:104:16 +./parse.go:104:16: from p.parsed.items = make([]item, 0, 2 * bytes.Count(data, []byte{...}) + 4) (assign) at ./parse.go:104:10 +./parse.go:105:23: []sectionKey{...} escapes to heap in parse: +./parse.go:105:23: flow: {heap} ← &{storage for []sectionKey{...}}: +./parse.go:105:23: from []sectionKey{...} (spill) at ./parse.go:105:23 +./parse.go:105:23: from p.parsed.secs = []sectionKey{...} (assign) at ./parse.go:105:9 +./parse.go:102:12: parameter name leaks to {heap} for parse with derefs=0: +./parse.go:102:12: flow: {storage for &parser{...}} ← name: +./parse.go:102:12: from parser{...} (struct literal element) at ./parse.go:103:14 +./parse.go:102:12: flow: p ← &{storage for &parser{...}}: +./parse.go:102:12: from &parser{...} (spill) at ./parse.go:103:7 +./parse.go:102:12: from p := &parser{...} (assign) at ./parse.go:103:4 +./parse.go:102:12: flow: {temp} ← p: +./parse.go:102:12: from (*parser).run(p) (call parameter) at ./parse.go:106:17 +./parse.go:102:12: flow: {heap} ← *{temp}: +./parse.go:102:25: parameter data leaks to {heap} for parse with derefs=0: +./parse.go:102:25: flow: {storage for &parser{...}} ← data: +./parse.go:102:25: from parser{...} (struct literal element) at ./parse.go:103:14 +./parse.go:102:25: flow: p ← &{storage for &parser{...}}: +./parse.go:102:25: from &parser{...} (spill) at ./parse.go:103:7 +./parse.go:102:25: from p := &parser{...} (assign) at ./parse.go:103:4 +./parse.go:102:25: flow: {temp} ← p: +./parse.go:102:25: from (*parser).run(p) (call parameter) at ./parse.go:106:17 +./parse.go:102:25: flow: {heap} ← *{temp}: +./parse.go:103:62: make(map[sectionKey]int32) escapes to heap in parse: +./parse.go:103:62: flow: {storage for &parser{...}} ← &{storage for make(map[sectionKey]int32)}: +./parse.go:103:62: from make(map[sectionKey]int32) (spill) at ./parse.go:103:62 +./parse.go:103:62: from parser{...} (struct literal element) at ./parse.go:103:14 +./parse.go:103:62: flow: p ← &{storage for &parser{...}}: +./parse.go:103:62: from &parser{...} (spill) at ./parse.go:103:7 +./parse.go:103:62: from p := &parser{...} (assign) at ./parse.go:103:4 +./parse.go:103:62: flow: {temp} ← p: +./parse.go:103:62: from (*parser).run(p) (call parameter) at ./parse.go:106:17 +./parse.go:103:62: flow: {heap} ← *{temp}: +./parse.go:103:97: make(map[string]string) escapes to heap in parse: +./parse.go:103:97: flow: {storage for &parser{...}} ← &{storage for make(map[string]string)}: +./parse.go:103:97: from make(map[string]string) (spill) at ./parse.go:103:97 +./parse.go:103:97: from parser{...} (struct literal element) at ./parse.go:103:14 +./parse.go:103:97: flow: p ← &{storage for &parser{...}}: +./parse.go:103:97: from &parser{...} (spill) at ./parse.go:103:7 +./parse.go:103:97: from p := &parser{...} (assign) at ./parse.go:103:4 +./parse.go:103:97: flow: {temp} ← p: +./parse.go:103:97: from (*parser).run(p) (call parameter) at ./parse.go:106:17 +./parse.go:103:97: flow: {heap} ← *{temp}: +./parse.go:102:12: parameter name leaks to ~r0 for parse with derefs=0: +./parse.go:102:12: flow: {storage for &parser{...}} ← name: +./parse.go:102:12: from parser{...} (struct literal element) at ./parse.go:103:14 +./parse.go:102:12: flow: p ← &{storage for &parser{...}}: +./parse.go:102:12: from &parser{...} (spill) at ./parse.go:103:7 +./parse.go:102:12: from p := &parser{...} (assign) at ./parse.go:103:4 +./parse.go:102:12: flow: ~r0 ← *p: +./parse.go:102:12: from p.parsed (dot of pointer) at ./parse.go:109:10 +./parse.go:102:12: from return p.parsed, nil (return) at ./parse.go:109:2 +./parse.go:102:25: parameter data leaks to ~r0 for parse with derefs=0: +./parse.go:102:25: flow: {storage for &parser{...}} ← data: +./parse.go:102:25: from parser{...} (struct literal element) at ./parse.go:103:14 +./parse.go:102:25: flow: p ← &{storage for &parser{...}}: +./parse.go:102:25: from &parser{...} (spill) at ./parse.go:103:7 +./parse.go:102:25: from p := &parser{...} (assign) at ./parse.go:103:4 +./parse.go:102:25: flow: ~r0 ← *p: +./parse.go:102:25: from p.parsed (dot of pointer) at ./parse.go:109:10 +./parse.go:102:25: from return p.parsed, nil (return) at ./parse.go:109:2 +./parse.go:102:12: leaking param: name +./parse.go:102:25: leaking param: data +./parse.go:103:7: &parser{...} does not escape +./parse.go:103:62: make(map[sectionKey]int32) escapes to heap +./parse.go:103:97: make(map[string]string) escapes to heap +./parse.go:104:16: make([]item, 0, 2 * bytes.Count(data, []byte{...}) + 4) escapes to heap +./parse.go:104:54: []byte{...} does not escape +./parse.go:105:23: []sectionKey{...} escapes to heap +./edit.go:111:7: parameter f leaks to {heap} for (*File).apply with derefs=1: +./edit.go:111:7: flow: {heap} ← *f: +./edit.go:111:7: from f.name (dot of pointer) at ./edit.go:121:19 +./edit.go:111:7: from parse(f.name, data) (call parameter) at ./edit.go:121:17 +./edit.go:113:14: make([]byte, 0, len(f.data) + 64) escapes to heap in (*File).apply: +./edit.go:113:14: flow: data ← &{storage for make([]byte, 0, len(f.data) + 64)}: +./edit.go:113:14: from make([]byte, 0, len(f.data) + 64) (spill) at ./edit.go:113:14 +./edit.go:113:14: from data := make([]byte, 0, len(f.data) + 64) (assign) at ./edit.go:113:7 +./edit.go:113:14: flow: {heap} ← data: +./edit.go:113:14: from parse(f.name, data) (call parameter) at ./edit.go:121:17 +./edit.go:116:16: append(data, f.data[at:s.start]...) escapes to heap in (*File).apply: +./edit.go:116:16: flow: data ← &{storage for append(data, f.data[at:s.start]...)}: +./edit.go:116:16: from append(data, f.data[at:s.start]...) (spill) at ./edit.go:116:16 +./edit.go:116:16: from data = append(data, f.data[at:s.start]...) (assign) at ./edit.go:116:8 +./edit.go:116:16: flow: {heap} ← data: +./edit.go:116:16: from parse(f.name, data) (call parameter) at ./edit.go:121:17 +./edit.go:117:16: append(data, s.text...) escapes to heap in (*File).apply: +./edit.go:117:16: flow: data ← &{storage for append(data, s.text...)}: +./edit.go:117:16: from append(data, s.text...) (spill) at ./edit.go:117:16 +./edit.go:117:16: from data = append(data, s.text...) (assign) at ./edit.go:117:8 +./edit.go:117:16: flow: {heap} ← data: +./edit.go:117:16: from parse(f.name, data) (call parameter) at ./edit.go:121:17 +./edit.go:120:15: append(data, f.data[at:]...) escapes to heap in (*File).apply: +./edit.go:120:15: flow: data ← &{storage for append(data, f.data[at:]...)}: +./edit.go:120:15: from append(data, f.data[at:]...) (spill) at ./edit.go:120:15 +./edit.go:120:15: from data = append(data, f.data[at:]...) (assign) at ./edit.go:120:7 +./edit.go:120:15: flow: {heap} ← data: +./edit.go:120:15: from parse(f.name, data) (call parameter) at ./edit.go:121:17 +./edit.go:111:34: parameter splices leaks to {heap} for (*File).apply with derefs=1: +./edit.go:111:34: flow: slices.x ← splices: +./edit.go:111:34: from slices..dict, slices.x, slices.cmp := &slices..dict.SortFunc[[]github.com/git-bug/gitconfig.splice,github.com/git-bug/gitconfig.splice], splices, func literal (assign-pair) at ./edit.go:112:17 +./edit.go:111:34: flow: {temp} ← slices.x: +./edit.go:111:34: from slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[11]uintptr)(slices..dict[0]), slices.x, 0, slices.n, bits.Len(uint(slices.n)), slices.cmp) (call parameter) at ./edit.go:112:17 +./edit.go:111:34: flow: {heap} ← *{temp}: +./edit.go:122:33: append(out, flatEntry{...}) escapes to heap in (*File).apply: +./edit.go:122:33: flow: out ← &{storage for append(out, flatEntry{...})}: +./edit.go:122:33: from append(out, flatEntry{...}) (spill) at ./edit.go:122:33 +./edit.go:122:33: from out = append(out, flatEntry{...}) (assign) at ./edit.go:122:33 +./edit.go:122:33: flow: ~r0 ← out: +./edit.go:122:33: from ~r0 = out (assign-pair) at ./edit.go:122:33 +./edit.go:122:33: flow: {heap} ← ~r0: +./edit.go:122:33: from check(~r0, ~r0) (call parameter) at ./edit.go:122:25 +./edit.go:122:52: append(out, flatEntry{...}) escapes to heap in (*File).apply: +./edit.go:122:52: flow: out ← &{storage for append(out, flatEntry{...})}: +./edit.go:122:52: from append(out, flatEntry{...}) (spill) at ./edit.go:122:52 +./edit.go:122:52: from out = append(out, flatEntry{...}) (assign) at ./edit.go:122:52 +./edit.go:122:52: flow: ~r0 ← out: +./edit.go:122:52: from ~r0 = out (assign-pair) at ./edit.go:122:52 +./edit.go:122:52: flow: {heap} ← ~r0: +./edit.go:122:52: from check(~r0, ~r0) (call parameter) at ./edit.go:122:25 +./edit.go:123:10: &KeyError{...} escapes to heap in (*File).apply: +./edit.go:123:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./edit.go:123:10: from &KeyError{...} (spill) at ./edit.go:123:10 +./edit.go:123:10: from &KeyError{...} (interface-converted) at ./edit.go:123:10 +./edit.go:123:10: from return &KeyError{...} (return) at ./edit.go:123:3 +./edit.go:111:22: parameter key leaks to {storage for &KeyError{...}} for (*File).apply with derefs=0: +./edit.go:111:22: flow: {storage for &KeyError{...}} ← key: +./edit.go:111:22: from KeyError{...} (struct literal element) at ./edit.go:123:19 +./edit.go:111:7: leaking param content: f +./edit.go:111:22: leaking param: key +./edit.go:111:34: leaking param content: splices +./edit.go:111:52: check does not escape +./edit.go:112:27: func literal does not escape +./edit.go:113:14: make([]byte, 0, len(f.data) + 64) escapes to heap +./edit.go:116:16: append escapes to heap +./edit.go:117:16: append escapes to heap +./edit.go:120:15: append escapes to heap +./edit.go:122:33: append escapes to heap +./edit.go:122:52: append escapes to heap +./edit.go:123:10: &KeyError{...} escapes to heap +./edit.go:133:15: checkKey capturing by value: k (addr=false assign=false width=56) +./edit.go:133:22: checkKey capturing by value: want (addr=false assign=false width=24) +./edit.go:133:37: checkKey capturing by value: implicitAllowed (addr=false assign=false width=1) +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1 with derefs=1: +./edit.go:134:14: flow: entries ← before: +./edit.go:134:14: from entries, _ := before, 0 (assign-pair) at ./edit.go:136:26 +./edit.go:134:14: flow: {temp} ← entries: +./edit.go:134:14: flow: e ← *{temp}: +./edit.go:134:14: from for loop (range-deref) at ./edit.go:136:26 +./edit.go:134:14: flow: {heap} ← e: +./edit.go:134:14: from append(out, e) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1 with derefs=1: +./edit.go:134:22: flow: entries ← after: +./edit.go:134:22: from entries, _ := after, 0 (assign-pair) at ./edit.go:136:49 +./edit.go:134:22: flow: {temp} ← entries: +./edit.go:134:22: flow: e ← *{temp}: +./edit.go:134:22: from for loop (range-deref) at ./edit.go:136:49 +./edit.go:134:22: flow: {heap} ← e: +./edit.go:134:22: from append(out, e) (call parameter) at ./edit.go:136:49 +./edit.go:134:9: func literal escapes to heap in checkKey: +./edit.go:134:9: flow: ~r0 ← &{storage for func literal}: +./edit.go:134:9: from func literal (spill) at ./edit.go:134:9 +./edit.go:134:9: from return func literal (return) at ./edit.go:134:2 +./edit.go:133:15: parameter k leaks to {storage for func literal} for checkKey with derefs=0: +./edit.go:133:15: flow: {storage for func literal} ← k: +./edit.go:133:15: from k (captured by a closure) at ./edit.go:135:50 +./edit.go:133:22: parameter want leaks to {storage for func literal} for checkKey with derefs=0: +./edit.go:133:22: flow: {storage for func literal} ← want: +./edit.go:133:22: from want (captured by a closure) at ./edit.go:140:22 +./edit.go:133:37: parameter implicitAllowed leaks to {storage for func literal} for checkKey with derefs=0: +./edit.go:133:37: flow: {storage for func literal} ← implicitAllowed: +./edit.go:133:37: from implicitAllowed (captured by a closure) at ./edit.go:144:44 +./edit.go:133:15: leaking param: k +./edit.go:133:22: leaking param: want +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:134:9: func literal escapes to heap +./edit.go:136:26: append does not escape +./edit.go:136:49: append does not escape +./edit.go:139:16: append does not escape +./edit.go:152:13: parameter entries leaks to {heap} for filter with derefs=1: +./edit.go:152:13: flow: {temp} ← entries: +./edit.go:152:13: flow: e ← *{temp}: +./edit.go:152:13: from for loop (range-deref) at ./edit.go:154:14 +./edit.go:152:13: flow: {heap} ← e: +./edit.go:152:13: from keep(e) (call parameter) at ./edit.go:155:10 +./edit.go:156:16: append(out, e) escapes to heap in filter: +./edit.go:156:16: flow: out ← &{storage for append(out, e)}: +./edit.go:156:16: from append(out, e) (spill) at ./edit.go:156:16 +./edit.go:156:16: from out = append(out, e) (assign) at ./edit.go:156:8 +./edit.go:156:16: flow: ~r0 ← out: +./edit.go:156:16: from return out (return) at ./edit.go:159:2 +./edit.go:152:13: leaking param content: entries +./edit.go:152:34: keep does not escape +./edit.go:156:16: append escapes to heap +./edit.go:166:7: f does not escape +./edit.go:174:7: f does not escape +./edit.go:184:7: f does not escape +./edit.go:194:7: f does not escape +./edit.go:203:7: f does not escape +./edit.go:232:15: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:232:15: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:232:15: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:232:15 +./edit.go:232:15: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:232:15 +./edit.go:232:15: append(strings.b.buf, strings.c) escapes to heap in quoteValue: +./edit.go:232:15: flow: {heap} ← &{storage for append(strings.b.buf, strings.c)}: +./edit.go:232:15: from append(strings.b.buf, strings.c) (spill) at ./edit.go:232:15 +./edit.go:232:15: from strings.b.buf = append(strings.b.buf, strings.c) (assign) at ./edit.go:232:15 +./edit.go:237:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:237:16: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:237:16: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:237:16 +./edit.go:237:16: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:237:16 +./edit.go:237:16: append(strings.b.buf, strings.c) escapes to heap in quoteValue: +./edit.go:237:16: flow: {heap} ← &{storage for append(strings.b.buf, strings.c)}: +./edit.go:237:16: from append(strings.b.buf, strings.c) (spill) at ./edit.go:237:16 +./edit.go:237:16: from strings.b.buf = append(strings.b.buf, strings.c) (assign) at ./edit.go:237:16 +./edit.go:238:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:238:16: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:238:16: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:238:16 +./edit.go:238:16: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:238:16 +./edit.go:238:16: append(strings.b.buf, strings.c) escapes to heap in quoteValue: +./edit.go:238:16: flow: {heap} ← &{storage for append(strings.b.buf, strings.c)}: +./edit.go:238:16: from append(strings.b.buf, strings.c) (spill) at ./edit.go:238:16 +./edit.go:238:16: from strings.b.buf = append(strings.b.buf, strings.c) (assign) at ./edit.go:238:16 +./edit.go:240:18: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:240:18: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:240:18: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:240:18 +./edit.go:240:18: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:240:18 +./edit.go:240:18: append(strings.b.buf, strings.s...) escapes to heap in quoteValue: +./edit.go:240:18: flow: {heap} ← &{storage for append(strings.b.buf, strings.s...)}: +./edit.go:240:18: from append(strings.b.buf, strings.s...) (spill) at ./edit.go:240:18 +./edit.go:240:18: from strings.b.buf = append(strings.b.buf, strings.s...) (assign) at ./edit.go:240:18 +./edit.go:242:18: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:242:18: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:242:18: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:242:18 +./edit.go:242:18: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:242:18 +./edit.go:242:18: append(strings.b.buf, strings.s...) escapes to heap in quoteValue: +./edit.go:242:18: flow: {heap} ← &{storage for append(strings.b.buf, strings.s...)}: +./edit.go:242:18: from append(strings.b.buf, strings.s...) (spill) at ./edit.go:242:18 +./edit.go:242:18: from strings.b.buf = append(strings.b.buf, strings.s...) (assign) at ./edit.go:242:18 +./edit.go:244:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:244:16: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:244:16: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:244:16 +./edit.go:244:16: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:244:16 +./edit.go:244:16: append(strings.b.buf, strings.c) escapes to heap in quoteValue: +./edit.go:244:16: flow: {heap} ← &{storage for append(strings.b.buf, strings.c)}: +./edit.go:244:16: from append(strings.b.buf, strings.c) (spill) at ./edit.go:244:16 +./edit.go:244:16: from strings.b.buf = append(strings.b.buf, strings.c) (assign) at ./edit.go:244:16 +./edit.go:248:15: "strings: illegal use of non-zero Builder copied by value" escapes to heap in quoteValue: +./edit.go:248:15: flow: {heap} ← &{storage for "strings: illegal use of non-zero Builder copied by value"}: +./edit.go:248:15: from "strings: illegal use of non-zero Builder copied by value" (spill) at ./edit.go:248:15 +./edit.go:248:15: from panic("strings: illegal use of non-zero Builder copied by value") (call parameter) at ./edit.go:248:15 +./edit.go:248:15: append(strings.b.buf, strings.c) escapes to heap in quoteValue: +./edit.go:248:15: flow: {heap} ← &{storage for append(strings.b.buf, strings.c)}: +./edit.go:248:15: from append(strings.b.buf, strings.c) (spill) at ./edit.go:248:15 +./edit.go:248:15: from strings.b.buf = append(strings.b.buf, strings.c) (assign) at ./edit.go:248:15 +./edit.go:228:17: v does not escape +./edit.go:232:15: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:232:15: append escapes to heap +./edit.go:237:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:237:16: append escapes to heap +./edit.go:238:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:238:16: append escapes to heap +./edit.go:240:18: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:240:18: append escapes to heap +./edit.go:242:18: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:242:18: append escapes to heap +./edit.go:244:16: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:244:16: append escapes to heap +./edit.go:248:15: "strings: illegal use of non-zero Builder copied by value" escapes to heap +./edit.go:248:15: append escapes to heap +./edit.go:222:49: "\t" + name + " = " + quoteValue(value) + "\n" escapes to heap in formatEntry: +./edit.go:222:49: flow: ~r0 ← &{storage for "\t" + name + " = " + quoteValue(value) + "\n"}: +./edit.go:222:49: from "\t" + name + " = " + quoteValue(value) + "\n" (spill) at ./edit.go:222:49 +./edit.go:222:49: from return "\t" + name + " = " + quoteValue(value) + "\n" (return) at ./edit.go:222:2 +./edit.go:221:18: name does not escape +./edit.go:221:24: value does not escape +./edit.go:222:49: "\t" + name + " = " + quoteValue(value) + "\n" escapes to heap +./edit.go:258:26: "strings.NewReplacer: odd argument count" escapes to heap in formatHeader: +./edit.go:258:26: flow: {heap} ← &{storage for "strings.NewReplacer: odd argument count"}: +./edit.go:258:26: from "strings.NewReplacer: odd argument count" (spill) at ./edit.go:258:26 +./edit.go:258:26: from panic("strings.NewReplacer: odd argument count") (call parameter) at ./edit.go:258:26 +./edit.go:254:28: parameter sub leaks to {heap} for formatHeader with derefs=0: +./edit.go:254:28: flow: {heap} ← sub: +./edit.go:254:28: from (*strings.Replacer).Replace(r, sub) (call parameter) at ./edit.go:259:42 +./edit.go:258:26: &strings.Replacer{...} escapes to heap in formatHeader: +./edit.go:258:26: flow: ~r0 ← &{storage for &strings.Replacer{...}}: +./edit.go:258:26: from &strings.Replacer{...} (spill) at ./edit.go:258:26 +./edit.go:258:26: from ~r0 = &strings.Replacer{...} (assign-pair) at ./edit.go:258:26 +./edit.go:258:26: flow: r ← ~r0: +./edit.go:258:26: from r := ~r0 (assign) at ./edit.go:258:4 +./edit.go:258:26: flow: {heap} ← r: +./edit.go:258:26: from (*strings.Replacer).Replace(r, sub) (call parameter) at ./edit.go:259:42 +./edit.go:258:26: append(([]string)(nil), strings.oldnew...) escapes to heap in formatHeader: +./edit.go:258:26: flow: {storage for &strings.Replacer{...}} ← &{storage for append(([]string)(nil), strings.oldnew...)}: +./edit.go:258:26: from append(([]string)(nil), strings.oldnew...) (spill) at ./edit.go:258:26 +./edit.go:258:26: from strings.Replacer{...} (struct literal element) at ./edit.go:258:26 +./edit.go:256:24: "[" + section + "]" escapes to heap in formatHeader: +./edit.go:256:24: flow: ~r0 ← &{storage for "[" + section + "]"}: +./edit.go:256:24: from "[" + section + "]" (spill) at ./edit.go:256:24 +./edit.go:256:24: from return "[" + section + "]" (return) at ./edit.go:256:3 +./edit.go:259:48: "[" + section + " \"" + (*strings.Replacer).Replace(r, sub) + "\"]" escapes to heap in formatHeader: +./edit.go:259:48: flow: ~r0 ← &{storage for "[" + section + " \"" + (*strings.Replacer).Replace(r, sub) + "\"]"}: +./edit.go:259:48: from "[" + section + " \"" + (*strings.Replacer).Replace(r, sub) + "\"]" (spill) at ./edit.go:259:48 +./edit.go:259:48: from return "[" + section + " \"" + (*strings.Replacer).Replace(r, sub) + "\"]" (return) at ./edit.go:259:2 +./edit.go:254:19: section does not escape +./edit.go:254:28: leaking param: sub +./edit.go:256:24: "[" + section + "]" escapes to heap +./edit.go:258:26: ... argument does not escape +./edit.go:258:26: "strings.NewReplacer: odd argument count" escapes to heap +./edit.go:258:26: &strings.Replacer{...} escapes to heap +./edit.go:258:26: append escapes to heap +./edit.go:259:48: "[" + section + " \"" + (*strings.Replacer).Replace(r, sub) + "\"]" escapes to heap +./edit.go:284:7: f does not escape +./edit.go:267:15: "\n" + line escapes to heap in (*File).replaceEntry: +./edit.go:267:15: flow: line ← &{storage for "\n" + line}: +./edit.go:267:15: from "\n" + line (spill) at ./edit.go:267:15 +./edit.go:267:15: from line = "\n" + line (assign) at ./edit.go:267:8 +./edit.go:267:15: flow: ~r0 ← line: +./edit.go:267:15: from splice{...} (struct literal element) at ./edit.go:269:15 +./edit.go:267:15: from return splice{...} (return) at ./edit.go:269:2 +./edit.go:264:21: "\t" + name + " = " + quoteValue(value) + "\n" escapes to heap in (*File).replaceEntry: +./edit.go:264:21: flow: ~r0 ← &{storage for "\t" + name + " = " + quoteValue(value) + "\n"}: +./edit.go:264:21: from "\t" + name + " = " + quoteValue(value) + "\n" (spill) at ./edit.go:264:21 +./edit.go:264:21: from ~r0 = "\t" + name + " = " + quoteValue(value) + "\n" (assign-pair) at ./edit.go:264:21 +./edit.go:264:21: flow: line ← ~r0: +./edit.go:264:21: from line := ~r0 (assign) at ./edit.go:264:7 +./edit.go:264:21: flow: ~r0 ← line: +./edit.go:264:21: from splice{...} (struct literal element) at ./edit.go:269:15 +./edit.go:264:21: from return splice{...} (return) at ./edit.go:269:2 +./edit.go:263:7: f does not escape +./edit.go:263:36: name does not escape +./edit.go:263:42: value does not escape +./edit.go:264:21: "\t" + name + " = " + quoteValue(value) + "\n" escapes to heap +./edit.go:267:15: "\n" + line escapes to heap +./edit.go:274:7: f does not escape +./edit.go:346:7: f does not escape +./edit.go:346:34: removed does not escape +./edit.go:346:43: trailing does not escape +./edit.go:334:32: a does not escape +./edit.go:334:35: b does not escape +./edit.go:341:18: append(merged, sp) escapes to heap in (*File).unsetEntries: +./edit.go:341:18: flow: merged ← &{storage for append(merged, sp)}: +./edit.go:341:18: from append(merged, sp) (spill) at ./edit.go:341:18 +./edit.go:341:18: from merged = append(merged, sp) (assign) at ./edit.go:341:10 +./edit.go:341:18: flow: ~r0 ← merged: +./edit.go:341:18: from return merged (return) at ./edit.go:343:2 +./edit.go:313:7: f does not escape +./edit.go:313:29: idx does not escape +./edit.go:314:17: make(map[int]bool, len(idx)) does not escape +./edit.go:319:18: make(map[int]bool) does not escape +./edit.go:330:19: append does not escape +./edit.go:334:27: func literal does not escape +./edit.go:341:18: append escapes to heap +./edit.go:398:39: parameter sub leaks to {heap} for (*File).insert with derefs=0: +./edit.go:398:39: flow: {heap} ← sub: +./edit.go:398:39: from formatHeader(section, sub, k.HasSubsection) (call parameter) at ./edit.go:408:72 +./edit.go:408:111: ~r0 + formatHeader(section, sub, k.HasSubsection) + "\n" + line escapes to heap in (*File).insert: +./edit.go:408:111: flow: ~r0 ← &{storage for ~r0 + formatHeader(section, sub, k.HasSubsection) + "\n" + line}: +./edit.go:408:111: from ~r0 + formatHeader(section, sub, k.HasSubsection) + "\n" + line (spill) at ./edit.go:408:111 +./edit.go:408:111: from splice{...} (struct literal element) at ./edit.go:408:16 +./edit.go:408:111: from return splice{...} (return) at ./edit.go:408:3 +./edit.go:427:58: ~r0 + line escapes to heap in (*File).insert: +./edit.go:427:58: flow: ~r0 ← &{storage for ~r0 + line}: +./edit.go:427:58: from ~r0 + line (spill) at ./edit.go:427:58 +./edit.go:427:58: from splice{...} (struct literal element) at ./edit.go:427:16 +./edit.go:427:58: from return splice{...} (return) at ./edit.go:427:3 +./edit.go:435:31: "\n" + line escapes to heap in (*File).insert: +./edit.go:435:31: flow: ~r0 ← &{storage for "\n" + line}: +./edit.go:435:31: from "\n" + line (spill) at ./edit.go:435:31 +./edit.go:435:31: from splice{...} (struct literal element) at ./edit.go:435:15 +./edit.go:435:31: from return splice{...} (return) at ./edit.go:435:2 +./edit.go:406:21: "\t" + name + " = " + quoteValue(value) + "\n" escapes to heap in (*File).insert: +./edit.go:406:21: flow: ~r0 ← &{storage for "\t" + name + " = " + quoteValue(value) + "\n"}: +./edit.go:406:21: from "\t" + name + " = " + quoteValue(value) + "\n" (spill) at ./edit.go:406:21 +./edit.go:406:21: from ~r0 = "\t" + name + " = " + quoteValue(value) + "\n" (assign-pair) at ./edit.go:406:21 +./edit.go:406:21: flow: line ← ~r0: +./edit.go:406:21: from line := ~r0 (assign) at ./edit.go:406:7 +./edit.go:406:21: flow: ~r0 ← line: +./edit.go:406:21: from splice{...} (struct literal element) at ./edit.go:425:17 +./edit.go:406:21: from return splice{...} (return) at ./edit.go:425:4 +./edit.go:398:7: f does not escape +./edit.go:398:23: k does not escape +./edit.go:398:30: section does not escape +./edit.go:398:39: leaking param: sub +./edit.go:398:44: name does not escape +./edit.go:398:50: value does not escape +./edit.go:406:21: "\t" + name + " = " + quoteValue(value) + "\n" escapes to heap +./edit.go:408:111: ~r0 + formatHeader(section, sub, k.HasSubsection) + "\n" + line escapes to heap +./edit.go:427:58: ~r0 + line escapes to heap +./edit.go:435:31: "\n" + line escapes to heap +./edit.go:441:10: &KeyError{...} escapes to heap in sectionArg: +./edit.go:441:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./edit.go:441:10: from &KeyError{...} (spill) at ./edit.go:441:10 +./edit.go:441:10: from &KeyError{...} (interface-converted) at ./edit.go:441:10 +./edit.go:441:10: from return &KeyError{...} (return) at ./edit.go:441:3 +./edit.go:439:17: parameter section leaks to {storage for &KeyError{...}} for sectionArg with derefs=0: +./edit.go:439:17: flow: {storage for &KeyError{...}} ← section: +./edit.go:439:17: from KeyError{...} (struct literal element) at ./edit.go:441:19 +./edit.go:439:17: leaking param: section +./edit.go:441:10: &KeyError{...} escapes to heap +./edit.go:451:16: append(idx, i) escapes to heap in (*File).headersOf: +./edit.go:451:16: flow: idx ← &{storage for append(idx, i)}: +./edit.go:451:16: from append(idx, i) (spill) at ./edit.go:451:16 +./edit.go:451:16: from idx = append(idx, i) (assign) at ./edit.go:451:8 +./edit.go:451:16: flow: ~r0 ← idx: +./edit.go:451:16: from return idx (return) at ./edit.go:454:2 +./edit.go:447:7: f does not escape +./edit.go:447:26: sk does not escape +./edit.go:451:16: append escapes to heap +./edit.go:459:43: (*File).removeSection capturing by value: sk (addr=false assign=false width=40) +./edit.go:459:30: parameter name leaks to {heap} for (*File).removeSection with derefs=0: +./edit.go:459:30: flow: {heap} ← name: +./edit.go:459:30: from (*File).apply(f, name, splices, func literal) (call parameter) at ./edit.go:475:16 +./edit.go:459:7: parameter f leaks to {heap} for (*File).removeSection with derefs=1: +./edit.go:459:7: flow: {temp} ← f: +./edit.go:459:7: from (*File).apply(f, name, splices, func literal) (call parameter) at ./edit.go:475:16 +./edit.go:459:7: flow: {heap} ← *{temp}: +./edit.go:475:37: parameter before leaks to {heap} for (*File).removeSection.func1 with derefs=1: +./edit.go:475:37: flow: entries ← before: +./edit.go:475:37: from entries, _ := before, 0 (assign-pair) at ./edit.go:476:29 +./edit.go:475:37: flow: {temp} ← entries: +./edit.go:475:37: flow: e ← *{temp}: +./edit.go:475:37: from for loop (range-deref) at ./edit.go:476:29 +./edit.go:475:37: flow: {heap} ← e: +./edit.go:475:37: from append(out, e) (call parameter) at ./edit.go:476:29 +./edit.go:459:7: leaking param content: f +./edit.go:459:30: leaking param: name +./edit.go:459:43: sk does not escape +./edit.go:475:37: leaking param content: before +./edit.go:475:45: after does not escape +./edit.go:460:24: append does not escape +./edit.go:473:19: append does not escape +./edit.go:475:32: func literal does not escape +./edit.go:476:29: append does not escape +./edit.go:483:43: (*File).renameSection capturing by value: sk (addr=false assign=false width=40) +./edit.go:483:47: (*File).renameSection capturing by value: to (addr=false assign=false width=40) +./edit.go:483:62: parameter header leaks to {heap} for (*File).renameSection with derefs=0: +./edit.go:483:62: flow: {heap} ← header: +./edit.go:483:62: from splice{...} (struct literal element) at ./edit.go:495:36 +./edit.go:483:62: from append(splices, splice{...}) (call parameter) at ./edit.go:495:20 +./edit.go:483:30: parameter name leaks to {heap} for (*File).renameSection with derefs=0: +./edit.go:483:30: flow: {heap} ← name: +./edit.go:483:30: from (*File).apply(f, name, splices, func literal) (call parameter) at ./edit.go:522:16 +./edit.go:483:47: parameter to leaks to {heap} for (*File).renameSection with derefs=0: +./edit.go:483:47: flow: {heap} ← to: +./edit.go:483:47: from before[i].sec = to (assign) at ./edit.go:525:19 +./edit.go:508:18: header + "\n" escapes to heap in (*File).renameSection: +./edit.go:508:18: flow: text ← &{storage for header + "\n"}: +./edit.go:508:18: from header + "\n" (spill) at ./edit.go:508:18 +./edit.go:508:18: from text := header + "\n" (assign) at ./edit.go:508:8 +./edit.go:508:18: flow: {heap} ← text: +./edit.go:508:18: from splice{...} (struct literal element) at ./edit.go:519:35 +./edit.go:508:18: from append(splices, splice{...}) (call parameter) at ./edit.go:519:19 +./edit.go:515:27: string(f.data[it.start:it.end]) escapes to heap in (*File).renameSection: +./edit.go:515:27: flow: text ← &{storage for string(f.data[it.start:it.end])}: +./edit.go:515:27: from string(f.data[it.start:it.end]) (spill) at ./edit.go:515:27 +./edit.go:515:27: from text += string(f.data[it.start:it.end]) (assign) at ./edit.go:515:11 +./edit.go:515:27: flow: {heap} ← text: +./edit.go:515:27: from splice{...} (struct literal element) at ./edit.go:519:35 +./edit.go:515:27: from append(splices, splice{...}) (call parameter) at ./edit.go:519:19 +./edit.go:483:7: parameter f leaks to {heap} for (*File).renameSection with derefs=1: +./edit.go:483:7: flow: {temp} ← f: +./edit.go:483:7: from (*File).apply(f, name, splices, func literal) (call parameter) at ./edit.go:522:16 +./edit.go:483:7: flow: {heap} ← *{temp}: +./edit.go:486:10: &KeyError{...} escapes to heap in (*File).renameSection: +./edit.go:486:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./edit.go:486:10: from &KeyError{...} (spill) at ./edit.go:486:10 +./edit.go:486:10: from &KeyError{...} (interface-converted) at ./edit.go:486:10 +./edit.go:486:10: from return &KeyError{...} (return) at ./edit.go:486:3 +./edit.go:483:30: parameter name leaks to {storage for &KeyError{...}} for (*File).renameSection with derefs=0: +./edit.go:483:30: flow: {storage for &KeyError{...}} ← name: +./edit.go:483:30: from KeyError{...} (struct literal element) at ./edit.go:486:19 +./edit.go:483:7: leaking param content: f +./edit.go:483:30: leaking param: name +./edit.go:483:43: sk does not escape +./edit.go:483:47: leaking param: to +./edit.go:483:62: leaking param: header +./edit.go:522:37: before does not escape +./edit.go:522:45: after does not escape +./edit.go:484:24: append does not escape +./edit.go:486:10: &KeyError{...} escapes to heap +./edit.go:495:20: append does not escape +./edit.go:508:18: header + "\n" escapes to heap +./edit.go:515:27: string(f.data[it.start:it.end]) escapes to heap +./edit.go:519:19: append does not escape +./edit.go:522:32: func literal does not escape +./env.go:45:41: "Scope(" + ~r0 + ")" escapes to heap in Scope.String: +./env.go:45:41: flow: ~r0 ← &{storage for "Scope(" + ~r0 + ")"}: +./env.go:45:41: from "Scope(" + ~r0 + ")" (spill) at ./env.go:45:41 +./env.go:45:41: from return "Scope(" + ~r0 + ")" (return) at ./env.go:45:2 +./env.go:45:41: "Scope(" + ~r0 + ")" escapes to heap +./env.go:121:35: osEnviron{} escapes to heap in Discover: +./env.go:121:35: flow: {heap} ← &{storage for osEnviron{}}: +./env.go:121:35: from osEnviron{} (spill) at ./env.go:121:35 +./env.go:121:35: from discover(gitDir, osEnviron{}) (call parameter) at ./env.go:121:17 +./env.go:120:15: parameter gitDir leaks to {heap} for Discover with derefs=0: +./env.go:120:15: flow: {heap} ← gitDir: +./env.go:120:15: from discover(gitDir, osEnviron{}) (call parameter) at ./env.go:121:17 +./env.go:120:15: leaking param: gitDir +./env.go:121:35: osEnviron{} escapes to heap +./load.go:42:35: string(data) escapes to heap in Env.repo: +./load.go:42:35: flow: dir ← &{storage for string(data)}: +./load.go:42:35: from string(data) (spill) at ./load.go:42:35 +./load.go:42:35: from strings.TrimRight(string(data), "\r\n") (call parameter) at ./load.go:42:27 +./load.go:42:35: from dir := strings.TrimRight(string(data), "\r\n") (assign) at ./load.go:42:7 +./load.go:42:35: flow: filepath.path ← dir: +./load.go:42:35: from filepath.path := dir (assign-pair) at ./load.go:46:31 +./load.go:42:35: flow: {temp} ← filepath.path: +./load.go:42:35: from filepathlite.Clean(filepath.path) (call parameter) at ./load.go:46:31 +./load.go:42:35: flow: ~r0 ← {temp}: +./load.go:42:35: from ~r0 = filepathlite.Clean(filepath.path) (assign-pair) at ./load.go:46:31 +./load.go:42:35: flow: {heap} ← ~r0: +./load.go:42:35: from r.commonDir = ~r0 (assign) at ./load.go:46:15 +./load.go:31:7: parameter e leaks to {heap} for Env.repo with derefs=0: +./load.go:31:7: flow: {storage for ... argument} ← e: +./load.go:31:7: from e.GitDir (dot) at ./load.go:39:40 +./load.go:31:7: from ... argument (slice-literal-element) at ./load.go:39:38 +./load.go:31:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:31:7: from ... argument (spill) at ./load.go:39:38 +./load.go:31:7: from filepath.elem := ... argument (assign-pair) at ./load.go:39:38 +./load.go:31:7: flow: {temp} ← filepath.elem: +./load.go:31:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:39:38 +./load.go:31:7: flow: ~r0 ← *{temp}: +./load.go:31:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:39:38 +./load.go:31:7: flow: path ← ~r0: +./load.go:31:7: from path := ~r0 (assign-pair) at ./load.go:39:24 +./load.go:31:7: flow: {heap} ← path: +./load.go:31:7: from readConfigFile(path, int64(4096)) (call parameter) at ./load.go:39:24 +./load.go:36:15: &FileError{...} escapes to heap in Env.repo: +./load.go:36:15: flow: ~r1 ← &{storage for &FileError{...}}: +./load.go:36:15: from &FileError{...} (spill) at ./load.go:36:15 +./load.go:36:15: from &FileError{...} (interface-converted) at ./load.go:36:15 +./load.go:36:15: from return nil, &FileError{...} (return) at ./load.go:36:3 +./load.go:48:15: &FileError{...} escapes to heap in Env.repo: +./load.go:48:15: flow: ~r1 ← &{storage for &FileError{...}}: +./load.go:48:15: from &FileError{...} (spill) at ./load.go:48:15 +./load.go:48:15: from &FileError{...} (interface-converted) at ./load.go:48:15 +./load.go:48:15: from return nil, &FileError{...} (return) at ./load.go:48:3 +./load.go:31:7: parameter e leaks to {storage for &FileError{...}} for Env.repo with derefs=0: +./load.go:31:7: flow: {storage for ... argument} ← e: +./load.go:31:7: from e.GitDir (dot) at ./load.go:48:47 +./load.go:31:7: from ... argument (slice-literal-element) at ./load.go:48:45 +./load.go:31:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:31:7: from ... argument (spill) at ./load.go:48:45 +./load.go:31:7: from filepath.elem := ... argument (assign-pair) at ./load.go:48:45 +./load.go:31:7: flow: {temp} ← filepath.elem: +./load.go:31:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:48:45 +./load.go:31:7: flow: ~r0 ← *{temp}: +./load.go:31:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:48:45 +./load.go:31:7: flow: {storage for &FileError{...}} ← ~r0: +./load.go:31:7: from FileError{...} (struct literal element) at ./load.go:48:25 +./load.go:31:7: parameter e leaks to {storage for &FileError{...}} for Env.repo with derefs=0: +./load.go:31:7: flow: {storage for &FileError{...}} ← e: +./load.go:31:7: from e.GitDir (dot) at ./load.go:36:33 +./load.go:31:7: from FileError{...} (struct literal element) at ./load.go:36:25 +./load.go:38:7: &repo{...} escapes to heap in Env.repo: +./load.go:38:7: flow: r ← &{storage for &repo{...}}: +./load.go:38:7: from &repo{...} (spill) at ./load.go:38:7 +./load.go:38:7: from r := &repo{...} (assign) at ./load.go:38:4 +./load.go:38:7: flow: ~r0 ← r: +./load.go:38:7: from return r, nil (return) at ./load.go:50:2 +./load.go:31:7: parameter e leaks to {storage for &repo{...}} for Env.repo with derefs=0: +./load.go:31:7: flow: {storage for &repo{...}} ← e: +./load.go:31:7: from e.GitDir (dot) at ./load.go:38:22 +./load.go:31:7: from repo{...} (struct literal element) at ./load.go:38:12 +./load.go:31:7: leaking param: e +./load.go:36:15: &FileError{...} escapes to heap +./load.go:38:7: &repo{...} escapes to heap +./load.go:39:38: ... argument does not escape +./load.go:42:35: string(data) escapes to heap +./load.go:44:23: ... argument does not escape +./load.go:48:15: &FileError{...} escapes to heap +./load.go:48:45: ... argument does not escape +./load.go:180:7: parameter e leaks to {heap} for Env.newLoader with derefs=0: +./load.go:180:7: flow: {heap} ← e: +./load.go:180:7: from Env.repo(e) (call parameter) at ./load.go:181:18 +./load.go:185:9: &loader{...} escapes to heap in Env.newLoader: +./load.go:185:9: flow: ~r0 ← &{storage for &loader{...}}: +./load.go:185:9: from &loader{...} (spill) at ./load.go:185:9 +./load.go:185:9: from return &loader{...}, nil (return) at ./load.go:185:2 +./load.go:180:7: parameter e leaks to {storage for &loader{...}} for Env.newLoader with derefs=0: +./load.go:180:7: flow: {storage for &loader{...}} ← e: +./load.go:180:7: from loader{...} (struct literal element) at ./load.go:185:16 +./load.go:180:7: leaking param: e +./load.go:185:9: &loader{...} escapes to heap +./load.go:163:65: syscall.Errno(20) escapes to heap in isMissing: +./load.go:163:65: flow: {heap} ← &{storage for syscall.Errno(20)}: +./load.go:163:65: from syscall.Errno(20) (spill) at ./load.go:163:65 +./load.go:163:65: from errors.Is(err, syscall.Errno(20)) (call parameter) at ./load.go:163:52 +./load.go:162:16: parameter err leaks to {heap} for isMissing with derefs=0: +./load.go:162:16: flow: {heap} ← err: +./load.go:162:16: from errors.Is(err, fs.ErrNotExist) (call parameter) at ./load.go:163:18 +./load.go:162:16: leaking param: err +./load.go:163:65: syscall.Errno(20) escapes to heap +./glob.go:107:22: append(tok.ranges, [2]byte{...}) escapes to heap in compileClass: +./glob.go:107:22: flow: tok ← &{storage for append(tok.ranges, [2]byte{...})}: +./glob.go:107:22: from append(tok.ranges, [2]byte{...}) (spill) at ./glob.go:107:22 +./glob.go:107:22: from tok.ranges = append(tok.ranges, [2]byte{...}) (assign) at ./glob.go:107:14 +./glob.go:107:22: flow: ~r0 ← tok: +./glob.go:107:22: from return tok, i + 1, true (return) at ./glob.go:91:4 +./glob.go:80:19: p does not escape +./glob.go:107:22: append escapes to heap +./glob.go:40:18: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:40:18: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:40:18: from append(toks, globToken{...}) (spill) at ./glob.go:40:18 +./glob.go:40:18: from toks = append(toks, globToken{...}) (assign) at ./glob.go:40:10 +./glob.go:40:18: flow: ~r0 ← toks: +./glob.go:40:18: from return toks (return) at ./glob.go:75:2 +./glob.go:46:18: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:46:18: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:46:18: from append(toks, globToken{...}) (spill) at ./glob.go:46:18 +./glob.go:46:18: from toks = append(toks, globToken{...}) (assign) at ./glob.go:46:10 +./glob.go:46:18: flow: ~r0 ← toks: +./glob.go:46:18: from return toks (return) at ./glob.go:75:2 +./glob.go:54:17: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:54:17: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:54:17: from append(toks, globToken{...}) (spill) at ./glob.go:54:17 +./glob.go:54:17: from toks = append(toks, globToken{...}) (assign) at ./glob.go:54:9 +./glob.go:54:17: flow: ~r0 ← toks: +./glob.go:54:17: from return toks (return) at ./glob.go:75:2 +./glob.go:56:17: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:56:17: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:56:17: from append(toks, globToken{...}) (spill) at ./glob.go:56:17 +./glob.go:56:17: from toks = append(toks, globToken{...}) (assign) at ./glob.go:56:9 +./glob.go:56:17: flow: ~r0 ← toks: +./glob.go:56:17: from return toks (return) at ./glob.go:75:2 +./glob.go:60:18: append(toks, tok) escapes to heap in compileGlob: +./glob.go:60:18: flow: toks ← &{storage for append(toks, tok)}: +./glob.go:60:18: from append(toks, tok) (spill) at ./glob.go:60:18 +./glob.go:60:18: from toks = append(toks, tok) (assign) at ./glob.go:60:10 +./glob.go:60:18: flow: ~r0 ← toks: +./glob.go:60:18: from return toks (return) at ./glob.go:75:2 +./glob.go:63:18: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:63:18: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:63:18: from append(toks, globToken{...}) (spill) at ./glob.go:63:18 +./glob.go:63:18: from toks = append(toks, globToken{...}) (assign) at ./glob.go:63:10 +./glob.go:63:18: flow: ~r0 ← toks: +./glob.go:63:18: from return toks (return) at ./glob.go:75:2 +./glob.go:67:17: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:67:17: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:67:17: from append(toks, globToken{...}) (spill) at ./glob.go:67:17 +./glob.go:67:17: from toks = append(toks, globToken{...}) (assign) at ./glob.go:67:9 +./glob.go:67:17: flow: ~r0 ← toks: +./glob.go:67:17: from return toks (return) at ./glob.go:75:2 +./glob.go:70:17: append(toks, globToken{...}) escapes to heap in compileGlob: +./glob.go:70:17: flow: toks ← &{storage for append(toks, globToken{...})}: +./glob.go:70:17: from append(toks, globToken{...}) (spill) at ./glob.go:70:17 +./glob.go:70:17: from toks = append(toks, globToken{...}) (assign) at ./glob.go:70:9 +./glob.go:70:17: flow: ~r0 ← toks: +./glob.go:70:17: from return toks (return) at ./glob.go:75:2 +./glob.go:32:18: p does not escape +./glob.go:40:18: append escapes to heap +./glob.go:46:18: append escapes to heap +./glob.go:54:17: append escapes to heap +./glob.go:56:17: append escapes to heap +./glob.go:60:18: append escapes to heap +./glob.go:63:18: append escapes to heap +./glob.go:67:17: append escapes to heap +./glob.go:70:17: append escapes to heap +./glob.go:113:7: t does not escape +./glob.go:157:7: append(ss.list, id) escapes to heap in globMatch: +./glob.go:157:7: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:157:7: from append(ss.list, id) (spill) at ./glob.go:157:7 +./glob.go:157:7: from ss.list = append(ss.list, id) (assign) at ./glob.go:157:7 +./glob.go:160:13: globMatch ignoring self-assignment in ss.list = ss.list[:0] +./glob.go:165:13: append(ss.list, id) escapes to heap in globMatch: +./glob.go:165:13: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:165:13: from append(ss.list, id) (spill) at ./glob.go:165:13 +./glob.go:165:13: from ss.list = append(ss.list, id) (assign) at ./glob.go:165:13 +./glob.go:167:11: append(ss.list, id) escapes to heap in globMatch: +./glob.go:167:11: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:167:11: from append(ss.list, id) (spill) at ./glob.go:167:11 +./glob.go:167:11: from ss.list = append(ss.list, id) (assign) at ./glob.go:167:11 +./glob.go:177:11: append(ss.list, id) escapes to heap in globMatch: +./glob.go:177:11: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:177:11: from append(ss.list, id) (spill) at ./glob.go:177:11 +./glob.go:177:11: from ss.list = append(ss.list, id) (assign) at ./glob.go:177:11 +./glob.go:181:11: append(ss.list, id) escapes to heap in globMatch: +./glob.go:181:11: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:181:11: from append(ss.list, id) (spill) at ./glob.go:181:11 +./glob.go:181:11: from ss.list = append(ss.list, id) (assign) at ./glob.go:181:11 +./glob.go:185:11: append(ss.list, id) escapes to heap in globMatch: +./glob.go:185:11: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:185:11: from append(ss.list, id) (spill) at ./glob.go:185:11 +./glob.go:185:11: from ss.list = append(ss.list, id) (assign) at ./glob.go:185:11 +./glob.go:189:11: append(ss.list, id) escapes to heap in globMatch: +./glob.go:189:11: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:189:11: from append(ss.list, id) (spill) at ./glob.go:189:11 +./glob.go:189:11: from ss.list = append(ss.list, id) (assign) at ./glob.go:189:11 +./glob.go:192:13: append(ss.list, id) escapes to heap in globMatch: +./glob.go:192:13: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:192:13: from append(ss.list, id) (spill) at ./glob.go:192:13 +./glob.go:192:13: from ss.list = append(ss.list, id) (assign) at ./glob.go:192:13 +./glob.go:194:11: append(ss.list, id) escapes to heap in globMatch: +./glob.go:194:11: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:194:11: from append(ss.list, id) (spill) at ./glob.go:194:11 +./glob.go:194:11: from ss.list = append(ss.list, id) (assign) at ./glob.go:194:11 +./glob.go:197:10: append(ss.list, id) escapes to heap in globMatch: +./glob.go:197:10: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:197:10: from append(ss.list, id) (spill) at ./glob.go:197:10 +./glob.go:197:10: from ss.list = append(ss.list, id) (assign) at ./glob.go:197:10 +./glob.go:144:21: make([]uint32, size) escapes to heap in globMatch: +./glob.go:144:21: flow: {storage for &stateSet{...}} ← &{storage for make([]uint32, size)}: +./glob.go:144:21: from make([]uint32, size) (spill) at ./glob.go:144:21 +./glob.go:144:21: from stateSet{...} (struct literal element) at ./glob.go:144:21 +./glob.go:144:21: flow: ~r0 ← &{storage for &stateSet{...}}: +./glob.go:144:21: from &stateSet{...} (spill) at ./glob.go:144:21 +./glob.go:144:21: from ~r0 = &stateSet{...} (assign-pair) at ./glob.go:144:21 +./glob.go:144:21: flow: next ← ~r0: +./glob.go:144:21: from next := ~r0 (assign) at ./glob.go:144:7 +./glob.go:144:21: flow: ss ← next: +./glob.go:144:21: from ss, id := next, id (assign-pair) at ./glob.go:165:13 +./glob.go:144:21: flow: {temp} ← *ss: +./glob.go:144:21: from ss.list (dot of pointer) at ./glob.go:165:13 +./glob.go:144:21: from append(ss.list, id) (call parameter) at ./glob.go:165:13 +./glob.go:144:21: flow: {heap} ← {temp}: +./glob.go:144:21: from ss.list = append(ss.list, id) (assign) at ./glob.go:165:13 +./glob.go:143:20: make([]uint32, size) escapes to heap in globMatch: +./glob.go:143:20: flow: {storage for &stateSet{...}} ← &{storage for make([]uint32, size)}: +./glob.go:143:20: from make([]uint32, size) (spill) at ./glob.go:143:20 +./glob.go:143:20: from stateSet{...} (struct literal element) at ./glob.go:143:20 +./glob.go:143:20: flow: ~r0 ← &{storage for &stateSet{...}}: +./glob.go:143:20: from &stateSet{...} (spill) at ./glob.go:143:20 +./glob.go:143:20: from ~r0 = &stateSet{...} (assign-pair) at ./glob.go:143:20 +./glob.go:143:20: flow: cur ← ~r0: +./glob.go:143:20: from cur := ~r0 (assign) at ./glob.go:143:6 +./glob.go:143:20: flow: set ← cur: +./glob.go:143:20: from set, s := cur, int32(0) (assign-pair) at ./glob.go:157:7 +./glob.go:143:20: flow: ss ← set: +./glob.go:143:20: from ss, id := set, int32(2) * s (assign-pair) at ./glob.go:157:7 +./glob.go:143:20: flow: {temp} ← *ss: +./glob.go:143:20: from ss.list (dot of pointer) at ./glob.go:157:7 +./glob.go:143:20: from append(ss.list, id) (call parameter) at ./glob.go:157:7 +./glob.go:143:20: flow: {heap} ← {temp}: +./glob.go:143:20: from ss.list = append(ss.list, id) (assign) at ./glob.go:157:7 +./glob.go:137:16: pattern does not escape +./glob.go:137:25: name does not escape +./glob.go:143:20: &stateSet{...} does not escape +./glob.go:143:20: make([]uint32, size) escapes to heap +./glob.go:144:21: &stateSet{...} does not escape +./glob.go:144:21: make([]uint32, size) escapes to heap +./glob.go:157:7: append escapes to heap +./glob.go:165:13: append escapes to heap +./glob.go:167:11: append escapes to heap +./glob.go:177:11: append escapes to heap +./glob.go:181:11: append escapes to heap +./glob.go:185:11: append escapes to heap +./glob.go:189:11: append escapes to heap +./glob.go:192:13: append escapes to heap +./glob.go:194:11: append escapes to heap +./glob.go:197:10: append escapes to heap +./load.go:330:7: parameter l leaks to {heap} for (*loader).gitdir with derefs=2: +./load.go:330:7: flow: {heap} ← **l: +./load.go:330:7: from l.repo (dot of pointer) at ./load.go:356:41 +./load.go:330:7: from l.repo.gitDir (dot of pointer) at ./load.go:356:46 +./load.go:330:7: from filepath.EvalSymlinks(l.repo.gitDir) (call parameter) at ./load.go:356:39 +./load.go:334:18: &FileError{...} escapes to heap in (*loader).gitdir: +./load.go:334:18: flow: ~r1 ← &{storage for &FileError{...}}: +./load.go:334:18: from &FileError{...} (spill) at ./load.go:334:18 +./load.go:334:18: from &FileError{...} (interface-converted) at ./load.go:334:18 +./load.go:334:18: from return false, &FileError{...} (return) at ./load.go:334:4 +./load.go:330:25: parameter pattern leaks to {storage for &FileError{...}} for (*loader).gitdir with derefs=0: +./load.go:330:25: flow: {storage for &FileError{...}} ← pattern: +./load.go:330:25: from FileError{...} (struct literal element) at ./load.go:334:28 +./load.go:336:47: ~r0 + pattern[2:] escapes to heap in (*loader).gitdir: +./load.go:336:47: flow: pattern ← &{storage for ~r0 + pattern[2:]}: +./load.go:336:47: from ~r0 + pattern[2:] (spill) at ./load.go:336:47 +./load.go:336:47: from pattern = ~r0 + pattern[2:] (assign) at ./load.go:336:11 +./load.go:336:47: flow: {storage for &FileError{...}} ← pattern: +./load.go:336:47: from FileError{...} (struct literal element) at ./load.go:334:28 +./load.go:341:67: ~r0 + pattern[1:] escapes to heap in (*loader).gitdir: +./load.go:341:67: flow: pattern ← &{storage for ~r0 + pattern[1:]}: +./load.go:341:67: from ~r0 + pattern[1:] (spill) at ./load.go:341:67 +./load.go:341:67: from pattern = ~r0 + pattern[1:] (assign) at ./load.go:341:11 +./load.go:341:67: flow: {storage for &FileError{...}} ← pattern: +./load.go:341:67: from FileError{...} (struct literal element) at ./load.go:334:28 +./load.go:347:19: "**/" + pattern escapes to heap in (*loader).gitdir: +./load.go:347:19: flow: pattern ← &{storage for "**/" + pattern}: +./load.go:347:19: from "**/" + pattern (spill) at ./load.go:347:19 +./load.go:347:19: from pattern = "**/" + pattern (assign) at ./load.go:347:11 +./load.go:347:19: flow: {storage for &FileError{...}} ← pattern: +./load.go:347:19: from FileError{...} (struct literal element) at ./load.go:334:28 +./load.go:330:7: leaking param content: l +./load.go:330:25: leaking param: pattern +./load.go:330:34: from does not escape +./load.go:334:18: &FileError{...} escapes to heap +./load.go:336:47: ~r0 + pattern[2:] escapes to heap +./load.go:341:67: ~r0 + pattern[1:] escapes to heap +./load.go:347:19: "**/" + pattern escapes to heap +./load.go:104:7: parameter r leaks to {heap} for (*repo).branch with derefs=1: +./load.go:104:7: flow: {storage for ... argument} ← *r: +./load.go:104:7: from r.gitDir (dot of pointer) at ./load.go:105:40 +./load.go:104:7: from ... argument (slice-literal-element) at ./load.go:105:38 +./load.go:104:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:104:7: from ... argument (spill) at ./load.go:105:38 +./load.go:104:7: from filepath.elem := ... argument (assign-pair) at ./load.go:105:38 +./load.go:104:7: flow: {temp} ← filepath.elem: +./load.go:104:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:105:38 +./load.go:104:7: flow: ~r0 ← *{temp}: +./load.go:104:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:105:38 +./load.go:104:7: flow: path ← ~r0: +./load.go:104:7: from path := ~r0 (assign-pair) at ./load.go:105:24 +./load.go:104:7: flow: {heap} ← path: +./load.go:104:7: from readConfigFile(path, int64(4096)) (call parameter) at ./load.go:105:24 +./load.go:109:36: string(data) escapes to heap in (*repo).branch: +./load.go:109:36: flow: strings.s ← &{storage for string(data)}: +./load.go:109:36: from string(data) (spill) at ./load.go:109:36 +./load.go:109:36: from strings.s, strings.prefix := string(data), "ref:" (assign-pair) at ./load.go:109:28 +./load.go:109:36: flow: stringslite.s ← strings.s: +./load.go:109:36: from stringslite.s, stringslite.prefix := strings.s, strings.prefix (assign-pair) at ./load.go:109:28 +./load.go:109:36: flow: stringslite.after ← stringslite.s: +./load.go:109:36: from stringslite.after, stringslite.found = stringslite.s, false (assign-pair) at ./load.go:109:28 +./load.go:109:36: flow: .autotmp_4 ← stringslite.after: +./load.go:109:36: flow: strings.after ← .autotmp_4: +./load.go:109:36: from strings.after, strings.found = string(.autotmp_4), .autotmp_5 (assign-pair) at ./load.go:109:28 +./load.go:109:36: flow: .autotmp_8 ← strings.after: +./load.go:109:36: flow: s ← .autotmp_8: +./load.go:109:36: from s, ok := string(.autotmp_8), .autotmp_9 (assign-pair) at ./load.go:109:8 +./load.go:109:36: flow: strings.s ← s: +./load.go:109:36: from strings.s, strings.prefix := s, "refs/heads/" (assign-pair) at ./load.go:114:31 +./load.go:109:36: flow: stringslite.s ← strings.s: +./load.go:109:36: from stringslite.s, stringslite.prefix := strings.s, strings.prefix (assign-pair) at ./load.go:114:31 +./load.go:109:36: flow: stringslite.after ← stringslite.s: +./load.go:109:36: from stringslite.after, stringslite.found = stringslite.s, false (assign-pair) at ./load.go:114:31 +./load.go:109:36: flow: .autotmp_4 ← stringslite.after: +./load.go:109:36: flow: strings.after ← .autotmp_4: +./load.go:109:36: from strings.after, strings.found = string(.autotmp_4), .autotmp_5 (assign-pair) at ./load.go:114:31 +./load.go:109:36: flow: .autotmp_11 ← strings.after: +./load.go:109:36: flow: name ← .autotmp_11: +./load.go:109:36: from name, ok := string(.autotmp_11), .autotmp_12 (assign-pair) at ./load.go:114:11 +./load.go:109:36: flow: ~r0 ← name: +./load.go:109:36: from return name (return) at ./load.go:118:2 +./load.go:104:7: leaking param content: r +./load.go:105:38: ... argument does not escape +./load.go:109:36: string(data) escapes to heap +./load.go:300:7: parameter l leaks to {heap} for (*loader).condition with derefs=2: +./load.go:300:7: flow: {heap} ← **l: +./load.go:300:7: from (*loader).gitdir(l, cond[7:], from, false) (call parameter) at ./load.go:303:18 +./load.go:300:28: parameter cond leaks to {heap} for (*loader).condition with derefs=0: +./load.go:300:28: flow: {heap} ← cond: +./load.go:300:28: from cond[7:] (slice) at ./load.go:303:23 +./load.go:300:28: from (*loader).gitdir(l, cond[7:], from, false) (call parameter) at ./load.go:303:18 +./load.go:300:7: leaking param content: l +./load.go:300:28: leaking param: cond +./load.go:300:34: from does not escape +./load.go:229:12: (*loader).load ignoring self-assignment in l.entries = l.entries[:start] +./load.go:231:21: append(l.entries, e) escapes to heap in (*loader).load: +./load.go:231:21: flow: {heap} ← &{storage for append(l.entries, e)}: +./load.go:231:21: from append(l.entries, e) (spill) at ./load.go:231:21 +./load.go:231:21: from l.entries = append(l.entries, e) (assign) at ./load.go:231:13 +./load.go:206:23: parameter path leaks to {heap} for (*loader).load with derefs=0: +./load.go:206:23: flow: {heap} ← path: +./load.go:206:23: from readConfigFile(path, int64(67108864) - l.bytes) (call parameter) at ./load.go:211:29 +./load.go:241:26: parameter e leaks to {heap} for (*loader).include with derefs=0: +./load.go:241:26: flow: {heap} ← e: +./load.go:241:26: from e.Key (dot) at ./load.go:248:27 +./load.go:241:26: from e.Key.Subsection (dot) at ./load.go:248:31 +./load.go:241:26: from (*loader).condition(l, e.Key.Subsection, from) (call parameter) at ./load.go:248:25 +./load.go:206:7: parameter l leaks to {heap} for (*loader).load with derefs=1: +./load.go:206:7: flow: {temp} ← *l: +./load.go:206:7: from l.entries (dot of pointer) at ./load.go:231:23 +./load.go:206:7: from append(l.entries, e) (call parameter) at ./load.go:231:21 +./load.go:206:7: flow: {temp} ← {temp}: +./load.go:206:7: flow: {heap} ← {temp}: +./load.go:206:7: from l.entries = append(l.entries, e) (assign) at ./load.go:231:13 +./load.go:241:7: parameter l leaks to {heap} for (*loader).include with derefs=1: +./load.go:241:7: flow: l ← l: +./load.go:241:7: from (*loader).load(l, path, scope, depth + 1) (call parameter) at ./load.go:275:15 +./load.go:241:7: flow: {temp} ← *l: +./load.go:241:7: from l.entries (dot of pointer) at ./load.go:231:23 +./load.go:241:7: from append(l.entries, e) (call parameter) at ./load.go:231:21 +./load.go:241:7: flow: {temp} ← {temp}: +./load.go:241:7: flow: {heap} ← {temp}: +./load.go:241:7: from l.entries = append(l.entries, e) (assign) at ./load.go:231:13 +./load.go:270:26: ~r0 + path escapes to heap in (*loader).include: +./load.go:270:26: flow: path ← &{storage for ~r0 + path}: +./load.go:270:26: from ~r0 + path (spill) at ./load.go:270:26 +./load.go:270:26: from path = ~r0 + path (assign) at ./load.go:270:8 +./load.go:270:26: flow: path ← path: +./load.go:270:26: from (*loader).load(l, path, scope, depth + 1) (call parameter) at ./load.go:275:15 +./load.go:270:26: flow: {heap} ← path: +./load.go:270:26: from readConfigFile(path, int64(67108864) - l.bytes) (call parameter) at ./load.go:211:29 +./load.go:225:24: append(out, Entry{...}) escapes to heap in (*loader).load: +./load.go:225:24: flow: out ← &{storage for append(out, Entry{...})}: +./load.go:225:24: from append(out, Entry{...}) (spill) at ./load.go:225:24 +./load.go:225:24: from out = append(out, Entry{...}) (assign) at ./load.go:225:24 +./load.go:225:24: flow: ~r0 ← out: +./load.go:225:24: from ~r0 = out (assign-pair) at ./load.go:225:24 +./load.go:225:24: flow: {heap} ← ~r0: +./load.go:225:24: from l.entries = ~r0 (assign) at ./load.go:225:12 +./load.go:273:139: path escapes to heap in (*loader).include: +./load.go:273:139: flow: {storage for ... argument} ← &{storage for path}: +./load.go:273:139: from path (spill) at ./load.go:273:139 +./load.go:273:139: from ... argument (slice-literal-element) at ./load.go:273:48 +./load.go:273:139: flow: fmt.a ← &{storage for ... argument}: +./load.go:273:139: from ... argument (spill) at ./load.go:273:48 +./load.go:273:139: from fmt.format, fmt.a := "%w while including %s from %s; this might be due to circular includes", ... argument (assign-pair) at ./load.go:273:48 +./load.go:273:139: flow: {heap} ← *fmt.a: +./load.go:273:139: from fmt.errorf(fmt.format, fmt.a...) (call parameter) at ./load.go:273:48 +./load.go:273:145: from escapes to heap in (*loader).include: +./load.go:273:145: flow: {storage for ... argument} ← &{storage for from}: +./load.go:273:145: from from (spill) at ./load.go:273:145 +./load.go:273:145: from ... argument (slice-literal-element) at ./load.go:273:48 +./load.go:273:145: flow: fmt.a ← &{storage for ... argument}: +./load.go:273:145: from ... argument (spill) at ./load.go:273:48 +./load.go:273:145: from fmt.format, fmt.a := "%w while including %s from %s; this might be due to circular includes", ... argument (assign-pair) at ./load.go:273:48 +./load.go:273:145: flow: {heap} ← *fmt.a: +./load.go:273:145: from fmt.errorf(fmt.format, fmt.a...) (call parameter) at ./load.go:273:48 +./load.go:241:35: parameter from leaks to {storage for from} for (*loader).include with derefs=0: +./load.go:241:35: flow: {storage for from} ← from: +./load.go:241:35: from from (interface-converted) at ./load.go:273:145 +./load.go:206:23: parameter path leaks to {storage for from} for (*loader).load with derefs=0: +./load.go:206:23: flow: from ← path: +./load.go:206:23: from (*loader).include(l, e, path, scope, depth) (call parameter) at ./load.go:232:22 +./load.go:206:23: flow: {storage for from} ← from: +./load.go:206:23: from from (interface-converted) at ./load.go:273:145 +./load.go:264:10: &FileError{...} escapes to heap in (*loader).include: +./load.go:264:10: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:264:10: from &FileError{...} (spill) at ./load.go:264:10 +./load.go:264:10: from &FileError{...} (interface-converted) at ./load.go:264:10 +./load.go:264:10: from return &FileError{...} (return) at ./load.go:264:3 +./load.go:268:11: &FileError{...} escapes to heap in (*loader).include: +./load.go:268:11: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:268:11: from &FileError{...} (spill) at ./load.go:268:11 +./load.go:268:11: from &FileError{...} (interface-converted) at ./load.go:268:11 +./load.go:268:11: from return &FileError{...} (return) at ./load.go:268:4 +./load.go:273:10: &FileError{...} escapes to heap in (*loader).include: +./load.go:273:10: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:273:10: from &FileError{...} (spill) at ./load.go:273:10 +./load.go:273:10: from &FileError{...} (interface-converted) at ./load.go:273:10 +./load.go:273:10: from return &FileError{...} (return) at ./load.go:273:3 +./load.go:257:16: &KeyError{...} escapes to heap in (*loader).include: +./load.go:257:16: flow: ~r0 ← &{storage for &KeyError{...}}: +./load.go:257:16: from &KeyError{...} (spill) at ./load.go:257:16 +./load.go:257:16: from &KeyError{...} (interface-converted) at ./load.go:257:16 +./load.go:257:16: from ~r0 = &KeyError{...} (assign-pair) at ./load.go:257:16 +./load.go:257:16: flow: ~r0 ← ~r0: +./load.go:257:16: from return ~r0 (return) at ./load.go:257:3 +./load.go:257:16: &FileError{...} escapes to heap in (*loader).include: +./load.go:257:16: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:257:16: from &FileError{...} (spill) at ./load.go:257:16 +./load.go:257:16: from &FileError{...} (interface-converted) at ./load.go:257:16 +./load.go:257:16: from ~r0 = &FileError{...} (assign-pair) at ./load.go:257:16 +./load.go:257:16: flow: ~r0 ← ~r0: +./load.go:257:16: from return ~r0 (return) at ./load.go:257:3 +./load.go:209:10: &FileError{...} escapes to heap in (*loader).load: +./load.go:209:10: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:209:10: from &FileError{...} (spill) at ./load.go:209:10 +./load.go:209:10: from &FileError{...} (interface-converted) at ./load.go:209:10 +./load.go:209:10: from return &FileError{...} (return) at ./load.go:209:3 +./load.go:209:10: flow: ~r0 ← ~r0: +./load.go:209:10: from return (*loader).load(l, path, scope, depth + 1) (return) at ./load.go:275:2 +./load.go:216:10: &FileError{...} escapes to heap in (*loader).load: +./load.go:216:10: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:216:10: from &FileError{...} (spill) at ./load.go:216:10 +./load.go:216:10: from &FileError{...} (interface-converted) at ./load.go:216:10 +./load.go:216:10: from return &FileError{...} (return) at ./load.go:216:3 +./load.go:216:10: flow: ~r0 ← ~r0: +./load.go:216:10: from return (*loader).load(l, path, scope, depth + 1) (return) at ./load.go:275:2 +./load.go:222:10: &FileError{...} escapes to heap in (*loader).load: +./load.go:222:10: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:222:10: from &FileError{...} (spill) at ./load.go:222:10 +./load.go:222:10: from &FileError{...} (interface-converted) at ./load.go:222:10 +./load.go:222:10: from return &FileError{...} (return) at ./load.go:222:3 +./load.go:222:10: flow: ~r0 ← ~r0: +./load.go:222:10: from return (*loader).load(l, path, scope, depth + 1) (return) at ./load.go:275:2 +./load.go:206:23: parameter path leaks to {storage for &FileError{...}} for (*loader).load with derefs=0: +./load.go:206:23: flow: {storage for &FileError{...}} ← path: +./load.go:206:23: from FileError{...} (struct literal element) at ./load.go:222:20 +./load.go:206:23: parameter path leaks to {storage for &FileError{...}} for (*loader).load with derefs=0: +./load.go:206:23: flow: {storage for &FileError{...}} ← path: +./load.go:206:23: from FileError{...} (struct literal element) at ./load.go:216:20 +./load.go:206:23: parameter path leaks to {storage for &FileError{...}} for (*loader).load with derefs=0: +./load.go:206:23: flow: {storage for &FileError{...}} ← path: +./load.go:206:23: from FileError{...} (struct literal element) at ./load.go:209:20 +./load.go:273:48: &errors.errorString{...} escapes to heap in (*loader).include: +./load.go:273:48: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./load.go:273:48: from &errors.errorString{...} (spill) at ./load.go:273:48 +./load.go:273:48: from &errors.errorString{...} (interface-converted) at ./load.go:273:48 +./load.go:273:48: from ~r0 = &errors.errorString{...} (assign-pair) at ./load.go:273:48 +./load.go:273:48: flow: fmt.err ← ~r0: +./load.go:273:48: from fmt.err = ~r0 (assign-pair) at ./load.go:273:48 +./load.go:273:48: flow: {storage for &FileError{...}} ← fmt.err: +./load.go:273:48: from FileError{...} (struct literal element) at ./load.go:273:20 +./load.go:241:35: parameter from leaks to {storage for &FileError{...}} for (*loader).include with derefs=0: +./load.go:241:35: flow: {storage for &FileError{...}} ← from: +./load.go:241:35: from FileError{...} (struct literal element) at ./load.go:264:20 +./load.go:206:23: parameter path leaks to {storage for &FileError{...}} for (*loader).load with derefs=0: +./load.go:206:23: flow: from ← path: +./load.go:206:23: from (*loader).include(l, e, path, scope, depth) (call parameter) at ./load.go:232:22 +./load.go:206:23: flow: {storage for &FileError{...}} ← from: +./load.go:206:23: from FileError{...} (struct literal element) at ./load.go:264:20 +./load.go:257:16: k.Section + "." + k.Subsection + "." + k.Name escapes to heap in (*loader).include: +./load.go:257:16: flow: ~r0 ← &{storage for k.Section + "." + k.Subsection + "." + k.Name}: +./load.go:257:16: from k.Section + "." + k.Subsection + "." + k.Name (spill) at ./load.go:257:16 +./load.go:257:16: from ~r0 = k.Section + "." + k.Subsection + "." + k.Name (assign-pair) at ./load.go:257:16 +./load.go:257:16: flow: {storage for &KeyError{...}} ← ~r0: +./load.go:257:16: from KeyError{...} (struct literal element) at ./load.go:257:16 +./load.go:257:16: k.Section + "." + k.Name escapes to heap in (*loader).include: +./load.go:257:16: flow: ~r0 ← &{storage for k.Section + "." + k.Name}: +./load.go:257:16: from k.Section + "." + k.Name (spill) at ./load.go:257:16 +./load.go:257:16: from ~r0 = k.Section + "." + k.Name (assign-pair) at ./load.go:257:16 +./load.go:257:16: flow: {storage for &KeyError{...}} ← ~r0: +./load.go:257:16: from KeyError{...} (struct literal element) at ./load.go:257:16 +./load.go:257:16: &errors.errorString{...} escapes to heap in (*loader).include: +./load.go:257:16: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./load.go:257:16: from &errors.errorString{...} (spill) at ./load.go:257:16 +./load.go:257:16: from &errors.errorString{...} (interface-converted) at ./load.go:257:16 +./load.go:257:16: from ~r0 = &errors.errorString{...} (assign-pair) at ./load.go:257:16 +./load.go:257:16: flow: {storage for &KeyError{...}} ← ~r0: +./load.go:257:16: from KeyError{...} (struct literal element) at ./load.go:257:16 +./load.go:257:16: "gitconfig: " + msg escapes to heap in (*loader).include: +./load.go:257:16: flow: errors.text ← &{storage for "gitconfig: " + msg}: +./load.go:257:16: from "gitconfig: " + msg (spill) at ./load.go:257:16 +./load.go:257:16: from errors.text := "gitconfig: " + msg (assign-pair) at ./load.go:257:16 +./load.go:257:16: flow: {storage for &errors.errorString{...}} ← errors.text: +./load.go:257:16: from errors.errorString{...} (struct literal element) at ./load.go:257:16 +./load.go:257:16: &SyntaxError{...} escapes to heap in (*loader).include: +./load.go:257:16: flow: {storage for &FileError{...}} ← &{storage for &SyntaxError{...}}: +./load.go:257:16: from &SyntaxError{...} (spill) at ./load.go:257:16 +./load.go:257:16: from &SyntaxError{...} (interface-converted) at ./load.go:257:16 +./load.go:257:16: from FileError{...} (struct literal element) at ./load.go:257:16 +./load.go:241:35: parameter from leaks to {storage for &FileError{...}} for (*loader).include with derefs=0: +./load.go:241:35: flow: from ← from: +./load.go:241:35: from _, from, e, msg := 0, from, e, "missing value for include.path" (assign-pair) at ./load.go:257:16 +./load.go:241:35: flow: {storage for &FileError{...}} ← from: +./load.go:241:35: from FileError{...} (struct literal element) at ./load.go:257:16 +./load.go:206:23: parameter path leaks to {storage for &FileError{...}} for (*loader).load with derefs=0: +./load.go:206:23: flow: from ← path: +./load.go:206:23: from (*loader).include(l, e, path, scope, depth) (call parameter) at ./load.go:232:22 +./load.go:206:23: flow: from ← from: +./load.go:206:23: from _, from, e, msg := 0, from, e, "missing value for include.path" (assign-pair) at ./load.go:257:16 +./load.go:206:23: flow: {storage for &FileError{...}} ← from: +./load.go:206:23: from FileError{...} (struct literal element) at ./load.go:257:16 +./load.go:241:35: parameter from leaks to {storage for &SyntaxError{...}} for (*loader).include with derefs=0: +./load.go:241:35: flow: from ← from: +./load.go:241:35: from _, from, e, msg := 0, from, e, "missing value for include.path" (assign-pair) at ./load.go:257:16 +./load.go:241:35: flow: {storage for &SyntaxError{...}} ← from: +./load.go:241:35: from SyntaxError{...} (struct literal element) at ./load.go:257:16 +./load.go:206:23: parameter path leaks to {storage for &SyntaxError{...}} for (*loader).load with derefs=0: +./load.go:206:23: flow: from ← path: +./load.go:206:23: from (*loader).include(l, e, path, scope, depth) (call parameter) at ./load.go:232:22 +./load.go:206:23: flow: from ← from: +./load.go:206:23: from _, from, e, msg := 0, from, e, "missing value for include.path" (assign-pair) at ./load.go:257:16 +./load.go:206:23: flow: {storage for &SyntaxError{...}} ← from: +./load.go:206:23: from SyntaxError{...} (struct literal element) at ./load.go:257:16 +./load.go:206:7: leaking param content: l +./load.go:206:23: leaking param: path +./load.go:241:7: leaking param content: l +./load.go:241:26: leaking param: e +./load.go:241:35: leaking param: from +./load.go:209:10: &FileError{...} escapes to heap +./load.go:216:10: &FileError{...} escapes to heap +./load.go:222:10: &FileError{...} escapes to heap +./load.go:225:24: append escapes to heap +./load.go:228:21: go.shape.[]github.com/git-bug/gitconfig.Entry{} does not escape +./load.go:228:21: append does not escape +./load.go:231:21: append escapes to heap +./load.go:257:16: &KeyError{...} escapes to heap +./load.go:257:16: k.Section + "." + k.Subsection + "." + k.Name escapes to heap +./load.go:257:16: k.Section + "." + k.Name escapes to heap +./load.go:257:16: "gitconfig: " + msg escapes to heap +./load.go:257:16: &errors.errorString{...} escapes to heap +./load.go:257:16: &FileError{...} escapes to heap +./load.go:257:16: &SyntaxError{...} escapes to heap +./load.go:264:10: &FileError{...} escapes to heap +./load.go:268:11: &FileError{...} escapes to heap +./load.go:270:26: ~r0 + path escapes to heap +./load.go:273:10: &FileError{...} escapes to heap +./load.go:273:48: ... argument does not escape +./load.go:273:139: path escapes to heap +./load.go:273:145: from escapes to heap +./load.go:273:48: &errors.errorString{...} escapes to heap +./load.go:198:7: fe escapes to heap in (*loader).loadFile: +./load.go:198:7: flow: {heap} ← &fe: +./load.go:198:7: from &fe (address-of) at ./load.go:199:21 +./load.go:198:7: from &fe (interface-converted) at ./load.go:199:21 +./load.go:198:7: from errors.As(err, &fe) (call parameter) at ./load.go:199:15 +./load.go:195:27: parameter path leaks to {heap} for (*loader).loadFile with derefs=0: +./load.go:195:27: flow: {heap} ← path: +./load.go:195:27: from (*loader).load(l, path, scope, 0) (call parameter) at ./load.go:196:15 +./load.go:195:7: parameter l leaks to {heap} for (*loader).loadFile with derefs=1: +./load.go:195:7: flow: {temp} ← l: +./load.go:195:7: from (*loader).load(l, path, scope, 0) (call parameter) at ./load.go:196:15 +./load.go:195:7: flow: {heap} ← *{temp}: +./load.go:195:7: leaking param content: l +./load.go:195:27: leaking param: path +./load.go:198:7: moved to heap: fe +./load.go:80:23: parameter entries leaks to {heap} for worktreeConfigIn with derefs=1: +./load.go:80:23: flow: {temp} ← entries: +./load.go:80:23: flow: e ← *{temp}: +./load.go:80:23: from for loop (range-deref) at ./load.go:83:14 +./load.go:80:23: flow: {heap} ← e: +./load.go:80:23: from Entry.Bool(e) (call parameter) at ./load.go:87:19 +./load.go:89:18: &FileError{...} escapes to heap in worktreeConfigIn: +./load.go:89:18: flow: ~r1 ← &{storage for &FileError{...}}: +./load.go:89:18: from &FileError{...} (spill) at ./load.go:89:18 +./load.go:89:18: from &FileError{...} (interface-converted) at ./load.go:89:18 +./load.go:89:18: from return false, &FileError{...} (return) at ./load.go:89:4 +./load.go:80:40: parameter path leaks to {storage for &FileError{...}} for worktreeConfigIn with derefs=0: +./load.go:80:40: flow: {storage for &FileError{...}} ← path: +./load.go:80:40: from FileError{...} (struct literal element) at ./load.go:89:28 +./load.go:80:23: leaking param content: entries +./load.go:80:40: leaking param: path +./load.go:89:18: &FileError{...} escapes to heap +./key.go:15:17: &KeyError{...} escapes to heap in ParseKey: +./key.go:15:17: flow: ~r1 ← &{storage for &KeyError{...}}: +./key.go:15:17: from &KeyError{...} (spill) at ./key.go:15:17 +./key.go:15:17: from &KeyError{...} (interface-converted) at ./key.go:15:17 +./key.go:15:17: from return Key{}, &KeyError{...} (return) at ./key.go:15:3 +./key.go:12:15: parameter s leaks to {storage for &KeyError{...}} for ParseKey with derefs=0: +./key.go:12:15: flow: {storage for &KeyError{...}} ← s: +./key.go:12:15: from KeyError{...} (struct literal element) at ./key.go:15:26 +./key.go:12:15: parameter s leaks to ~r0 for ParseKey with derefs=0: +./key.go:12:15: flow: {temp} ← s: +./key.go:12:15: from parseKey(s) (call parameter) at ./key.go:13:19 +./key.go:12:15: flow: .autotmp_5 ← {temp}: +./key.go:12:15: flow: k ← .autotmp_5: +./key.go:12:15: from k, ok := Key(.autotmp_5), .autotmp_6 (assign-pair) at ./key.go:13:8 +./key.go:12:15: flow: ~r0 ← k: +./key.go:12:15: from return k, nil (return) at ./key.go:17:2 +./key.go:12:15: leaking param: s +./key.go:15:17: &KeyError{...} escapes to heap +./load.go:384:21: append(l.entries, e) escapes to heap in (*loader).command: +./load.go:384:21: flow: {heap} ← &{storage for append(l.entries, e)}: +./load.go:384:21: from append(l.entries, e) (spill) at ./load.go:384:21 +./load.go:384:21: from l.entries = append(l.entries, e) (assign) at ./load.go:384:13 +./load.go:374:7: parameter l leaks to {heap} for (*loader).command with derefs=1: +./load.go:374:7: flow: {temp} ← *l: +./load.go:374:7: from l.entries (dot of pointer) at ./load.go:384:23 +./load.go:374:7: from append(l.entries, e) (call parameter) at ./load.go:384:21 +./load.go:374:7: flow: {temp} ← {temp}: +./load.go:374:7: flow: {heap} ← {temp}: +./load.go:374:7: from l.entries = append(l.entries, e) (assign) at ./load.go:384:13 +./load.go:374:7: leaking param content: l +./load.go:384:21: append escapes to heap +./env.go:137:7: parameter e leaks to {heap} for Env.Load with derefs=0: +./env.go:137:7: flow: {heap} ← e: +./env.go:137:7: from Env.newLoader(e) (call parameter) at ./env.go:138:23 +./env.go:137:7: leaking param: e +./env.go:154:42: ... argument does not escape +./env.go:157:68: ... argument does not escape +./env.go:162:46: ... argument does not escape +./env.go:178:7: parameter e leaks to {heap} for Env.LoadProtected with derefs=0: +./env.go:178:7: flow: {heap} ← e: +./env.go:178:7: from Env.newLoader(e) (call parameter) at ./env.go:179:23 +./env.go:178:7: leaking param: e +./env.go:247:15: parameter path leaks to {heap} for realPath with derefs=0: +./env.go:247:15: flow: {heap} ← path: +./env.go:247:15: from filepath.EvalSymlinks(path) (call parameter) at ./env.go:248:36 +./env.go:247:15: parameter path leaks to ~r0 for realPath with derefs=0: +./env.go:247:15: flow: filepath.path ← path: +./env.go:247:15: from filepath.path := path (assign-pair) at ./env.go:251:23 +./env.go:247:15: flow: {temp} ← filepath.path: +./env.go:247:15: from filepathlite.Clean(filepath.path) (call parameter) at ./env.go:251:23 +./env.go:247:15: flow: ~r0 ← {temp}: +./env.go:247:15: from ~r0 = filepathlite.Clean(filepath.path) (assign-pair) at ./env.go:251:23 +./env.go:247:15: flow: ~r0 ← ~r0: +./env.go:247:15: from return ~r0 (return) at ./env.go:251:2 +./env.go:247:15: leaking param: path +./env.go:208:7: parameter e leaks to {heap} for Env.SafeDirectory with derefs=0: +./env.go:208:7: flow: {heap} ← e: +./env.go:208:7: from Env.LoadProtected(e) (call parameter) at ./env.go:209:27 +./env.go:208:28: parameter dir leaks to {heap} for Env.SafeDirectory with derefs=0: +./env.go:208:28: flow: {heap} ← dir: +./env.go:208:28: from realPath(dir) (call parameter) at ./env.go:213:16 +./env.go:208:7: leaking param: e +./env.go:208:28: leaking param: dir +./env.go:227:80: ~r0 + "/" does not escape +./load.go:63:7: parameter r leaks to {heap} for (*repo).worktreeConfigEnabled with derefs=1: +./load.go:63:7: flow: r ← r: +./load.go:63:7: from r := r (assign-pair) at ./load.go:64:43 +./load.go:63:7: flow: {storage for ... argument} ← *r: +./load.go:63:7: from r.commonDir (dot of pointer) at ./load.go:64:43 +./load.go:63:7: from ... argument (slice-literal-element) at ./load.go:64:43 +./load.go:63:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:63:7: from ... argument (spill) at ./load.go:64:43 +./load.go:63:7: from filepath.elem := ... argument (assign-pair) at ./load.go:64:43 +./load.go:63:7: flow: {temp} ← filepath.elem: +./load.go:63:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:64:43 +./load.go:63:7: flow: ~r0 ← *{temp}: +./load.go:63:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:64:43 +./load.go:63:7: flow: ~r0 ← ~r0: +./load.go:63:7: from ~r0 = ~r0 (assign-pair) at ./load.go:64:43 +./load.go:63:7: flow: {heap} ← ~r0: +./load.go:63:7: from readConfigFile(~r0, int64(67108864)) (call parameter) at ./load.go:64:29 +./load.go:69:17: &FileError{...} escapes to heap in (*repo).worktreeConfigEnabled: +./load.go:69:17: flow: ~r1 ← &{storage for &FileError{...}}: +./load.go:69:17: from &FileError{...} (spill) at ./load.go:69:17 +./load.go:69:17: from &FileError{...} (interface-converted) at ./load.go:69:17 +./load.go:69:17: from return false, &FileError{...} (return) at ./load.go:69:3 +./load.go:73:17: &FileError{...} escapes to heap in (*repo).worktreeConfigEnabled: +./load.go:73:17: flow: ~r1 ← &{storage for &FileError{...}}: +./load.go:73:17: from &FileError{...} (spill) at ./load.go:73:17 +./load.go:73:17: from &FileError{...} (interface-converted) at ./load.go:73:17 +./load.go:73:17: from return false, &FileError{...} (return) at ./load.go:73:3 +./load.go:63:7: parameter r leaks to {storage for &FileError{...}} for (*repo).worktreeConfigEnabled with derefs=1: +./load.go:63:7: flow: r ← r: +./load.go:63:7: from r := r (assign-pair) at ./load.go:73:47 +./load.go:63:7: flow: {storage for ... argument} ← *r: +./load.go:63:7: from r.commonDir (dot of pointer) at ./load.go:73:47 +./load.go:63:7: from ... argument (slice-literal-element) at ./load.go:73:47 +./load.go:63:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:63:7: from ... argument (spill) at ./load.go:73:47 +./load.go:63:7: from filepath.elem := ... argument (assign-pair) at ./load.go:73:47 +./load.go:63:7: flow: {temp} ← filepath.elem: +./load.go:63:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:73:47 +./load.go:63:7: flow: ~r0 ← *{temp}: +./load.go:63:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:73:47 +./load.go:63:7: flow: ~r0 ← ~r0: +./load.go:63:7: from ~r0 = ~r0 (assign-pair) at ./load.go:73:47 +./load.go:63:7: flow: {storage for &FileError{...}} ← ~r0: +./load.go:63:7: from FileError{...} (struct literal element) at ./load.go:73:27 +./load.go:63:7: parameter r leaks to {storage for &FileError{...}} for (*repo).worktreeConfigEnabled with derefs=1: +./load.go:63:7: flow: r ← r: +./load.go:63:7: from r := r (assign-pair) at ./load.go:69:47 +./load.go:63:7: flow: {storage for ... argument} ← *r: +./load.go:63:7: from r.commonDir (dot of pointer) at ./load.go:69:47 +./load.go:63:7: from ... argument (slice-literal-element) at ./load.go:69:47 +./load.go:63:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:63:7: from ... argument (spill) at ./load.go:69:47 +./load.go:63:7: from filepath.elem := ... argument (assign-pair) at ./load.go:69:47 +./load.go:63:7: flow: {temp} ← filepath.elem: +./load.go:63:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:69:47 +./load.go:63:7: flow: ~r0 ← *{temp}: +./load.go:63:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:69:47 +./load.go:63:7: flow: ~r0 ← ~r0: +./load.go:63:7: from ~r0 = ~r0 (assign-pair) at ./load.go:69:47 +./load.go:63:7: flow: {storage for &FileError{...}} ← ~r0: +./load.go:63:7: from FileError{...} (struct literal element) at ./load.go:69:27 +./load.go:63:7: leaking param content: r +./load.go:64:43: ... argument does not escape +./load.go:69:17: &FileError{...} escapes to heap +./load.go:69:47: ... argument does not escape +./load.go:71:31: ... argument does not escape +./load.go:73:17: &FileError{...} escapes to heap +./load.go:73:47: ... argument does not escape +./load.go:75:61: ... argument does not escape +./load.go:75:35: append does not escape +./load.go:75:84: ... argument does not escape +./load.go:97:7: parameter r leaks to {heap} for (*repo).severalWorktrees with derefs=1: +./load.go:97:7: flow: {storage for ... argument} ← *r: +./load.go:97:7: from r.commonDir (dot of pointer) at ./load.go:98:44 +./load.go:97:7: from ... argument (slice-literal-element) at ./load.go:98:42 +./load.go:97:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:97:7: from ... argument (spill) at ./load.go:98:42 +./load.go:97:7: from filepath.elem := ... argument (assign-pair) at ./load.go:98:42 +./load.go:97:7: flow: {temp} ← filepath.elem: +./load.go:97:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:98:42 +./load.go:97:7: flow: ~r0 ← *{temp}: +./load.go:97:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:98:42 +./load.go:97:7: flow: {heap} ← ~r0: +./load.go:97:7: from os.ReadDir(~r0) (call parameter) at ./load.go:98:28 +./load.go:97:7: leaking param content: r +./load.go:98:42: ... argument does not escape +./env.go:353:7: parameter e leaks to {heap} for Env.scopeFile with derefs=0: +./env.go:353:7: flow: {heap} ← e: +./env.go:353:7: from Env.repo(e) (call parameter) at ./env.go:354:18 +./env.go:353:7: leaking param: e +./env.go:362:23: ... argument does not escape +./env.go:369:26: ... argument does not escape +./env.go:373:22: ... argument does not escape +./env.go:269:7: parameter e leaks to {heap} for Env.LoadScope with derefs=0: +./env.go:269:7: flow: {heap} ← e: +./env.go:269:7: from Env.newLoader(e) (call parameter) at ./env.go:270:23 +./env.go:269:7: leaking param: e +./env.go:307:7: parameter e leaks to {heap} for Env.LoadFile with derefs=0: +./env.go:307:7: flow: {heap} ← e: +./env.go:307:7: from Env.newLoader(e) (call parameter) at ./env.go:308:23 +./env.go:307:23: parameter path leaks to {heap} for Env.LoadFile with derefs=0: +./env.go:307:23: flow: {heap} ← path: +./env.go:307:23: from (*loader).loadFile(l, path, Scope(0), false) (call parameter) at ./env.go:312:22 +./env.go:307:7: leaking param: e +./env.go:307:23: leaking param: path +./env.go:326:7: parameter e leaks to {heap} for Env.WritePath with derefs=0: +./env.go:326:7: flow: {heap} ← e: +./env.go:326:7: from Env.scopeFile(e, s) (call parameter) at ./env.go:344:30 +./env.go:326:7: parameter e leaks to ~r0 for Env.WritePath with derefs=0: +./env.go:326:7: flow: ~r0 ← e: +./env.go:326:7: from e.SystemConfig (dot) at ./env.go:332:11 +./env.go:326:7: from return e.SystemConfig, nil (return) at ./env.go:332:3 +./env.go:326:7: leaking param: e +./update.go:96:18: parameter path leaks to {heap} for followLinks with derefs=0: +./update.go:96:18: flow: p ← path: +./update.go:96:18: from p := path (assign) at ./update.go:97:4 +./update.go:96:18: flow: {heap} ← p: +./update.go:96:18: from os.Lstat(p) (call parameter) at ./update.go:99:22 +./update.go:104:15: &FileError{...} escapes to heap in followLinks: +./update.go:104:15: flow: ~r1 ← &{storage for &FileError{...}}: +./update.go:104:15: from &FileError{...} (spill) at ./update.go:104:15 +./update.go:104:15: from &FileError{...} (interface-converted) at ./update.go:104:15 +./update.go:104:15: from return "", &FileError{...} (return) at ./update.go:104:4 +./update.go:110:15: &FileError{...} escapes to heap in followLinks: +./update.go:110:15: flow: ~r1 ← &{storage for &FileError{...}}: +./update.go:110:15: from &FileError{...} (spill) at ./update.go:110:15 +./update.go:110:15: from &FileError{...} (interface-converted) at ./update.go:110:15 +./update.go:110:15: from return "", &FileError{...} (return) at ./update.go:110:4 +./update.go:112:15: &FileError{...} escapes to heap in followLinks: +./update.go:112:15: flow: ~r1 ← &{storage for &FileError{...}}: +./update.go:112:15: from &FileError{...} (spill) at ./update.go:112:15 +./update.go:112:15: from &FileError{...} (interface-converted) at ./update.go:112:15 +./update.go:112:15: from return "", &FileError{...} (return) at ./update.go:112:4 +./update.go:116:15: &FileError{...} escapes to heap in followLinks: +./update.go:116:15: flow: ~r1 ← &{storage for &FileError{...}}: +./update.go:116:15: from &FileError{...} (spill) at ./update.go:116:15 +./update.go:116:15: from &FileError{...} (interface-converted) at ./update.go:116:15 +./update.go:116:15: from return "", &FileError{...} (return) at ./update.go:116:4 +./update.go:96:18: parameter path leaks to {storage for &FileError{...}} for followLinks with derefs=0: +./update.go:96:18: flow: p ← path: +./update.go:96:18: from p := path (assign) at ./update.go:97:4 +./update.go:96:18: flow: {storage for &FileError{...}} ← p: +./update.go:96:18: from FileError{...} (struct literal element) at ./update.go:116:25 +./update.go:96:18: parameter path leaks to {storage for &FileError{...}} for followLinks with derefs=0: +./update.go:96:18: flow: {storage for &FileError{...}} ← path: +./update.go:96:18: from FileError{...} (struct literal element) at ./update.go:112:25 +./update.go:96:18: parameter path leaks to {storage for &FileError{...}} for followLinks with derefs=0: +./update.go:96:18: flow: p ← path: +./update.go:96:18: from p := path (assign) at ./update.go:97:4 +./update.go:96:18: flow: {storage for &FileError{...}} ← p: +./update.go:96:18: from FileError{...} (struct literal element) at ./update.go:110:25 +./update.go:96:18: parameter path leaks to {storage for &FileError{...}} for followLinks with derefs=0: +./update.go:96:18: flow: p ← path: +./update.go:96:18: from p := path (assign) at ./update.go:97:4 +./update.go:96:18: flow: {storage for &FileError{...}} ← p: +./update.go:96:18: from FileError{...} (struct literal element) at ./update.go:104:25 +./update.go:96:18: parameter path leaks to ~r0 for followLinks with derefs=0: +./update.go:96:18: flow: p ← path: +./update.go:96:18: from p := path (assign) at ./update.go:97:4 +./update.go:96:18: flow: ~r0 ← p: +./update.go:96:18: from return p, nil (return) at ./update.go:101:4 +./update.go:96:18: leaking param: path +./update.go:104:15: &FileError{...} escapes to heap +./update.go:110:15: &FileError{...} escapes to heap +./update.go:112:15: &FileError{...} escapes to heap +./update.go:116:15: &FileError{...} escapes to heap +./update.go:119:24: ... argument does not escape +./update.go:87:18: callClosing capturing by value: f (addr=false assign=false width=8) +./update.go:87:18: parameter f leaks to {heap} for callClosing with derefs=0: +./update.go:87:18: flow: {heap} ← f: +./update.go:87:18: from fn(f) (call parameter) at ./update.go:89:11 +./update.go:87:18: leaking param: f +./update.go:87:27: fn does not escape +./update.go:88:8: func literal does not escape +./update.go:33:2: updateFile capturing by ref: committed (addr=false assign=true width=1) +./update.go:26:2: updateFile capturing by value: lock (addr=false assign=false width=8) +./update.go:25:2: updateFile capturing by value: lockPath (addr=false assign=false width=16) +./update.go:25:2: updateFile capturing by value: lockPath (addr=false assign=false width=16) +./update.go:20:2: updateFile capturing by value: target (addr=false assign=false width=16) +./update.go:19:17: parameter path leaks to {heap} for updateFile with derefs=0: +./update.go:19:17: flow: {heap} ← path: +./update.go:19:17: from followLinks(path) (call parameter) at ./update.go:20:28 +./update.go:25:21: target + ".lock" escapes to heap in updateFile: +./update.go:25:21: flow: lockPath ← &{storage for target + ".lock"}: +./update.go:25:21: from target + ".lock" (spill) at ./update.go:25:21 +./update.go:25:21: from lockPath := target + ".lock" (assign) at ./update.go:25:11 +./update.go:25:21: flow: {heap} ← lockPath: +./update.go:25:21: from os.OpenFile(lockPath, 193, fs.FileMode(438)) (call parameter) at ./update.go:26:26 +./update.go:29:11: &FileError{...} escapes to heap in updateFile: +./update.go:29:11: flow: err ← &{storage for &FileError{...}}: +./update.go:29:11: from &FileError{...} (spill) at ./update.go:29:11 +./update.go:29:11: from &FileError{...} (interface-converted) at ./update.go:29:11 +./update.go:29:11: from return &FileError{...} (return) at ./update.go:29:4 +./update.go:29:11: flow: {heap} ← err: +./update.go:29:11: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:31:10: &FileError{...} escapes to heap in updateFile: +./update.go:31:10: flow: err ← &{storage for &FileError{...}}: +./update.go:31:10: from &FileError{...} (spill) at ./update.go:31:10 +./update.go:31:10: from &FileError{...} (interface-converted) at ./update.go:31:10 +./update.go:31:10: from return &FileError{...} (return) at ./update.go:31:3 +./update.go:31:10: flow: {heap} ← err: +./update.go:31:10: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:48:10: &FileError{...} escapes to heap in updateFile: +./update.go:48:10: flow: err ← &{storage for &FileError{...}}: +./update.go:48:10: from &FileError{...} (spill) at ./update.go:48:10 +./update.go:48:10: from &FileError{...} (interface-converted) at ./update.go:48:10 +./update.go:48:10: from return &FileError{...} (return) at ./update.go:48:3 +./update.go:48:10: flow: {heap} ← err: +./update.go:48:10: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:52:10: &FileError{...} escapes to heap in updateFile: +./update.go:52:10: flow: err ← &{storage for &FileError{...}}: +./update.go:52:10: from &FileError{...} (spill) at ./update.go:52:10 +./update.go:52:10: from &FileError{...} (interface-converted) at ./update.go:52:10 +./update.go:52:10: from return &FileError{...} (return) at ./update.go:52:3 +./update.go:52:10: flow: {heap} ← err: +./update.go:52:10: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:64:10: &FileError{...} escapes to heap in updateFile: +./update.go:64:10: flow: err ← &{storage for &FileError{...}}: +./update.go:64:10: from &FileError{...} (spill) at ./update.go:64:10 +./update.go:64:10: from &FileError{...} (interface-converted) at ./update.go:64:10 +./update.go:64:10: from return &FileError{...} (return) at ./update.go:64:3 +./update.go:64:10: flow: {heap} ← err: +./update.go:64:10: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:69:11: &FileError{...} escapes to heap in updateFile: +./update.go:69:11: flow: err ← &{storage for &FileError{...}}: +./update.go:69:11: from &FileError{...} (spill) at ./update.go:69:11 +./update.go:69:11: from &FileError{...} (interface-converted) at ./update.go:69:11 +./update.go:69:11: from return &FileError{...} (return) at ./update.go:69:4 +./update.go:69:11: flow: {heap} ← err: +./update.go:69:11: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:73:11: &FileError{...} escapes to heap in updateFile: +./update.go:73:11: flow: err ← &{storage for &FileError{...}}: +./update.go:73:11: from &FileError{...} (spill) at ./update.go:73:11 +./update.go:73:11: from &FileError{...} (interface-converted) at ./update.go:73:11 +./update.go:73:11: from return &FileError{...} (return) at ./update.go:73:4 +./update.go:73:11: flow: {heap} ← err: +./update.go:73:11: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:77:10: &FileError{...} escapes to heap in updateFile: +./update.go:77:10: flow: err ← &{storage for &FileError{...}}: +./update.go:77:10: from &FileError{...} (spill) at ./update.go:77:10 +./update.go:77:10: from &FileError{...} (interface-converted) at ./update.go:77:10 +./update.go:77:10: from return &FileError{...} (return) at ./update.go:77:3 +./update.go:77:10: flow: {heap} ← err: +./update.go:77:10: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:80:10: &FileError{...} escapes to heap in updateFile: +./update.go:80:10: flow: err ← &{storage for &FileError{...}}: +./update.go:80:10: from &FileError{...} (spill) at ./update.go:80:10 +./update.go:80:10: from &FileError{...} (interface-converted) at ./update.go:80:10 +./update.go:80:10: from return &FileError{...} (return) at ./update.go:80:3 +./update.go:80:10: flow: {heap} ← err: +./update.go:80:10: from errors.Is(err, fs.ErrExist) (call parameter) at ./update.go:28:15 +./update.go:54:7: &File{...} escapes to heap in updateFile: +./update.go:54:7: flow: f ← &{storage for &File{...}}: +./update.go:54:7: from &File{...} (spill) at ./update.go:54:7 +./update.go:54:7: from f := &File{...} (assign) at ./update.go:54:4 +./update.go:54:7: flow: {heap} ← f: +./update.go:54:7: from callClosing(f, fn) (call parameter) at ./update.go:56:23 +./update.go:19:17: parameter path leaks to {storage for &File{...}} for updateFile with derefs=0: +./update.go:19:17: flow: {storage for &File{...}} ← path: +./update.go:19:17: from File{...} (struct literal element) at ./update.go:54:12 +./update.go:19:17: parameter path leaks to {storage for &FileError{...}} for updateFile with derefs=0: +./update.go:19:17: flow: {storage for &FileError{...}} ← path: +./update.go:19:17: from FileError{...} (struct literal element) at ./update.go:52:20 +./update.go:19:17: parameter path leaks to {storage for &FileError{...}} for updateFile with derefs=0: +./update.go:19:17: flow: {storage for &FileError{...}} ← path: +./update.go:19:17: from FileError{...} (struct literal element) at ./update.go:48:20 +./update.go:19:17: leaking param: path +./update.go:19:43: fn does not escape +./update.go:25:21: target + ".lock" escapes to heap +./update.go:29:11: &FileError{...} escapes to heap +./update.go:31:10: &FileError{...} escapes to heap +./update.go:34:8: func literal does not escape +./update.go:48:10: &FileError{...} escapes to heap +./update.go:52:10: &FileError{...} escapes to heap +./update.go:54:7: &File{...} escapes to heap +./update.go:59:16: string(bytes.a) does not escape +./update.go:59:16: string(bytes.b) does not escape +./update.go:64:10: &FileError{...} escapes to heap +./update.go:69:11: &FileError{...} escapes to heap +./update.go:73:11: &FileError{...} escapes to heap +./update.go:77:10: &FileError{...} escapes to heap +./update.go:79:27: func literal does not escape +./update.go:80:10: &FileError{...} escapes to heap +./env.go:379:7: parameter e leaks to {heap} for Env.UpdateScope with derefs=0: +./env.go:379:7: flow: {heap} ← e: +./env.go:379:7: from Env.scopeFile(e, s) (call parameter) at ./env.go:385:33 +./env.go:379:7: leaking param: e +./env.go:379:35: fn does not escape +./env.go:424:25: parameter path leaks to {heap} for Env.UpdateFile with derefs=0: +./env.go:424:25: flow: {heap} ← path: +./env.go:424:25: from updateFile(path, Scope(0), fn) (call parameter) at ./env.go:425:19 +./env.go:424:7: e does not escape +./env.go:424:25: leaking param: path +./env.go:424:38: fn does not escape +./errors.go:74:7: parameter e leaks to {heap} for (*KeyError).Error with derefs=1: +./errors.go:74:7: flow: {storage for ... argument} ← *e: +./errors.go:74:7: from e.Err (dot of pointer) at ./errors.go:75:32 +./errors.go:74:7: from e.Err (interface-converted) at ./errors.go:75:32 +./errors.go:74:7: from ... argument (slice-literal-element) at ./errors.go:75:20 +./errors.go:74:7: flow: {heap} ← {storage for ... argument}: +./errors.go:74:7: from ... argument (spill) at ./errors.go:75:20 +./errors.go:74:7: from fmt.Sprintf("%v: %q", ... argument...) (call parameter) at ./errors.go:75:20 +./errors.go:75:39: e.Key escapes to heap in (*KeyError).Error: +./errors.go:75:39: flow: {storage for ... argument} ← &{storage for e.Key}: +./errors.go:75:39: from e.Key (spill) at ./errors.go:75:39 +./errors.go:75:39: from ... argument (slice-literal-element) at ./errors.go:75:20 +./errors.go:75:39: flow: {heap} ← {storage for ... argument}: +./errors.go:75:39: from ... argument (spill) at ./errors.go:75:20 +./errors.go:75:39: from fmt.Sprintf("%v: %q", ... argument...) (call parameter) at ./errors.go:75:20 +./errors.go:74:7: parameter e leaks to {storage for e.Key} for (*KeyError).Error with derefs=1: +./errors.go:74:7: flow: {storage for e.Key} ← *e: +./errors.go:74:7: from e.Key (dot of pointer) at ./errors.go:75:39 +./errors.go:74:7: from e.Key (interface-converted) at ./errors.go:75:39 +./errors.go:74:7: leaking param content: e +./errors.go:75:20: ... argument does not escape +./errors.go:75:39: e.Key escapes to heap +./errors.go:78:7: parameter e leaks to ~r0 for (*KeyError).Unwrap with derefs=1: +./errors.go:78:7: flow: ~r0 ← *e: +./errors.go:78:7: from e.Err (dot of pointer) at ./errors.go:79:10 +./errors.go:78:7: from return e.Err (return) at ./errors.go:79:2 +./errors.go:78:7: leaking param: e to result ~r0 level=1 +./errors.go:91:6: se escapes to heap in (*FileError).Error: +./errors.go:91:6: flow: {heap} ← &se: +./errors.go:91:6: from &se (address-of) at ./errors.go:92:22 +./errors.go:91:6: from &se (interface-converted) at ./errors.go:92:22 +./errors.go:91:6: from errors.As(e.Err, &se) (call parameter) at ./errors.go:92:14 +./errors.go:90:7: parameter e leaks to {heap} for (*FileError).Error with derefs=1: +./errors.go:90:7: flow: {heap} ← *e: +./errors.go:90:7: from e.Err (dot of pointer) at ./errors.go:92:16 +./errors.go:90:7: from errors.As(e.Err, &se) (call parameter) at ./errors.go:92:14 +./errors.go:93:18: e.Msg escapes to heap in (*FileError).Error: +./errors.go:93:18: flow: {storage for ... argument} ← &{storage for e.Msg}: +./errors.go:93:18: from e.Msg (spill) at ./errors.go:93:18 +./errors.go:93:18: from ... argument (slice-literal-element) at ./errors.go:93:18 +./errors.go:93:18: flow: {heap} ← {storage for ... argument}: +./errors.go:93:18: from ... argument (spill) at ./errors.go:93:18 +./errors.go:93:18: from fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) (call parameter) at ./errors.go:93:18 +./errors.go:93:18: e.Line escapes to heap in (*FileError).Error: +./errors.go:93:18: flow: {storage for ... argument} ← &{storage for e.Line}: +./errors.go:93:18: from e.Line (spill) at ./errors.go:93:18 +./errors.go:93:18: from ... argument (slice-literal-element) at ./errors.go:93:18 +./errors.go:93:18: flow: {heap} ← {storage for ... argument}: +./errors.go:93:18: from ... argument (spill) at ./errors.go:93:18 +./errors.go:93:18: from fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) (call parameter) at ./errors.go:93:18 +./errors.go:93:18: e.File escapes to heap in (*FileError).Error: +./errors.go:93:18: flow: {storage for ... argument} ← &{storage for e.File}: +./errors.go:93:18: from e.File (spill) at ./errors.go:93:18 +./errors.go:93:18: from ... argument (slice-literal-element) at ./errors.go:93:18 +./errors.go:93:18: flow: {heap} ← {storage for ... argument}: +./errors.go:93:18: from ... argument (spill) at ./errors.go:93:18 +./errors.go:93:18: from fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) (call parameter) at ./errors.go:93:18 +./errors.go:95:43: e.Path escapes to heap in (*FileError).Error: +./errors.go:95:43: flow: {storage for ... argument} ← &{storage for e.Path}: +./errors.go:95:43: from e.Path (spill) at ./errors.go:95:43 +./errors.go:95:43: from ... argument (slice-literal-element) at ./errors.go:95:20 +./errors.go:95:43: flow: {heap} ← {storage for ... argument}: +./errors.go:95:43: from ... argument (spill) at ./errors.go:95:20 +./errors.go:95:43: from fmt.Sprintf("gitconfig: %s: %v", ... argument...) (call parameter) at ./errors.go:95:20 +./errors.go:90:7: parameter e leaks to {storage for e.Path} for (*FileError).Error with derefs=1: +./errors.go:90:7: flow: {storage for e.Path} ← *e: +./errors.go:90:7: from e.Path (dot of pointer) at ./errors.go:95:43 +./errors.go:90:7: from e.Path (interface-converted) at ./errors.go:95:43 +./errors.go:90:7: leaking param content: e +./errors.go:91:6: moved to heap: se +./errors.go:93:18: ... argument does not escape +./errors.go:93:18: e.Msg escapes to heap +./errors.go:93:18: e.Line escapes to heap +./errors.go:93:18: e.File escapes to heap +./errors.go:95:20: ... argument does not escape +./errors.go:95:43: e.Path escapes to heap +./errors.go:98:7: parameter e leaks to ~r0 for (*FileError).Unwrap with derefs=1: +./errors.go:98:7: flow: ~r0 ← *e: +./errors.go:98:7: from e.Err (dot of pointer) at ./errors.go:99:10 +./errors.go:98:7: from return e.Err (return) at ./errors.go:99:2 +./errors.go:98:7: leaking param: e to result ~r0 level=1 +./errors.go:112:58: e.Msg escapes to heap in (*SyntaxError).Error: +./errors.go:112:58: flow: {storage for ... argument} ← &{storage for e.Msg}: +./errors.go:112:58: from e.Msg (spill) at ./errors.go:112:58 +./errors.go:112:58: from ... argument (slice-literal-element) at ./errors.go:112:20 +./errors.go:112:58: flow: {heap} ← {storage for ... argument}: +./errors.go:112:58: from ... argument (spill) at ./errors.go:112:20 +./errors.go:112:58: from fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) (call parameter) at ./errors.go:112:20 +./errors.go:112:65: e.Line escapes to heap in (*SyntaxError).Error: +./errors.go:112:65: flow: {storage for ... argument} ← &{storage for e.Line}: +./errors.go:112:65: from e.Line (spill) at ./errors.go:112:65 +./errors.go:112:65: from ... argument (slice-literal-element) at ./errors.go:112:20 +./errors.go:112:65: flow: {heap} ← {storage for ... argument}: +./errors.go:112:65: from ... argument (spill) at ./errors.go:112:20 +./errors.go:112:65: from fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) (call parameter) at ./errors.go:112:20 +./errors.go:112:73: e.File escapes to heap in (*SyntaxError).Error: +./errors.go:112:73: flow: {storage for ... argument} ← &{storage for e.File}: +./errors.go:112:73: from e.File (spill) at ./errors.go:112:73 +./errors.go:112:73: from ... argument (slice-literal-element) at ./errors.go:112:20 +./errors.go:112:73: flow: {heap} ← {storage for ... argument}: +./errors.go:112:73: from ... argument (spill) at ./errors.go:112:20 +./errors.go:112:73: from fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) (call parameter) at ./errors.go:112:20 +./errors.go:111:7: parameter e leaks to {storage for e.File} for (*SyntaxError).Error with derefs=1: +./errors.go:111:7: flow: {storage for e.File} ← *e: +./errors.go:111:7: from e.File (dot of pointer) at ./errors.go:112:73 +./errors.go:111:7: from e.File (interface-converted) at ./errors.go:112:73 +./errors.go:111:7: parameter e leaks to {storage for e.Msg} for (*SyntaxError).Error with derefs=1: +./errors.go:111:7: flow: {storage for e.Msg} ← *e: +./errors.go:111:7: from e.Msg (dot of pointer) at ./errors.go:112:58 +./errors.go:111:7: from e.Msg (interface-converted) at ./errors.go:112:58 +./errors.go:111:7: leaking param content: e +./errors.go:112:20: ... argument does not escape +./errors.go:112:58: e.Msg escapes to heap +./errors.go:112:65: e.Line escapes to heap +./errors.go:112:73: e.File escapes to heap +./errors.go:124:7: parameter e leaks to {heap} for (*ValueError).Error with derefs=1: +./errors.go:124:7: flow: {heap} ← *e: +./errors.go:124:7: from e.Err (dot of pointer) at ./errors.go:135:18 +./errors.go:124:7: from e.Err.Error() (call parameter) at ./errors.go:135:28 +./errors.go:127:60: e.Type escapes to heap in (*ValueError).Error: +./errors.go:127:60: flow: {storage for ... argument} ← &{storage for e.Type}: +./errors.go:127:60: from e.Type (spill) at ./errors.go:127:60 +./errors.go:127:60: from ... argument (slice-literal-element) at ./errors.go:127:20 +./errors.go:127:60: flow: {heap} ← {storage for ... argument}: +./errors.go:127:60: from ... argument (spill) at ./errors.go:127:20 +./errors.go:127:60: from fmt.Sprintf("gitconfig: missing %s value for %q", ... argument...) (call parameter) at ./errors.go:127:20 +./errors.go:127:85: ~r0 escapes to heap in (*ValueError).Error: +./errors.go:127:85: flow: {storage for ... argument} ← &{storage for ~r0}: +./errors.go:127:85: from ~r0 (spill) at ./errors.go:127:85 +./errors.go:127:85: from ... argument (slice-literal-element) at ./errors.go:127:20 +./errors.go:127:85: flow: {heap} ← {storage for ... argument}: +./errors.go:127:85: from ... argument (spill) at ./errors.go:127:20 +./errors.go:127:85: from fmt.Sprintf("gitconfig: missing %s value for %q", ... argument...) (call parameter) at ./errors.go:127:20 +./errors.go:129:59: e.Type escapes to heap in (*ValueError).Error: +./errors.go:129:59: flow: {storage for ... argument} ← &{storage for e.Type}: +./errors.go:129:59: from e.Type (spill) at ./errors.go:129:59 +./errors.go:129:59: from ... argument (slice-literal-element) at ./errors.go:129:20 +./errors.go:129:59: flow: {heap} ← {storage for ... argument}: +./errors.go:129:59: from ... argument (spill) at ./errors.go:129:20 +./errors.go:129:59: from fmt.Sprintf("gitconfig: bad %s value %q for %q", ... argument...) (call parameter) at ./errors.go:129:20 +./errors.go:129:73: e.Entry.Value escapes to heap in (*ValueError).Error: +./errors.go:129:73: flow: {storage for ... argument} ← &{storage for e.Entry.Value}: +./errors.go:129:73: from e.Entry.Value (spill) at ./errors.go:129:73 +./errors.go:129:73: from ... argument (slice-literal-element) at ./errors.go:129:20 +./errors.go:129:73: flow: {heap} ← {storage for ... argument}: +./errors.go:129:73: from ... argument (spill) at ./errors.go:129:20 +./errors.go:129:73: from fmt.Sprintf("gitconfig: bad %s value %q for %q", ... argument...) (call parameter) at ./errors.go:129:20 +./errors.go:129:99: ~r0 escapes to heap in (*ValueError).Error: +./errors.go:129:99: flow: {storage for ... argument} ← &{storage for ~r0}: +./errors.go:129:99: from ~r0 (spill) at ./errors.go:129:99 +./errors.go:129:99: from ... argument (slice-literal-element) at ./errors.go:129:20 +./errors.go:129:99: flow: {heap} ← {storage for ... argument}: +./errors.go:129:99: from ... argument (spill) at ./errors.go:129:20 +./errors.go:129:99: from fmt.Sprintf("gitconfig: bad %s value %q for %q", ... argument...) (call parameter) at ./errors.go:129:20 +./errors.go:132:51: e.Entry.Origin.File escapes to heap in (*ValueError).Error: +./errors.go:132:51: flow: {storage for ... argument} ← &{storage for e.Entry.Origin.File}: +./errors.go:132:51: from e.Entry.Origin.File (spill) at ./errors.go:132:51 +./errors.go:132:51: from ... argument (slice-literal-element) at ./errors.go:132:21 +./errors.go:132:51: flow: {heap} ← {storage for ... argument}: +./errors.go:132:51: from ... argument (spill) at ./errors.go:132:21 +./errors.go:132:51: from fmt.Sprintf(" in file %s", ... argument...) (call parameter) at ./errors.go:132:21 +./errors.go:124:7: parameter e leaks to {storage for e.Entry.Origin.File} for (*ValueError).Error with derefs=1: +./errors.go:124:7: flow: {storage for e.Entry.Origin.File} ← *e: +./errors.go:124:7: from e.Entry (dot of pointer) at ./errors.go:132:38 +./errors.go:124:7: from e.Entry.Origin (dot) at ./errors.go:132:44 +./errors.go:124:7: from e.Entry.Origin.File (dot) at ./errors.go:132:51 +./errors.go:124:7: from e.Entry.Origin.File (interface-converted) at ./errors.go:132:51 +./errors.go:129:99: k.Section + "." + k.Subsection + "." + k.Name escapes to heap in (*ValueError).Error: +./errors.go:129:99: flow: ~r0 ← &{storage for k.Section + "." + k.Subsection + "." + k.Name}: +./errors.go:129:99: from k.Section + "." + k.Subsection + "." + k.Name (spill) at ./errors.go:129:99 +./errors.go:129:99: from ~r0 = k.Section + "." + k.Subsection + "." + k.Name (assign-pair) at ./errors.go:129:99 +./errors.go:129:99: flow: {storage for ~r0} ← ~r0: +./errors.go:129:99: from ~r0 (interface-converted) at ./errors.go:129:99 +./errors.go:129:99: k.Section + "." + k.Name escapes to heap in (*ValueError).Error: +./errors.go:129:99: flow: ~r0 ← &{storage for k.Section + "." + k.Name}: +./errors.go:129:99: from k.Section + "." + k.Name (spill) at ./errors.go:129:99 +./errors.go:129:99: from ~r0 = k.Section + "." + k.Name (assign-pair) at ./errors.go:129:99 +./errors.go:129:99: flow: {storage for ~r0} ← ~r0: +./errors.go:129:99: from ~r0 (interface-converted) at ./errors.go:129:99 +./errors.go:124:7: parameter e leaks to {storage for e.Entry.Value} for (*ValueError).Error with derefs=1: +./errors.go:124:7: flow: {storage for e.Entry.Value} ← *e: +./errors.go:124:7: from e.Entry (dot of pointer) at ./errors.go:129:67 +./errors.go:124:7: from e.Entry.Value (dot) at ./errors.go:129:73 +./errors.go:124:7: from e.Entry.Value (interface-converted) at ./errors.go:129:73 +./errors.go:124:7: parameter e leaks to {storage for e.Type} for (*ValueError).Error with derefs=1: +./errors.go:124:7: flow: {storage for e.Type} ← *e: +./errors.go:124:7: from e.Type (dot of pointer) at ./errors.go:129:59 +./errors.go:124:7: from e.Type (interface-converted) at ./errors.go:129:59 +./errors.go:127:85: k.Section + "." + k.Subsection + "." + k.Name escapes to heap in (*ValueError).Error: +./errors.go:127:85: flow: ~r0 ← &{storage for k.Section + "." + k.Subsection + "." + k.Name}: +./errors.go:127:85: from k.Section + "." + k.Subsection + "." + k.Name (spill) at ./errors.go:127:85 +./errors.go:127:85: from ~r0 = k.Section + "." + k.Subsection + "." + k.Name (assign-pair) at ./errors.go:127:85 +./errors.go:127:85: flow: {storage for ~r0} ← ~r0: +./errors.go:127:85: from ~r0 (interface-converted) at ./errors.go:127:85 +./errors.go:127:85: k.Section + "." + k.Name escapes to heap in (*ValueError).Error: +./errors.go:127:85: flow: ~r0 ← &{storage for k.Section + "." + k.Name}: +./errors.go:127:85: from k.Section + "." + k.Name (spill) at ./errors.go:127:85 +./errors.go:127:85: from ~r0 = k.Section + "." + k.Name (assign-pair) at ./errors.go:127:85 +./errors.go:127:85: flow: {storage for ~r0} ← ~r0: +./errors.go:127:85: from ~r0 (interface-converted) at ./errors.go:127:85 +./errors.go:124:7: parameter e leaks to {storage for e.Type} for (*ValueError).Error with derefs=1: +./errors.go:124:7: flow: {storage for e.Type} ← *e: +./errors.go:124:7: from e.Type (dot of pointer) at ./errors.go:127:60 +./errors.go:124:7: from e.Type (interface-converted) at ./errors.go:127:60 +./errors.go:135:15: ": " + e.Err.Error() escapes to heap in (*ValueError).Error: +./errors.go:135:15: flow: msg ← &{storage for ": " + e.Err.Error()}: +./errors.go:135:15: from ": " + e.Err.Error() (spill) at ./errors.go:135:15 +./errors.go:135:15: from msg += ": " + e.Err.Error() (assign) at ./errors.go:135:7 +./errors.go:135:15: flow: ~r0 ← msg: +./errors.go:135:15: from return msg (return) at ./errors.go:137:2 +./errors.go:124:7: leaking param content: e +./errors.go:127:20: ... argument does not escape +./errors.go:127:60: e.Type escapes to heap +./errors.go:127:85: ~r0 escapes to heap +./errors.go:127:85: k.Section + "." + k.Subsection + "." + k.Name escapes to heap +./errors.go:127:85: k.Section + "." + k.Name escapes to heap +./errors.go:129:20: ... argument does not escape +./errors.go:129:59: e.Type escapes to heap +./errors.go:129:73: e.Entry.Value escapes to heap +./errors.go:129:99: ~r0 escapes to heap +./errors.go:129:99: k.Section + "." + k.Subsection + "." + k.Name escapes to heap +./errors.go:129:99: k.Section + "." + k.Name escapes to heap +./errors.go:132:21: ... argument does not escape +./errors.go:132:51: e.Entry.Origin.File escapes to heap +./errors.go:135:15: ": " + e.Err.Error() escapes to heap +./errors.go:140:7: parameter e leaks to ~r0 for (*ValueError).Unwrap with derefs=1: +./errors.go:140:7: flow: ~r0 ← *e: +./errors.go:140:7: from e.Err (dot of pointer) at ./errors.go:141:10 +./errors.go:140:7: from return e.Err (return) at ./errors.go:141:2 +./errors.go:140:7: leaking param: e to result ~r0 level=1 +./file.go:55:12: parameter name leaks to {heap} for Parse with derefs=0: +./file.go:55:12: flow: {heap} ← name: +./file.go:55:12: from parse(name, data) (call parameter) at ./file.go:57:17 +./file.go:55:25: parameter data leaks to {heap} for Parse with derefs=0: +./file.go:55:25: flow: {heap} ← data: +./file.go:55:25: from parse(name, data) (call parameter) at ./file.go:57:17 +./file.go:56:20: append([]byte{}, bytes.b...) escapes to heap in Parse: +./file.go:56:20: flow: ~r0 ← &{storage for append([]byte{}, bytes.b...)}: +./file.go:56:20: from append([]byte{}, bytes.b...) (spill) at ./file.go:56:20 +./file.go:56:20: from ~r0 = append([]byte{}, bytes.b...) (assign-pair) at ./file.go:56:20 +./file.go:56:20: flow: data ← ~r0: +./file.go:56:20: from data = ~r0 (assign) at ./file.go:56:7 +./file.go:56:20: flow: {heap} ← data: +./file.go:56:20: from parse(name, data) (call parameter) at ./file.go:57:17 +./file.go:56:20: []byte{} escapes to heap in Parse: +./file.go:56:20: flow: {temp} ← &{storage for []byte{}}: +./file.go:56:20: from []byte{} (spill) at ./file.go:56:20 +./file.go:56:20: from append([]byte{}, bytes.b...) (call parameter) at ./file.go:56:20 +./file.go:56:20: flow: ~r0 ← {temp}: +./file.go:56:20: from ~r0 = append([]byte{}, bytes.b...) (assign-pair) at ./file.go:56:20 +./file.go:56:20: flow: data ← ~r0: +./file.go:56:20: from data = ~r0 (assign) at ./file.go:56:7 +./file.go:56:20: flow: {heap} ← data: +./file.go:56:20: from parse(name, data) (call parameter) at ./file.go:57:17 +./file.go:61:9: &File{...} escapes to heap in Parse: +./file.go:61:9: flow: ~r0 ← &{storage for &File{...}}: +./file.go:61:9: from &File{...} (spill) at ./file.go:61:9 +./file.go:61:9: from return &File{...}, nil (return) at ./file.go:61:2 +./file.go:55:12: parameter name leaks to {storage for &File{...}} for Parse with derefs=0: +./file.go:55:12: flow: {storage for &File{...}} ← name: +./file.go:55:12: from File{...} (struct literal element) at ./file.go:61:14 +./file.go:55:25: parameter data leaks to {storage for &File{...}} for Parse with derefs=0: +./file.go:55:25: flow: {storage for &File{...}} ← data: +./file.go:55:25: from File{...} (struct literal element) at ./file.go:61:14 +./file.go:55:12: leaking param: name +./file.go:55:25: leaking param: data +./file.go:56:20: []byte{} escapes to heap +./file.go:56:20: append escapes to heap +./file.go:61:9: &File{...} escapes to heap +./file.go:67:20: append([]byte{}, bytes.b...) escapes to heap in (*File).Bytes: +./file.go:67:20: flow: ~r0 ← &{storage for append([]byte{}, bytes.b...)}: +./file.go:67:20: from append([]byte{}, bytes.b...) (spill) at ./file.go:67:20 +./file.go:67:20: from ~r0 = append([]byte{}, bytes.b...) (assign-pair) at ./file.go:67:20 +./file.go:67:20: flow: ~r0 ← ~r0: +./file.go:67:20: from return ~r0 (return) at ./file.go:67:2 +./file.go:67:20: []byte{} escapes to heap in (*File).Bytes: +./file.go:67:20: flow: {temp} ← &{storage for []byte{}}: +./file.go:67:20: from []byte{} (spill) at ./file.go:67:20 +./file.go:67:20: from append([]byte{}, bytes.b...) (call parameter) at ./file.go:67:20 +./file.go:67:20: flow: ~r0 ← {temp}: +./file.go:67:20: from ~r0 = append([]byte{}, bytes.b...) (assign-pair) at ./file.go:67:20 +./file.go:67:20: flow: ~r0 ← ~r0: +./file.go:67:20: from return ~r0 (return) at ./file.go:67:2 +./file.go:66:7: f does not escape +./file.go:67:20: []byte{} escapes to heap +./file.go:67:20: append escapes to heap +./file.go:78:7: parameter f leaks to {heap} for (*File).Config with derefs=1: +./file.go:78:7: flow: file ← *f: +./file.go:78:7: from f.name (dot of pointer) at ./file.go:80:42 +./file.go:78:7: from p, scope, file, out := f.parsed, f.scope, f.name, nil (assign-pair) at ./file.go:80:31 +./file.go:78:7: flow: {heap} ← file: +./file.go:78:7: from Origin{...} (struct literal element) at ./file.go:80:31 +./file.go:78:7: from Entry{...} (struct literal element) at ./file.go:80:31 +./file.go:78:7: from append(out, Entry{...}) (call parameter) at ./file.go:80:31 +./file.go:80:31: append(out, Entry{...}) escapes to heap in (*File).Config: +./file.go:80:31: flow: out ← &{storage for append(out, Entry{...})}: +./file.go:80:31: from append(out, Entry{...}) (spill) at ./file.go:80:31 +./file.go:80:31: from out = append(out, Entry{...}) (assign) at ./file.go:80:31 +./file.go:80:31: flow: ~r0 ← out: +./file.go:80:31: from ~r0 = out (assign-pair) at ./file.go:80:31 +./file.go:80:31: flow: {heap} ← ~r0: +./file.go:80:31: from newConfig(~r0) (call parameter) at ./file.go:80:21 +./file.go:78:7: parameter f leaks to ~r0 for (*File).Config with derefs=1: +./file.go:78:7: flow: ~r0 ← *f: +./file.go:78:7: from f.snap (dot of pointer) at ./file.go:82:10 +./file.go:78:7: from return f.snap (return) at ./file.go:82:2 +./file.go:78:7: leaking param content: f +./file.go:80:31: append escapes to heap +./file.go:105:78: (*File).Set capturing by value: k (addr=false assign=false width=56) +./file.go:105:78: (*File).Set capturing by value: want (addr=false assign=false width=24) +./file.go:105:78: (*File).Set capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:105:78: (*File).Set capturing by value: k (addr=false assign=false width=56) +./file.go:105:78: (*File).Set capturing by value: k (addr=false assign=false width=56) +./file.go:107:80: (*File).Set capturing by value: k (addr=false assign=false width=56) +./file.go:107:80: (*File).Set capturing by value: want (addr=false assign=false width=24) +./file.go:107:80: (*File).Set capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:107:80: (*File).Set capturing by value: k (addr=false assign=false width=56) +./file.go:107:80: (*File).Set capturing by value: k (addr=false assign=false width=56) +./file.go:92:20: parameter key leaks to {heap} for (*File).Set with derefs=0: +./file.go:92:20: flow: {heap} ← key: +./file.go:92:20: from (*File).prepare(f, key, &value) (call parameter) at ./file.go:93:41 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1#X5GLJN/kegQ=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1#X5GLJN/kegQ=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1#v+T99KtU5ug=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1#v+T99KtU5ug=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./file.go:92:7: parameter f leaks to {heap} for (*File).Set with derefs=1: +./file.go:92:7: flow: {temp} ← f: +./file.go:92:7: from (*File).apply(f, key, []splice{...}, ~r0) (call parameter) at ./file.go:105:17 +./file.go:92:7: flow: {heap} ← *{temp}: +./file.go:100:10: &KeyError{...} escapes to heap in (*File).Set: +./file.go:100:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:100:10: from &KeyError{...} (spill) at ./file.go:100:10 +./file.go:100:10: from &KeyError{...} (interface-converted) at ./file.go:100:10 +./file.go:100:10: from return &KeyError{...} (return) at ./file.go:100:3 +./file.go:92:20: parameter key leaks to {storage for &KeyError{...}} for (*File).Set with derefs=0: +./file.go:92:20: flow: {storage for &KeyError{...}} ← key: +./file.go:92:20: from KeyError{...} (struct literal element) at ./file.go:100:19 +./file.go:92:7: leaking param content: f +./file.go:92:20: leaking param: key +./file.go:92:25: value does not escape +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./file.go:97:20: append does not escape +./file.go:100:10: &KeyError{...} escapes to heap +./file.go:105:31: []splice{...} does not escape +./file.go:105:90: []string{...} does not escape +./file.go:105:78: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:107:30: []splice{...} does not escape +./file.go:107:92: []string{...} does not escape +./file.go:107:80: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:119:81: (*File).ReplaceAll capturing by value: k (addr=false assign=false width=56) +./file.go:119:81: (*File).ReplaceAll capturing by value: want (addr=false assign=false width=24) +./file.go:119:81: (*File).ReplaceAll capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:119:81: (*File).ReplaceAll capturing by value: k (addr=false assign=false width=56) +./file.go:119:81: (*File).ReplaceAll capturing by value: k (addr=false assign=false width=56) +./file.go:127:39: (*File).ReplaceAll capturing by value: k (addr=false assign=false width=56) +./file.go:127:39: (*File).ReplaceAll capturing by value: want (addr=false assign=false width=24) +./file.go:127:39: (*File).ReplaceAll capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:127:39: (*File).ReplaceAll capturing by value: k (addr=false assign=false width=56) +./file.go:127:39: (*File).ReplaceAll capturing by value: k (addr=false assign=false width=56) +./file.go:112:27: parameter key leaks to {heap} for (*File).ReplaceAll with derefs=0: +./file.go:112:27: flow: {heap} ← key: +./file.go:112:27: from (*File).prepare(f, key, &value) (call parameter) at ./file.go:113:41 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1#MKVITQ+eOz4=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1#MKVITQ+eOz4=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1#QqliH0hbpFs=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1#QqliH0hbpFs=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./file.go:112:7: parameter f leaks to {heap} for (*File).ReplaceAll with derefs=1: +./file.go:112:7: flow: {temp} ← f: +./file.go:112:7: from (*File).apply(f, key, []splice{...}, ~r0) (call parameter) at ./file.go:119:17 +./file.go:112:7: flow: {heap} ← *{temp}: +./file.go:112:7: leaking param content: f +./file.go:112:27: leaking param: key +./file.go:112:32: value does not escape +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./file.go:117:20: append does not escape +./file.go:119:31: []splice{...} does not escape +./file.go:119:93: []string{...} does not escape +./file.go:119:81: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:124:19: append does not escape +./file.go:126:18: append does not escape +./file.go:127:51: []string{...} does not escape +./file.go:127:39: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:132:2: (*File).Add capturing by value: k (addr=false assign=false width=56) +./file.go:136:6: (*File).Add capturing by value: want (addr=false assign=false width=24) +./file.go:144:18: (*File).Add.func1 capturing by value: k (addr=false assign=false width=56) +./file.go:144:18: (*File).Add.func1 capturing by value: want (addr=false assign=false width=24) +./file.go:144:18: (*File).Add.func1 capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:144:18: (*File).Add.func1 capturing by value: k (addr=false assign=false width=56) +./file.go:144:18: (*File).Add.func1 capturing by value: k (addr=false assign=false width=56) +./file.go:131:20: parameter key leaks to {heap} for (*File).Add with derefs=0: +./file.go:131:20: flow: {heap} ← key: +./file.go:131:20: from (*File).prepare(f, key, &value) (call parameter) at ./file.go:132:41 +./file.go:131:25: parameter value leaks to {heap} for (*File).Add with derefs=0: +./file.go:131:25: flow: {heap} ← value: +./file.go:131:25: from append(want, value) (call parameter) at ./file.go:140:15 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.1#r3iRv3NFPAI=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.1#r3iRv3NFPAI=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./file.go:131:7: parameter f leaks to {heap} for (*File).Add with derefs=1: +./file.go:131:7: flow: {temp} ← f: +./file.go:131:7: from (*File).apply(f, key, []splice{...}, func literal) (call parameter) at ./file.go:141:16 +./file.go:131:7: flow: {heap} ← *{temp}: +./file.go:141:85: parameter after leaks to {heap} for (*File).Add.func1 with derefs=1: +./file.go:141:85: flow: entries ← after: +./file.go:141:85: from entries, _ := after, 0 (assign-pair) at ./file.go:143:16 +./file.go:141:85: flow: {temp} ← entries: +./file.go:141:85: flow: e ← *{temp}: +./file.go:141:85: from for loop (range-deref) at ./file.go:143:16 +./file.go:141:85: flow: {heap} ← e: +./file.go:141:85: from append(out, e) (call parameter) at ./file.go:143:16 +./file.go:141:77: parameter before leaks to {heap} for (*File).Add.func1 with derefs=1: +./file.go:141:77: flow: before ← before: +./file.go:141:77: from before, after := before, after (assign-pair) at ./file.go:144:33 +./file.go:141:77: flow: entries ← before: +./file.go:141:77: from entries, _ := before, 0 (assign-pair) at ./file.go:144:33 +./file.go:141:77: flow: {temp} ← entries: +./file.go:141:77: flow: e ← *{temp}: +./file.go:141:77: from for loop (range-deref) at ./file.go:144:33 +./file.go:141:77: flow: {heap} ← e: +./file.go:141:77: from append(out, e) (call parameter) at ./file.go:144:33 +./file.go:131:7: leaking param content: f +./file.go:131:20: leaking param: key +./file.go:131:25: leaking param: value +./file.go:141:77: leaking param content: before +./file.go:141:85: leaking param content: after +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./file.go:137:31: append does not escape +./file.go:138:16: append does not escape +./file.go:140:15: append does not escape +./file.go:141:30: []splice{...} does not escape +./file.go:141:72: func literal does not escape +./file.go:143:16: append does not escape +./file.go:144:18: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:144:33: append does not escape +./file.go:144:33: append does not escape +./file.go:144:33: append does not escape +./file.go:161:52: (*File).Unset capturing by value: k (addr=false assign=false width=56) +./file.go:161:52: (*File).Unset capturing by value: want (addr=false assign=false width=24) +./file.go:161:52: (*File).Unset capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:161:52: (*File).Unset capturing by value: k (addr=false assign=false width=56) +./file.go:161:52: (*File).Unset capturing by value: k (addr=false assign=false width=56) +./file.go:151:22: parameter key leaks to {heap} for (*File).Unset with derefs=0: +./file.go:151:22: flow: {heap} ← key: +./file.go:151:22: from (*File).prepare(f, key, nil) (call parameter) at ./file.go:152:30 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1#CZPSVfsmoKE=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1#CZPSVfsmoKE=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./file.go:151:7: parameter f leaks to {heap} for (*File).Unset with derefs=1: +./file.go:151:7: flow: {temp} ← f: +./file.go:151:7: from (*File).apply(f, key, (*File).unsetEntries(f, idx), ~r0) (call parameter) at ./file.go:161:17 +./file.go:151:7: flow: {heap} ← *{temp}: +./file.go:164:16: &KeyError{...} escapes to heap in (*File).Unset: +./file.go:164:16: flow: err ← &{storage for &KeyError{...}}: +./file.go:164:16: from &KeyError{...} (spill) at ./file.go:164:16 +./file.go:164:16: from &KeyError{...} (interface-converted) at ./file.go:164:16 +./file.go:164:16: from return false, &KeyError{...} (return) at ./file.go:164:2 +./file.go:151:22: parameter key leaks to {storage for &KeyError{...}} for (*File).Unset with derefs=0: +./file.go:151:22: flow: {storage for &KeyError{...}} ← key: +./file.go:151:22: from KeyError{...} (struct literal element) at ./file.go:164:25 +./file.go:151:7: leaking param content: f +./file.go:151:22: leaking param: key +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./file.go:156:20: append does not escape +./file.go:161:52: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:164:16: &KeyError{...} escapes to heap +./file.go:178:54: (*File).UnsetAll capturing by value: k (addr=false assign=false width=56) +./file.go:178:54: (*File).UnsetAll capturing by value: want (addr=false assign=false width=24) +./file.go:178:54: (*File).UnsetAll capturing by value: implicitAllowed (addr=false assign=false width=1) +./file.go:178:54: (*File).UnsetAll capturing by value: k (addr=false assign=false width=56) +./file.go:178:54: (*File).UnsetAll capturing by value: k (addr=false assign=false width=56) +./file.go:169:25: parameter key leaks to {heap} for (*File).UnsetAll with derefs=0: +./file.go:169:25: flow: {heap} ← key: +./file.go:169:25: from (*File).prepare(f, key, nil) (call parameter) at ./file.go:170:30 +./edit.go:134:14: parameter before leaks to {heap} for checkKey.func1#tan11JIgj98=# with derefs=1: +./edit.go:134:14: flow: {heap} ← *before: +./edit.go:134:14: from filter(before, other) (call parameter) at ./edit.go:136:26 +./edit.go:134:22: parameter after leaks to {heap} for checkKey.func1#tan11JIgj98=# with derefs=1: +./edit.go:134:22: flow: {heap} ← *after: +./edit.go:134:22: from filter(after, other) (call parameter) at ./edit.go:136:49 +./file.go:169:7: parameter f leaks to {heap} for (*File).UnsetAll with derefs=1: +./file.go:169:7: flow: {temp} ← f: +./file.go:169:7: from (*File).apply(f, key, (*File).unsetEntries(f, idx), ~r0) (call parameter) at ./file.go:178:19 +./file.go:169:7: flow: {heap} ← *{temp}: +./file.go:169:7: leaking param content: f +./file.go:169:25: leaking param: key +./edit.go:134:14: leaking param content: before +./edit.go:134:22: leaking param content: after +./edit.go:135:17: e does not escape +./edit.go:139:29: e does not escape +./file.go:174:20: append does not escape +./file.go:178:54: func literal does not escape +./edit.go:135:12: func literal does not escape +./edit.go:139:24: func literal does not escape +./file.go:187:30: parameter section leaks to {heap} for (*File).RemoveSection with derefs=0: +./file.go:187:30: flow: {heap} ← section: +./file.go:187:30: from (*File).removeSection(f, section, sectionKey{...}) (call parameter) at ./file.go:194:24 +./file.go:187:7: parameter f leaks to {heap} for (*File).RemoveSection with derefs=1: +./file.go:187:7: flow: {temp} ← f: +./file.go:187:7: from (*File).removeSection(f, section, sectionKey{...}) (call parameter) at ./file.go:194:24 +./file.go:187:7: flow: {heap} ← *{temp}: +./file.go:194:64: string(b) escapes to heap in (*File).RemoveSection: +./file.go:194:64: flow: ~r0 ← &{storage for string(b)}: +./file.go:194:64: from string(b) (spill) at ./file.go:194:64 +./file.go:194:64: from ~r0 = string(b) (assign-pair) at ./file.go:194:64 +./file.go:188:23: &KeyError{...} escapes to heap in (*File).RemoveSection: +./file.go:188:23: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:188:23: from &KeyError{...} (spill) at ./file.go:188:23 +./file.go:188:23: from &KeyError{...} (interface-converted) at ./file.go:188:23 +./file.go:188:23: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:188:23 +./file.go:188:23: flow: err ← ~r0: +./file.go:188:23: from err := ~r0 (assign) at ./file.go:188:9 +./file.go:188:23: flow: err ← err: +./file.go:188:23: from return false, err (return) at ./file.go:189:3 +./file.go:191:22: &KeyError{...} escapes to heap in (*File).RemoveSection: +./file.go:191:22: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:191:22: from &KeyError{...} (spill) at ./file.go:191:22 +./file.go:191:22: from &KeyError{...} (interface-converted) at ./file.go:191:22 +./file.go:191:22: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:191:22 +./file.go:191:22: flow: err ← ~r0: +./file.go:191:22: from err := ~r0 (assign) at ./file.go:191:9 +./file.go:191:22: flow: err ← err: +./file.go:191:22: from return false, err (return) at ./file.go:192:3 +./file.go:187:30: parameter section leaks to {storage for &KeyError{...}} for (*File).RemoveSection with derefs=0: +./file.go:187:30: flow: section ← section: +./file.go:187:30: from section := section (assign-pair) at ./file.go:191:22 +./file.go:187:30: flow: {storage for &KeyError{...}} ← section: +./file.go:187:30: from KeyError{...} (struct literal element) at ./file.go:191:22 +./file.go:187:30: parameter section leaks to {storage for &KeyError{...}} for (*File).RemoveSection with derefs=0: +./file.go:187:30: flow: key ← section: +./file.go:187:30: from f, key := f, section (assign-pair) at ./file.go:188:23 +./file.go:187:30: flow: {storage for &KeyError{...}} ← key: +./file.go:187:30: from KeyError{...} (struct literal element) at ./file.go:188:23 +./file.go:187:7: leaking param content: f +./file.go:187:30: leaking param: section +./file.go:188:23: &KeyError{...} escapes to heap +./file.go:191:22: &KeyError{...} escapes to heap +./file.go:194:64: ([]byte)(s) does not escape +./file.go:194:64: string(b) escapes to heap +./file.go:200:33: parameter section leaks to {heap} for (*File).RemoveSubsection with derefs=0: +./file.go:200:33: flow: {heap} ← section: +./file.go:200:33: from (*File).removeSection(f, section, sectionKey{...}) (call parameter) at ./file.go:210:24 +./file.go:200:7: parameter f leaks to {heap} for (*File).RemoveSubsection with derefs=1: +./file.go:200:7: flow: {temp} ← f: +./file.go:200:7: from (*File).removeSection(f, section, sectionKey{...}) (call parameter) at ./file.go:210:24 +./file.go:200:7: flow: {heap} ← *{temp}: +./file.go:210:55: string(b) escapes to heap in (*File).RemoveSubsection: +./file.go:210:55: flow: ~r0 ← &{storage for string(b)}: +./file.go:210:55: from string(b) (spill) at ./file.go:210:55 +./file.go:210:55: from ~r0 = string(b) (assign-pair) at ./file.go:210:55 +./file.go:208:17: &KeyError{...} escapes to heap in (*File).RemoveSubsection: +./file.go:208:17: flow: err ← &{storage for &KeyError{...}}: +./file.go:208:17: from &KeyError{...} (spill) at ./file.go:208:17 +./file.go:208:17: from &KeyError{...} (interface-converted) at ./file.go:208:17 +./file.go:208:17: from return false, &KeyError{...} (return) at ./file.go:208:3 +./file.go:201:23: &KeyError{...} escapes to heap in (*File).RemoveSubsection: +./file.go:201:23: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:201:23: from &KeyError{...} (spill) at ./file.go:201:23 +./file.go:201:23: from &KeyError{...} (interface-converted) at ./file.go:201:23 +./file.go:201:23: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:201:23 +./file.go:201:23: flow: err ← ~r0: +./file.go:201:23: from err := ~r0 (assign) at ./file.go:201:9 +./file.go:201:23: flow: err ← err: +./file.go:201:23: from return false, err (return) at ./file.go:202:3 +./file.go:204:22: &KeyError{...} escapes to heap in (*File).RemoveSubsection: +./file.go:204:22: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:204:22: from &KeyError{...} (spill) at ./file.go:204:22 +./file.go:204:22: from &KeyError{...} (interface-converted) at ./file.go:204:22 +./file.go:204:22: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:204:22 +./file.go:204:22: flow: err ← ~r0: +./file.go:204:22: from err := ~r0 (assign) at ./file.go:204:9 +./file.go:204:22: flow: err ← err: +./file.go:204:22: from return false, err (return) at ./file.go:205:3 +./file.go:200:42: parameter sub leaks to {storage for &KeyError{...}} for (*File).RemoveSubsection with derefs=0: +./file.go:200:42: flow: {storage for &KeyError{...}} ← sub: +./file.go:200:42: from KeyError{...} (struct literal element) at ./file.go:208:26 +./file.go:200:33: parameter section leaks to {storage for &KeyError{...}} for (*File).RemoveSubsection with derefs=0: +./file.go:200:33: flow: section ← section: +./file.go:200:33: from section := section (assign-pair) at ./file.go:204:22 +./file.go:200:33: flow: {storage for &KeyError{...}} ← section: +./file.go:200:33: from KeyError{...} (struct literal element) at ./file.go:204:22 +./file.go:200:33: parameter section leaks to {storage for &KeyError{...}} for (*File).RemoveSubsection with derefs=0: +./file.go:200:33: flow: key ← section: +./file.go:200:33: from f, key := f, section (assign-pair) at ./file.go:201:23 +./file.go:200:33: flow: {storage for &KeyError{...}} ← key: +./file.go:200:33: from KeyError{...} (struct literal element) at ./file.go:201:23 +./file.go:200:7: leaking param content: f +./file.go:200:33: leaking param: section +./file.go:200:42: leaking param: sub +./file.go:201:23: &KeyError{...} escapes to heap +./file.go:204:22: &KeyError{...} escapes to heap +./file.go:208:17: &KeyError{...} escapes to heap +./file.go:210:55: ([]byte)(s) does not escape +./file.go:210:55: string(b) escapes to heap +./file.go:220:30: parameter oldName leaks to {heap} for (*File).RenameSection with derefs=0: +./file.go:220:30: flow: {heap} ← oldName: +./file.go:220:30: from (*File).renameSection(f, oldName, sectionKey{...}, to, formatHeader(newName, "", false)) (call parameter) at ./file.go:231:24 +./file.go:220:7: parameter f leaks to {heap} for (*File).RenameSection with derefs=1: +./file.go:220:7: flow: {temp} ← f: +./file.go:220:7: from (*File).renameSection(f, oldName, sectionKey{...}, to, formatHeader(newName, "", false)) (call parameter) at ./file.go:231:24 +./file.go:220:7: flow: {heap} ← *{temp}: +./file.go:230:38: string(b) escapes to heap in (*File).RenameSection: +./file.go:230:38: flow: ~r0 ← &{storage for string(b)}: +./file.go:230:38: from string(b) (spill) at ./file.go:230:38 +./file.go:230:38: from ~r0 = string(b) (assign-pair) at ./file.go:230:38 +./file.go:230:38: flow: to ← ~r0: +./file.go:230:38: from sectionKey{...} (struct literal element) at ./file.go:230:18 +./file.go:230:38: from to := sectionKey{...} (assign) at ./file.go:230:5 +./file.go:230:38: flow: {heap} ← to: +./file.go:230:38: from (*File).renameSection(f, oldName, sectionKey{...}, to, formatHeader(newName, "", false)) (call parameter) at ./file.go:231:24 +./file.go:220:39: parameter newName leaks to {heap} for (*File).RenameSection with derefs=0: +./file.go:220:39: flow: s ← newName: +./file.go:220:39: from s := newName (assign-pair) at ./file.go:230:38 +./file.go:220:39: flow: ~r0 ← s: +./file.go:220:39: from ~r0 = s (assign-pair) at ./file.go:230:38 +./file.go:220:39: flow: to ← ~r0: +./file.go:220:39: from sectionKey{...} (struct literal element) at ./file.go:230:18 +./file.go:220:39: from to := sectionKey{...} (assign) at ./file.go:230:5 +./file.go:220:39: flow: {heap} ← to: +./file.go:220:39: from (*File).renameSection(f, oldName, sectionKey{...}, to, formatHeader(newName, "", false)) (call parameter) at ./file.go:231:24 +./file.go:231:64: string(b) escapes to heap in (*File).RenameSection: +./file.go:231:64: flow: ~r0 ← &{storage for string(b)}: +./file.go:231:64: from string(b) (spill) at ./file.go:231:64 +./file.go:231:64: from ~r0 = string(b) (assign-pair) at ./file.go:231:64 +./file.go:221:23: &KeyError{...} escapes to heap in (*File).RenameSection: +./file.go:221:23: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:221:23: from &KeyError{...} (spill) at ./file.go:221:23 +./file.go:221:23: from &KeyError{...} (interface-converted) at ./file.go:221:23 +./file.go:221:23: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:221:23 +./file.go:221:23: flow: err ← ~r0: +./file.go:221:23: from err := ~r0 (assign) at ./file.go:221:9 +./file.go:221:23: flow: ~r0 ← err: +./file.go:221:23: from return err (return) at ./file.go:222:3 +./file.go:224:22: &KeyError{...} escapes to heap in (*File).RenameSection: +./file.go:224:22: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:224:22: from &KeyError{...} (spill) at ./file.go:224:22 +./file.go:224:22: from &KeyError{...} (interface-converted) at ./file.go:224:22 +./file.go:224:22: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:224:22 +./file.go:224:22: flow: err ← ~r0: +./file.go:224:22: from err := ~r0 (assign) at ./file.go:224:9 +./file.go:224:22: flow: ~r0 ← err: +./file.go:224:22: from return err (return) at ./file.go:225:3 +./file.go:227:22: &KeyError{...} escapes to heap in (*File).RenameSection: +./file.go:227:22: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:227:22: from &KeyError{...} (spill) at ./file.go:227:22 +./file.go:227:22: from &KeyError{...} (interface-converted) at ./file.go:227:22 +./file.go:227:22: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:227:22 +./file.go:227:22: flow: err ← ~r0: +./file.go:227:22: from err := ~r0 (assign) at ./file.go:227:9 +./file.go:227:22: flow: ~r0 ← err: +./file.go:227:22: from return err (return) at ./file.go:228:3 +./file.go:220:39: parameter newName leaks to {storage for &KeyError{...}} for (*File).RenameSection with derefs=0: +./file.go:220:39: flow: section ← newName: +./file.go:220:39: from section := newName (assign-pair) at ./file.go:227:22 +./file.go:220:39: flow: {storage for &KeyError{...}} ← section: +./file.go:220:39: from KeyError{...} (struct literal element) at ./file.go:227:22 +./file.go:220:30: parameter oldName leaks to {storage for &KeyError{...}} for (*File).RenameSection with derefs=0: +./file.go:220:30: flow: section ← oldName: +./file.go:220:30: from section := oldName (assign-pair) at ./file.go:224:22 +./file.go:220:30: flow: {storage for &KeyError{...}} ← section: +./file.go:220:30: from KeyError{...} (struct literal element) at ./file.go:224:22 +./file.go:220:30: parameter oldName leaks to {storage for &KeyError{...}} for (*File).RenameSection with derefs=0: +./file.go:220:30: flow: key ← oldName: +./file.go:220:30: from f, key := f, oldName (assign-pair) at ./file.go:221:23 +./file.go:220:30: flow: {storage for &KeyError{...}} ← key: +./file.go:220:30: from KeyError{...} (struct literal element) at ./file.go:221:23 +./file.go:220:7: leaking param content: f +./file.go:220:30: leaking param: oldName +./file.go:220:39: leaking param: newName +./file.go:221:23: &KeyError{...} escapes to heap +./file.go:224:22: &KeyError{...} escapes to heap +./file.go:227:22: &KeyError{...} escapes to heap +./file.go:230:38: ([]byte)(s) does not escape +./file.go:230:38: string(b) escapes to heap +./file.go:231:64: ([]byte)(s) does not escape +./file.go:231:64: string(b) escapes to heap +./file.go:240:33: parameter section leaks to {heap} for (*File).RenameSubsection with derefs=0: +./file.go:240:33: flow: {heap} ← section: +./file.go:240:33: from (*File).renameSection(f, section, sectionKey{...}, sectionKey{...}, formatHeader(section, newSub, true)) (call parameter) at ./file.go:253:24 +./file.go:240:50: parameter newSub leaks to {heap} for (*File).RenameSubsection with derefs=0: +./file.go:240:50: flow: {heap} ← newSub: +./file.go:240:50: from sectionKey{...} (struct literal element) at ./file.go:253:75 +./file.go:240:50: from (*File).renameSection(f, section, sectionKey{...}, sectionKey{...}, formatHeader(section, newSub, true)) (call parameter) at ./file.go:253:24 +./file.go:240:7: parameter f leaks to {heap} for (*File).RenameSubsection with derefs=1: +./file.go:240:7: flow: {temp} ← f: +./file.go:240:7: from (*File).renameSection(f, section, sectionKey{...}, sectionKey{...}, formatHeader(section, newSub, true)) (call parameter) at ./file.go:253:24 +./file.go:240:7: flow: {heap} ← *{temp}: +./file.go:252:19: string(b) escapes to heap in (*File).RenameSubsection: +./file.go:252:19: flow: ~r0 ← &{storage for string(b)}: +./file.go:252:19: from string(b) (spill) at ./file.go:252:19 +./file.go:252:19: from ~r0 = string(b) (assign-pair) at ./file.go:252:19 +./file.go:252:19: flow: sec ← ~r0: +./file.go:252:19: from sec := ~r0 (assign) at ./file.go:252:6 +./file.go:252:19: flow: {heap} ← sec: +./file.go:252:19: from sectionKey{...} (struct literal element) at ./file.go:253:75 +./file.go:252:19: from (*File).renameSection(f, section, sectionKey{...}, sectionKey{...}, formatHeader(section, newSub, true)) (call parameter) at ./file.go:253:24 +./file.go:249:11: &KeyError{...} escapes to heap in (*File).RenameSubsection: +./file.go:249:11: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:249:11: from &KeyError{...} (spill) at ./file.go:249:11 +./file.go:249:11: from &KeyError{...} (interface-converted) at ./file.go:249:11 +./file.go:249:11: from return &KeyError{...} (return) at ./file.go:249:4 +./file.go:241:23: &KeyError{...} escapes to heap in (*File).RenameSubsection: +./file.go:241:23: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:241:23: from &KeyError{...} (spill) at ./file.go:241:23 +./file.go:241:23: from &KeyError{...} (interface-converted) at ./file.go:241:23 +./file.go:241:23: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:241:23 +./file.go:241:23: flow: err ← ~r0: +./file.go:241:23: from err := ~r0 (assign) at ./file.go:241:9 +./file.go:241:23: flow: ~r0 ← err: +./file.go:241:23: from return err (return) at ./file.go:242:3 +./file.go:244:22: &KeyError{...} escapes to heap in (*File).RenameSubsection: +./file.go:244:22: flow: ~r0 ← &{storage for &KeyError{...}}: +./file.go:244:22: from &KeyError{...} (spill) at ./file.go:244:22 +./file.go:244:22: from &KeyError{...} (interface-converted) at ./file.go:244:22 +./file.go:244:22: from ~r0 = &KeyError{...} (assign-pair) at ./file.go:244:22 +./file.go:244:22: flow: err ← ~r0: +./file.go:244:22: from err := ~r0 (assign) at ./file.go:244:9 +./file.go:244:22: flow: ~r0 ← err: +./file.go:244:22: from return err (return) at ./file.go:245:3 +./file.go:240:42: parameter oldSub leaks to {storage for &KeyError{...}} for (*File).RenameSubsection with derefs=0: +./file.go:240:42: flow: {storage for []string{...}} ← oldSub: +./file.go:240:42: from []string{...} (slice-literal-element) at ./file.go:247:30 +./file.go:240:42: flow: {temp} ← &{storage for []string{...}}: +./file.go:240:42: from []string{...} (spill) at ./file.go:247:30 +./file.go:240:42: flow: sub ← *{temp}: +./file.go:240:42: from for loop (range-deref) at ./file.go:247:16 +./file.go:240:42: flow: {storage for &KeyError{...}} ← sub: +./file.go:240:42: from KeyError{...} (struct literal element) at ./file.go:249:20 +./file.go:240:50: parameter newSub leaks to {storage for &KeyError{...}} for (*File).RenameSubsection with derefs=0: +./file.go:240:50: flow: {storage for []string{...}} ← newSub: +./file.go:240:50: from []string{...} (slice-literal-element) at ./file.go:247:30 +./file.go:240:50: flow: {temp} ← &{storage for []string{...}}: +./file.go:240:50: from []string{...} (spill) at ./file.go:247:30 +./file.go:240:50: flow: sub ← *{temp}: +./file.go:240:50: from for loop (range-deref) at ./file.go:247:16 +./file.go:240:50: flow: {storage for &KeyError{...}} ← sub: +./file.go:240:50: from KeyError{...} (struct literal element) at ./file.go:249:20 +./file.go:240:33: parameter section leaks to {storage for &KeyError{...}} for (*File).RenameSubsection with derefs=0: +./file.go:240:33: flow: section ← section: +./file.go:240:33: from section := section (assign-pair) at ./file.go:244:22 +./file.go:240:33: flow: {storage for &KeyError{...}} ← section: +./file.go:240:33: from KeyError{...} (struct literal element) at ./file.go:244:22 +./file.go:240:33: parameter section leaks to {storage for &KeyError{...}} for (*File).RenameSubsection with derefs=0: +./file.go:240:33: flow: key ← section: +./file.go:240:33: from f, key := f, section (assign-pair) at ./file.go:241:23 +./file.go:240:33: flow: {storage for &KeyError{...}} ← key: +./file.go:240:33: from KeyError{...} (struct literal element) at ./file.go:241:23 +./file.go:240:7: leaking param content: f +./file.go:240:33: leaking param: section +./file.go:240:42: leaking param: oldSub +./file.go:240:50: leaking param: newSub +./file.go:241:23: &KeyError{...} escapes to heap +./file.go:244:22: &KeyError{...} escapes to heap +./file.go:247:30: []string{...} does not escape +./file.go:249:11: &KeyError{...} escapes to heap +./file.go:252:19: ([]byte)(s) does not escape +./file.go:252:19: string(b) escapes to heap +./glob.go:216:9: &stateSet{...} escapes to heap in newStateSet: +./glob.go:216:9: flow: ~r0 ← &{storage for &stateSet{...}}: +./glob.go:216:9: from &stateSet{...} (spill) at ./glob.go:216:9 +./glob.go:216:9: from return &stateSet{...} (return) at ./glob.go:216:2 +./glob.go:216:29: make([]uint32, size) escapes to heap in newStateSet: +./glob.go:216:29: flow: {storage for &stateSet{...}} ← &{storage for make([]uint32, size)}: +./glob.go:216:29: from make([]uint32, size) (spill) at ./glob.go:216:29 +./glob.go:216:29: from stateSet{...} (struct literal element) at ./glob.go:216:18 +./glob.go:216:9: &stateSet{...} escapes to heap +./glob.go:216:29: make([]uint32, size) escapes to heap +./glob.go:220:10: (*stateSet).reset ignoring self-assignment in ss.list = ss.list[:0] +./glob.go:219:7: ss does not escape +./glob.go:230:18: append(ss.list, id) escapes to heap in (*stateSet).add: +./glob.go:230:18: flow: {heap} ← &{storage for append(ss.list, id)}: +./glob.go:230:18: from append(ss.list, id) (spill) at ./glob.go:230:18 +./glob.go:230:18: from ss.list = append(ss.list, id) (assign) at ./glob.go:230:10 +./glob.go:225:7: parameter ss leaks to {heap} for (*stateSet).add with derefs=1: +./glob.go:225:7: flow: {temp} ← *ss: +./glob.go:225:7: from ss.list (dot of pointer) at ./glob.go:230:21 +./glob.go:225:7: from append(ss.list, id) (call parameter) at ./glob.go:230:18 +./glob.go:225:7: flow: {heap} ← {temp}: +./glob.go:225:7: from ss.list = append(ss.list, id) (assign) at ./glob.go:230:10 +./glob.go:225:7: leaking param content: ss +./glob.go:230:18: append escapes to heap +./glob.go:234:7: ss does not escape +./key.go:56:47: k.Section + "." + k.Subsection + "." + k.Name escapes to heap in Key.String: +./key.go:56:47: flow: ~r0 ← &{storage for k.Section + "." + k.Subsection + "." + k.Name}: +./key.go:56:47: from k.Section + "." + k.Subsection + "." + k.Name (spill) at ./key.go:56:47 +./key.go:56:47: from return k.Section + "." + k.Subsection + "." + k.Name (return) at ./key.go:56:3 +./key.go:58:25: k.Section + "." + k.Name escapes to heap in Key.String: +./key.go:58:25: flow: ~r0 ← &{storage for k.Section + "." + k.Name}: +./key.go:58:25: from k.Section + "." + k.Name (spill) at ./key.go:58:25 +./key.go:58:25: from return k.Section + "." + k.Name (return) at ./key.go:58:2 +./key.go:54:7: k does not escape +./key.go:56:47: k.Section + "." + k.Subsection + "." + k.Name escapes to heap +./key.go:58:25: k.Section + "." + k.Name escapes to heap +./key.go:84:16: s does not escape +./key.go:98:19: s does not escape +./key.go:109:22: s does not escape +./key.go:124:18: string(b) escapes to heap in lowerASCII: +./key.go:124:18: flow: ~r0 ← &{storage for string(b)}: +./key.go:124:18: from string(b) (spill) at ./key.go:124:18 +./key.go:124:18: from return string(b) (return) at ./key.go:124:4 +./key.go:115:17: parameter s leaks to ~r0 for lowerASCII with derefs=0: +./key.go:115:17: flow: ~r0 ← s: +./key.go:115:17: from return s (return) at ./key.go:127:2 +./key.go:115:17: leaking param: s to result ~r0 level=0 +./key.go:118:16: ([]byte)(s) does not escape +./key.go:124:18: string(b) escapes to heap +./load.go:53:7: parameter r leaks to ~r0 for (*repo).localConfig with derefs=1: +./load.go:53:7: flow: {storage for ... argument} ← *r: +./load.go:53:7: from r.commonDir (dot of pointer) at ./load.go:54:24 +./load.go:53:7: from ... argument (slice-literal-element) at ./load.go:54:22 +./load.go:53:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:53:7: from ... argument (spill) at ./load.go:54:22 +./load.go:53:7: from filepath.elem := ... argument (assign-pair) at ./load.go:54:22 +./load.go:53:7: flow: {temp} ← filepath.elem: +./load.go:53:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:54:22 +./load.go:53:7: flow: ~r0 ← *{temp}: +./load.go:53:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:54:22 +./load.go:53:7: flow: ~r0 ← ~r0: +./load.go:53:7: from return ~r0 (return) at ./load.go:54:2 +./load.go:53:7: leaking param: r to result ~r0 level=1 +./load.go:54:22: ... argument does not escape +./load.go:57:7: parameter r leaks to ~r0 for (*repo).worktreeConfig with derefs=1: +./load.go:57:7: flow: {storage for ... argument} ← *r: +./load.go:57:7: from r.gitDir (dot of pointer) at ./load.go:58:24 +./load.go:57:7: from ... argument (slice-literal-element) at ./load.go:58:22 +./load.go:57:7: flow: filepath.elem ← &{storage for ... argument}: +./load.go:57:7: from ... argument (spill) at ./load.go:58:22 +./load.go:57:7: from filepath.elem := ... argument (assign-pair) at ./load.go:58:22 +./load.go:57:7: flow: {temp} ← filepath.elem: +./load.go:57:7: from filepath.join(filepath.elem) (call parameter) at ./load.go:58:22 +./load.go:57:7: flow: ~r0 ← *{temp}: +./load.go:57:7: from ~r0 = filepath.join(filepath.elem) (assign-pair) at ./load.go:58:22 +./load.go:57:7: flow: ~r0 ← ~r0: +./load.go:57:7: from return ~r0 (return) at ./load.go:58:2 +./load.go:57:7: leaking param: r to result ~r0 level=1 +./load.go:58:22: ... argument does not escape +./load.go:122:16: parameter path leaks to {heap} for readSmall with derefs=0: +./load.go:122:16: flow: {heap} ← path: +./load.go:122:16: from readConfigFile(path, int64(4096)) (call parameter) at ./load.go:123:23 +./load.go:122:16: leaking param: path +./load.go:188:7: parameter l leaks to {heap} for (*loader).config with derefs=1: +./load.go:188:7: flow: {heap} ← *l: +./load.go:188:7: from l.entries (dot of pointer) at ./load.go:189:20 +./load.go:188:7: from newConfig(l.entries) (call parameter) at ./load.go:189:18 +./load.go:188:7: leaking param content: l +./load.go:282:10: &KeyError{...} escapes to heap in (*loader).fail: +./load.go:282:10: flow: ~r0 ← &{storage for &KeyError{...}}: +./load.go:282:10: from &KeyError{...} (spill) at ./load.go:282:10 +./load.go:282:10: from &KeyError{...} (interface-converted) at ./load.go:282:10 +./load.go:282:10: from return &KeyError{...} (return) at ./load.go:282:3 +./load.go:284:9: &FileError{...} escapes to heap in (*loader).fail: +./load.go:284:9: flow: ~r0 ← &{storage for &FileError{...}}: +./load.go:284:9: from &FileError{...} (spill) at ./load.go:284:9 +./load.go:284:9: from &FileError{...} (interface-converted) at ./load.go:284:9 +./load.go:284:9: from return &FileError{...} (return) at ./load.go:284:2 +./load.go:280:23: parameter from leaks to {storage for &FileError{...}} for (*loader).fail with derefs=0: +./load.go:280:23: flow: {storage for &FileError{...}} ← from: +./load.go:280:23: from FileError{...} (struct literal element) at ./load.go:284:19 +./load.go:284:37: &SyntaxError{...} escapes to heap in (*loader).fail: +./load.go:284:37: flow: {storage for &FileError{...}} ← &{storage for &SyntaxError{...}}: +./load.go:284:37: from &SyntaxError{...} (spill) at ./load.go:284:37 +./load.go:284:37: from &SyntaxError{...} (interface-converted) at ./load.go:284:37 +./load.go:284:37: from FileError{...} (struct literal element) at ./load.go:284:19 +./load.go:280:23: parameter from leaks to {storage for &SyntaxError{...}} for (*loader).fail with derefs=0: +./load.go:280:23: flow: {storage for &SyntaxError{...}} ← from: +./load.go:280:23: from SyntaxError{...} (struct literal element) at ./load.go:284:49 +./load.go:280:45: parameter msg leaks to {storage for &SyntaxError{...}} for (*loader).fail with derefs=0: +./load.go:280:45: flow: {storage for &SyntaxError{...}} ← msg: +./load.go:280:45: from SyntaxError{...} (struct literal element) at ./load.go:284:49 +./load.go:282:37: k.Section + "." + k.Subsection + "." + k.Name escapes to heap in (*loader).fail: +./load.go:282:37: flow: ~r0 ← &{storage for k.Section + "." + k.Subsection + "." + k.Name}: +./load.go:282:37: from k.Section + "." + k.Subsection + "." + k.Name (spill) at ./load.go:282:37 +./load.go:282:37: from ~r0 = k.Section + "." + k.Subsection + "." + k.Name (assign-pair) at ./load.go:282:37 +./load.go:282:37: flow: {storage for &KeyError{...}} ← ~r0: +./load.go:282:37: from KeyError{...} (struct literal element) at ./load.go:282:19 +./load.go:282:37: k.Section + "." + k.Name escapes to heap in (*loader).fail: +./load.go:282:37: flow: ~r0 ← &{storage for k.Section + "." + k.Name}: +./load.go:282:37: from k.Section + "." + k.Name (spill) at ./load.go:282:37 +./load.go:282:37: from ~r0 = k.Section + "." + k.Name (assign-pair) at ./load.go:282:37 +./load.go:282:37: flow: {storage for &KeyError{...}} ← ~r0: +./load.go:282:37: from KeyError{...} (struct literal element) at ./load.go:282:19 +./load.go:282:56: &errors.errorString{...} escapes to heap in (*loader).fail: +./load.go:282:56: flow: ~r0 ← &{storage for &errors.errorString{...}}: +./load.go:282:56: from &errors.errorString{...} (spill) at ./load.go:282:56 +./load.go:282:56: from &errors.errorString{...} (interface-converted) at ./load.go:282:56 +./load.go:282:56: from ~r0 = &errors.errorString{...} (assign-pair) at ./load.go:282:56 +./load.go:282:56: flow: {storage for &KeyError{...}} ← ~r0: +./load.go:282:56: from KeyError{...} (struct literal element) at ./load.go:282:19 +./load.go:282:71: "gitconfig: " + msg escapes to heap in (*loader).fail: +./load.go:282:71: flow: errors.text ← &{storage for "gitconfig: " + msg}: +./load.go:282:71: from "gitconfig: " + msg (spill) at ./load.go:282:71 +./load.go:282:71: from errors.text := "gitconfig: " + msg (assign-pair) at ./load.go:282:56 +./load.go:282:71: flow: {storage for &errors.errorString{...}} ← errors.text: +./load.go:282:71: from errors.errorString{...} (struct literal element) at ./load.go:282:56 +./load.go:280:7: l does not escape +./load.go:280:23: leaking param: from +./load.go:280:36: e does not escape +./load.go:280:45: leaking param: msg +./load.go:282:10: &KeyError{...} escapes to heap +./load.go:282:37: k.Section + "." + k.Subsection + "." + k.Name escapes to heap +./load.go:282:37: k.Section + "." + k.Name escapes to heap +./load.go:282:71: "gitconfig: " + msg escapes to heap +./load.go:282:56: &errors.errorString{...} escapes to heap +./load.go:284:9: &FileError{...} escapes to heap +./load.go:284:37: &SyntaxError{...} escapes to heap +./load.go:290:16: parameter path leaks to ~r0 for dirPrefix with derefs=0: +./load.go:290:16: flow: ~r0 ← path: +./load.go:290:16: from path[:i + 1] (slice) at ./load.go:295:13 +./load.go:290:16: from return path[:i + 1] (return) at ./load.go:295:2 +./load.go:290:16: leaking param: path to result ~r0 level=0 +./load.go:366:19: p does not escape +./parse.go:51:7: parameter p leaks to ~r0 for (*parsed).secOf with derefs=2: +./parse.go:51:7: flow: ~r0 ← **p: +./parse.go:51:7: from p.secs (dot of pointer) at ./parse.go:51:54 +./parse.go:51:7: from p.secs[it.sec] (dot of pointer) at ./parse.go:51:59 +./parse.go:51:7: from return p.secs[it.sec] (return) at ./parse.go:51:46 +./parse.go:51:7: leaking param: p to result ~r0 level=2 +./parse.go:55:39: parameter file leaks to {heap} for (*parsed).entries with derefs=0: +./parse.go:55:39: flow: {heap} ← file: +./parse.go:55:39: from Origin{...} (struct literal element) at ./parse.go:66:20 +./parse.go:55:39: from Entry{...} (struct literal element) at ./parse.go:62:26 +./parse.go:55:39: from append(out, Entry{...}) (call parameter) at ./parse.go:62:15 +./parse.go:55:52: parameter out leaks to {heap} for (*parsed).entries with derefs=1: +./parse.go:55:52: flow: {temp} ← out: +./parse.go:55:52: from append(out, Entry{...}) (call parameter) at ./parse.go:62:15 +./parse.go:55:52: flow: {heap} ← *{temp}: +./parse.go:55:52: from append(out, Entry{...}) (appendee slice) at ./parse.go:62:15 +./parse.go:55:7: parameter p leaks to {heap} for (*parsed).entries with derefs=2: +./parse.go:55:7: flow: sec ← **p: +./parse.go:55:7: from p.secs (dot of pointer) at ./parse.go:60:11 +./parse.go:55:7: from p.secs[it.sec] (dot of pointer) at ./parse.go:60:16 +./parse.go:55:7: from sec := p.secs[it.sec] (assign) at ./parse.go:60:7 +./parse.go:55:7: flow: {heap} ← sec: +./parse.go:55:7: from sec.section (dot) at ./parse.go:63:30 +./parse.go:55:7: from Key{...} (struct literal element) at ./parse.go:63:17 +./parse.go:55:7: from Entry{...} (struct literal element) at ./parse.go:62:26 +./parse.go:55:7: from append(out, Entry{...}) (call parameter) at ./parse.go:62:15 +./parse.go:55:52: parameter out leaks to ~r0 for (*parsed).entries with derefs=0: +./parse.go:55:52: flow: ~r0 ← out: +./parse.go:55:52: from return out (return) at ./parse.go:69:2 +./parse.go:62:15: append(out, Entry{...}) escapes to heap in (*parsed).entries: +./parse.go:62:15: flow: out ← &{storage for append(out, Entry{...})}: +./parse.go:62:15: from append(out, Entry{...}) (spill) at ./parse.go:62:15 +./parse.go:62:15: from out = append(out, Entry{...}) (assign) at ./parse.go:62:7 +./parse.go:62:15: flow: ~r0 ← out: +./parse.go:62:15: from return out (return) at ./parse.go:69:2 +./parse.go:55:7: leaking param content: p +./parse.go:55:39: leaking param: file +./parse.go:55:52: leaking param content: out +./parse.go:55:52: leaking param: out to result ~r0 level=0 +./parse.go:62:15: append escapes to heap +./parse.go:71:7: parameter p leaks to ~r0 for (*parsed).nameOf with derefs=2: +./parse.go:71:7: flow: ~r0 ← **p: +./parse.go:71:7: from p.texts (dot of pointer) at ./parse.go:71:52 +./parse.go:71:7: from p.texts[it.text] (dot of pointer) at ./parse.go:71:58 +./parse.go:71:7: from p.texts[it.text].name (dot) at ./parse.go:71:67 +./parse.go:71:7: from return p.texts[it.text].name (return) at ./parse.go:71:44 +./parse.go:71:7: leaking param: p to result ~r0 level=2 +./parse.go:72:7: parameter p leaks to ~r0 for (*parsed).valueOf with derefs=2: +./parse.go:72:7: flow: ~r0 ← **p: +./parse.go:72:7: from p.texts (dot of pointer) at ./parse.go:72:52 +./parse.go:72:7: from p.texts[it.text] (dot of pointer) at ./parse.go:72:58 +./parse.go:72:7: from p.texts[it.text].value (dot) at ./parse.go:72:67 +./parse.go:72:7: from return p.texts[it.text].value (return) at ./parse.go:72:44 +./parse.go:72:7: leaking param: p to result ~r0 level=2 +./parse.go:113:9: &SyntaxError{...} escapes to heap in (*parser).errorAt: +./parse.go:113:9: flow: ~r0 ← &{storage for &SyntaxError{...}}: +./parse.go:113:9: from &SyntaxError{...} (spill) at ./parse.go:113:9 +./parse.go:113:9: from &SyntaxError{...} (interface-converted) at ./parse.go:113:9 +./parse.go:113:9: from return &SyntaxError{...} (return) at ./parse.go:113:2 +./parse.go:112:7: parameter p leaks to {storage for &SyntaxError{...}} for (*parser).errorAt with derefs=1: +./parse.go:112:7: flow: {storage for &SyntaxError{...}} ← *p: +./parse.go:112:7: from p.name (dot of pointer) at ./parse.go:113:29 +./parse.go:112:7: from SyntaxError{...} (struct literal element) at ./parse.go:113:21 +./parse.go:112:36: parameter msg leaks to {storage for &SyntaxError{...}} for (*parser).errorAt with derefs=0: +./parse.go:112:36: flow: {storage for &SyntaxError{...}} ← msg: +./parse.go:112:36: from SyntaxError{...} (struct literal element) at ./parse.go:113:21 +./parse.go:112:7: leaking param content: p +./parse.go:112:36: leaking param: msg +./parse.go:113:9: &SyntaxError{...} escapes to heap +./parse.go:117:18: append(p.parsed.items, item{...}) escapes to heap in (*parser).add: +./parse.go:117:18: flow: {heap} ← &{storage for append(p.parsed.items, item{...})}: +./parse.go:117:18: from append(p.parsed.items, item{...}) (spill) at ./parse.go:117:18 +./parse.go:117:18: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:117:10 +./parse.go:116:7: parameter p leaks to {heap} for (*parser).add with derefs=1: +./parse.go:116:7: flow: {temp} ← *p: +./parse.go:116:7: from p.parsed (dot of pointer) at ./parse.go:117:20 +./parse.go:116:7: from p.parsed.items (dot) at ./parse.go:117:20 +./parse.go:116:7: from append(p.parsed.items, item{...}) (call parameter) at ./parse.go:117:18 +./parse.go:116:7: flow: {heap} ← {temp}: +./parse.go:116:7: from p.parsed.items = append(p.parsed.items, item{...}) (assign) at ./parse.go:117:10 +./parse.go:116:7: parameter p leaks to ~r0 for (*parser).add with derefs=1: +./parse.go:116:7: flow: ~r0 ← *p: +./parse.go:116:7: from p.parsed (dot of pointer) at ./parse.go:118:11 +./parse.go:116:7: from p.parsed.items (dot) at ./parse.go:118:11 +./parse.go:116:7: from p.parsed.items[len(p.parsed.items) - 1] (dot of pointer) at ./parse.go:118:17 +./parse.go:116:7: from &p.parsed.items[len(p.parsed.items) - 1] (address-of) at ./parse.go:118:9 +./parse.go:116:7: from return &p.parsed.items[len(p.parsed.items) - 1] (return) at ./parse.go:118:2 +./parse.go:116:7: leaking param content: p +./parse.go:117:18: append escapes to heap +./parse.go:139:18: append(p.parsed.secs, sec) escapes to heap in (*parser).setSection: +./parse.go:139:18: flow: {heap} ← &{storage for append(p.parsed.secs, sec)}: +./parse.go:139:18: from append(p.parsed.secs, sec) (spill) at ./parse.go:139:18 +./parse.go:139:18: from p.parsed.secs = append(p.parsed.secs, sec) (assign) at ./parse.go:139:10 +./parse.go:135:29: parameter sec leaks to {heap} for (*parser).setSection with derefs=0: +./parse.go:135:29: flow: {heap} ← sec: +./parse.go:135:29: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:139:18 +./parse.go:135:7: parameter p leaks to {heap} for (*parser).setSection with derefs=1: +./parse.go:135:7: flow: {temp} ← *p: +./parse.go:135:7: from p.parsed (dot of pointer) at ./parse.go:139:20 +./parse.go:135:7: from p.parsed.secs (dot) at ./parse.go:139:20 +./parse.go:135:7: from append(p.parsed.secs, sec) (call parameter) at ./parse.go:139:18 +./parse.go:135:7: flow: {temp} ← {temp}: +./parse.go:135:7: flow: {heap} ← {temp}: +./parse.go:135:7: from p.parsed.secs = append(p.parsed.secs, sec) (assign) at ./parse.go:139:10 +./parse.go:135:7: leaking param content: p +./parse.go:135:29: leaking param: sec +./parse.go:139:18: append escapes to heap +./parse.go:148:14: data does not escape +./parse.go:159:16: data does not escape +./parse.go:164:17: data does not escape +./platform.go:14:18: parameter path leaks to {heap} for openRegular with derefs=0: +./platform.go:14:18: flow: {heap} ← path: +./platform.go:14:18: from os.OpenFile(path, flag, fs.FileMode(0)) (call parameter) at ./platform.go:19:20 +./platform.go:14:18: leaking param: path +./value.go:137:21: s does not escape +./value.go:137:24: t does not escape +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: parameter atomic.x leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).CompareAndSwap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:68:33 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:68:31 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:68:30 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: parameter atomic.old leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).CompareAndSwap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: flow: {heap} ← atomic.old: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: from atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:68:30 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: parameter atomic.new leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).CompareAndSwap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: flow: {heap} ← atomic.new: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: from atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:68:30 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: parameter atomic.x leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Swap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:72 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:70 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from atomic.SwapPointer(&atomic.x.v, unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:69 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: parameter atomic.new leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Swap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: flow: {heap} ← atomic.new: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: from atomic.SwapPointer(&atomic.x.v, unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:69 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: parameter atomic.x leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Store with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:53 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:51 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from atomic.StorePointer(&atomic.x.v, unsafe.Pointer(atomic.val)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:50 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: parameter atomic.val leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Store with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: flow: {heap} ← atomic.val: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: from atomic.StorePointer(&atomic.x.v, unsafe.Pointer(atomic.val)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:50 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: parameter atomic.x leaks to {heap} for (*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Load with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:60 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:58 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from atomic.LoadPointer(&atomic.x.v) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:57 +:1: parameter p leaks to {heap} for .eq.SI with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.SI with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: parameter atomic.x leaks to {heap} for (*Pointer[os.dirInfo]).CompareAndSwap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: flow: atomic.x ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from atomic.x, atomic..dict, atomic.old, atomic.new := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.old, atomic.new (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:7: from atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: parameter atomic.old leaks to {heap} for (*Pointer[os.dirInfo]).CompareAndSwap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: flow: atomic.old ← atomic.old: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: from atomic.x, atomic..dict, atomic.old, atomic.new := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.old, atomic.new (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: flow: {heap} ← atomic.old: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:37: from atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: parameter atomic.new leaks to {heap} for (*Pointer[os.dirInfo]).CompareAndSwap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: flow: atomic.new ← atomic.new: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: from atomic.x, atomic..dict, atomic.old, atomic.new := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.old, atomic.new (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: flow: {heap} ← atomic.new: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:42: from atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: parameter atomic.x leaks to {heap} for (*Pointer[os.dirInfo]).Swap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: flow: atomic.x ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from atomic.x, atomic..dict, atomic.new := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.new (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:7: from atomic.SwapPointer(&atomic.x.v, unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: parameter atomic.new leaks to {heap} for (*Pointer[os.dirInfo]).Swap with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: flow: atomic.new ← atomic.new: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: from atomic.x, atomic..dict, atomic.new := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.new (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: flow: {heap} ← atomic.new: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:27: from atomic.SwapPointer(&atomic.x.v, unsafe.Pointer(atomic.new)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: parameter atomic.x leaks to {heap} for (*Pointer[os.dirInfo]).Store with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: flow: atomic.x ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from atomic.x, atomic..dict, atomic.val := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.val (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:7: from atomic.StorePointer(&atomic.x.v, unsafe.Pointer(atomic.val)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: parameter atomic.val leaks to {heap} for (*Pointer[os.dirInfo]).Store with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: flow: atomic.val ← atomic.val: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: from atomic.x, atomic..dict, atomic.val := atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.val (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: flow: {heap} ← atomic.val: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:28: from atomic.StorePointer(&atomic.x.v, unsafe.Pointer(atomic.val)) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: parameter atomic.x leaks to {heap} for (*Pointer[os.dirInfo]).Load with derefs=0: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: flow: atomic.x ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from atomic.x, atomic..dict := atomic.x, &atomic..dict.Pointer[os.dirInfo] (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: flow: {heap} ← atomic.x: +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from atomic.x.v (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from &atomic.x.v (address-of) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:7: from atomic.LoadPointer(&atomic.x.v) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6 +:1: parameter p leaks to {heap} for .eq.SM8S with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.SM8S with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: parameter slices.s leaks to {heap} for Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: flow: {heap} ← *slices.s: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (appended slice...) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) escapes to heap in Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: flow: slices.~r0 ← &{storage for append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...)}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: from return append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:2 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: go.shape.[]github.com/git-bug/gitconfig.Entry{} escapes to heap in Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: flow: {temp} ← &{storage for go.shape.[]github.com/git-bug/gitconfig.Entry{}}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: from go.shape.[]github.com/git-bug/gitconfig.Entry{} (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: flow: {temp} ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: flow: slices.~r0 ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: from return append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:2 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: go.shape.[]github.com/git-bug/gitconfig.Entry{} escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: append escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: parameter slices.s leaks to {heap} for Clone[[]github.com/git-bug/gitconfig.Entry,github.com/git-bug/gitconfig.Entry] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: flow: slices.s ← slices.s: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from slices..dict, slices.s := &slices..dict.Clone[[]github.com/git-bug/gitconfig.Entry,github.com/git-bug/gitconfig.Entry], slices.s (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: flow: {heap} ← *slices.s: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (appended slice...) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) escapes to heap in Clone[[]github.com/git-bug/gitconfig.Entry,github.com/git-bug/gitconfig.Entry]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← &{storage for append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...)}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from slices.~r0 = append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← slices.~r0: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from return slices.~r0 (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: go.shape.[]github.com/git-bug/gitconfig.Entry{} escapes to heap in Clone[[]github.com/git-bug/gitconfig.Entry,github.com/git-bug/gitconfig.Entry]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: {temp} ← &{storage for go.shape.[]github.com/git-bug/gitconfig.Entry{}}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from go.shape.[]github.com/git-bug/gitconfig.Entry{} (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: {temp} ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from slices.~r0 = append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← slices.~r0: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from return slices.~r0 (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: go.shape.[]github.com/git-bug/gitconfig.Entry{} escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: append escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: parameter slices.x leaks to {heap} for SortFunc[go.shape.[]github.com/git-bug/gitconfig.splice,go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: flow: {temp} ← slices.x: +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: from slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[11]uintptr)(slices..dict[0]), slices.x, 0, slices.n, bits.Len(uint(slices.n)), slices.cmp) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:32:16 +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: flow: {heap} ← *{temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:323:20: slices.reverseRangeCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:323:20: slices.reverseRangeCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.i], slices.data[slices.j] = slices.data[slices.j], slices.data[slices.i] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:298:27: parameter slices.data leaks to {heap} for order2CmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:298:27: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:298:27: from slices.data[slices.b] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:299:13 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:298:27: from slices.cmp(slices.data[slices.b], slices.data[slices.a]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:299:8 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:315:35: parameter slices.data leaks to {heap} for medianAdjacentCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:315:35: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:315:35: from slices.medianCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[3]uintptr)(slices..dict[0]), slices.data, slices.a - 1, slices.a, slices.a + 1, slices.swaps, slices.cmp) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:316:22 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:13:23: slices.insertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:13:23: slices.insertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] ignoring self-assignment in slices.data[slices.j], slices.data[slices.j - 1] = slices.data[slices.j - 1], slices.data[slices.j] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:10:34: parameter slices.data leaks to {heap} for insertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:10:34: flow: {heap} ← *slices.data: +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:10:34: from slices.data[slices.j] (dot of pointer) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:12:33 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:10:34: from slices.cmp(slices.data[slices.j], slices.data[slices.j - 1]) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:12:28 +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: parameter slices.x leaks to {heap} for SortFunc[[]github.com/git-bug/gitconfig.splice,github.com/git-bug/gitconfig.splice] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: flow: slices.x ← slices.x: +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: from slices..dict, slices.x, slices.cmp := &slices..dict.SortFunc[[]github.com/git-bug/gitconfig.splice,github.com/git-bug/gitconfig.splice], slices.x, slices.cmp (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: flow: {temp} ← slices.x: +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: from slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[11]uintptr)(slices..dict[0]), slices.x, 0, slices.n, bits.Len(uint(slices.n)), slices.cmp) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:30: flow: {heap} ← *{temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: parameter slices.s leaks to {heap} for Clone[go.shape.[]string,go.shape.string] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: flow: {heap} ← *slices.s: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]string{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]string{}, slices.s...) (appended slice...) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: append(go.shape.[]string{}, slices.s...) escapes to heap in Clone[go.shape.[]string,go.shape.string]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: flow: slices.~r0 ← &{storage for append(go.shape.[]string{}, slices.s...)}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: from append(go.shape.[]string{}, slices.s...) (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: from return append(go.shape.[]string{}, slices.s...) (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:2 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: go.shape.[]string{} escapes to heap in Clone[go.shape.[]string,go.shape.string]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: flow: {temp} ← &{storage for go.shape.[]string{}}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: from go.shape.[]string{} (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: from append(go.shape.[]string{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: flow: {temp} ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: flow: slices.~r0 ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: from return append(go.shape.[]string{}, slices.s...) (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:2 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:17: go.shape.[]string{} escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:362:15: append escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: parameter slices.s leaks to {heap} for Clone[[]string,string] with derefs=1: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: flow: slices.s ← slices.s: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from slices..dict, slices.s := &slices..dict.Clone[[]string,string], slices.s (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: flow: {heap} ← *slices.s: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]string{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:27: from append(go.shape.[]string{}, slices.s...) (appended slice...) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: append(go.shape.[]string{}, slices.s...) escapes to heap in Clone[[]string,string]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← &{storage for append(go.shape.[]string{}, slices.s...)}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from append(go.shape.[]string{}, slices.s...) (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from slices.~r0 = append(go.shape.[]string{}, slices.s...) (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← slices.~r0: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from return slices.~r0 (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: go.shape.[]string{} escapes to heap in Clone[[]string,string]: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: {temp} ← &{storage for go.shape.[]string{}}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from go.shape.[]string{} (spill) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from append(go.shape.[]string{}, slices.s...) (call parameter) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: {temp} ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← {temp}: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from slices.~r0 = append(go.shape.[]string{}, slices.s...) (assign-pair) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: flow: slices.~r0 ← slices.~r0: +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: from return slices.~r0 (return) at ../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: go.shape.[]string{} escapes to heap +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: append escapes to heap +:1: parameter p leaks to {heap} for .eq.SSM1K7SSM1K7M8SM8SI with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.SSM1K7SSM1K7M8SM8SI with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter p leaks to {heap} for .eq.SSM1K7SSM1K7M8SM8 with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.SSM1K7SSM1K7M8SM8 with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter p leaks to {heap} for .eq.SSM1K7S with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.SSM1K7S with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter p leaks to {heap} for .eq.SSM1 with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.SSM1 with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(0)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(0))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(0))), *(*uintptr)(unsafe.Add(p, uintptr(8)))) (call parameter) at :1 +:1: parameter p leaks to {heap} for .eq.M8SM8 with derefs=1: +:1: flow: {heap} ← *p: +:1: from unsafe.Add(p, uintptr(8)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(p, uintptr(8))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(8))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(8))), *(*uintptr)(unsafe.Add(p, uintptr(16)))) (call parameter) at :1 +:1: parameter q leaks to {heap} for .eq.M8SM8 with derefs=1: +:1: flow: {heap} ← *q: +:1: from unsafe.Add(q, uintptr(8)) (call parameter) at :1 +:1: from *(*unsafe.Pointer)(unsafe.Add(q, uintptr(8))) (indirection) at :1 +:1: from runtime.memequal(*(*unsafe.Pointer)(unsafe.Add(p, uintptr(8))), *(*unsafe.Pointer)(unsafe.Add(q, uintptr(8))), *(*uintptr)(unsafe.Add(p, uintptr(16)))) (call parameter) at :1 +:1: k.Section + "." + k.Subsection + "." + k.Name escapes to heap in (*Key).String: +:1: flow: ~r0 ← &{storage for k.Section + "." + k.Subsection + "." + k.Name}: +:1: from k.Section + "." + k.Subsection + "." + k.Name (spill) at :1 +:1: from ~r0 = k.Section + "." + k.Subsection + "." + k.Name (assign-pair) at :1 +:1: flow: ~r0 ← ~r0: +:1: from return ~r0 (return) at :1 +:1: k.Section + "." + k.Name escapes to heap in (*Key).String: +:1: flow: ~r0 ← &{storage for k.Section + "." + k.Name}: +:1: from k.Section + "." + k.Name (spill) at :1 +:1: from ~r0 = k.Section + "." + k.Name (assign-pair) at :1 +:1: flow: ~r0 ← ~r0: +:1: from return ~r0 (return) at :1 +:1: k.Section + "." + k.Subsection + "." + k.Name escapes to heap +:1: k.Section + "." + k.Name escapes to heap +./key.go:69:7: parameter k leaks to ~r0 for (*Key).sectionKey with derefs=1: +./key.go:69:7: flow: k ← *k: +./key.go:69:7: from k (indirection) at :1 +./key.go:69:7: from k := k (assign-pair) at :1 +./key.go:69:7: flow: ~r0 ← k: +./key.go:69:7: from k.Section (dot) at :1 +./key.go:69:7: from sectionKey{...} (struct literal element) at :1 +./key.go:69:7: from ~r0 = sectionKey{...} (assign-pair) at :1 +./key.go:69:7: flow: ~r0 ← ~r0: +./key.go:69:7: from return ~r0 (return) at :1 +./config.go:234:7: parameter e leaks to {heap} for (*Entry).Bool with derefs=1: +./config.go:234:7: flow: {heap} ← *e: +./config.go:234:7: from e (indirection) at :1 +./config.go:234:7: from Entry.Bool(e) (call parameter) at :1 +./config.go:248:7: parameter e leaks to {heap} for (*Entry).Int with derefs=1: +./config.go:248:7: flow: {heap} ← *e: +./config.go:248:7: from e (indirection) at :1 +./config.go:248:7: from Entry.Int(e) (call parameter) at :1 +./config.go:265:7: parameter e leaks to {heap} for (*Entry).Path with derefs=1: +./config.go:265:7: flow: {heap} ← *e: +./config.go:265:7: from e (indirection) at :1 +./config.go:265:7: from Entry.Path(e, home) (call parameter) at :1 +:1: &ValueError{...} escapes to heap in (*Entry).error: +:1: flow: ~r0 ← &{storage for &ValueError{...}}: +:1: from &ValueError{...} (spill) at :1 +:1: from &ValueError{...} (interface-converted) at :1 +:1: from ~r0 = &ValueError{...} (assign-pair) at :1 +:1: flow: ~r0 ← ~r0: +:1: from return ~r0 (return) at :1 +./config.go:191:7: parameter e leaks to {storage for &ValueError{...}} for (*Entry).error with derefs=1: +./config.go:191:7: flow: e ← *e: +./config.go:191:7: from e (indirection) at :1 +./config.go:191:7: from e, typ, err := e, typ, err (assign-pair) at :1 +./config.go:191:7: flow: {storage for &ValueError{...}} ← e: +./config.go:191:7: from ValueError{...} (struct literal element) at :1 +./config.go:191:22: parameter typ leaks to {storage for &ValueError{...}} for (*Entry).error with derefs=0: +./config.go:191:22: flow: typ ← typ: +./config.go:191:22: from e, typ, err := e, typ, err (assign-pair) at :1 +./config.go:191:22: flow: {storage for &ValueError{...}} ← typ: +./config.go:191:22: from ValueError{...} (struct literal element) at :1 +./config.go:191:34: parameter err leaks to {storage for &ValueError{...}} for (*Entry).error with derefs=0: +./config.go:191:34: flow: err ← err: +./config.go:191:34: from e, typ, err := e, typ, err (assign-pair) at :1 +./config.go:191:34: flow: {storage for &ValueError{...}} ← err: +./config.go:191:34: from ValueError{...} (struct literal element) at :1 +:1: &ValueError{...} escapes to heap +:1: parameter ~p0 leaks to {heap} for environ.Lookup with derefs=0: +:1: flow: {heap} ← ~p0: +:1: from ~p0.Lookup(key) (call parameter) at :1 +./discover.go:15:9: parameter key leaks to {heap} for environ.Lookup with derefs=0: +./discover.go:15:9: flow: {heap} ← key: +./discover.go:15:9: from ~p0.Lookup(key) (call parameter) at :1 +./discover.go:20:25: parameter key leaks to {heap} for (*osEnviron).Lookup with derefs=0: +./discover.go:20:25: flow: key ← key: +./discover.go:20:25: from ~p0, key := ~p0, key (assign-pair) at :1 +./discover.go:20:25: flow: {heap} ← key: +./discover.go:20:25: from os.LookupEnv(key) (call parameter) at :1 +./env.go:137:7: parameter e leaks to {heap} for (*Env).Load with derefs=1: +./env.go:137:7: flow: {heap} ← *e: +./env.go:137:7: from e (indirection) at :1 +./env.go:137:7: from Env.Load(e) (call parameter) at :1 +./env.go:307:7: parameter e leaks to {heap} for (*Env).LoadFile with derefs=1: +./env.go:307:7: flow: {heap} ← *e: +./env.go:307:7: from e (indirection) at :1 +./env.go:307:7: from Env.LoadFile(e, path) (call parameter) at :1 +./env.go:307:23: parameter path leaks to {heap} for (*Env).LoadFile with derefs=0: +./env.go:307:23: flow: {heap} ← path: +./env.go:307:23: from Env.LoadFile(e, path) (call parameter) at :1 +./env.go:178:7: parameter e leaks to {heap} for (*Env).LoadProtected with derefs=1: +./env.go:178:7: flow: {heap} ← *e: +./env.go:178:7: from e (indirection) at :1 +./env.go:178:7: from Env.LoadProtected(e) (call parameter) at :1 +./env.go:269:7: parameter e leaks to {heap} for (*Env).LoadScope with derefs=1: +./env.go:269:7: flow: {heap} ← *e: +./env.go:269:7: from e (indirection) at :1 +./env.go:269:7: from Env.LoadScope(e, s) (call parameter) at :1 +./env.go:208:7: parameter e leaks to {heap} for (*Env).SafeDirectory with derefs=1: +./env.go:208:7: flow: {heap} ← *e: +./env.go:208:7: from e (indirection) at :1 +./env.go:208:7: from Env.SafeDirectory(e, dir) (call parameter) at :1 +./env.go:208:28: parameter dir leaks to {heap} for (*Env).SafeDirectory with derefs=0: +./env.go:208:28: flow: {heap} ← dir: +./env.go:208:28: from Env.SafeDirectory(e, dir) (call parameter) at :1 +./env.go:424:25: parameter path leaks to {heap} for (*Env).UpdateFile with derefs=0: +./env.go:424:25: flow: path ← path: +./env.go:424:25: from e, path, fn := e, path, fn (assign-pair) at :1 +./env.go:424:25: flow: {heap} ← path: +./env.go:424:25: from updateFile(path, Scope(0), fn) (call parameter) at :1 +./env.go:379:7: parameter e leaks to {heap} for (*Env).UpdateScope with derefs=1: +./env.go:379:7: flow: {heap} ← *e: +./env.go:379:7: from e (indirection) at :1 +./env.go:379:7: from Env.UpdateScope(e, s, fn) (call parameter) at :1 +./env.go:326:7: parameter e leaks to {heap} for (*Env).WritePath with derefs=1: +./env.go:326:7: flow: {heap} ← *e: +./env.go:326:7: from e (indirection) at :1 +./env.go:326:7: from Env.WritePath(e, s) (call parameter) at :1 +./load.go:180:7: parameter e leaks to {heap} for (*Env).newLoader with derefs=1: +./load.go:180:7: flow: {heap} ← *e: +./load.go:180:7: from e (indirection) at :1 +./load.go:180:7: from Env.newLoader(e) (call parameter) at :1 +./load.go:31:7: parameter e leaks to {heap} for (*Env).repo with derefs=1: +./load.go:31:7: flow: {heap} ← *e: +./load.go:31:7: from e (indirection) at :1 +./load.go:31:7: from Env.repo(e) (call parameter) at :1 +./env.go:353:7: parameter e leaks to {heap} for (*Env).scopeFile with derefs=1: +./env.go:353:7: flow: {heap} ← *e: +./env.go:353:7: from e (indirection) at :1 +./env.go:353:7: from Env.scopeFile(e, s) (call parameter) at :1 +./parse.go:55:39: parameter file leaks to {heap} for (*File).entries with derefs=0: +./parse.go:55:39: flow: file ← file: +./parse.go:55:39: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:39: flow: {heap} ← file: +./parse.go:55:39: from Origin{...} (struct literal element) at :1 +./parse.go:55:39: from Entry{...} (struct literal element) at :1 +./parse.go:55:39: from append(out, Entry{...}) (call parameter) at :1 +./parse.go:55:52: parameter out leaks to {heap} for (*File).entries with derefs=1: +./parse.go:55:52: flow: out ← out: +./parse.go:55:52: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:52: flow: {temp} ← out: +./parse.go:55:52: from append(out, Entry{...}) (call parameter) at :1 +./parse.go:55:52: flow: {heap} ← *{temp}: +./parse.go:55:52: from append(out, Entry{...}) (appendee slice) at :1 +./parse.go:55:7: parameter p leaks to {heap} for (*File).entries with derefs=2: +./parse.go:55:7: flow: p ← p: +./parse.go:55:7: from p.parsed (dot of pointer) at :1 +./parse.go:55:7: from p.parsed (address-of) at :1 +./parse.go:55:7: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:7: flow: sec ← **p: +./parse.go:55:7: from p.secs (dot of pointer) at :1 +./parse.go:55:7: from p.secs[it.sec] (dot of pointer) at :1 +./parse.go:55:7: from sec := p.secs[it.sec] (assign) at :1 +./parse.go:55:7: flow: {heap} ← sec: +./parse.go:55:7: from sec.section (dot) at :1 +./parse.go:55:7: from Key{...} (struct literal element) at :1 +./parse.go:55:7: from Entry{...} (struct literal element) at :1 +./parse.go:55:7: from append(out, Entry{...}) (call parameter) at :1 +./parse.go:55:52: parameter out leaks to ~r0 for (*File).entries with derefs=0: +./parse.go:55:52: flow: out ← out: +./parse.go:55:52: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:52: flow: ~r0 ← out: +./parse.go:55:52: from ~r0 = out (assign-pair) at :1 +./parse.go:55:52: flow: ~r0 ← ~r0: +./parse.go:55:52: from return ~r0 (return) at :1 +:1: append(out, Entry{...}) escapes to heap in (*File).entries: +:1: flow: out ← &{storage for append(out, Entry{...})}: +:1: from append(out, Entry{...}) (spill) at :1 +:1: from out = append(out, Entry{...}) (assign) at :1 +:1: flow: ~r0 ← out: +:1: from ~r0 = out (assign-pair) at :1 +:1: flow: ~r0 ← ~r0: +:1: from return ~r0 (return) at :1 +:1: append escapes to heap +./parse.go:71:7: parameter p leaks to ~r0 for (*File).nameOf with derefs=2: +./parse.go:71:7: flow: p ← p: +./parse.go:71:7: from p.parsed (dot of pointer) at :1 +./parse.go:71:7: from p.parsed (address-of) at :1 +./parse.go:71:7: from p, it := p.parsed, it (assign-pair) at :1 +./parse.go:71:7: flow: ~r0 ← **p: +./parse.go:71:7: from p.texts (dot of pointer) at :1 +./parse.go:71:7: from p.texts[it.text] (dot of pointer) at :1 +./parse.go:71:7: from p.texts[it.text].name (dot) at :1 +./parse.go:71:7: from ~r0 = p.texts[it.text].name (assign-pair) at :1 +./parse.go:71:7: flow: ~r0 ← ~r0: +./parse.go:71:7: from return ~r0 (return) at :1 +./parse.go:51:7: parameter p leaks to ~r0 for (*File).secOf with derefs=2: +./parse.go:51:7: flow: p ← p: +./parse.go:51:7: from p.parsed (dot of pointer) at :1 +./parse.go:51:7: from p.parsed (address-of) at :1 +./parse.go:51:7: from p, it := p.parsed, it (assign-pair) at :1 +./parse.go:51:7: flow: ~r0 ← **p: +./parse.go:51:7: from p.secs (dot of pointer) at :1 +./parse.go:51:7: from p.secs[it.sec] (dot of pointer) at :1 +./parse.go:51:7: from ~r0 = p.secs[it.sec] (assign-pair) at :1 +./parse.go:51:7: flow: ~r0 ← ~r0: +./parse.go:51:7: from return ~r0 (return) at :1 +./parse.go:72:7: parameter p leaks to ~r0 for (*File).valueOf with derefs=2: +./parse.go:72:7: flow: p ← p: +./parse.go:72:7: from p.parsed (dot of pointer) at :1 +./parse.go:72:7: from p.parsed (address-of) at :1 +./parse.go:72:7: from p, it := p.parsed, it (assign-pair) at :1 +./parse.go:72:7: flow: ~r0 ← **p: +./parse.go:72:7: from p.texts (dot of pointer) at :1 +./parse.go:72:7: from p.texts[it.text] (dot of pointer) at :1 +./parse.go:72:7: from p.texts[it.text].value (dot) at :1 +./parse.go:72:7: from ~r0 = p.texts[it.text].value (assign-pair) at :1 +./parse.go:72:7: flow: ~r0 ← ~r0: +./parse.go:72:7: from return ~r0 (return) at :1 +./parse.go:55:39: parameter file leaks to {heap} for (*parser).entries with derefs=0: +./parse.go:55:39: flow: file ← file: +./parse.go:55:39: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:39: flow: {heap} ← file: +./parse.go:55:39: from Origin{...} (struct literal element) at :1 +./parse.go:55:39: from Entry{...} (struct literal element) at :1 +./parse.go:55:39: from append(out, Entry{...}) (call parameter) at :1 +./parse.go:55:52: parameter out leaks to {heap} for (*parser).entries with derefs=1: +./parse.go:55:52: flow: out ← out: +./parse.go:55:52: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:52: flow: {temp} ← out: +./parse.go:55:52: from append(out, Entry{...}) (call parameter) at :1 +./parse.go:55:52: flow: {heap} ← *{temp}: +./parse.go:55:52: from append(out, Entry{...}) (appendee slice) at :1 +./parse.go:55:7: parameter p leaks to {heap} for (*parser).entries with derefs=2: +./parse.go:55:7: flow: p ← p: +./parse.go:55:7: from p.parsed (dot of pointer) at :1 +./parse.go:55:7: from p.parsed (address-of) at :1 +./parse.go:55:7: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:7: flow: sec ← **p: +./parse.go:55:7: from p.secs (dot of pointer) at :1 +./parse.go:55:7: from p.secs[it.sec] (dot of pointer) at :1 +./parse.go:55:7: from sec := p.secs[it.sec] (assign) at :1 +./parse.go:55:7: flow: {heap} ← sec: +./parse.go:55:7: from sec.section (dot) at :1 +./parse.go:55:7: from Key{...} (struct literal element) at :1 +./parse.go:55:7: from Entry{...} (struct literal element) at :1 +./parse.go:55:7: from append(out, Entry{...}) (call parameter) at :1 +./parse.go:55:52: parameter out leaks to ~r0 for (*parser).entries with derefs=0: +./parse.go:55:52: flow: out ← out: +./parse.go:55:52: from p, scope, file, out := p.parsed, scope, file, out (assign-pair) at :1 +./parse.go:55:52: flow: ~r0 ← out: +./parse.go:55:52: from ~r0 = out (assign-pair) at :1 +./parse.go:55:52: flow: ~r0 ← ~r0: +./parse.go:55:52: from return ~r0 (return) at :1 +:1: append(out, Entry{...}) escapes to heap in (*parser).entries: +:1: flow: out ← &{storage for append(out, Entry{...})}: +:1: from append(out, Entry{...}) (spill) at :1 +:1: from out = append(out, Entry{...}) (assign) at :1 +:1: flow: ~r0 ← out: +:1: from ~r0 = out (assign-pair) at :1 +:1: flow: ~r0 ← ~r0: +:1: from return ~r0 (return) at :1 +:1: append escapes to heap +./parse.go:71:7: parameter p leaks to ~r0 for (*parser).nameOf with derefs=2: +./parse.go:71:7: flow: p ← p: +./parse.go:71:7: from p.parsed (dot of pointer) at :1 +./parse.go:71:7: from p.parsed (address-of) at :1 +./parse.go:71:7: from p, it := p.parsed, it (assign-pair) at :1 +./parse.go:71:7: flow: ~r0 ← **p: +./parse.go:71:7: from p.texts (dot of pointer) at :1 +./parse.go:71:7: from p.texts[it.text] (dot of pointer) at :1 +./parse.go:71:7: from p.texts[it.text].name (dot) at :1 +./parse.go:71:7: from ~r0 = p.texts[it.text].name (assign-pair) at :1 +./parse.go:71:7: flow: ~r0 ← ~r0: +./parse.go:71:7: from return ~r0 (return) at :1 +./parse.go:51:7: parameter p leaks to ~r0 for (*parser).secOf with derefs=2: +./parse.go:51:7: flow: p ← p: +./parse.go:51:7: from p.parsed (dot of pointer) at :1 +./parse.go:51:7: from p.parsed (address-of) at :1 +./parse.go:51:7: from p, it := p.parsed, it (assign-pair) at :1 +./parse.go:51:7: flow: ~r0 ← **p: +./parse.go:51:7: from p.secs (dot of pointer) at :1 +./parse.go:51:7: from p.secs[it.sec] (dot of pointer) at :1 +./parse.go:51:7: from ~r0 = p.secs[it.sec] (assign-pair) at :1 +./parse.go:51:7: flow: ~r0 ← ~r0: +./parse.go:51:7: from return ~r0 (return) at :1 +./parse.go:72:7: parameter p leaks to ~r0 for (*parser).valueOf with derefs=2: +./parse.go:72:7: flow: p ← p: +./parse.go:72:7: from p.parsed (dot of pointer) at :1 +./parse.go:72:7: from p.parsed (address-of) at :1 +./parse.go:72:7: from p, it := p.parsed, it (assign-pair) at :1 +./parse.go:72:7: flow: ~r0 ← **p: +./parse.go:72:7: from p.texts (dot of pointer) at :1 +./parse.go:72:7: from p.texts[it.text] (dot of pointer) at :1 +./parse.go:72:7: from p.texts[it.text].value (dot) at :1 +./parse.go:72:7: from ~r0 = p.texts[it.text].value (assign-pair) at :1 +./parse.go:72:7: flow: ~r0 ← ~r0: +./parse.go:72:7: from return ~r0 (return) at :1 diff --git a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/inline.txt b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/inline.txt new file mode 100644 index 0000000..df6eb87 --- /dev/null +++ b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/inline.txt @@ -0,0 +1,777 @@ +./key.go:69:6: can inline Key.sectionKey with cost 11 as: method(Key) func() sectionKey { return sectionKey{...} } +./config.go:31:6: cannot inline newConfig: function too complex: cost 88 exceeds budget 80 +./key.go:78:6: can inline isAlpha with cost 12 as: func(byte) bool { return byte(97) <= c | byte(32) && c | byte(32) <= byte(122) } +./key.go:74:6: can inline isKeyChar with cost 28 as: func(byte) bool { return isAlpha(c) || byte(48) <= c && c <= byte(57) || c == byte(45) } +./key.go:84:6: can inline validName with cost 77 as: func(string) bool { if s == "" || !isAlpha(s[0]) { return false }; for loop; return true } +./key.go:115:6: can inline lowerASCII with cost 66 as: func(string) string { for loop; return s } +./key.go:98:6: can inline validSection with cost 52 as: func(string) bool { for loop; return true } +./key.go:109:6: can inline validSubsection with cost 70 as: func(string) bool { return !strings.ContainsAny(s, "\n\x00") } +./key.go:25:6: cannot inline parseKey: function too complex: cost 668 exceeds budget 80 +./config.go:49:6: cannot inline (*Config).lookup: function too complex: cost 83 exceeds budget 80 +./config.go:61:6: can inline (*Config).filter with cost 17 as: method(*Config) func(sectionKey) iter.Seq[github.com/git-bug/gitconfig.Entry] { return func literal } +./config.go:62:9: can inline (*Config).filter.func1 with cost 52 as: func(func(Entry) bool) { if c == nil { return }; for loop } +./config.go:75:6: cannot inline (*Config).Get: function too complex: cost 83 exceeds budget 80 +./config.go:85:6: cannot inline (*Config).GetAll: function too complex: cost 96 exceeds budget 80 +./config.go:98:6: can inline (*Config).All with cost 17 as: method(*Config) func() iter.Seq[github.com/git-bug/gitconfig.Entry] { return func literal } +./config.go:99:9: can inline (*Config).All.func1 with cost 35 as: func(func(Entry) bool) { if c == nil { return }; for loop } +./config.go:113:6: cannot inline (*Config).Section: function too complex: cost 91 exceeds budget 80 +./config.go:120:6: cannot inline (*Config).Subsection: function too complex: cost 95 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: can inline slices.Clone[go.shape.[]string,go.shape.string] with cost 11 as: func(*[4]uintptr, go.shape.[]string) go.shape.[]string { if slices.s == nil { return nil }; return append(go.shape.[]string{}, slices.s...) } +./config.go:128:6: cannot inline (*Config).Subsections: function too complex: cost 94 exceeds budget 80 +./config.go:136:6: can inline (*Config).Value with cost 74 as: method(*Config) func(string, string) string { if ok { return e.Value }; return def } +./value.go:137:6: can inline equalFoldASCII with cost 64 as: func(string, string) bool { if len(s) != len(t) { return false }; for loop; return true } +./value.go:100:6: can inline digitValue with cost 37 as: func(byte) int { switch statement; return 36 } +./value.go:35:6: cannot inline parseInt: function too complex: cost 397 exceeds budget 80 +./value.go:15:6: cannot inline parseBool: function too complex: cost 510 exceeds budget 80 +./config.go:191:6: can inline Entry.error with cost 10 as: method(Entry) func(string, error) error { return &ValueError{...} } +./config.go:234:6: cannot inline Entry.Bool: function too complex: cost 97 exceeds budget 80 +./config.go:146:6: cannot inline (*Config).Bool: function too complex: cost 149 exceeds budget 80 +./config.go:248:6: cannot inline Entry.Int: function too complex: cost 110 exceeds budget 80 +./config.go:161:6: cannot inline (*Config).Int: function too complex: cost 149 exceeds budget 80 +./value.go:120:6: cannot inline expandPath: function too complex: cost 97 exceeds budget 80 +./config.go:265:6: cannot inline Entry.Path: function too complex: cost 111 exceeds budget 80 +./config.go:177:6: cannot inline (*Config).Path: function too complex: cost 219 exceeds budget 80 +./discover.go:20:6: can inline osEnviron.Lookup with cost 70 as: method(osEnviron) func(string) (string, bool) { return string(.autotmp_4), .autotmp_5 } +./discover.go:22:6: can inline get with cost 68 as: func(environ, string) string { v, _ := string(.autotmp_4), .autotmp_5; return v } +./discover.go:112:6: cannot inline countEntries: function too complex: cost 733 exceeds budget 80 +./discover.go:186:6: cannot inline unquote: function too complex: cost 237 exceeds budget 80 +./discover.go:181:6: can inline isParamSpace with cost 74 as: func(byte) bool { return strings.IndexByte(" \t\n\v\f\r", c) >= 0 } +./discover.go:148:6: cannot inline parseParameters: function too complex: cost 573 exceeds budget 80 +./platform.go:35:6: cannot inline transient: function too complex: cost 191 exceeds budget 80 +./platform.go:43:6: cannot inline retryTransient: function too complex: cost 405 exceeds budget 80 +./platform.go:14:6: can inline openRegular with cost 80 as: func(string) (*os.File, error) { flag := 0; flag |= 2048; return (*os.File)(.autotmp_4), .autotmp_5 } +./load.go:130:6: cannot inline readConfigFile: function too complex: cost 93 exceeds budget 80 +./load.go:135:24: cannot inline readConfigFile.func1: unhandled op DEFER +./load.go:140:3: can inline readConfigFile.func1.deferwrap1 with cost 69 as: func() { (*os.File).Close(.autotmp_5) } +./load.go:122:6: can inline readSmall with cost 71 as: func(string) ([]byte, error) { return ([]byte)(.autotmp_3), .autotmp_4 } +./discover.go:86:6: cannot inline resolveGitDir: function too complex: cost 605 exceeds budget 80 +./discover.go:27:6: cannot inline discover: function too complex: cost 1102 exceeds budget 80 +./edit.go:28:6: cannot inline editKey: function too complex: cost 226 exceeds budget 80 +./edit.go:66:6: can inline (*File).checkOpen with cost 13 as: method(*File) func(string) error { if f.closed { return &KeyError{...} }; return nil } +./edit.go:59:6: cannot inline checkValue: function too complex: cost 84 exceeds budget 80 +./edit.go:43:6: cannot inline (*File).prepare: function too complex: cost 192 exceeds budget 80 +./parse.go:51:6: can inline (*parsed).secOf with cost 6 as: method(*parsed) func(item) sectionKey { return p.secs[it.sec] } +./parse.go:71:6: can inline (*parsed).nameOf with cost 7 as: method(*parsed) func(item) string { return p.texts[it.text].name } +./edit.go:74:6: can inline (*File).entriesOf with cost 76 as: method(*File) func(Key) []int { idx = ; sk := Key.sectionKey(k); for loop; return idx } +./parse.go:72:6: can inline (*parsed).valueOf with cost 7 as: method(*parsed) func(item) string { return p.texts[it.text].value } +./edit.go:95:6: can inline flatten with cost 64 as: func(parsed) []flatEntry { out = ; for loop; return out } +./edit.go:105:6: can inline flatEntry.is with cost 26 as: method(flatEntry) func(Key) bool { return e.inSection && e.sec == Key.sectionKey(k) && e.name == k.Name } +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:10:6: can inline slices.insertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with cost 73 as: func(*[2]uintptr, []go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, int, int, func(go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }) int) { for loop } +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:20:6: cannot inline slices.siftDownCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 121 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:38:6: cannot inline slices.heapSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 200 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:240:6: cannot inline slices.breakPatternsCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 119 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:298:6: can inline slices.order2CmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with cost 38 as: func(*[2]uintptr, []go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, int, int, *int, func(go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }) int) (int, int) { if slices.cmp(slices.data[slices.b], slices.data[slices.a]) < 0 { *slices.swaps++; return slices.b, slices.a }; return slices.a, slices.b } +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:307:6: cannot inline slices.medianCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 158 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:315:6: can inline slices.medianAdjacentCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with cost 74 as: func(*[3]uintptr, []go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, int, *int, func(go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }) int) int { return slices.medianCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[3]uintptr)(slices..dict[0]), slices.data, slices.a - 1, slices.a, slices.a + 1, slices.swaps, slices.cmp) } +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:261:6: cannot inline slices.choosePivotCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 401 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:319:6: can inline slices.reverseRangeCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with cost 35 as: func(*[2]uintptr, []go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, int, int, func(go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }) int) { slices.i := slices.a; slices.j := slices.b - 1; for loop } +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:195:6: cannot inline slices.partialInsertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 228 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:173:6: cannot inline slices.partitionEqualCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 124 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:135:6: cannot inline slices.partitionCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 248 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:61:6: cannot inline slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]: function too complex: cost 832 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:6: can inline slices.SortFunc[go.shape.[]github.com/git-bug/gitconfig.splice,go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] with cost 77 as: func(*[5]uintptr, go.shape.[]github.com/git-bug/gitconfig.splice, func(go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }, go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }) int) { slices.n := len(slices.x); slices.pdqsortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }]((*[11]uintptr)(slices..dict[0]), slices.x, 0, slices.n, bits.Len(uint(slices.n)), slices.cmp) } +./edit.go:112:27: can inline (*File).apply.func1 with cost 6 as: func(splice, splice) int { return a.start - b.start } +./parse.go:116:6: can inline (*parser).add with cost 36 as: method(*parser) func(itemKind, int, int) *item { p.parsed.items = append(p.parsed.items, item{...}); return &p.parsed.items[len(p.parsed.items) - 1] } +./parse.go:159:6: can inline isNewline with cost 27 as: func([]byte, int) bool { return data[i] == byte(10) || data[i] == byte(13) && i + 1 < len(data) && data[i + 1] == byte(10) } +./parse.go:148:6: can inline isSpace with cost 46 as: func([]byte, int) bool { switch statement; return false } +./parse.go:164:6: can inline newlineLen with cost 10 as: func([]byte, int) int { if data[i] == byte(13) { return 2 }; return 1 } +./parse.go:112:6: can inline (*parser).errorAt with cost 11 as: method(*parser) func(int, string) error { return &SyntaxError{...} } +./parse.go:135:6: can inline (*parser).setSection with cost 44 as: method(*parser) func(sectionKey) { i, ok := int32(.autotmp_4), .autotmp_5; if !ok { i = int32(len(p.parsed.secs)); p.parsed.secs = append(p.parsed.secs, sec); p.secIndex[sec] = i }; p.sec = i; p.inSection = true } +./parse.go:215:6: cannot inline (*parser).header: function too complex: cost 863 exceeds budget 80 +./parse.go:122:6: cannot inline (*parser).addEntry: function too complex: cost 162 exceeds budget 80 +./parse.go:334:6: cannot inline (*parser).value: unhandled op DEFER +./parse.go:337:8: can inline (*parser).value.func1 with cost 6 as: func() { p.buf = buf[:0] } +./parse.go:342:11: can inline (*parser).value.func2 with cost 15 as: func(int) { if pending >= 0 { buf = append(buf, d[pending:at]...); pending = -1 } } +./parse.go:296:6: cannot inline (*parser).entry: function too complex: cost 384 exceeds budget 80 +./parse.go:171:6: cannot inline (*parser).run: function too complex: cost 677 exceeds budget 80 +./parse.go:102:6: cannot inline parse: function too complex: cost 172 exceeds budget 80 +./edit.go:111:6: cannot inline (*File).apply: function too complex: cost 399 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:20:6: can inline slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }] with cost 25 as: func(*[1]uintptr, go.shape.[]github.com/git-bug/gitconfig.flatEntry, go.shape.[]github.com/git-bug/gitconfig.flatEntry) bool { if len(slices.s1) != len(slices.s2) { return false }; for loop; return true } +./edit.go:152:6: can inline filter with cost 38 as: func([]flatEntry, func(flatEntry) bool) []flatEntry { out = ; for loop; return out } +./edit.go:133:6: can inline checkKey with cost 17 as: func(Key, []string, bool) func([]flatEntry, []flatEntry) bool { return func literal } +./edit.go:134:9: can inline checkKey.func1 with cost 231 as: func([]flatEntry, []flatEntry) bool { other := func literal; if !slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }](&slices..dict.Equal[[]github.com/git-bug/gitconfig.flatEntry,github.com/git-bug/gitconfig.flatEntry], filter(before, other), filter(after, other)) { return false }; got := filter(after, func literal); if len(got) != len(want) { return false }; for loop; return true } +./edit.go:135:12: can inline checkKey.func1.1 with cost 31 as: func(flatEntry) bool { return !flatEntry.is(e, k) } +./edit.go:139:24: can inline checkKey.func1.2 with cost 30 as: func(flatEntry) bool { return flatEntry.is(e, k) } +./edit.go:166:6: can inline (*File).ownStart with cost 31 as: method(*File) func(int) int { if i > 0 && f.parsed.items[i - 1].kind == itemKind(1) { return f.parsed.items[i - 1].start }; return f.parsed.items[i].start } +./edit.go:174:6: can inline (*File).firstOnLine with cost 45 as: method(*File) func(int) bool { j := i - 1; if j >= 0 && f.parsed.items[j].kind == itemKind(1) { j-- }; return j < 0 || f.parsed.items[j].kind == itemKind(2) || f.parsed.items[j].kind == itemKind(0) } +./edit.go:184:6: can inline (*File).newlineAfter with cost 30 as: method(*File) func(int) int { for loop; return -1 } +./edit.go:194:6: can inline (*File).lineEnd with cost 52 as: method(*File) func(int) int { if n >= 0 { return f.parsed.items[n].end }; return len(f.data) } +./edit.go:203:6: can inline (*File).terminator with cost 62 as: method(*File) func() string { if len(f.parsed.items) == 0 { return "" }; last := f.parsed.items[len(f.parsed.items) - 1]; switch statement; return "\n" } +./edit.go:228:6: cannot inline quoteValue: function too complex: cost 413 exceeds budget 80 +./edit.go:221:6: can inline formatEntry with cost 66 as: func(string, string) string { return "\t" + name + " = " + quoteValue(value) + "\n" } +./edit.go:254:6: cannot inline formatHeader: function too complex: cost 103 exceeds budget 80 +./edit.go:284:6: cannot inline (*File).entryLineEnd: function too complex: cost 100 exceeds budget 80 +./edit.go:263:6: cannot inline (*File).replaceEntry: function too complex: cost 229 exceeds budget 80 +./edit.go:274:6: cannot inline (*File).removeEntry: function too complex: cost 249 exceeds budget 80 +./edit.go:348:16: can inline (*File).unsetEntry.func1 with cost 23 as: func(item) bool { return it.kind == itemKind(4) && (*parsed).secOf(f.parsed, it) == f.parsed.secs[sec] } +./edit.go:346:6: cannot inline (*File).unsetEntry: function too complex: cost 515 exceeds budget 80 +./edit.go:334:27: can inline (*File).unsetEntries.func1 with cost 6 as: func(splice, splice) int { return a.start - b.start } +./edit.go:313:6: cannot inline (*File).unsetEntries: function too complex: cost 298 exceeds budget 80 +./edit.go:398:6: cannot inline (*File).insert: function too complex: cost 594 exceeds budget 80 +./edit.go:439:6: can inline sectionArg with cost 71 as: func(string) error { if section == "" || !validSection(section) { return &KeyError{...} }; return nil } +./edit.go:447:6: can inline (*File).headersOf with cost 40 as: method(*File) func(sectionKey) []int { idx = ; for loop; return idx } +./edit.go:459:6: cannot inline (*File).removeSection: function too complex: cost 265 exceeds budget 80 +./edit.go:475:32: can inline (*File).removeSection.func1 with cost 88 as: func([]flatEntry, []flatEntry) bool { return slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }](&slices..dict.Equal[[]github.com/git-bug/gitconfig.flatEntry,github.com/git-bug/gitconfig.flatEntry], filter(before, func literal), after) } +./edit.go:476:38: can inline (*File).removeSection.func1.1 with cost 9 as: func(flatEntry) bool { return !e.inSection || e.sec != sk } +./edit.go:483:6: cannot inline (*File).renameSection: function too complex: cost 479 exceeds budget 80 +./edit.go:522:32: can inline (*File).renameSection.func1 with cost 55 as: func([]flatEntry, []flatEntry) bool { for loop; return slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }](&slices..dict.Equal[[]github.com/git-bug/gitconfig.flatEntry,github.com/git-bug/gitconfig.flatEntry], before, after) } +./env.go:30:6: cannot inline Scope.String: function too complex: cost 99 exceeds budget 80 +./env.go:120:6: can inline Discover with cost 72 as: func(string) (Env, error) { return Env(.autotmp_3), .autotmp_4 } +./load.go:31:6: cannot inline Env.repo: function too complex: cost 583 exceeds budget 80 +./load.go:180:6: cannot inline Env.newLoader: function too complex: cost 81 exceeds budget 80 +./load.go:162:6: cannot inline isMissing: function too complex: cost 125 exceeds budget 80 +./parse.go:55:6: can inline (*parsed).entries with cost 71 as: method(*parsed) func(Scope, string, []Entry) []Entry { for loop; return out } +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: can inline slices.Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }] with cost 11 as: func(*[4]uintptr, go.shape.[]github.com/git-bug/gitconfig.Entry) go.shape.[]github.com/git-bug/gitconfig.Entry { if slices.s == nil { return nil }; return append(go.shape.[]github.com/git-bug/gitconfig.Entry{}, slices.s...) } +./load.go:290:6: can inline dirPrefix with cost 44 as: func(string) string { i := strings.LastIndexByte(path, byte(47)); return path[:i + 1] } +./load.go:366:6: can inline isAbsPattern with cost 61 as: func(string) bool { if strings.HasPrefix(p, "/") { return true }; return false && filepath.IsAbs(filepath.FromSlash(p)) } +./glob.go:80:6: cannot inline compileClass: function too complex: cost 167 exceeds budget 80 +./glob.go:32:6: cannot inline compileGlob: function too complex: cost 307 exceeds budget 80 +./glob.go:215:6: can inline newStateSet with cost 8 as: func(int32) *stateSet { return &stateSet{...} } +./glob.go:225:6: can inline (*stateSet).add with cost 26 as: method(*stateSet) func(int32) bool { if ss.mark[id] == ss.gen { return false }; ss.mark[id] = ss.gen; ss.list = append(ss.list, id); return true } +./glob.go:219:6: can inline (*stateSet).reset with cost 11 as: method(*stateSet) func() { ss.list = ss.list[:0]; ss.gen++ } +./glob.go:131:6: can inline sameByte with cost 28 as: func(byte, byte, bool) bool { return a == b || fold && isAlpha(a) && a ^ byte(32) == b } +./glob.go:124:6: can inline swapCase with cost 22 as: func(byte) byte { if isAlpha(c) { return c ^ byte(32) }; return c } +./glob.go:113:6: cannot inline globToken.inClass: function too complex: cost 95 exceeds budget 80 +./glob.go:234:6: can inline (*stateSet).has with cost 8 as: method(*stateSet) func(int32) bool { return ss.mark[id] == ss.gen } +./glob.go:148:11: can inline globMatch.func1 with cost 60 as: func(*stateSet, int32) { for loop } +./glob.go:137:6: cannot inline globMatch: function too complex: cost 936 exceeds budget 80 +./load.go:330:6: cannot inline (*loader).gitdir: function too complex: cost 532 exceeds budget 80 +./load.go:104:6: cannot inline (*repo).branch: function too complex: cost 333 exceeds budget 80 +./load.go:300:6: cannot inline (*loader).condition: function too complex: cost 408 exceeds budget 80 +./key.go:54:6: can inline Key.String with cost 20 as: method(Key) func() string { if k.HasSubsection { return k.Section + "." + k.Subsection + "." + k.Name }; return k.Section + "." + k.Name } +./load.go:280:6: can inline (*loader).fail with cost 62 as: method(*loader) func(string, Entry, string) error { if from == "" { return &KeyError{...} }; return &FileError{...} } +./load.go:206:6: cannot inline (*loader).load: function too complex: cost 452 exceeds budget 80 +./load.go:241:6: cannot inline (*loader).include: function too complex: cost 529 exceeds budget 80 +./load.go:195:6: cannot inline (*loader).loadFile: function too complex: cost 208 exceeds budget 80 +./load.go:53:6: can inline (*repo).localConfig with cost 69 as: method(*repo) func() string { return filepath.Join(... argument...) } +./load.go:80:6: cannot inline worktreeConfigIn: function too complex: cost 117 exceeds budget 80 +./load.go:57:6: can inline (*repo).worktreeConfig with cost 69 as: method(*repo) func() string { return filepath.Join(... argument...) } +./key.go:12:6: cannot inline ParseKey: function too complex: cost 82 exceeds budget 80 +./load.go:374:6: cannot inline (*loader).command: function too complex: cost 188 exceeds budget 80 +./load.go:188:6: can inline (*loader).config with cost 62 as: method(*loader) func() *Config { return newConfig(l.entries) } +./env.go:137:6: cannot inline Env.Load: function too complex: cost 819 exceeds budget 80 +./env.go:178:6: cannot inline Env.LoadProtected: function too complex: cost 368 exceeds budget 80 +./env.go:247:6: cannot inline realPath: function too complex: cost 138 exceeds budget 80 +./env.go:208:6: cannot inline Env.SafeDirectory: function too complex: cost 686 exceeds budget 80 +./load.go:63:6: cannot inline (*repo).worktreeConfigEnabled: function too complex: cost 791 exceeds budget 80 +./load.go:97:6: cannot inline (*repo).severalWorktrees: function too complex: cost 143 exceeds budget 80 +./env.go:353:6: cannot inline Env.scopeFile: function too complex: cost 447 exceeds budget 80 +./env.go:269:6: cannot inline Env.LoadScope: function too complex: cost 508 exceeds budget 80 +./env.go:307:6: cannot inline Env.LoadFile: function too complex: cost 212 exceeds budget 80 +./env.go:326:6: cannot inline Env.WritePath: function too complex: cost 208 exceeds budget 80 +./update.go:96:6: cannot inline followLinks: function too complex: cost 571 exceeds budget 80 +./update.go:87:6: cannot inline callClosing: unhandled op DEFER +./update.go:88:8: can inline callClosing.func1 with cost 4 as: func() { f.closed = true } +./update.go:19:6: cannot inline updateFile: unhandled op DEFER +./update.go:34:8: can inline updateFile.func1 with cost 132 as: func() { if !committed { (*os.File).Close(lock); os.Remove(lockPath) } } +./update.go:79:27: can inline updateFile.func2 with cost 67 as: func() error { return os.Rename(lockPath, target) } +./env.go:379:6: cannot inline Env.UpdateScope: function too complex: cost 217 exceeds budget 80 +./env.go:424:6: can inline Env.UpdateFile with cost 63 as: method(Env) func(string, func(*File) error) error { return updateFile(path, Scope(0), fn) } +./errors.go:74:6: can inline (*KeyError).Error with cost 69 as: method(*KeyError) func() string { return fmt.Sprintf("%v: %q", ... argument...) } +./errors.go:78:6: can inline (*KeyError).Unwrap with cost 3 as: method(*KeyError) func() error { return e.Err } +./errors.go:111:6: can inline (*SyntaxError).Error with cost 72 as: method(*SyntaxError) func() string { return fmt.Sprintf("gitconfig: %s line %d in file %s", ... argument...) } +./errors.go:90:6: cannot inline (*FileError).Error: function too complex: cost 219 exceeds budget 80 +./errors.go:98:6: can inline (*FileError).Unwrap with cost 3 as: method(*FileError) func() error { return e.Err } +./errors.go:124:6: cannot inline (*ValueError).Error: function too complex: cost 344 exceeds budget 80 +./errors.go:140:6: can inline (*ValueError).Unwrap with cost 3 as: method(*ValueError) func() error { return e.Err } +./file.go:55:6: cannot inline Parse: function too complex: cost 101 exceeds budget 80 +./file.go:66:6: can inline (*File).Bytes with cost 16 as: method(*File) func() []byte { return bytes.Clone(f.data) } +./file.go:78:6: cannot inline (*File).Config: function too complex: cost 150 exceeds budget 80 +./file.go:92:6: cannot inline (*File).Set: function too complex: cost 520 exceeds budget 80 +./file.go:112:6: cannot inline (*File).ReplaceAll: function too complex: cost 559 exceeds budget 80 +./file.go:131:6: cannot inline (*File).Add: function too complex: cost 340 exceeds budget 80 +./file.go:141:72: can inline (*File).Add.func1 with cost 158 as: func([]flatEntry, []flatEntry) bool { got := filter(after, func literal); return .autotmp_4(before, after) && !got[len(got) - 1].implicit } +./file.go:143:24: can inline (*File).Add.func1.1 with cost 30 as: func(flatEntry) bool { return flatEntry.is(e, k) } +./file.go:151:6: cannot inline (*File).Unset: function too complex: cost 329 exceeds budget 80 +./file.go:169:6: cannot inline (*File).UnsetAll: function too complex: cost 324 exceeds budget 80 +./file.go:187:6: cannot inline (*File).RemoveSection: function too complex: cost 253 exceeds budget 80 +./file.go:200:6: cannot inline (*File).RemoveSubsection: function too complex: cost 341 exceeds budget 80 +./file.go:220:6: cannot inline (*File).RenameSection: function too complex: cost 464 exceeds budget 80 +./file.go:240:6: cannot inline (*File).RenameSubsection: function too complex: cost 412 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6: can inline atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).CompareAndSwap with cost 63 as: method(*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]) func(*[16]uintptr, *go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }, *go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }) bool { return atomic.CompareAndSwapPointer(&atomic.x.v, unsafe.Pointer(atomic.old), unsafe.Pointer(atomic.new)) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6: can inline atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Swap with cost 62 as: method(*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]) func(*[16]uintptr, *go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }) *go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int } { return (*go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int })(atomic.SwapPointer(&atomic.x.v, unsafe.Pointer(atomic.new))) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6: can inline atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Store with cost 61 as: method(*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]) func(*[16]uintptr, *go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }) { atomic.StorePointer(&atomic.x.v, unsafe.Pointer(atomic.val)) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6: can inline atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Load with cost 4 as: method(*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]) func(*[16]uintptr) *go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int } { return (*go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int })(atomic.LoadPointer(&atomic.x.v)) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6: can inline atomic.(*Pointer[os.dirInfo]).CompareAndSwap with cost 70 as: method(*atomic.Pointer[os.dirInfo]) func(*os.dirInfo, *os.dirInfo) bool { return (*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).CompareAndSwap(atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.old, atomic.new) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6: can inline atomic.(*Pointer[os.dirInfo]).Swap with cost 68 as: method(*atomic.Pointer[os.dirInfo]) func(*os.dirInfo) *os.dirInfo { return (*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Swap(atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.new) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6: can inline atomic.(*Pointer[os.dirInfo]).Store with cost 66 as: method(*atomic.Pointer[os.dirInfo]) func(*os.dirInfo) { (*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Store(atomic.x, &atomic..dict.Pointer[os.dirInfo], atomic.val) } +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6: can inline atomic.(*Pointer[os.dirInfo]).Load with cost 9 as: method(*atomic.Pointer[os.dirInfo]) func() *os.dirInfo { return (*atomic.Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Load(atomic.x, &atomic..dict.Pointer[os.dirInfo]) } +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:20:6: can inline slices.Equal[[]github.com/git-bug/gitconfig.flatEntry,github.com/git-bug/gitconfig.flatEntry] with cost 32 as: func([]flatEntry, []flatEntry) bool { return slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }](&slices..dict.Equal[[]github.com/git-bug/gitconfig.flatEntry,github.com/git-bug/gitconfig.flatEntry], slices.s1, slices.s2) } +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: can inline slices.Clone[[]github.com/git-bug/gitconfig.Entry,github.com/git-bug/gitconfig.Entry] with cost 17 as: func([]Entry) []Entry { return slices.Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }](&slices..dict.Clone[[]github.com/git-bug/gitconfig.Entry,github.com/git-bug/gitconfig.Entry], slices.s) } +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:6: cannot inline slices.SortFunc[[]github.com/git-bug/gitconfig.splice,github.com/git-bug/gitconfig.splice]: function too complex: cost 83 exceeds budget 80 +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: can inline slices.Clone[[]string,string] with cost 17 as: func([]string) []string { return slices.Clone[go.shape.[]string,go.shape.string](&slices..dict.Clone[[]string,string], slices.s) } +./discover.go:136:36: inlining call to errors.New +./edit.go:17:31: inlining call to errors.New +./errors.go:23:26: inlining call to errors.New +./errors.go:27:32: inlining call to errors.New +./errors.go:32:28: inlining call to errors.New +./errors.go:36:30: inlining call to errors.New +./errors.go:40:24: inlining call to errors.New +./errors.go:44:30: inlining call to errors.New +./errors.go:48:27: inlining call to errors.New +./errors.go:52:28: inlining call to errors.New +./errors.go:56:29: inlining call to errors.New +./errors.go:60:25: inlining call to errors.New +./errors.go:64:24: inlining call to errors.New +./update.go:16:33: inlining call to errors.New +./value.go:12:32: inlining call to errors.New +./value.go:113:26: inlining call to errors.New +./value.go:114:26: inlining call to errors.New +./value.go:115:26: inlining call to errors.New +./config.go:40:28: inlining call to Key.sectionKey +./key.go:75:16: inlining call to isAlpha +./key.go:85:24: inlining call to isAlpha +./key.go:89:16: inlining call to isKeyChar +./key.go:89:16: inlining call to isAlpha +./key.go:100:16: inlining call to isKeyChar +./key.go:100:16: inlining call to isAlpha +./key.go:110:29: inlining call to strings.ContainsAny +./key.go:26:28: inlining call to strings.IndexByte +./key.go:27:31: inlining call to strings.LastIndexByte +./key.go:32:15: inlining call to validName +./key.go:35:27: inlining call to lowerASCII +./key.go:37:19: inlining call to validSection +./key.go:40:25: inlining call to lowerASCII +./key.go:43:18: inlining call to validSection +./key.go:43:49: inlining call to validSubsection +./key.go:46:24: inlining call to lowerASCII +./key.go:26:28: inlining call to stringslite.IndexByte +./key.go:27:31: inlining call to bytealg.LastIndexByteString +./key.go:32:15: inlining call to isAlpha +./key.go:32:15: inlining call to isKeyChar +./key.go:37:19: inlining call to isKeyChar +./key.go:43:18: inlining call to isKeyChar +./key.go:43:49: inlining call to strings.ContainsAny +./key.go:32:15: inlining call to isAlpha +./key.go:37:19: inlining call to isAlpha +./key.go:43:18: inlining call to isAlpha +./config.go:67:23: inlining call to Key.sectionKey +./config.go:114:48: inlining call to lowerASCII +./config.go:114:17: inlining call to (*Config).filter +./config.go:121:39: inlining call to lowerASCII +./config.go:121:17: inlining call to (*Config).filter +./config.go:132:46: inlining call to lowerASCII +./config.go:132:21: inlining call to slices.Clone[go.shape.[]string,go.shape.string] +./value.go:45:66: inlining call to digitValue +./value.go:56:25: inlining call to digitValue +./value.go:17:30: inlining call to equalFoldASCII +./value.go:17:58: inlining call to equalFoldASCII +./value.go:17:83: inlining call to equalFoldASCII +./value.go:19:21: inlining call to equalFoldASCII +./value.go:19:48: inlining call to equalFoldASCII +./value.go:19:74: inlining call to equalFoldASCII +./config.go:240:24: inlining call to Entry.error +./config.go:250:20: inlining call to Entry.error +./config.go:254:20: inlining call to Entry.error +./value.go:122:24: inlining call to strings.HasPrefix +./value.go:124:36: inlining call to strings.HasPrefix +./value.go:129:24: inlining call to strings.HasPrefix +./value.go:122:24: inlining call to stringslite.HasPrefix +./value.go:124:36: inlining call to stringslite.HasPrefix +./value.go:129:24: inlining call to stringslite.HasPrefix +./config.go:267:21: inlining call to Entry.error +./config.go:271:21: inlining call to Entry.error +./discover.go:113:14: inlining call to get +./discover.go:119:25: inlining call to fmt.Errorf +./discover.go:123:57: inlining call to strconv.Itoa +./discover.go:125:26: inlining call to fmt.Errorf +./discover.go:127:61: inlining call to strconv.Itoa +./discover.go:129:26: inlining call to fmt.Errorf +./discover.go:119:25: inlining call to errors.New +./discover.go:123:57: inlining call to strconv.Itoa +./discover.go:125:26: inlining call to errors.New +./discover.go:127:61: inlining call to strconv.Itoa +./discover.go:129:26: inlining call to errors.New +./discover.go:193:25: inlining call to strings.IndexByte +./discover.go:197:17: inlining call to strings.(*Builder).WriteString +./discover.go:200:16: inlining call to strings.(*Builder).WriteByte +./discover.go:204:19: inlining call to strings.(*Builder).String +./discover.go:193:25: inlining call to stringslite.IndexByte +./discover.go:197:17: inlining call to strings.(*Builder).copyCheck +./discover.go:200:16: inlining call to strings.(*Builder).copyCheck +./discover.go:197:17: inlining call to abi.NoEscape +./discover.go:200:16: inlining call to abi.NoEscape +./discover.go:182:26: inlining call to strings.IndexByte +./discover.go:182:26: inlining call to stringslite.IndexByte +./discover.go:156:32: inlining call to strings.CutPrefix +./discover.go:158:30: inlining call to isParamSpace +./discover.go:167:36: inlining call to strings.Cut +./discover.go:172:33: inlining call to isParamSpace +./discover.go:156:32: inlining call to stringslite.CutPrefix +./discover.go:158:30: inlining call to strings.IndexByte +./discover.go:172:33: inlining call to strings.IndexByte +./discover.go:156:32: inlining call to stringslite.HasPrefix +./discover.go:158:30: inlining call to stringslite.IndexByte +./discover.go:172:33: inlining call to stringslite.IndexByte +./load.go:136:24: inlining call to openRegular +./load.go:145:26: inlining call to fs.FileMode.IsRegular +./load.go:151:40: inlining call to io.LimitReader +./load.go:140:16: inlining call to os.(*File).Close +./discover.go:92:39: inlining call to fs.FileMode.IsRegular +./discover.go:95:24: inlining call to readSmall +./discover.go:99:31: inlining call to strings.CutPrefix +./discover.go:101:54: inlining call to errors.New +./discover.go:103:20: inlining call to filepath.IsAbs +./discover.go:104:36: inlining call to filepath.Dir +./discover.go:104:23: inlining call to filepath.Join +./discover.go:99:31: inlining call to stringslite.CutPrefix +./discover.go:103:20: inlining call to filepathlite.IsAbs +./discover.go:99:31: inlining call to stringslite.HasPrefix +./discover.go:103:20: inlining call to stringslite.HasPrefix +./discover.go:30:14: inlining call to get +./discover.go:40:16: inlining call to get +./discover.go:41:57: inlining call to filepath.Join +./discover.go:43:57: inlining call to filepath.Join +./discover.go:46:57: inlining call to filepath.Join +./discover.go:54:28: inlining call to fmt.Errorf +./discover.go:70:36: inlining call to get +./discover.go:54:28: inlining call to errors.New +./edit.go:33:28: inlining call to strings.IndexByte +./edit.go:34:31: inlining call to strings.LastIndexByte +./edit.go:33:28: inlining call to stringslite.IndexByte +./edit.go:34:31: inlining call to bytealg.LastIndexByteString +./edit.go:60:22: inlining call to strings.IndexByte +./edit.go:60:22: inlining call to stringslite.IndexByte +./edit.go:44:23: inlining call to (*File).checkOpen +./edit.go:76:20: inlining call to Key.sectionKey +./edit.go:78:53: inlining call to (*parsed).secOf +./edit.go:78:75: inlining call to (*parsed).nameOf +./edit.go:99:53: inlining call to (*parsed).secOf +./edit.go:99:67: inlining call to (*parsed).nameOf +./edit.go:99:82: inlining call to (*parsed).valueOf +./edit.go:106:45: inlining call to Key.sectionKey +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:244:28: inlining call to slices.nextPowerOfTwo +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:247:33: inlining call to slices.(*xorshift).Next +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:308:22: inlining call to slices.order2CmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:309:22: inlining call to slices.order2CmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:310:22: inlining call to slices.order2CmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:279:29: inlining call to slices.medianAdjacentCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:280:29: inlining call to slices.medianAdjacentCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:281:29: inlining call to slices.medianAdjacentCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:73:24: inlining call to slices.insertionSortCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/zsortanyfunc.go:91:23: inlining call to slices.reverseRangeCmpFunc[go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +./parse.go:153:20: inlining call to isNewline +./parse.go:219:30: inlining call to isKeyChar +./parse.go:222:20: inlining call to lowerASCII +./parse.go:224:19: inlining call to (*parser).errorAt +./parse.go:231:20: inlining call to (*parser).errorAt +./parse.go:234:30: inlining call to strings.IndexByte +./parse.go:240:14: inlining call to isSpace +./parse.go:241:28: inlining call to isSpace +./parse.go:245:20: inlining call to (*parser).errorAt +./parse.go:251:21: inlining call to (*parser).errorAt +./parse.go:260:22: inlining call to (*parser).errorAt +./parse.go:267:21: inlining call to (*parser).errorAt +./parse.go:273:30: inlining call to isNewline +./parse.go:274:20: inlining call to (*parser).errorAt +./parse.go:277:20: inlining call to (*parser).errorAt +./parse.go:281:30: inlining call to strings.IndexByte +./parse.go:287:19: inlining call to (*parser).errorAt +./parse.go:290:14: inlining call to (*parser).setSection +./parse.go:292:7: inlining call to (*parser).add +./parse.go:219:30: inlining call to isAlpha +./parse.go:234:30: inlining call to stringslite.IndexByte +./parse.go:240:14: inlining call to isNewline +./parse.go:241:28: inlining call to isNewline +./parse.go:281:30: inlining call to stringslite.IndexByte +./parse.go:123:13: inlining call to (*parser).add +./parse.go:126:17: inlining call to lowerASCII +./parse.go:350:15: inlining call to isNewline +./parse.go:352:34: inlining call to (*parser).errorAt +./parse.go:360:35: inlining call to (*parser).errorAt +./parse.go:362:10: inlining call to (*parser).value.func2 +./parse.go:367:9: inlining call to (*parser).value.func2 +./parse.go:368:16: inlining call to isNewline +./parse.go:369:24: inlining call to newlineLen +./parse.go:386:34: inlining call to (*parser).errorAt +./parse.go:390:9: inlining call to (*parser).value.func2 +./parse.go:404:9: inlining call to (*parser).value.func2 +./parse.go:410:32: inlining call to (*parser).errorAt +./parse.go:413:25: inlining call to bytes.IndexByte +./parse.go:300:29: inlining call to isKeyChar +./parse.go:308:29: inlining call to isNewline +./parse.go:314:19: inlining call to (*parser).errorAt +./parse.go:300:29: inlining call to isAlpha +./parse.go:173:20: inlining call to bytes.HasPrefix +./parse.go:174:8: inlining call to (*parser).add +./parse.go:181:15: inlining call to isSpace +./parse.go:182:33: inlining call to isSpace +./parse.go:185:9: inlining call to (*parser).add +./parse.go:186:17: inlining call to isNewline +./parse.go:187:23: inlining call to newlineLen +./parse.go:188:9: inlining call to (*parser).add +./parse.go:192:27: inlining call to bytes.IndexByte +./parse.go:199:9: inlining call to (*parser).add +./parse.go:204:15: inlining call to isAlpha +./parse.go:209:20: inlining call to (*parser).errorAt +./parse.go:173:20: inlining call to bytes.Equal +./parse.go:181:15: inlining call to isNewline +./parse.go:182:33: inlining call to isNewline +./edit.go:112:17: inlining call to slices.SortFunc[go.shape.[]github.com/git-bug/gitconfig.splice,go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +./edit.go:122:33: inlining call to flatten +./edit.go:122:52: inlining call to flatten +./edit.go:122:33: inlining call to (*parsed).secOf +./edit.go:122:33: inlining call to (*parsed).nameOf +./edit.go:122:33: inlining call to (*parsed).valueOf +./edit.go:122:52: inlining call to (*parsed).secOf +./edit.go:122:52: inlining call to (*parsed).nameOf +./edit.go:122:52: inlining call to (*parsed).valueOf +./edit.go:136:26: inlining call to filter +./edit.go:136:49: inlining call to filter +./edit.go:136:19: inlining call to slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }] +./edit.go:139:16: inlining call to filter +./edit.go:136:26: inlining call to checkKey.func1.1 +./edit.go:136:49: inlining call to checkKey.func1.1 +./edit.go:139:16: inlining call to checkKey.func1.2 +./edit.go:136:26: inlining call to flatEntry.is +./edit.go:136:49: inlining call to flatEntry.is +./edit.go:139:16: inlining call to flatEntry.is +./edit.go:136:26: inlining call to Key.sectionKey +./edit.go:136:49: inlining call to Key.sectionKey +./edit.go:139:16: inlining call to Key.sectionKey +./edit.go:135:49: inlining call to flatEntry.is +./edit.go:135:49: inlining call to Key.sectionKey +./edit.go:139:60: inlining call to flatEntry.is +./edit.go:139:60: inlining call to Key.sectionKey +./edit.go:195:24: inlining call to (*File).newlineAfter +./edit.go:230:28: inlining call to strings.HasPrefix +./edit.go:230:57: inlining call to strings.HasSuffix +./edit.go:230:88: inlining call to strings.ContainsAny +./edit.go:232:15: inlining call to strings.(*Builder).WriteByte +./edit.go:237:16: inlining call to strings.(*Builder).WriteByte +./edit.go:238:16: inlining call to strings.(*Builder).WriteByte +./edit.go:240:18: inlining call to strings.(*Builder).WriteString +./edit.go:242:18: inlining call to strings.(*Builder).WriteString +./edit.go:244:16: inlining call to strings.(*Builder).WriteByte +./edit.go:248:15: inlining call to strings.(*Builder).WriteByte +./edit.go:250:18: inlining call to strings.(*Builder).String +./edit.go:230:28: inlining call to stringslite.HasPrefix +./edit.go:230:57: inlining call to stringslite.HasSuffix +./edit.go:232:15: inlining call to strings.(*Builder).copyCheck +./edit.go:237:16: inlining call to strings.(*Builder).copyCheck +./edit.go:238:16: inlining call to strings.(*Builder).copyCheck +./edit.go:240:18: inlining call to strings.(*Builder).copyCheck +./edit.go:242:18: inlining call to strings.(*Builder).copyCheck +./edit.go:244:16: inlining call to strings.(*Builder).copyCheck +./edit.go:248:15: inlining call to strings.(*Builder).copyCheck +./edit.go:232:15: inlining call to abi.NoEscape +./edit.go:237:16: inlining call to abi.NoEscape +./edit.go:238:16: inlining call to abi.NoEscape +./edit.go:240:18: inlining call to abi.NoEscape +./edit.go:242:18: inlining call to abi.NoEscape +./edit.go:244:16: inlining call to abi.NoEscape +./edit.go:248:15: inlining call to abi.NoEscape +./edit.go:258:26: inlining call to strings.NewReplacer +./edit.go:285:21: inlining call to (*File).newlineAfter +./edit.go:264:21: inlining call to formatEntry +./edit.go:265:19: inlining call to (*File).firstOnLine +./edit.go:269:26: inlining call to (*File).ownStart +./edit.go:275:18: inlining call to (*File).firstOnLine +./edit.go:276:27: inlining call to (*File).ownStart +./edit.go:278:26: inlining call to (*File).ownStart +./edit.go:354:66: inlining call to (*File).unsetEntry.func1 +./edit.go:357:16: inlining call to (*File).unsetEntry.func1 +./edit.go:364:82: inlining call to (*File).unsetEntry.func1 +./edit.go:380:20: inlining call to (*File).lineEnd +./edit.go:354:66: inlining call to (*parsed).secOf +./edit.go:357:16: inlining call to (*parsed).secOf +./edit.go:364:82: inlining call to (*parsed).secOf +./edit.go:380:20: inlining call to (*File).newlineAfter +./edit.go:348:76: inlining call to (*parsed).secOf +./edit.go:334:17: inlining call to slices.SortFunc[go.shape.[]github.com/git-bug/gitconfig.splice,go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +./edit.go:399:20: inlining call to Key.sectionKey +./edit.go:402:38: inlining call to (*parsed).secOf +./edit.go:406:21: inlining call to formatEntry +./edit.go:408:55: inlining call to (*File).terminator +./edit.go:418:25: inlining call to (*File).newlineAfter +./edit.go:427:55: inlining call to (*File).terminator +./edit.go:440:35: inlining call to validSection +./edit.go:440:35: inlining call to isKeyChar +./edit.go:440:35: inlining call to isAlpha +./edit.go:450:38: inlining call to (*parsed).secOf +./edit.go:460:24: inlining call to (*File).headersOf +./edit.go:469:21: inlining call to (*File).ownStart +./edit.go:473:46: inlining call to (*File).ownStart +./edit.go:460:24: inlining call to (*parsed).secOf +./edit.go:476:29: inlining call to filter +./edit.go:476:22: inlining call to slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }] +./edit.go:476:29: inlining call to (*File).removeSection.func1.1 +./edit.go:484:24: inlining call to (*File).headersOf +./edit.go:494:20: inlining call to (*File).firstOnLine +./edit.go:505:25: inlining call to (*File).newlineAfter +./edit.go:512:40: inlining call to (*parsed).secOf +./edit.go:519:46: inlining call to (*File).ownStart +./edit.go:519:60: inlining call to (*File).lineEnd +./edit.go:484:24: inlining call to (*parsed).secOf +./edit.go:519:60: inlining call to (*File).newlineAfter +./edit.go:528:22: inlining call to slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }] +./env.go:45:32: inlining call to strconv.Itoa +./env.go:45:32: inlining call to strconv.Itoa +./load.go:35:20: inlining call to filepath.IsAbs +./load.go:39:38: inlining call to filepath.Join +./load.go:39:24: inlining call to readSmall +./load.go:43:21: inlining call to filepath.IsAbs +./load.go:44:23: inlining call to filepath.Join +./load.go:46:31: inlining call to filepath.Clean +./load.go:48:45: inlining call to filepath.Join +./load.go:35:20: inlining call to filepathlite.IsAbs +./load.go:43:21: inlining call to filepathlite.IsAbs +./load.go:35:20: inlining call to stringslite.HasPrefix +./load.go:43:21: inlining call to stringslite.HasPrefix +./load.go:291:28: inlining call to strings.LastIndexByte +./load.go:291:28: inlining call to bytealg.LastIndexByteString +./load.go:367:22: inlining call to strings.HasPrefix +./load.go:370:71: inlining call to filepath.FromSlash +./load.go:370:52: inlining call to filepath.IsAbs +./load.go:367:22: inlining call to stringslite.HasPrefix +./load.go:370:71: inlining call to filepathlite.FromSlash +./load.go:370:52: inlining call to filepathlite.IsAbs +./load.go:370:52: inlining call to stringslite.HasPrefix +./glob.go:132:34: inlining call to isAlpha +./glob.go:125:12: inlining call to isAlpha +./glob.go:116:56: inlining call to swapCase +./glob.go:116:71: inlining call to swapCase +./glob.go:116:56: inlining call to isAlpha +./glob.go:116:71: inlining call to isAlpha +./glob.go:143:20: inlining call to newStateSet +./glob.go:144:21: inlining call to newStateSet +./glob.go:157:7: inlining call to globMatch.func1 +./glob.go:160:13: inlining call to (*stateSet).reset +./glob.go:165:13: inlining call to (*stateSet).add +./glob.go:167:11: inlining call to globMatch.func1 +./glob.go:176:16: inlining call to sameByte +./glob.go:177:11: inlining call to globMatch.func1 +./glob.go:181:11: inlining call to globMatch.func1 +./glob.go:185:11: inlining call to globMatch.func1 +./glob.go:189:11: inlining call to globMatch.func1 +./glob.go:192:13: inlining call to (*stateSet).add +./glob.go:194:11: inlining call to globMatch.func1 +./glob.go:197:10: inlining call to globMatch.func1 +./glob.go:205:16: inlining call to (*stateSet).has +./glob.go:157:7: inlining call to (*stateSet).add +./glob.go:167:11: inlining call to (*stateSet).add +./glob.go:176:16: inlining call to isAlpha +./glob.go:177:11: inlining call to (*stateSet).add +./glob.go:181:11: inlining call to (*stateSet).add +./glob.go:185:11: inlining call to (*stateSet).add +./glob.go:189:11: inlining call to (*stateSet).add +./glob.go:194:11: inlining call to (*stateSet).add +./glob.go:197:10: inlining call to (*stateSet).add +./glob.go:149:14: inlining call to (*stateSet).add +./load.go:332:24: inlining call to strings.HasPrefix +./load.go:336:39: inlining call to dirPrefix +./load.go:336:29: inlining call to filepath.ToSlash +./load.go:337:24: inlining call to strings.HasPrefix +./load.go:341:48: inlining call to filepath.ToSlash +./load.go:341:31: inlining call to strings.TrimSuffix +./load.go:346:18: inlining call to isAbsPattern +./load.go:349:22: inlining call to strings.HasSuffix +./load.go:352:25: inlining call to filepath.ToSlash +./load.go:357:30: inlining call to filepath.ToSlash +./load.go:332:24: inlining call to stringslite.HasPrefix +./load.go:336:39: inlining call to strings.LastIndexByte +./load.go:336:29: inlining call to filepathlite.ToSlash +./load.go:337:24: inlining call to stringslite.HasPrefix +./load.go:341:48: inlining call to filepathlite.ToSlash +./load.go:341:31: inlining call to stringslite.TrimSuffix +./load.go:346:18: inlining call to strings.HasPrefix +./load.go:346:18: inlining call to filepath.FromSlash +./load.go:346:18: inlining call to filepath.IsAbs +./load.go:349:22: inlining call to stringslite.HasSuffix +./load.go:352:25: inlining call to filepathlite.ToSlash +./load.go:357:30: inlining call to filepathlite.ToSlash +./load.go:336:39: inlining call to bytealg.LastIndexByteString +./load.go:341:31: inlining call to stringslite.HasSuffix +./load.go:346:18: inlining call to stringslite.HasPrefix +./load.go:346:18: inlining call to filepathlite.FromSlash +./load.go:346:18: inlining call to filepathlite.IsAbs +./load.go:346:18: inlining call to stringslite.HasPrefix +./load.go:105:38: inlining call to filepath.Join +./load.go:105:24: inlining call to readSmall +./load.go:109:28: inlining call to strings.CutPrefix +./load.go:114:31: inlining call to strings.CutPrefix +./load.go:109:28: inlining call to stringslite.CutPrefix +./load.go:114:31: inlining call to stringslite.CutPrefix +./load.go:109:28: inlining call to stringslite.HasPrefix +./load.go:114:31: inlining call to stringslite.HasPrefix +./load.go:302:24: inlining call to strings.HasPrefix +./load.go:304:24: inlining call to strings.HasPrefix +./load.go:306:24: inlining call to strings.HasPrefix +./load.go:317:23: inlining call to strings.HasSuffix +./load.go:302:24: inlining call to stringslite.HasPrefix +./load.go:304:24: inlining call to stringslite.HasPrefix +./load.go:306:24: inlining call to stringslite.HasPrefix +./load.go:317:23: inlining call to stringslite.HasSuffix +./load.go:282:37: inlining call to Key.String +./load.go:282:56: inlining call to errors.New +./load.go:225:24: inlining call to (*parsed).entries +./load.go:228:21: inlining call to slices.Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }] +./load.go:257:16: inlining call to (*loader).fail +./load.go:266:20: inlining call to filepath.IsAbs +./load.go:270:19: inlining call to dirPrefix +./load.go:273:48: inlining call to fmt.Errorf +./load.go:257:16: inlining call to Key.String +./load.go:257:16: inlining call to errors.New +./load.go:266:20: inlining call to filepathlite.IsAbs +./load.go:270:19: inlining call to strings.LastIndexByte +./load.go:273:48: inlining call to errors.New +./load.go:266:20: inlining call to stringslite.HasPrefix +./load.go:270:19: inlining call to bytealg.LastIndexByteString +./load.go:54:22: inlining call to filepath.Join +./load.go:58:22: inlining call to filepath.Join +./env.go:154:42: inlining call to (*repo).localConfig +./env.go:157:68: inlining call to (*repo).localConfig +./env.go:162:46: inlining call to (*repo).worktreeConfig +./env.go:170:17: inlining call to (*loader).config +./env.go:154:42: inlining call to filepath.Join +./env.go:157:68: inlining call to filepath.Join +./env.go:162:46: inlining call to filepath.Join +./env.go:196:17: inlining call to (*loader).config +./env.go:251:23: inlining call to filepath.Clean +./env.go:221:25: inlining call to strings.HasSuffix +./env.go:222:50: inlining call to strings.TrimSuffix +./env.go:227:49: inlining call to strings.TrimSuffix +./env.go:227:25: inlining call to strings.HasPrefix +./env.go:233:36: inlining call to filepath.IsAbs +./env.go:221:25: inlining call to stringslite.HasSuffix +./env.go:222:50: inlining call to stringslite.TrimSuffix +./env.go:227:49: inlining call to stringslite.TrimSuffix +./env.go:227:25: inlining call to stringslite.HasPrefix +./env.go:233:36: inlining call to filepathlite.IsAbs +./env.go:222:50: inlining call to stringslite.HasSuffix +./env.go:227:49: inlining call to stringslite.HasSuffix +./env.go:233:36: inlining call to stringslite.HasPrefix +./load.go:64:43: inlining call to (*repo).localConfig +./load.go:69:47: inlining call to (*repo).localConfig +./load.go:71:31: inlining call to (*repo).localConfig +./load.go:73:47: inlining call to (*repo).localConfig +./load.go:75:61: inlining call to (*repo).localConfig +./load.go:75:35: inlining call to (*parsed).entries +./load.go:75:84: inlining call to (*repo).localConfig +./load.go:64:43: inlining call to filepath.Join +./load.go:69:47: inlining call to filepath.Join +./load.go:71:31: inlining call to filepath.Join +./load.go:73:47: inlining call to filepath.Join +./load.go:75:61: inlining call to filepath.Join +./load.go:75:84: inlining call to filepath.Join +./load.go:98:42: inlining call to filepath.Join +./env.go:362:23: inlining call to (*repo).localConfig +./env.go:369:26: inlining call to (*repo).worktreeConfig +./env.go:373:22: inlining call to (*repo).localConfig +./env.go:362:23: inlining call to filepath.Join +./env.go:369:26: inlining call to filepath.Join +./env.go:373:22: inlining call to filepath.Join +./env.go:300:17: inlining call to (*loader).config +./env.go:315:17: inlining call to (*loader).config +./update.go:107:27: inlining call to fs.FileMode.IsRegular +./update.go:114:27: inlining call to os.Readlink +./update.go:118:21: inlining call to filepath.IsAbs +./update.go:119:37: inlining call to filepath.Dir +./update.go:119:24: inlining call to filepath.Join +./update.go:118:21: inlining call to filepathlite.IsAbs +./update.go:118:21: inlining call to stringslite.HasPrefix +./update.go:59:16: inlining call to bytes.Equal +./update.go:72:38: inlining call to fs.FileMode.Perm +./update.go:72:23: inlining call to os.(*File).Chmod +./update.go:76:22: inlining call to os.(*File).Close +./update.go:36:14: inlining call to os.(*File).Close +./update.go:79:58: inlining call to os.Rename +./errors.go:93:18: inlining call to (*SyntaxError).Error +./errors.go:127:85: inlining call to Key.String +./errors.go:129:99: inlining call to Key.String +./file.go:56:20: inlining call to bytes.Clone +./file.go:67:20: inlining call to bytes.Clone +./file.go:80:31: inlining call to (*parsed).entries +./file.go:97:20: inlining call to (*File).entriesOf +./file.go:102:54: inlining call to (*parsed).valueOf +./file.go:105:78: inlining call to checkKey +./file.go:107:80: inlining call to checkKey +./file.go:97:20: inlining call to Key.sectionKey +./file.go:97:20: inlining call to (*parsed).secOf +./file.go:97:20: inlining call to (*parsed).nameOf +./file.go:117:20: inlining call to (*File).entriesOf +./file.go:119:81: inlining call to checkKey +./file.go:127:39: inlining call to checkKey +./file.go:117:20: inlining call to Key.sectionKey +./file.go:117:20: inlining call to (*parsed).secOf +./file.go:117:20: inlining call to (*parsed).nameOf +./file.go:137:31: inlining call to (*File).entriesOf +./file.go:138:32: inlining call to (*parsed).valueOf +./file.go:137:31: inlining call to Key.sectionKey +./file.go:137:31: inlining call to (*parsed).secOf +./file.go:137:31: inlining call to (*parsed).nameOf +./file.go:143:16: inlining call to filter +./file.go:144:18: inlining call to checkKey +./edit.go:134:9: can inline checkKey.1 with cost 231 as: func([]flatEntry, []flatEntry) bool { other := func literal; if !slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }](&slices..dict.Equal[[]github.com/git-bug/gitconfig.flatEntry,github.com/git-bug/gitconfig.flatEntry], filter(before, other), filter(after, other)) { return false }; got := filter(after, func literal); if len(got) != len(want) { return false }; for loop; return true } +./file.go:144:33: inlining call to checkKey.1 +./file.go:143:16: inlining call to (*File).Add.func1.1 +./file.go:144:33: inlining call to filter +./file.go:144:33: inlining call to filter +./file.go:144:33: inlining call to slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }] +./file.go:144:33: inlining call to filter +./file.go:143:16: inlining call to flatEntry.is +./edit.go:135:12: can inline checkKey.1.1 with cost 31 as: func(flatEntry) bool { return !flatEntry.is(e, k) } +./file.go:144:33: inlining call to checkKey.1.1 +./file.go:144:33: inlining call to checkKey.1.1 +./edit.go:139:24: can inline checkKey.1.2 with cost 30 as: func(flatEntry) bool { return flatEntry.is(e, k) } +./file.go:144:33: inlining call to checkKey.1.2 +./file.go:143:16: inlining call to Key.sectionKey +./file.go:144:33: inlining call to flatEntry.is +./file.go:144:33: inlining call to flatEntry.is +./file.go:144:33: inlining call to flatEntry.is +./file.go:144:33: inlining call to Key.sectionKey +./file.go:144:33: inlining call to Key.sectionKey +./file.go:144:33: inlining call to Key.sectionKey +./file.go:143:60: inlining call to flatEntry.is +./file.go:143:60: inlining call to Key.sectionKey +./file.go:156:20: inlining call to (*File).entriesOf +./file.go:161:52: inlining call to checkKey +./file.go:156:20: inlining call to Key.sectionKey +./file.go:156:20: inlining call to (*parsed).secOf +./file.go:156:20: inlining call to (*parsed).nameOf +./file.go:174:20: inlining call to (*File).entriesOf +./file.go:178:54: inlining call to checkKey +./file.go:174:20: inlining call to Key.sectionKey +./file.go:174:20: inlining call to (*parsed).secOf +./file.go:174:20: inlining call to (*parsed).nameOf +./file.go:188:23: inlining call to (*File).checkOpen +./file.go:191:22: inlining call to sectionArg +./file.go:194:64: inlining call to lowerASCII +./file.go:191:22: inlining call to validSection +./file.go:191:22: inlining call to isKeyChar +./file.go:191:22: inlining call to isAlpha +./file.go:201:23: inlining call to (*File).checkOpen +./file.go:204:22: inlining call to sectionArg +./file.go:207:21: inlining call to validSubsection +./file.go:210:55: inlining call to lowerASCII +./file.go:204:22: inlining call to validSection +./file.go:207:21: inlining call to strings.ContainsAny +./file.go:204:22: inlining call to isKeyChar +./file.go:204:22: inlining call to isAlpha +./file.go:221:23: inlining call to (*File).checkOpen +./file.go:224:22: inlining call to sectionArg +./file.go:227:22: inlining call to sectionArg +./file.go:230:38: inlining call to lowerASCII +./file.go:231:64: inlining call to lowerASCII +./file.go:224:22: inlining call to validSection +./file.go:227:22: inlining call to validSection +./file.go:224:22: inlining call to isKeyChar +./file.go:227:22: inlining call to isKeyChar +./file.go:224:22: inlining call to isAlpha +./file.go:227:22: inlining call to isAlpha +./file.go:241:23: inlining call to (*File).checkOpen +./file.go:244:22: inlining call to sectionArg +./file.go:248:22: inlining call to validSubsection +./file.go:252:19: inlining call to lowerASCII +./file.go:244:22: inlining call to validSection +./file.go:248:22: inlining call to strings.ContainsAny +./file.go:244:22: inlining call to isKeyChar +./file.go:244:22: inlining call to isAlpha +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:67:6: inlining call to atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).CompareAndSwap +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:64:6: inlining call to atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Swap +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:61:6: inlining call to atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Store +../../.asdf/installs/golang/1.27.1/go/src/sync/atomic/type.go:58:6: inlining call to atomic.(*Pointer[go.shape.struct { os.mu sync.Mutex; os.buf *[]uint8; os.nbuf int; os.bufp int }]).Load +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:20:6: inlining call to slices.Equal[go.shape.[]github.com/git-bug/gitconfig.flatEntry,go.shape.struct { github.com/git-bug/gitconfig.inSection bool; github.com/git-bug/gitconfig.sec github.com/git-bug/gitconfig.sectionKey; github.com/git-bug/gitconfig.name string; github.com/git-bug/gitconfig.value string; github.com/git-bug/gitconfig.implicit bool }] +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: inlining call to slices.Clone[go.shape.[]github.com/git-bug/gitconfig.Entry,go.shape.struct { Key github.com/git-bug/gitconfig.Key; Value string; Implicit bool; Origin github.com/git-bug/gitconfig.Origin }] +../../.asdf/installs/golang/1.27.1/go/src/slices/sort.go:30:6: inlining call to slices.SortFunc[go.shape.[]github.com/git-bug/gitconfig.splice,go.shape.struct { github.com/git-bug/gitconfig.start int; github.com/git-bug/gitconfig.end int; github.com/git-bug/gitconfig.text string }] +../../.asdf/installs/golang/1.27.1/go/src/slices/slices.go:355:6: inlining call to slices.Clone[go.shape.[]string,go.shape.string] diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkConfig.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkConfig.profile" new file mode 100644 index 0000000..42e7eec Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkConfig.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" new file mode 100644 index 0000000..905f9a1 Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" new file mode 100644 index 0000000..aec4854 Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" new file mode 100644 index 0000000..36f7953 Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" new file mode 100644 index 0000000..f02c378 Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkFile.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkFile.profile" new file mode 100644 index 0000000..ede89cd Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkFile.profile" differ diff --git "a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkParse.profile" "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkParse.profile" new file mode 100644 index 0000000..bc528f5 Binary files /dev/null and "b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/mem/\341\232\227\341\232\227BenchmarkParse.profile" differ diff --git a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/meta.json b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/meta.json new file mode 100644 index 0000000..e50571b --- /dev/null +++ b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/meta.json @@ -0,0 +1,25 @@ +{ + "name": "baseline", + "benchs": [ + "BenchmarkConfig", + "BenchmarkEnv/Load", + "BenchmarkEnv/LoadProtected", + "BenchmarkEnv/UpdateFile", + "BenchmarkEnv/UpdateFileUnchanged", + "BenchmarkParse", + "BenchmarkFile" + ], + "git_commit": "74c4f677f7d380f117dd4e27dccad1b7bc042184", + "machine": { + "goos": "linux", + "goarch": "amd64", + "num_cpu": 12, + "physical_cores": 6, + "threads_per_core": 2, + "cpu": "AMD Ryzen 5 3600 6-Core Processor", + "cache_l1d": 32768, + "cache_l2": 524288, + "cache_l3": 16777216 + }, + "go_version": "go1.27.1" +} diff --git a/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/results.bench b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/results.bench new file mode 100644 index 0000000..85f90cd --- /dev/null +++ b/.benchspotter/sessions/01a1208c-0b84-756b-ae17-41dad78c5d66/results.bench @@ -0,0 +1,515 @@ +goos: linux +goarch: amd64 +pkg: github.com/git-bug/gitconfig +cpu: AMD Ryzen 5 3600 6-Core Processor + +BenchmarkConfig/Get/n=10-12 14258364 84.61 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 11616332 91.39 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 14528949 79.53 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 15695257 79.17 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 14692323 81.03 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 15027236 80.15 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 15000626 80.44 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 14747677 80.75 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 14881987 78.91 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=10-12 14367001 78.17 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17897163 76.34 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17117432 72.35 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17168808 70.82 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 16738501 70.24 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17353603 70.18 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17190526 70.49 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17237251 70.51 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 16342612 72.06 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 17331231 71.01 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=10-12 16958082 69.44 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetAll/n=10-12 8302170 146 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8551143 140.9 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8366583 142.1 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8581399 141.3 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8435746 143 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8549962 141.8 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8560082 142.2 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8564126 140.8 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8098938 143.4 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=10-12 8406213 141.3 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 11655912 98.27 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12310513 98.48 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12261072 97.66 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 11990024 98.67 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12416205 97.79 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12161940 98.02 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 11422404 98.38 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12226353 97.65 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12222757 101.2 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=10-12 12205959 99.22 ns/op 160 B/op 1 allocs/op +BenchmarkConfig/Bool/n=10-12 12719163 94.15 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 12479598 91.18 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 13643412 93.72 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 12883803 95.02 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 12572098 93.63 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 12732076 91.74 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 11523427 95.25 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 12880951 93.48 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 13717155 91.76 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=10-12 12044908 93.87 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14823087 80.91 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14933941 79.41 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14306074 80.63 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 15050762 80.84 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14350953 81.81 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14687533 80.63 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14862950 80.24 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14778423 80.3 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14765944 80.9 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Get/n=1000-12 14944434 78.87 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16004414 69.83 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 17301386 70.89 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16725273 71.53 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16157006 73.71 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16342540 72.31 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16515879 72.82 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16555854 72.38 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 16302658 72.57 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 17515651 68.03 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetMissing/n=1000-12 17854065 67.84 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7403095 164.9 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7022899 158.7 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7744276 154.9 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7795984 153.4 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7884241 156.3 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7666278 156 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7638884 155 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7741814 155.8 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7483338 157.9 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/GetAll/n=1000-12 7881342 154.5 ns/op 112 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 243411 5136 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 232696 5398 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 260228 4994 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 237465 4994 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 246728 4955 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 240993 4974 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 236058 4930 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 251113 5024 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 240362 5437 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Subsections/n=1000-12 233271 5048 ns/op 16384 B/op 1 allocs/op +BenchmarkConfig/Bool/n=1000-12 13485553 95.75 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12291747 96.46 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12537336 96.21 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12286720 94.72 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12781742 95.16 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12860050 91.14 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 13554027 95.22 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12768790 91.99 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12932084 97.73 ns/op 0 B/op 0 allocs/op +BenchmarkConfig/Bool/n=1000-12 12512444 96.15 ns/op 0 B/op 0 allocs/op +BenchmarkEnv/Load-12 4587 243570 ns/op 332496 B/op 932 allocs/op +BenchmarkEnv/Load-12 5019 242712 ns/op 332491 B/op 932 allocs/op +BenchmarkEnv/Load-12 4894 246259 ns/op 332490 B/op 932 allocs/op +BenchmarkEnv/Load-12 4629 248029 ns/op 332490 B/op 932 allocs/op +BenchmarkEnv/Load-12 5098 246052 ns/op 332491 B/op 932 allocs/op +BenchmarkEnv/Load-12 4822 247390 ns/op 332491 B/op 932 allocs/op +BenchmarkEnv/Load-12 4803 245124 ns/op 332491 B/op 932 allocs/op +BenchmarkEnv/Load-12 4958 245544 ns/op 332490 B/op 932 allocs/op +BenchmarkEnv/Load-12 4904 254155 ns/op 332491 B/op 932 allocs/op +BenchmarkEnv/Load-12 4854 260887 ns/op 332491 B/op 932 allocs/op +BenchmarkEnv/LoadProtected-12 16602 70730 ns/op 62025 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 17863 66326 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18573 65110 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18350 65565 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18253 65437 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18346 65797 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18129 65922 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18188 65878 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18511 65015 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18404 65223 ns/op 62024 B/op 219 allocs/op +BenchmarkEnv/UpdateFile-12 4302 279626 ns/op 460670 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4226 280489 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4446 281411 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4358 281226 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4174 286693 ns/op 460667 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4124 283315 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4100 286072 ns/op 460667 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4066 281985 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4084 284529 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4342 283372 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 112376 ns/op 136546 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 113978 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 9598 112873 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 113880 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 112611 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 111374 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 111106 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 9643 113570 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 114057 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 113367 ns/op 136544 B/op 480 allocs/op +BenchmarkParse/realistic/1KiB-12 90344 13098 ns/op 87.42 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 91362 12899 ns/op 88.77 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 92511 13077 ns/op 87.56 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 90705 13067 ns/op 87.63 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 88819 13053 ns/op 87.72 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 91244 13200 ns/op 86.75 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 93631 13036 ns/op 87.83 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 90574 13102 ns/op 87.39 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 91056 13063 ns/op 87.65 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 89734 13133 ns/op 87.19 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/64KiB-12 1885 620033 ns/op 106 MB/s 1.403045e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1868 630203 ns/op 104.29 MB/s 1.403045e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1975 620008 ns/op 106 MB/s 1.403046e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1977 635108 ns/op 103.48 MB/s 1.403045e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1732 647975 ns/op 101.43 MB/s 1.403046e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1958 641927 ns/op 102.38 MB/s 1.403045e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1857 648946 ns/op 101.28 MB/s 1.403048e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1796 665087 ns/op 98.82 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1852 666264 ns/op 98.64 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1911 640820 ns/op 102.56 MB/s 1.403045e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/1024KiB-12 111 1.0735175e+07 ns/op 97.68 MB/s 2.3841443e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 99 1.080651e+07 ns/op 97.03 MB/s 2.3841441e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 111 1.0627264e+07 ns/op 98.67 MB/s 2.3841498e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 110 1.1035223e+07 ns/op 95.02 MB/s 2.3841446e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 103 1.1453782e+07 ns/op 91.55 MB/s 2.3841449e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 96 1.0607565e+07 ns/op 98.85 MB/s 2.3841443e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 94 1.0842011e+07 ns/op 96.72 MB/s 2.3841443e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 98 1.0670711e+07 ns/op 98.27 MB/s 2.3841443e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 93 1.0834271e+07 ns/op 96.78 MB/s 2.3841442e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 109 1.0916785e+07 ns/op 96.05 MB/s 2.3841447e+07 B/op 59583 allocs/op +BenchmarkParse/one_long_value/1KiB-12 208312 5639 ns/op 181.77 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 216039 5557 ns/op 184.44 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 222303 5588 ns/op 183.44 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 211384 5683 ns/op 180.35 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 202292 5687 ns/op 180.22 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 204313 5485 ns/op 186.88 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 219504 5576 ns/op 183.81 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 217599 5576 ns/op 183.83 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 220388 5596 ns/op 183.16 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 212626 5622 ns/op 182.33 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4424 270803 ns/op 242.01 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4549 272614 ns/op 240.4 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4503 268264 ns/op 244.3 MB/s 426121 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4387 290383 ns/op 225.69 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4417 270492 ns/op 242.29 MB/s 426121 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4287 271129 ns/op 241.72 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4450 285618 ns/op 229.46 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4231 269120 ns/op 243.52 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4388 273997 ns/op 239.19 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4498 268827 ns/op 243.79 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 202 5.736891e+06 ns/op 182.78 MB/s 7.348433e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 218 5.408119e+06 ns/op 193.89 MB/s 7.348433e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 212 5.631481e+06 ns/op 186.2 MB/s 7.348432e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 212 5.716579e+06 ns/op 183.43 MB/s 7.348428e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 212 5.541765e+06 ns/op 189.21 MB/s 7.348437e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 213 5.616398e+06 ns/op 186.7 MB/s 7.348419e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 211 5.637667e+06 ns/op 185.99 MB/s 7.348424e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 210 5.598808e+06 ns/op 187.29 MB/s 7.348419e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 222 5.492982e+06 ns/op 190.89 MB/s 7.348427e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 218 5.575146e+06 ns/op 188.08 MB/s 7.348417e+06 B/op 44 allocs/op +BenchmarkParse/continuations/1KiB-12 154375 7904 ns/op 129.8 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 137611 7949 ns/op 129.07 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 151771 7724 ns/op 132.83 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 147100 8147 ns/op 125.94 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 151825 8182 ns/op 125.4 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 159620 7082 ns/op 144.87 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 177375 6979 ns/op 147.01 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 169858 7041 ns/op 145.71 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 167949 7040 ns/op 145.74 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 169417 7412 ns/op 138.41 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/64KiB-12 3145 446197 ns/op 146.88 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 2827 443114 ns/op 147.9 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 2652 428170 ns/op 153.07 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3066 415606 ns/op 157.69 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3049 418954 ns/op 156.43 MB/s 1.580874e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3372 426723 ns/op 153.58 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3306 421887 ns/op 155.35 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 2436 437463 ns/op 149.81 MB/s 1.580876e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 2803 419370 ns/op 156.28 MB/s 1.580875e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 2821 441901 ns/op 148.31 MB/s 1.580876e+06 B/op 31 allocs/op +BenchmarkParse/continuations/1024KiB-12 220 5.294937e+06 ns/op 198.03 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 225 5.241623e+06 ns/op 200.05 MB/s 2.5100112e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 224 5.307707e+06 ns/op 197.56 MB/s 2.5100112e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 229 5.27292e+06 ns/op 198.86 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 228 5.231762e+06 ns/op 200.43 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 224 5.274249e+06 ns/op 198.81 MB/s 2.5100112e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 220 5.302059e+06 ns/op 197.77 MB/s 2.5100113e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 229 5.282274e+06 ns/op 198.51 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 226 5.27206e+06 ns/op 198.89 MB/s 2.5100113e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 224 5.280571e+06 ns/op 198.57 MB/s 2.5100116e+07 B/op 41 allocs/op +BenchmarkParse/quotes/1KiB-12 240855 4747 ns/op 216.15 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 253161 4747 ns/op 216.12 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 250918 4689 ns/op 218.79 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 248462 4766 ns/op 215.29 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 234488 4796 ns/op 213.91 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 244756 4739 ns/op 216.5 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 246656 4902 ns/op 209.32 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 234475 5024 ns/op 204.23 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 244708 4951 ns/op 207.22 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 244052 4953 ns/op 207.17 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/64KiB-12 5151 238317 ns/op 275 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5024 231617 ns/op 282.96 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5220 229357 ns/op 285.75 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5389 234313 ns/op 279.7 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 4928 236522 ns/op 277.09 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5232 231366 ns/op 283.27 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 4923 235167 ns/op 278.69 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5342 230393 ns/op 284.46 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5144 231818 ns/op 282.71 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5340 235776 ns/op 277.97 MB/s 262280 B/op 31 allocs/op +BenchmarkParse/quotes/1024KiB-12 340 3.632466e+06 ns/op 288.67 MB/s 4.120727e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 319 3.704436e+06 ns/op 283.06 MB/s 4.120727e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 336 3.590574e+06 ns/op 292.04 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 294 3.89878e+06 ns/op 268.95 MB/s 4.120731e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 328 3.842416e+06 ns/op 272.9 MB/s 4.120731e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 312 3.770604e+06 ns/op 278.09 MB/s 4.120727e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 321 3.662682e+06 ns/op 286.29 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 346 3.581917e+06 ns/op 292.74 MB/s 4.120727e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 300 4.104923e+06 ns/op 255.44 MB/s 4.120725e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 295 3.807931e+06 ns/op 275.37 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1KiB-12 325599 3586 ns/op 287.24 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 336165 3502 ns/op 294.14 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 300940 3510 ns/op 293.48 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 334930 3601 ns/op 286.02 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 325407 3520 ns/op 292.58 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 335010 3501 ns/op 294.22 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 309054 3565 ns/op 288.94 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 358725 3495 ns/op 294.68 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 328683 3556 ns/op 289.68 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 329809 3545 ns/op 290.52 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/64KiB-12 7090 162697 ns/op 402.85 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7256 161692 ns/op 405.35 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7370 161366 ns/op 406.17 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7095 163451 ns/op 400.99 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7468 171028 ns/op 383.22 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7407 160669 ns/op 407.93 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 6834 167212 ns/op 391.97 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7346 163531 ns/op 400.79 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7309 161794 ns/op 405.1 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7324 162580 ns/op 403.14 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/1024KiB-12 477 2.568169e+06 ns/op 408.3 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 492 2.493425e+06 ns/op 420.54 MB/s 4.120726e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 477 2.571703e+06 ns/op 407.74 MB/s 4.120726e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 475 2.442108e+06 ns/op 429.38 MB/s 4.120725e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 477 2.516388e+06 ns/op 416.7 MB/s 4.120729e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 446 2.537743e+06 ns/op 413.19 MB/s 4.120726e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 456 2.486657e+06 ns/op 421.68 MB/s 4.120727e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 438 2.733399e+06 ns/op 383.62 MB/s 4.120729e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 458 2.614714e+06 ns/op 401.03 MB/s 4.120726e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 492 2.77589e+06 ns/op 377.75 MB/s 4.120731e+06 B/op 41 allocs/op +BenchmarkParse/same_key/1KiB-12 68368 17704 ns/op 57.95 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 64387 18874 ns/op 54.36 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 67264 17767 ns/op 57.75 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 66754 18011 ns/op 56.96 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 65496 18357 ns/op 55.89 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 66531 17979 ns/op 57.07 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 68023 17767 ns/op 57.75 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 66466 18162 ns/op 56.49 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 61884 19209 ns/op 53.41 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 68253 17851 ns/op 57.47 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/64KiB-12 712 1.623269e+06 ns/op 40.37 MB/s 4.445863e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 693 1.713606e+06 ns/op 38.25 MB/s 4.445864e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 747 1.65773e+06 ns/op 39.53 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 704 1.673912e+06 ns/op 39.15 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 739 1.70773e+06 ns/op 38.38 MB/s 4.445864e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 757 1.656674e+06 ns/op 39.56 MB/s 4.445863e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 721 1.680749e+06 ns/op 38.99 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 763 1.639936e+06 ns/op 39.96 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 718 1.643463e+06 ns/op 39.88 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 710 1.739015e+06 ns/op 37.69 MB/s 4.445866e+06 B/op 31 allocs/op +BenchmarkParse/same_key/1024KiB-12 46 2.5490713e+07 ns/op 41.14 MB/s 7.4962713e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 45 2.5928325e+07 ns/op 40.44 MB/s 7.4962594e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 46 2.551367e+07 ns/op 41.1 MB/s 7.4962596e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 43 2.7723388e+07 ns/op 37.82 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 42 2.8073295e+07 ns/op 37.35 MB/s 7.4962594e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 38 2.7600063e+07 ns/op 37.99 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 36 2.7890114e+07 ns/op 37.6 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 40 2.9165348e+07 ns/op 35.95 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 40 2.8395829e+07 ns/op 36.93 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 44 2.6767205e+07 ns/op 39.17 MB/s 7.4962718e+07 B/op 43 allocs/op +BenchmarkParse/headers/1KiB-12 111475 10751 ns/op 95.25 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 110578 10660 ns/op 96.06 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 112911 10723 ns/op 95.49 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 112904 10753 ns/op 95.23 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 112624 10778 ns/op 95.01 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 111030 10799 ns/op 94.83 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 113292 10631 ns/op 96.32 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 112798 10790 ns/op 94.9 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 111158 10725 ns/op 95.48 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 113005 10687 ns/op 95.81 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1741 641781 ns/op 102.12 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1796 640829 ns/op 102.27 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1833 641820 ns/op 102.11 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1852 639703 ns/op 102.45 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1844 636648 ns/op 102.94 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1894 647593 ns/op 101.2 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1867 633572 ns/op 103.44 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1867 636784 ns/op 102.92 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1941 638279 ns/op 102.68 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1834 634835 ns/op 103.23 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 120 9.840621e+06 ns/op 106.56 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 121 9.854336e+06 ns/op 106.41 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 122 9.71464e+06 ns/op 107.94 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 122 9.76116e+06 ns/op 107.42 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 121 9.780622e+06 ns/op 107.21 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 120 9.859726e+06 ns/op 106.35 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 123 9.75166e+06 ns/op 107.53 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 121 9.846607e+06 ns/op 106.49 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 123 9.635251e+06 ns/op 108.83 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 122 9.688004e+06 ns/op 108.23 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/long_subsection/1KiB-12 519026 2026 ns/op 506.82 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 572042 2042 ns/op 502.9 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 595951 2087 ns/op 491.98 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 580828 2049 ns/op 501.34 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 618507 2046 ns/op 501.84 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 580020 2148 ns/op 478.03 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 582886 2062 ns/op 498.07 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 547491 2037 ns/op 504.16 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 591332 2016 ns/op 509.42 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 547256 2070 ns/op 496.08 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12032 99299 ns/op 660.02 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12170 98395 ns/op 666.08 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12337 96487 ns/op 679.25 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12370 97164 ns/op 674.52 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12255 97493 ns/op 672.24 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12483 96815 ns/op 676.95 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12420 97012 ns/op 675.57 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12183 98665 ns/op 664.26 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12513 96224 ns/op 681.11 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12544 96060 ns/op 682.27 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 637 1.885907e+06 ns/op 556.01 MB/s 4.120278e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 619 1.914979e+06 ns/op 547.57 MB/s 4.120277e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 606 1.895725e+06 ns/op 553.13 MB/s 4.120277e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 658 1.89123e+06 ns/op 554.44 MB/s 4.120279e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 633 1.905776e+06 ns/op 550.21 MB/s 4.12028e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 616 1.929449e+06 ns/op 543.46 MB/s 4.120279e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 648 1.880385e+06 ns/op 557.64 MB/s 4.120276e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 645 1.904431e+06 ns/op 550.6 MB/s 4.120281e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 634 1.908513e+06 ns/op 549.42 MB/s 4.120277e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 596 1.908037e+06 ns/op 549.56 MB/s 4.120276e+06 B/op 36 allocs/op +BenchmarkParse/comments/1KiB-12 390549 3017 ns/op 340.43 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 405556 2971 ns/op 345.65 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 404995 3015 ns/op 340.58 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 395350 3029 ns/op 339.1 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 392308 3057 ns/op 335.94 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 386764 3042 ns/op 337.57 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 401968 3033 ns/op 338.65 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 348346 3022 ns/op 339.87 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 393630 3027 ns/op 339.29 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 406990 3062 ns/op 335.42 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 6589 158810 ns/op 412.73 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7358 164238 ns/op 399.09 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7141 160829 ns/op 407.55 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7294 158015 ns/op 414.81 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7669 157771 ns/op 415.45 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7027 159721 ns/op 410.38 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7195 162734 ns/op 402.78 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 6668 160569 ns/op 408.21 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7077 160660 ns/op 407.98 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7322 163013 ns/op 402.09 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 500 2.402606e+06 ns/op 436.43 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 496 2.360589e+06 ns/op 444.2 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 512 2.337546e+06 ns/op 448.58 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 508 2.331435e+06 ns/op 449.76 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 501 2.393732e+06 ns/op 438.05 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 507 2.349216e+06 ns/op 446.35 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 499 2.356872e+06 ns/op 444.9 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 496 2.360781e+06 ns/op 444.17 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 505 2.341432e+06 ns/op 447.84 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 507 2.352952e+06 ns/op 445.64 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkFile/Set/n=10-12 32388 36874 ns/op 79156 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 34860 34576 ns/op 79156 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 31663 36448 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 32635 35288 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 35481 33951 ns/op 79156 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 35470 34372 ns/op 79156 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 34107 34743 ns/op 79156 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 34537 34198 ns/op 79156 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 33093 34686 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 35330 34200 ns/op 79156 B/op 161 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1777142 672 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1836876 649.5 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1785456 674.4 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1784798 673 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1765108 679.3 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1772678 675.4 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1765484 675.8 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1776146 674.5 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1762842 678.2 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1763796 678.2 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22210 53586 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22615 52871 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22689 52944 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23026 53225 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22468 53241 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22190 53655 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22042 53898 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22264 53898 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22448 53929 ns/op 112073 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 20338 58390 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 21932 53985 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22218 54146 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22770 52957 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 23065 52758 ns/op 110457 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 23254 52308 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22642 52538 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22845 53523 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 23142 52208 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22696 52526 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22670 53289 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/Bytes/n=10-12 4063782 299.9 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3894992 299.4 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4059357 296.6 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4062432 297.7 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3986314 303.2 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4088090 305.8 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3747225 310.9 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4041183 303.3 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3936990 295.9 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4056452 304.3 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Set/n=1000-12 270 4.665043e+06 ns/op 9.080753e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 264 4.600826e+06 ns/op 9.080755e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 249 4.663659e+06 ns/op 9.08075e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 262 4.560795e+06 ns/op 9.080753e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 261 4.545252e+06 ns/op 9.080751e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 255 4.585769e+06 ns/op 9.080751e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 265 4.528743e+06 ns/op 9.08076e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 260 4.55837e+06 ns/op 9.080754e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 254 4.733236e+06 ns/op 9.080751e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 268 4.517181e+06 ns/op 9.080751e+06 B/op 8158 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24054 50062 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23992 49860 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24243 49966 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24116 50252 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24015 49049 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23538 50949 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24710 47454 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24528 49760 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24070 50063 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24132 49845 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 165 7.069379e+06 ns/op 1.2362811e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 171 6.921937e+06 ns/op 1.2362807e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 169 7.026478e+06 ns/op 1.2362812e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 170 6.985809e+06 ns/op 1.2362812e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 172 6.945601e+06 ns/op 1.2362793e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 175 6.895789e+06 ns/op 1.2362807e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 171 6.970291e+06 ns/op 1.2362805e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 172 6.93129e+06 ns/op 1.2362808e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 174 6.858132e+06 ns/op 1.2362807e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 165 7.168207e+06 ns/op 1.2362814e+07 B/op 16254 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 163 7.088205e+06 ns/op 1.292619e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 169 7.026903e+06 ns/op 1.2926194e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 172 7.00595e+06 ns/op 1.2926191e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 168 6.915877e+06 ns/op 1.2926181e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 171 6.807039e+06 ns/op 1.2926174e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 175 7.013696e+06 ns/op 1.292619e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 165 7.023768e+06 ns/op 1.2926192e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 169 6.876612e+06 ns/op 1.2926181e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 166 7.025686e+06 ns/op 1.2926188e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 166 7.155696e+06 ns/op 1.2926196e+07 B/op 12280 allocs/op +BenchmarkFile/Bytes/n=1000-12 42536 28533 ns/op 155649 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 39154 30934 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 38112 31965 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 37158 32409 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 38313 32188 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 41460 30948 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 36631 32433 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 40725 31776 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 37750 32391 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 38304 31891 ns/op 155648 B/op 1 allocs/op diff --git "a/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/cpu/\341\232\227\341\232\227BenchmarkProbe.profile" "b/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/cpu/\341\232\227\341\232\227BenchmarkProbe.profile" new file mode 100644 index 0000000..8b52abf Binary files /dev/null and "b/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/cpu/\341\232\227\341\232\227BenchmarkProbe.profile" differ diff --git a/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/meta.json b/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/meta.json new file mode 100644 index 0000000..3dc61e1 --- /dev/null +++ b/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/meta.json @@ -0,0 +1,19 @@ +{ + "name": "probes", + "benchs": [ + "BenchmarkProbe" + ], + "git_commit": "74c4f677f7d380f117dd4e27dccad1b7bc042184", + "machine": { + "goos": "linux", + "goarch": "amd64", + "num_cpu": 12, + "physical_cores": 6, + "threads_per_core": 2, + "cpu": "AMD Ryzen 5 3600 6-Core Processor", + "cache_l1d": 32768, + "cache_l2": 524288, + "cache_l3": 16777216 + }, + "go_version": "go1.27.1" +} diff --git a/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/results.bench b/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/results.bench new file mode 100644 index 0000000..a4c0219 --- /dev/null +++ b/.benchspotter/sessions/01a12097-6c1b-7531-9b08-41118f50760f/results.bench @@ -0,0 +1,59 @@ +goos: linux +goarch: amd64 +pkg: github.com/git-bug/gitconfig +cpu: AMD Ryzen 5 3600 6-Core Processor + +BenchmarkProbe/UnsetAllSections/n=256-12 1214 986139 ns/op 222858 B/op 52 allocs/op +BenchmarkProbe/UnsetAllSections/n=256-12 1204 990883 ns/op 222849 B/op 52 allocs/op +BenchmarkProbe/UnsetAllSections/n=256-12 1213 1.010632e+06 ns/op 222849 B/op 52 allocs/op +BenchmarkProbe/UnsetAllFlat/n=256-12 2242 526884 ns/op 168832 B/op 52 allocs/op +BenchmarkProbe/UnsetAllFlat/n=256-12 2242 518990 ns/op 168832 B/op 52 allocs/op +BenchmarkProbe/UnsetAllFlat/n=256-12 2272 523023 ns/op 168832 B/op 52 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=256-12 19441 61817 ns/op 160481 B/op 57 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=256-12 19671 60646 ns/op 160480 B/op 57 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=256-12 19303 61461 ns/op 160480 B/op 57 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=256-12 17032 71112 ns/op 168393 B/op 303 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=256-12 16491 72511 ns/op 168393 B/op 303 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=256-12 16771 71676 ns/op 168393 B/op 303 allocs/op +BenchmarkProbe/RemoveSectionSections/n=256-12 15346 78806 ns/op 213305 B/op 48 allocs/op +BenchmarkProbe/RemoveSectionSections/n=256-12 15358 78362 ns/op 213304 B/op 48 allocs/op +BenchmarkProbe/RemoveSectionSections/n=256-12 15223 78671 ns/op 213304 B/op 48 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=256-12 738 1.632159e+06 ns/op 684595 B/op 5954 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=256-12 724 1.64687e+06 ns/op 684595 B/op 5954 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=256-12 730 1.642532e+06 ns/op 684594 B/op 5954 allocs/op +BenchmarkProbe/UnsetAllSections/n=1024-12 76 1.573481e+07 ns/op 1.014829e+06 B/op 66 allocs/op +BenchmarkProbe/UnsetAllSections/n=1024-12 75 1.5914246e+07 ns/op 1.01483e+06 B/op 66 allocs/op +BenchmarkProbe/UnsetAllSections/n=1024-12 76 1.5651015e+07 ns/op 1.01483e+06 B/op 66 allocs/op +BenchmarkProbe/UnsetAllFlat/n=1024-12 140 8.511731e+06 ns/op 982063 B/op 67 allocs/op +BenchmarkProbe/UnsetAllFlat/n=1024-12 141 8.474946e+06 ns/op 982062 B/op 67 allocs/op +BenchmarkProbe/UnsetAllFlat/n=1024-12 138 8.596572e+06 ns/op 982061 B/op 67 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=1024-12 4036 288188 ns/op 946276 B/op 70 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=1024-12 4200 287403 ns/op 946275 B/op 70 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=1024-12 4214 290750 ns/op 946276 B/op 70 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=1024-12 2360 505646 ns/op 1.982294e+06 B/op 1079 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=1024-12 2403 486532 ns/op 1.982296e+06 B/op 1079 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=1024-12 2577 492874 ns/op 1.982295e+06 B/op 1079 allocs/op +BenchmarkProbe/RemoveSectionSections/n=1024-12 3654 340132 ns/op 977854 B/op 60 allocs/op +BenchmarkProbe/RemoveSectionSections/n=1024-12 3583 348576 ns/op 977853 B/op 60 allocs/op +BenchmarkProbe/RemoveSectionSections/n=1024-12 3486 345554 ns/op 977854 B/op 60 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=1024-12 180 6.586774e+06 ns/op 2.81671e+06 B/op 23631 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=1024-12 183 6.480406e+06 ns/op 2.816709e+06 B/op 23631 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=1024-12 183 6.524314e+06 ns/op 2.816715e+06 B/op 23631 allocs/op +BenchmarkProbe/UnsetAllSections/n=4096-12 4 2.86652704e+08 ns/op 4.161488e+06 B/op 92 allocs/op +BenchmarkProbe/UnsetAllSections/n=4096-12 4 2.96065105e+08 ns/op 4.161488e+06 B/op 92 allocs/op +BenchmarkProbe/UnsetAllSections/n=4096-12 4 2.90395055e+08 ns/op 4.161544e+06 B/op 93 allocs/op +BenchmarkProbe/UnsetAllFlat/n=4096-12 8 1.39637449e+08 ns/op 3.92391e+06 B/op 93 allocs/op +BenchmarkProbe/UnsetAllFlat/n=4096-12 8 1.3865881e+08 ns/op 3.923896e+06 B/op 93 allocs/op +BenchmarkProbe/UnsetAllFlat/n=4096-12 8 1.38232921e+08 ns/op 3.923938e+06 B/op 93 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=4096-12 694 1.74319e+06 ns/op 3.777282e+06 B/op 84 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=4096-12 596 1.923503e+06 ns/op 3.777278e+06 B/op 84 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=4096-12 637 1.852339e+06 ns/op 3.777276e+06 B/op 84 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=4096-12 230 5.192618e+06 ns/op 2.8214256e+07 B/op 4164 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=4096-12 230 5.140154e+06 ns/op 2.8214258e+07 B/op 4164 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=4096-12 230 5.269271e+06 ns/op 2.8214286e+07 B/op 4164 allocs/op +BenchmarkProbe/RemoveSectionSections/n=4096-12 525 2.310713e+06 ns/op 4.01364e+06 B/op 74 allocs/op +BenchmarkProbe/RemoveSectionSections/n=4096-12 546 2.299224e+06 ns/op 4.013644e+06 B/op 74 allocs/op +BenchmarkProbe/RemoveSectionSections/n=4096-12 538 2.278819e+06 ns/op 4.013644e+06 B/op 74 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=4096-12 42 2.7449247e+07 ns/op 1.1423055e+07 B/op 94301 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=4096-12 42 2.7776282e+07 ns/op 1.1423061e+07 B/op 94301 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=4096-12 43 2.7625295e+07 ns/op 1.1423066e+07 B/op 94301 allocs/op diff --git "a/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/cpu/\341\232\227\341\232\227BenchmarkProbe.profile" "b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/cpu/\341\232\227\341\232\227BenchmarkProbe.profile" new file mode 100644 index 0000000..5e525cf Binary files /dev/null and "b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/cpu/\341\232\227\341\232\227BenchmarkProbe.profile" differ diff --git a/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/git.diff b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/git.diff new file mode 100644 index 0000000..371e4b1 --- /dev/null +++ b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/git.diff @@ -0,0 +1,144 @@ +diff --git a/edit.go b/edit.go +index 75c1521..3862ece 100644 +--- a/edit.go ++++ b/edit.go +@@ -311,10 +311,6 @@ func (f *File) entryLineEnd(i int) int { + // after the previous header or a BOM (the rest of its line becomes a + // newline), and ends at the next header, its indentation included. + func (f *File) unsetEntries(idx []int) []splice { +- removed := make(map[int]bool, len(idx)) +- for _, i := range idx { +- removed[i] = true +- } + // Comments at the end of a removed entry's line play no part. + trailing := make(map[int]bool) + for _, i := range idx { +@@ -324,10 +320,21 @@ func (f *File) unsetEntries(idx []int) []splice { + } + } + } ++ // comments[j] counts the comments of items[:j] that play a part. ++ comments := make([]int, len(f.items)+1) ++ for j, it := range f.items { ++ comments[j+1] = comments[j] ++ if it.kind == itemComment && !trailing[j] { ++ comments[j+1]++ ++ } ++ } + +- var splices []splice +- for _, i := range idx { +- splices = append(splices, f.unsetEntry(i, removed, trailing)) ++ // From the last entry back, so that a forward scan reaching a removed ++ // entry can stop where that entry's own scan did. ++ forward := make(map[int]int, len(idx)) ++ splices := make([]splice, len(idx)) ++ for n := len(idx) - 1; n >= 0; n-- { ++ splices[n] = f.unsetEntry(idx[n], forward, comments) + } + + // Merge the regions that overlap: a region may hold other entries. +@@ -343,7 +350,11 @@ func (f *File) unsetEntries(idx []int) []splice { + return merged + } + +-func (f *File) unsetEntry(i int, removed, trailing map[int]bool) splice { ++// unsetEntry returns the splice that removes the entry at i. forward holds ++// where the forward scan of each removed entry after i stopped; the scan of ++// i is added to it. comments counts the comments that play a part, as ++// unsetEntries computes it. ++func (f *File) unsetEntry(i int, forward map[int]int, comments []int) splice { + sec := f.items[i].sec + sameHeader := func(it item) bool { return it.kind == itemHeader && f.secOf(it) == f.secs[sec] } + +@@ -360,11 +371,17 @@ func (f *File) unsetEntry(i int, removed, trailing map[int]bool) splice { + } + fw := i + 1 + for ; fw < len(f.items); fw++ { ++ if end, ok := forward[fw]; ok { ++ // A removed entry: the scan goes on as its own did. ++ fw = end ++ break ++ } + it := f.items[fw] +- if it.kind == itemEntry && !removed[fw] || it.kind == itemHeader && !sameHeader(it) { ++ if it.kind == itemEntry || it.kind == itemHeader && !sameHeader(it) { + break + } + } ++ forward[i] = fw + if !headers || fw < len(f.items) && f.items[fw].kind == itemEntry { + return f.removeEntry(i) + } +@@ -385,10 +402,10 @@ func (f *File) unsetEntry(i int, removed, trailing map[int]bool) splice { + if fw < len(f.items) { + end = f.items[fw].start + } +- for j, it := range f.items { +- if it.kind == itemComment && it.start >= start && it.end <= end && !trailing[j] { +- return f.removeEntry(i) +- } ++ // The region is items[from:fw]: start and end are item boundaries. ++ from, _ := slices.BinarySearchFunc(f.items, start, func(it item, start int) int { return it.start - start }) ++ if comments[fw]-comments[from] > 0 { ++ return f.removeEntry(i) + } + return splice{start, end, text} + } +@@ -505,18 +522,19 @@ func (f *File) renameSection(name string, sk, to sectionKey, header string) erro + if n := f.newlineAfter(h); n >= 0 { + end = n + 1 + } +- text := header + "\n" ++ var text strings.Builder ++ text.WriteString(header + "\n") + if rest < end && f.items[rest].kind != itemNewline { +- text += "\t" ++ text.WriteByte('\t') + for _, it := range f.items[rest:end] { + if it.kind == itemHeader && f.secOf(it) == sk { +- text += header ++ text.WriteString(header) + } else { +- text += string(f.data[it.start:it.end]) ++ text.Write(f.data[it.start:it.end]) + } + } + } +- splices = append(splices, splice{f.ownStart(h), f.lineEnd(h), text}) ++ splices = append(splices, splice{f.ownStart(h), f.lineEnd(h), text.String()}) + done = end - 1 + } + return f.apply(name, splices, func(before, after []flatEntry) bool { +diff --git a/load.go b/load.go +index ac7a002..0d2b65e 100644 +--- a/load.go ++++ b/load.go +@@ -175,6 +175,9 @@ type loader struct { + + branch string + branchRead bool ++ ++ realGitDir string // with '/', "" if it cannot be resolved ++ realDirRead bool + } + + func (e Env) newLoader() (*loader, error) { +@@ -353,10 +356,14 @@ func (l *loader) gitdir(pattern, from string, fold bool) (bool, error) { + if globMatch(pattern, dir, fold) { + return true, nil + } +- if real, err := filepath.EvalSymlinks(l.repo.gitDir); err == nil { +- if real := filepath.ToSlash(real); real != dir { +- return globMatch(pattern, real, fold), nil ++ if !l.realDirRead { ++ if real, err := filepath.EvalSymlinks(l.repo.gitDir); err == nil { ++ l.realGitDir = filepath.ToSlash(real) + } ++ l.realDirRead = true ++ } ++ if l.realGitDir != "" && l.realGitDir != dir { ++ return globMatch(pattern, l.realGitDir, fold), nil + } + return false, nil + } diff --git a/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/meta.json b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/meta.json new file mode 100644 index 0000000..e6fec94 --- /dev/null +++ b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/meta.json @@ -0,0 +1,19 @@ +{ + "name": "probes-fixed", + "benchs": [ + "BenchmarkProbe" + ], + "git_commit": "74c4f677f7d380f117dd4e27dccad1b7bc042184", + "machine": { + "goos": "linux", + "goarch": "amd64", + "num_cpu": 12, + "physical_cores": 6, + "threads_per_core": 2, + "cpu": "AMD Ryzen 5 3600 6-Core Processor", + "cache_l1d": 32768, + "cache_l2": 524288, + "cache_l3": 16777216 + }, + "go_version": "go1.27.1" +} diff --git a/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/results.bench b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/results.bench new file mode 100644 index 0000000..14bc489 --- /dev/null +++ b/.benchspotter/sessions/01a120a2-f2e8-7ce8-a8ac-b6a8bebf1cbb/results.bench @@ -0,0 +1,59 @@ +goos: linux +goarch: amd64 +pkg: github.com/git-bug/gitconfig +cpu: AMD Ryzen 5 3600 6-Core Processor + +BenchmarkProbe/UnsetAllSections/n=256-12 8848 126797 ns/op 224195 B/op 46 allocs/op +BenchmarkProbe/UnsetAllSections/n=256-12 9118 128210 ns/op 224192 B/op 46 allocs/op +BenchmarkProbe/UnsetAllSections/n=256-12 9129 128586 ns/op 224192 B/op 46 allocs/op +BenchmarkProbe/UnsetAllFlat/n=256-12 15631 76657 ns/op 165824 B/op 46 allocs/op +BenchmarkProbe/UnsetAllFlat/n=256-12 15644 77720 ns/op 165824 B/op 46 allocs/op +BenchmarkProbe/UnsetAllFlat/n=256-12 15744 75660 ns/op 165824 B/op 46 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=256-12 19417 62517 ns/op 160480 B/op 57 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=256-12 19240 61979 ns/op 160480 B/op 57 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=256-12 19669 61454 ns/op 160480 B/op 57 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=256-12 24957 46795 ns/op 63696 B/op 50 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=256-12 25459 46907 ns/op 63696 B/op 50 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=256-12 26306 45686 ns/op 63696 B/op 50 allocs/op +BenchmarkProbe/RemoveSectionSections/n=256-12 15030 80256 ns/op 213304 B/op 48 allocs/op +BenchmarkProbe/RemoveSectionSections/n=256-12 14792 80997 ns/op 213304 B/op 48 allocs/op +BenchmarkProbe/RemoveSectionSections/n=256-12 15039 80380 ns/op 213304 B/op 48 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=256-12 3999 292125 ns/op 470418 B/op 3149 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=256-12 4120 291655 ns/op 470418 B/op 3149 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=256-12 4045 299015 ns/op 470417 B/op 3149 allocs/op +BenchmarkProbe/UnsetAllSections/n=1024-12 2222 533993 ns/op 977389 B/op 57 allocs/op +BenchmarkProbe/UnsetAllSections/n=1024-12 2323 541567 ns/op 977390 B/op 57 allocs/op +BenchmarkProbe/UnsetAllSections/n=1024-12 2140 561709 ns/op 977391 B/op 57 allocs/op +BenchmarkProbe/UnsetAllFlat/n=1024-12 3334 341217 ns/op 922733 B/op 58 allocs/op +BenchmarkProbe/UnsetAllFlat/n=1024-12 3610 342554 ns/op 922732 B/op 58 allocs/op +BenchmarkProbe/UnsetAllFlat/n=1024-12 3540 346555 ns/op 922733 B/op 58 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=1024-12 4158 307842 ns/op 946276 B/op 70 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=1024-12 4003 318898 ns/op 946276 B/op 70 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=1024-12 4124 309992 ns/op 946278 B/op 70 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=1024-12 6123 179341 ns/op 306129 B/op 62 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=1024-12 6516 179272 ns/op 306129 B/op 62 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=1024-12 6736 179813 ns/op 306129 B/op 62 allocs/op +BenchmarkProbe/RemoveSectionSections/n=1024-12 3250 377700 ns/op 977854 B/op 60 allocs/op +BenchmarkProbe/RemoveSectionSections/n=1024-12 3145 374978 ns/op 977855 B/op 60 allocs/op +BenchmarkProbe/RemoveSectionSections/n=1024-12 3189 366536 ns/op 977853 B/op 60 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=1024-12 1074 1.136291e+06 ns/op 1.957399e+06 B/op 12378 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=1024-12 1044 1.174265e+06 ns/op 1.9574e+06 B/op 12378 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=1024-12 1011 1.195348e+06 ns/op 1.9574e+06 B/op 12378 allocs/op +BenchmarkProbe/UnsetAllSections/n=4096-12 416 2.928603e+06 ns/op 4.042093e+06 B/op 80 allocs/op +BenchmarkProbe/UnsetAllSections/n=4096-12 394 3.150212e+06 ns/op 4.042095e+06 B/op 80 allocs/op +BenchmarkProbe/UnsetAllSections/n=4096-12 382 3.067227e+06 ns/op 4.042092e+06 B/op 80 allocs/op +BenchmarkProbe/UnsetAllFlat/n=4096-12 618 1.86093e+06 ns/op 3.73899e+06 B/op 81 allocs/op +BenchmarkProbe/UnsetAllFlat/n=4096-12 662 1.841739e+06 ns/op 3.73899e+06 B/op 81 allocs/op +BenchmarkProbe/UnsetAllFlat/n=4096-12 650 1.870803e+06 ns/op 3.73899e+06 B/op 81 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=4096-12 736 1.624229e+06 ns/op 3.777274e+06 B/op 84 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=4096-12 722 1.625224e+06 ns/op 3.777273e+06 B/op 84 allocs/op +BenchmarkProbe/ReplaceAllFlat/n=4096-12 733 1.625364e+06 ns/op 3.777274e+06 B/op 84 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=4096-12 1546 779117 ns/op 1.594459e+06 B/op 79 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=4096-12 1620 756191 ns/op 1.594459e+06 B/op 79 allocs/op +BenchmarkProbe/RenameSectionOneLine/n=4096-12 1562 757840 ns/op 1.594459e+06 B/op 79 allocs/op +BenchmarkProbe/RemoveSectionSections/n=4096-12 549 2.265801e+06 ns/op 4.013646e+06 B/op 74 allocs/op +BenchmarkProbe/RemoveSectionSections/n=4096-12 516 2.31401e+06 ns/op 4.013645e+06 B/op 74 allocs/op +BenchmarkProbe/RemoveSectionSections/n=4096-12 534 2.275376e+06 ns/op 4.013646e+06 B/op 74 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=4096-12 206 5.824772e+06 ns/op 7.983289e+06 B/op 49256 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=4096-12 200 5.966529e+06 ns/op 7.983288e+06 B/op 49256 allocs/op +BenchmarkProbe/LoadIncludeIfGitdir/n=4096-12 200 5.946946e+06 ns/op 7.983294e+06 B/op 49256 allocs/op diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" new file mode 100644 index 0000000..fca1f1d Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" new file mode 100644 index 0000000..17ced3e Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadFileManyGitdirConditions.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadFileManyGitdirConditions.profile" new file mode 100644 index 0000000..94874e8 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadFileManyGitdirConditions.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" new file mode 100644 index 0000000..fec897c Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" new file mode 100644 index 0000000..58b2c67 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" new file mode 100644 index 0000000..87ad5b7 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/cpu/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" differ diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff new file mode 100644 index 0000000..b6e6218 --- /dev/null +++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff @@ -0,0 +1,69 @@ +diff --git a/env_test.go b/env_test.go +index c4f3ce1..780217e 100644 +--- a/env_test.go ++++ b/env_test.go +@@ -1697,6 +1697,16 @@ func BenchmarkEnv(b *testing.B) { + _, _ = env.LoadProtected() + } + }) ++ b.Run("LoadFileManyGitdirConditions", func(b *testing.B) { ++ // The git directory's real path is resolved once per load, not ++ // once per condition. ++ path := filepath.Join(tempDir(b), "config") ++ writeFile(b, path, strings.Repeat("[includeIf \"gitdir:/nope/\"]\n\tpath = x\n", 1000)) ++ b.ReportAllocs() ++ for b.Loop() { ++ _, _ = env.LoadFile(path) ++ } ++ }) + b.Run("UpdateFile", func(b *testing.B) { + path := filepath.Join(env.GitDir, "config") + i := 0 +diff --git a/file_test.go b/file_test.go +index 1415a54..cb6a960 100644 +--- a/file_test.go ++++ b/file_test.go +@@ -1063,6 +1063,43 @@ func BenchmarkParse(b *testing.B) { + } + } + ++// BenchmarkEdit measures edits that touch many lines at once, on inputs ++// built to hit their slow paths; each iteration parses the file first. ++// Throughput (MB/s) must not fall as the size grows: these edits are linear. ++func BenchmarkEdit(b *testing.B) { ++ edits := []struct { ++ name string ++ gen func(n int) []byte ++ edit func(f *gitconfig.File) error ++ }{ ++ {"UnsetAll one section", func(n int) []byte { return sized("[a]\n", "\tk = v\n", n) }, func(f *gitconfig.File) error { ++ _, err := f.UnsetAll("a.k") ++ return err ++ }}, ++ {"UnsetAll section per value", func(n int) []byte { return sized("", "[a]\n\tk = v\n", n) }, func(f *gitconfig.File) error { ++ _, err := f.UnsetAll("a.k") ++ return err ++ }}, ++ {"RenameSection one line", func(n int) []byte { return append(sized("", "[a]", n), " k = v\n"...) }, func(f *gitconfig.File) error { ++ return f.RenameSection("a", "b") ++ }}, ++ } ++ for _, e := range edits { ++ for _, n := range []int{1 << 10, 1 << 16, 1 << 20} { ++ data := e.gen(n) ++ b.Run(fmt.Sprintf("%s/%dKiB", e.name, n>>10), func(b *testing.B) { ++ b.SetBytes(int64(len(data))) ++ b.ReportAllocs() ++ for b.Loop() { ++ f, err := gitconfig.Parse("f", data) ++ require.NoError(b, err) ++ require.NoError(b, e.edit(f)) ++ } ++ }) ++ } ++ } ++} ++ + func BenchmarkFile(b *testing.B) { + for _, n := range []int{10, 1000} { + data := []byte(bigConfig(n)) diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEdit.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEdit.profile" new file mode 100644 index 0000000..264af6b Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEdit.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" new file mode 100644 index 0000000..c064ba5 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213Load.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadFileManyGitdirConditions.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadFileManyGitdirConditions.profile" new file mode 100644 index 0000000..224d24f Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadFileManyGitdirConditions.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" new file mode 100644 index 0000000..7392f21 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213LoadProtected.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" new file mode 100644 index 0000000..481ef63 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFile.profile" differ diff --git "a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" new file mode 100644 index 0000000..bca3e67 Binary files /dev/null and "b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/mem/\341\232\227\341\232\227BenchmarkEnv\341\232\213UpdateFileUnchanged.profile" differ diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json new file mode 100644 index 0000000..d9b3e36 --- /dev/null +++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json @@ -0,0 +1,24 @@ +{ + "name": "edit-benches", + "benchs": [ + "BenchmarkEnv/Load", + "BenchmarkEnv/LoadProtected", + "BenchmarkEnv/LoadFileManyGitdirConditions", + "BenchmarkEnv/UpdateFile", + "BenchmarkEnv/UpdateFileUnchanged", + "BenchmarkEdit" + ], + "git_commit": "9f097cd6b40f09dd20e5e00b92bd6ded3ca0f281", + "machine": { + "goos": "linux", + "goarch": "amd64", + "num_cpu": 12, + "physical_cores": 6, + "threads_per_core": 2, + "cpu": "AMD Ryzen 5 3600 6-Core Processor", + "cache_l1d": 32768, + "cache_l2": 524288, + "cache_l3": 16777216 + }, + "go_version": "go1.27.1" +} diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench new file mode 100644 index 0000000..df8598f --- /dev/null +++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench @@ -0,0 +1,145 @@ +goos: linux +goarch: amd64 +pkg: github.com/git-bug/gitconfig +cpu: AMD Ryzen 5 3600 6-Core Processor + +BenchmarkEnv/Load-12 5012 241503 ns/op 332510 B/op 932 allocs/op +BenchmarkEnv/Load-12 5055 237011 ns/op 332506 B/op 932 allocs/op +BenchmarkEnv/Load-12 4833 247790 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/Load-12 4948 262598 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/Load-12 4952 247394 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/Load-12 4938 237301 ns/op 332506 B/op 932 allocs/op +BenchmarkEnv/Load-12 4702 242795 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/Load-12 4986 237644 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/Load-12 5089 232608 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/Load-12 4891 234684 ns/op 332507 B/op 932 allocs/op +BenchmarkEnv/LoadProtected-12 18220 65050 ns/op 62041 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18184 65361 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18378 65616 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18579 64420 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18400 64840 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18554 64231 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18663 64566 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18583 64480 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18693 64180 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadProtected-12 18700 64691 ns/op 62040 B/op 219 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1105 1.123003e+06 ns/op 1.881754e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1070 1.101759e+06 ns/op 1.881736e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1053 1.101034e+06 ns/op 1.881737e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1146 1.094549e+06 ns/op 1.881736e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1125 1.082766e+06 ns/op 1.881735e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1135 1.108419e+06 ns/op 1.881735e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1063 1.09794e+06 ns/op 1.881737e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1086 1.10521e+06 ns/op 1.881736e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 1094 1.171132e+06 ns/op 1.881737e+06 B/op 12101 allocs/op +BenchmarkEnv/LoadFileManyGitdirConditions-12 986 1.125506e+06 ns/op 1.881736e+06 B/op 12101 allocs/op +BenchmarkEnv/UpdateFile-12 4190 271686 ns/op 460668 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4456 274471 ns/op 460667 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4729 275723 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4462 271889 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4380 270158 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4204 277096 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4135 278751 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4646 270537 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4436 272529 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFile-12 4336 272191 ns/op 460666 B/op 993 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 107961 ns/op 136545 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 107468 ns/op 136545 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 108063 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 109785 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 108510 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 108888 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 9543 112027 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 110303 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 110549 ns/op 136544 B/op 480 allocs/op +BenchmarkEnv/UpdateFileUnchanged-12 10000 109377 ns/op 136544 B/op 480 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24992 48458 ns/op 21.17 MB/s 116801 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25394 48523 ns/op 21.14 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 23578 48785 ns/op 21.03 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25435 47119 ns/op 21.77 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25606 47586 ns/op 21.56 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25616 47607 ns/op 21.55 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25425 47311 ns/op 21.69 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25312 47771 ns/op 21.48 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25722 47106 ns/op 21.78 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25611 46886 ns/op 21.88 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 229 5.223861e+06 ns/op 12.55 MB/s 9.285321e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 232 5.112092e+06 ns/op 12.82 MB/s 9.28532e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 238 5.116727e+06 ns/op 12.81 MB/s 9.285327e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 224 5.389997e+06 ns/op 12.16 MB/s 9.285338e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 218 5.208605e+06 ns/op 12.58 MB/s 9.285335e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 238 4.952627e+06 ns/op 13.23 MB/s 9.285355e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 236 4.986969e+06 ns/op 13.14 MB/s 9.285334e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 236 5.071369e+06 ns/op 12.92 MB/s 9.285339e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 235 5.001953e+06 ns/op 13.1 MB/s 9.285328e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 247 4.941395e+06 ns/op 13.26 MB/s 9.285326e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 18 7.4505016e+07 ns/op 14.07 MB/s 1.64574835e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.4735616e+07 ns/op 14.03 MB/s 1.64574529e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.5954921e+07 ns/op 13.81 MB/s 1.64574538e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.7238758e+07 ns/op 13.58 MB/s 1.64574515e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.7903187e+07 ns/op 13.46 MB/s 1.64574538e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.0328085e+07 ns/op 13.05 MB/s 1.64574541e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8560708e+07 ns/op 13.35 MB/s 1.64574538e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.6633014e+07 ns/op 13.68 MB/s 1.64574917e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8626452e+07 ns/op 13.34 MB/s 1.64574538e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 7.7921155e+07 ns/op 13.46 MB/s 1.64574525e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23151 52263 ns/op 19.78 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22872 51578 ns/op 20.05 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23139 52356 ns/op 19.75 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 20388 55894 ns/op 18.5 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23473 51433 ns/op 20.1 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23798 50874 ns/op 20.32 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23052 53274 ns/op 19.41 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23304 51407 ns/op 20.11 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23028 52248 ns/op 19.79 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23043 52120 ns/op 19.84 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 285 4.233862e+06 ns/op 15.48 MB/s 5.893476e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 279 4.254785e+06 ns/op 15.4 MB/s 5.893476e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.177645e+06 ns/op 15.69 MB/s 5.893478e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 283 4.225172e+06 ns/op 15.51 MB/s 5.893476e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.190192e+06 ns/op 15.64 MB/s 5.893479e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.19033e+06 ns/op 15.64 MB/s 5.893482e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 267 4.401359e+06 ns/op 14.89 MB/s 5.893483e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 289 4.19691e+06 ns/op 15.62 MB/s 5.89348e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 271 4.336991e+06 ns/op 15.11 MB/s 5.893481e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 282 4.261353e+06 ns/op 15.38 MB/s 5.893478e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.5209017e+07 ns/op 16.08 MB/s 1.10928098e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4119783e+07 ns/op 16.35 MB/s 1.10928086e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3904657e+07 ns/op 16.41 MB/s 1.10928086e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3818774e+07 ns/op 16.43 MB/s 1.10928051e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4315706e+07 ns/op 16.3 MB/s 1.10928086e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4330359e+07 ns/op 16.3 MB/s 1.10928072e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4296497e+07 ns/op 16.31 MB/s 1.10928086e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4794364e+07 ns/op 16.18 MB/s 1.10928079e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4290026e+07 ns/op 16.31 MB/s 1.10928107e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.5052421e+07 ns/op 16.12 MB/s 1.10928073e+08 B/op 359 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19653 60606 ns/op 17.04 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19838 60903 ns/op 16.96 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19718 61200 ns/op 16.88 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19851 60693 ns/op 17.02 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19701 60974 ns/op 16.94 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19495 60933 ns/op 16.95 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19958 60714 ns/op 17.01 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19819 60609 ns/op 17.04 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19388 61425 ns/op 16.82 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19386 61863 ns/op 16.7 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 200 5.925565e+06 ns/op 11.06 MB/s 1.0203044e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 202 5.820491e+06 ns/op 11.26 MB/s 1.0203058e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 206 5.833726e+06 ns/op 11.24 MB/s 1.0203054e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 198 5.979629e+06 ns/op 10.96 MB/s 1.0203056e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 200 6.009091e+06 ns/op 10.91 MB/s 1.0203047e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 198 5.995473e+06 ns/op 10.93 MB/s 1.0203043e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 199 5.953213e+06 ns/op 11.01 MB/s 1.0203049e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 194 6.095935e+06 ns/op 10.75 MB/s 1.0203063e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 201 5.925326e+06 ns/op 11.06 MB/s 1.0203056e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 205 5.881998e+06 ns/op 11.14 MB/s 1.0203051e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.003303e+07 ns/op 13.1 MB/s 1.75181749e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.0758246e+07 ns/op 12.98 MB/s 1.75181781e+08 B/op 151 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.8895688e+07 ns/op 13.29 MB/s 1.75181799e+08 B/op 151 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.8704744e+07 ns/op 13.32 MB/s 1.75181749e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.959157e+07 ns/op 13.17 MB/s 1.75181717e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.955495e+07 ns/op 13.18 MB/s 1.75181733e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.9595676e+07 ns/op 13.17 MB/s 1.75181733e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.9909298e+07 ns/op 13.12 MB/s 1.7518173e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.0221386e+07 ns/op 13.07 MB/s 1.75181749e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.9051805e+07 ns/op 13.26 MB/s 1.7518179e+08 B/op 151 allocs/op diff --git "a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" new file mode 100644 index 0000000..de6e28d Binary files /dev/null and "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" differ diff --git "a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkFile.profile" "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkFile.profile" new file mode 100644 index 0000000..f388e3a Binary files /dev/null and "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkFile.profile" differ diff --git "a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkParse.profile" "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkParse.profile" new file mode 100644 index 0000000..e2547d7 Binary files /dev/null and "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/cpu/\341\232\227\341\232\227BenchmarkParse.profile" differ diff --git a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/git.diff b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/git.diff new file mode 100644 index 0000000..c6ea134 --- /dev/null +++ b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/git.diff @@ -0,0 +1,325 @@ +diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff +new file mode 100644 +index 0000000..b6e6218 +--- /dev/null ++++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff +@@ -0,0 +1,69 @@ ++diff --git a/env_test.go b/env_test.go ++index c4f3ce1..780217e 100644 ++--- a/env_test.go +++++ b/env_test.go ++@@ -1697,6 +1697,16 @@ func BenchmarkEnv(b *testing.B) { ++ _, _ = env.LoadProtected() ++ } ++ }) +++ b.Run("LoadFileManyGitdirConditions", func(b *testing.B) { +++ // The git directory's real path is resolved once per load, not +++ // once per condition. +++ path := filepath.Join(tempDir(b), "config") +++ writeFile(b, path, strings.Repeat("[includeIf \"gitdir:/nope/\"]\n\tpath = x\n", 1000)) +++ b.ReportAllocs() +++ for b.Loop() { +++ _, _ = env.LoadFile(path) +++ } +++ }) ++ b.Run("UpdateFile", func(b *testing.B) { ++ path := filepath.Join(env.GitDir, "config") ++ i := 0 ++diff --git a/file_test.go b/file_test.go ++index 1415a54..cb6a960 100644 ++--- a/file_test.go +++++ b/file_test.go ++@@ -1063,6 +1063,43 @@ func BenchmarkParse(b *testing.B) { ++ } ++ } ++ +++// BenchmarkEdit measures edits that touch many lines at once, on inputs +++// built to hit their slow paths; each iteration parses the file first. +++// Throughput (MB/s) must not fall as the size grows: these edits are linear. +++func BenchmarkEdit(b *testing.B) { +++ edits := []struct { +++ name string +++ gen func(n int) []byte +++ edit func(f *gitconfig.File) error +++ }{ +++ {"UnsetAll one section", func(n int) []byte { return sized("[a]\n", "\tk = v\n", n) }, func(f *gitconfig.File) error { +++ _, err := f.UnsetAll("a.k") +++ return err +++ }}, +++ {"UnsetAll section per value", func(n int) []byte { return sized("", "[a]\n\tk = v\n", n) }, func(f *gitconfig.File) error { +++ _, err := f.UnsetAll("a.k") +++ return err +++ }}, +++ {"RenameSection one line", func(n int) []byte { return append(sized("", "[a]", n), " k = v\n"...) }, func(f *gitconfig.File) error { +++ return f.RenameSection("a", "b") +++ }}, +++ } +++ for _, e := range edits { +++ for _, n := range []int{1 << 10, 1 << 16, 1 << 20} { +++ data := e.gen(n) +++ b.Run(fmt.Sprintf("%s/%dKiB", e.name, n>>10), func(b *testing.B) { +++ b.SetBytes(int64(len(data))) +++ b.ReportAllocs() +++ for b.Loop() { +++ f, err := gitconfig.Parse("f", data) +++ require.NoError(b, err) +++ require.NoError(b, e.edit(f)) +++ } +++ }) +++ } +++ } +++} +++ ++ func BenchmarkFile(b *testing.B) { ++ for _, n := range []int{10, 1000} { ++ data := []byte(bigConfig(n)) +diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json +new file mode 100644 +index 0000000..d9b3e36 +--- /dev/null ++++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json +@@ -0,0 +1,24 @@ ++{ ++ "name": "edit-benches", ++ "benchs": [ ++ "BenchmarkEnv/Load", ++ "BenchmarkEnv/LoadProtected", ++ "BenchmarkEnv/LoadFileManyGitdirConditions", ++ "BenchmarkEnv/UpdateFile", ++ "BenchmarkEnv/UpdateFileUnchanged", ++ "BenchmarkEdit" ++ ], ++ "git_commit": "9f097cd6b40f09dd20e5e00b92bd6ded3ca0f281", ++ "machine": { ++ "goos": "linux", ++ "goarch": "amd64", ++ "num_cpu": 12, ++ "physical_cores": 6, ++ "threads_per_core": 2, ++ "cpu": "AMD Ryzen 5 3600 6-Core Processor", ++ "cache_l1d": 32768, ++ "cache_l2": 524288, ++ "cache_l3": 16777216 ++ }, ++ "go_version": "go1.27.1" ++} +diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench +new file mode 100644 +index 0000000..df8598f +--- /dev/null ++++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench +@@ -0,0 +1,145 @@ ++goos: linux ++goarch: amd64 ++pkg: github.com/git-bug/gitconfig ++cpu: AMD Ryzen 5 3600 6-Core Processor ++ ++BenchmarkEnv/Load-12 5012 241503 ns/op 332510 B/op 932 allocs/op ++BenchmarkEnv/Load-12 5055 237011 ns/op 332506 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4833 247790 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4948 262598 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4952 247394 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4938 237301 ns/op 332506 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4702 242795 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4986 237644 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 5089 232608 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4891 234684 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/LoadProtected-12 18220 65050 ns/op 62041 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18184 65361 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18378 65616 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18579 64420 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18400 64840 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18554 64231 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18663 64566 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18583 64480 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18693 64180 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18700 64691 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1105 1.123003e+06 ns/op 1.881754e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1070 1.101759e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1053 1.101034e+06 ns/op 1.881737e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1146 1.094549e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1125 1.082766e+06 ns/op 1.881735e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1135 1.108419e+06 ns/op 1.881735e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1063 1.09794e+06 ns/op 1.881737e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1086 1.10521e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1094 1.171132e+06 ns/op 1.881737e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 986 1.125506e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/UpdateFile-12 4190 271686 ns/op 460668 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4456 274471 ns/op 460667 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4729 275723 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4462 271889 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4380 270158 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4204 277096 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4135 278751 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4646 270537 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4436 272529 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4336 272191 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 107961 ns/op 136545 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 107468 ns/op 136545 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 108063 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 109785 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 108510 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 108888 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 9543 112027 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 110303 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 110549 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 109377 ns/op 136544 B/op 480 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 24992 48458 ns/op 21.17 MB/s 116801 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25394 48523 ns/op 21.14 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 23578 48785 ns/op 21.03 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25435 47119 ns/op 21.77 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25606 47586 ns/op 21.56 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25616 47607 ns/op 21.55 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25425 47311 ns/op 21.69 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25312 47771 ns/op 21.48 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25722 47106 ns/op 21.78 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25611 46886 ns/op 21.88 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 229 5.223861e+06 ns/op 12.55 MB/s 9.285321e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 232 5.112092e+06 ns/op 12.82 MB/s 9.28532e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 238 5.116727e+06 ns/op 12.81 MB/s 9.285327e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 224 5.389997e+06 ns/op 12.16 MB/s 9.285338e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 218 5.208605e+06 ns/op 12.58 MB/s 9.285335e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 238 4.952627e+06 ns/op 13.23 MB/s 9.285355e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 236 4.986969e+06 ns/op 13.14 MB/s 9.285334e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 236 5.071369e+06 ns/op 12.92 MB/s 9.285339e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 235 5.001953e+06 ns/op 13.1 MB/s 9.285328e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 247 4.941395e+06 ns/op 13.26 MB/s 9.285326e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 18 7.4505016e+07 ns/op 14.07 MB/s 1.64574835e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.4735616e+07 ns/op 14.03 MB/s 1.64574529e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.5954921e+07 ns/op 13.81 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.7238758e+07 ns/op 13.58 MB/s 1.64574515e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.7903187e+07 ns/op 13.46 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.0328085e+07 ns/op 13.05 MB/s 1.64574541e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8560708e+07 ns/op 13.35 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.6633014e+07 ns/op 13.68 MB/s 1.64574917e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8626452e+07 ns/op 13.34 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 7.7921155e+07 ns/op 13.46 MB/s 1.64574525e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23151 52263 ns/op 19.78 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22872 51578 ns/op 20.05 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23139 52356 ns/op 19.75 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 20388 55894 ns/op 18.5 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23473 51433 ns/op 20.1 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23798 50874 ns/op 20.32 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23052 53274 ns/op 19.41 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23304 51407 ns/op 20.11 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23028 52248 ns/op 19.79 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23043 52120 ns/op 19.84 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 285 4.233862e+06 ns/op 15.48 MB/s 5.893476e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 279 4.254785e+06 ns/op 15.4 MB/s 5.893476e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.177645e+06 ns/op 15.69 MB/s 5.893478e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 283 4.225172e+06 ns/op 15.51 MB/s 5.893476e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.190192e+06 ns/op 15.64 MB/s 5.893479e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.19033e+06 ns/op 15.64 MB/s 5.893482e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 267 4.401359e+06 ns/op 14.89 MB/s 5.893483e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 289 4.19691e+06 ns/op 15.62 MB/s 5.89348e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 271 4.336991e+06 ns/op 15.11 MB/s 5.893481e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 282 4.261353e+06 ns/op 15.38 MB/s 5.893478e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.5209017e+07 ns/op 16.08 MB/s 1.10928098e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4119783e+07 ns/op 16.35 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3904657e+07 ns/op 16.41 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3818774e+07 ns/op 16.43 MB/s 1.10928051e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4315706e+07 ns/op 16.3 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4330359e+07 ns/op 16.3 MB/s 1.10928072e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4296497e+07 ns/op 16.31 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4794364e+07 ns/op 16.18 MB/s 1.10928079e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4290026e+07 ns/op 16.31 MB/s 1.10928107e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.5052421e+07 ns/op 16.12 MB/s 1.10928073e+08 B/op 359 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19653 60606 ns/op 17.04 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19838 60903 ns/op 16.96 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19718 61200 ns/op 16.88 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19851 60693 ns/op 17.02 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19701 60974 ns/op 16.94 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19495 60933 ns/op 16.95 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19958 60714 ns/op 17.01 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19819 60609 ns/op 17.04 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19388 61425 ns/op 16.82 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19386 61863 ns/op 16.7 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 200 5.925565e+06 ns/op 11.06 MB/s 1.0203044e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 202 5.820491e+06 ns/op 11.26 MB/s 1.0203058e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 206 5.833726e+06 ns/op 11.24 MB/s 1.0203054e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 198 5.979629e+06 ns/op 10.96 MB/s 1.0203056e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 200 6.009091e+06 ns/op 10.91 MB/s 1.0203047e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 198 5.995473e+06 ns/op 10.93 MB/s 1.0203043e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 199 5.953213e+06 ns/op 11.01 MB/s 1.0203049e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 194 6.095935e+06 ns/op 10.75 MB/s 1.0203063e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 201 5.925326e+06 ns/op 11.06 MB/s 1.0203056e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 205 5.881998e+06 ns/op 11.14 MB/s 1.0203051e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.003303e+07 ns/op 13.1 MB/s 1.75181749e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.0758246e+07 ns/op 12.98 MB/s 1.75181781e+08 B/op 151 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.8895688e+07 ns/op 13.29 MB/s 1.75181799e+08 B/op 151 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.8704744e+07 ns/op 13.32 MB/s 1.75181749e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.959157e+07 ns/op 13.17 MB/s 1.75181717e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.955495e+07 ns/op 13.18 MB/s 1.75181733e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.9595676e+07 ns/op 13.17 MB/s 1.75181733e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.9909298e+07 ns/op 13.12 MB/s 1.7518173e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.0221386e+07 ns/op 13.07 MB/s 1.75181749e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.9051805e+07 ns/op 13.26 MB/s 1.7518179e+08 B/op 151 allocs/op +diff --git a/env_test.go b/env_test.go +index c4f3ce1..780217e 100644 +--- a/env_test.go ++++ b/env_test.go +@@ -1697,6 +1697,16 @@ func BenchmarkEnv(b *testing.B) { + _, _ = env.LoadProtected() + } + }) ++ b.Run("LoadFileManyGitdirConditions", func(b *testing.B) { ++ // The git directory's real path is resolved once per load, not ++ // once per condition. ++ path := filepath.Join(tempDir(b), "config") ++ writeFile(b, path, strings.Repeat("[includeIf \"gitdir:/nope/\"]\n\tpath = x\n", 1000)) ++ b.ReportAllocs() ++ for b.Loop() { ++ _, _ = env.LoadFile(path) ++ } ++ }) + b.Run("UpdateFile", func(b *testing.B) { + path := filepath.Join(env.GitDir, "config") + i := 0 +diff --git a/file_test.go b/file_test.go +index 1415a54..cb6a960 100644 +--- a/file_test.go ++++ b/file_test.go +@@ -1063,6 +1063,43 @@ func BenchmarkParse(b *testing.B) { + } + } + ++// BenchmarkEdit measures edits that touch many lines at once, on inputs ++// built to hit their slow paths; each iteration parses the file first. ++// Throughput (MB/s) must not fall as the size grows: these edits are linear. ++func BenchmarkEdit(b *testing.B) { ++ edits := []struct { ++ name string ++ gen func(n int) []byte ++ edit func(f *gitconfig.File) error ++ }{ ++ {"UnsetAll one section", func(n int) []byte { return sized("[a]\n", "\tk = v\n", n) }, func(f *gitconfig.File) error { ++ _, err := f.UnsetAll("a.k") ++ return err ++ }}, ++ {"UnsetAll section per value", func(n int) []byte { return sized("", "[a]\n\tk = v\n", n) }, func(f *gitconfig.File) error { ++ _, err := f.UnsetAll("a.k") ++ return err ++ }}, ++ {"RenameSection one line", func(n int) []byte { return append(sized("", "[a]", n), " k = v\n"...) }, func(f *gitconfig.File) error { ++ return f.RenameSection("a", "b") ++ }}, ++ } ++ for _, e := range edits { ++ for _, n := range []int{1 << 10, 1 << 16, 1 << 20} { ++ data := e.gen(n) ++ b.Run(fmt.Sprintf("%s/%dKiB", e.name, n>>10), func(b *testing.B) { ++ b.SetBytes(int64(len(data))) ++ b.ReportAllocs() ++ for b.Loop() { ++ f, err := gitconfig.Parse("f", data) ++ require.NoError(b, err) ++ require.NoError(b, e.edit(f)) ++ } ++ }) ++ } ++ } ++} ++ + func BenchmarkFile(b *testing.B) { + for _, n := range []int{10, 1000} { + data := []byte(bigConfig(n)) diff --git "a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkEdit.profile" "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkEdit.profile" new file mode 100644 index 0000000..fde2cab Binary files /dev/null and "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkEdit.profile" differ diff --git "a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkFile.profile" "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkFile.profile" new file mode 100644 index 0000000..692b86d Binary files /dev/null and "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkFile.profile" differ diff --git "a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkParse.profile" "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkParse.profile" new file mode 100644 index 0000000..d69aa20 Binary files /dev/null and "b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/mem/\341\232\227\341\232\227BenchmarkParse.profile" differ diff --git a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/meta.json b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/meta.json new file mode 100644 index 0000000..3fa672b --- /dev/null +++ b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/meta.json @@ -0,0 +1,21 @@ +{ + "name": "pre-alloc", + "benchs": [ + "BenchmarkParse", + "BenchmarkEdit", + "BenchmarkFile" + ], + "git_commit": "9f097cd6b40f09dd20e5e00b92bd6ded3ca0f281", + "machine": { + "goos": "linux", + "goarch": "amd64", + "num_cpu": 12, + "physical_cores": 6, + "threads_per_core": 2, + "cpu": "AMD Ryzen 5 3600 6-Core Processor", + "cache_l1d": 32768, + "cache_l2": 524288, + "cache_l3": 16777216 + }, + "go_version": "go1.27.1" +} diff --git a/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/results.bench b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/results.bench new file mode 100644 index 0000000..fbbd9e8 --- /dev/null +++ b/.benchspotter/sessions/01a120ad-1c44-74d9-95fb-be4ffe0c882d/results.bench @@ -0,0 +1,465 @@ +goos: linux +goarch: amd64 +pkg: github.com/git-bug/gitconfig +cpu: AMD Ryzen 5 3600 6-Core Processor + +BenchmarkParse/realistic/1KiB-12 83026 13708 ns/op 83.53 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 87609 13688 ns/op 83.65 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 91252 12959 ns/op 88.36 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 91803 13511 ns/op 84.75 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 84506 15551 ns/op 73.63 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 70053 15386 ns/op 74.42 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 82062 16189 ns/op 70.73 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 89076 14005 ns/op 81.76 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 84879 14041 ns/op 81.55 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/1KiB-12 89379 13220 ns/op 86.61 MB/s 23584 B/op 103 allocs/op +BenchmarkParse/realistic/64KiB-12 1698 688465 ns/op 95.46 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1708 695199 ns/op 94.54 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1782 684237 ns/op 96.05 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1760 678508 ns/op 96.86 MB/s 1.40305e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1682 686529 ns/op 95.73 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1737 672764 ns/op 97.69 MB/s 1.403047e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1662 673737 ns/op 97.55 MB/s 1.403048e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1749 700319 ns/op 93.85 MB/s 1.403049e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1656 675158 ns/op 97.34 MB/s 1.403048e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/64KiB-12 1762 678389 ns/op 96.88 MB/s 1.403046e+06 B/op 3776 allocs/op +BenchmarkParse/realistic/1024KiB-12 110 1.0765335e+07 ns/op 97.4 MB/s 2.3841446e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 111 1.0676391e+07 ns/op 98.22 MB/s 2.384144e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 109 1.0949445e+07 ns/op 95.77 MB/s 2.3841441e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 111 1.0656516e+07 ns/op 98.4 MB/s 2.3841442e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 112 1.0709655e+07 ns/op 97.91 MB/s 2.3841442e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 93 1.0816731e+07 ns/op 96.94 MB/s 2.3841441e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 109 1.0856884e+07 ns/op 96.58 MB/s 2.3841446e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 111 1.0733721e+07 ns/op 97.69 MB/s 2.3841445e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 110 1.0762067e+07 ns/op 97.43 MB/s 2.3841443e+07 B/op 59583 allocs/op +BenchmarkParse/realistic/1024KiB-12 93 1.1137335e+07 ns/op 94.15 MB/s 2.3841446e+07 B/op 59583 allocs/op +BenchmarkParse/one_long_value/1KiB-12 198596 5630 ns/op 182.05 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 216338 5493 ns/op 186.6 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 219686 5556 ns/op 184.49 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 210943 5863 ns/op 174.83 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 206650 5705 ns/op 179.66 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 208838 5603 ns/op 182.92 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 220760 5539 ns/op 185.04 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 218010 5514 ns/op 185.89 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 200760 5545 ns/op 184.87 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/1KiB-12 220879 5709 ns/op 179.53 MB/s 6920 B/op 20 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4755 264593 ns/op 247.69 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4600 263787 ns/op 248.45 MB/s 426121 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4490 278786 ns/op 235.08 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 3930 264764 ns/op 247.53 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4484 268936 ns/op 243.69 MB/s 426121 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4318 263481 ns/op 248.74 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4380 270802 ns/op 242.01 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4580 265061 ns/op 247.25 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4633 265604 ns/op 246.75 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4692 264415 ns/op 247.86 MB/s 426122 B/op 33 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 228 5.495416e+06 ns/op 190.81 MB/s 7.348428e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 211 5.666804e+06 ns/op 185.04 MB/s 7.348426e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 214 5.537454e+06 ns/op 189.36 MB/s 7.348429e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 220 5.448728e+06 ns/op 192.44 MB/s 7.348437e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 214 5.531962e+06 ns/op 189.55 MB/s 7.348424e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 200 5.743669e+06 ns/op 182.56 MB/s 7.348426e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 204 5.811108e+06 ns/op 180.44 MB/s 7.348417e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 211 5.652375e+06 ns/op 185.51 MB/s 7.348431e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 216 5.56808e+06 ns/op 188.32 MB/s 7.348429e+06 B/op 44 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 212 5.695833e+06 ns/op 184.1 MB/s 7.348421e+06 B/op 44 allocs/op +BenchmarkParse/continuations/1KiB-12 161748 7217 ns/op 142.16 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 163987 7278 ns/op 140.97 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 165102 7100 ns/op 144.51 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 170420 7413 ns/op 138.41 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 146666 7563 ns/op 135.65 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 166305 7182 ns/op 142.86 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 171048 6938 ns/op 147.88 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 175833 7235 ns/op 141.81 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 165363 7455 ns/op 137.62 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/1KiB-12 158204 7316 ns/op 140.25 MB/s 25544 B/op 18 allocs/op +BenchmarkParse/continuations/64KiB-12 2934 393071 ns/op 166.73 MB/s 1.580873e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3181 385380 ns/op 170.06 MB/s 1.580873e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 2953 384010 ns/op 170.67 MB/s 1.580872e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3214 379961 ns/op 172.49 MB/s 1.580873e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3495 378436 ns/op 173.18 MB/s 1.580873e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3607 391178 ns/op 167.54 MB/s 1.580873e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3302 391269 ns/op 167.5 MB/s 1.580874e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3166 394305 ns/op 166.21 MB/s 1.580874e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3051 377800 ns/op 173.47 MB/s 1.580872e+06 B/op 31 allocs/op +BenchmarkParse/continuations/64KiB-12 3372 406805 ns/op 161.1 MB/s 1.580874e+06 B/op 31 allocs/op +BenchmarkParse/continuations/1024KiB-12 210 5.354373e+06 ns/op 195.84 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 229 5.234955e+06 ns/op 200.3 MB/s 2.5100112e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 219 5.353152e+06 ns/op 195.88 MB/s 2.5100115e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 230 5.319542e+06 ns/op 197.12 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 225 5.286862e+06 ns/op 198.34 MB/s 2.5100111e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 230 5.207043e+06 ns/op 201.38 MB/s 2.5100113e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 225 5.290348e+06 ns/op 198.21 MB/s 2.5100113e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 229 5.219685e+06 ns/op 200.89 MB/s 2.5100113e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 230 5.167203e+06 ns/op 202.93 MB/s 2.5100112e+07 B/op 41 allocs/op +BenchmarkParse/continuations/1024KiB-12 225 5.263038e+06 ns/op 199.23 MB/s 2.5100114e+07 B/op 41 allocs/op +BenchmarkParse/quotes/1KiB-12 254515 4658 ns/op 220.27 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 265190 4649 ns/op 220.7 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 259603 4630 ns/op 221.61 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 260840 4594 ns/op 223.32 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 263204 4635 ns/op 221.36 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 252586 4773 ns/op 214.96 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 251140 4670 ns/op 219.69 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 259864 4617 ns/op 222.23 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 261169 4624 ns/op 221.87 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/1KiB-12 252844 4609 ns/op 222.62 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/quotes/64KiB-12 4928 250430 ns/op 261.7 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5007 236883 ns/op 276.67 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5145 227674 ns/op 287.86 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 4978 232353 ns/op 282.06 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 4965 232323 ns/op 282.1 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 4812 242845 ns/op 269.88 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5360 238681 ns/op 274.58 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5016 229561 ns/op 285.49 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 5110 235491 ns/op 278.3 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/64KiB-12 4443 240182 ns/op 272.87 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/quotes/1024KiB-12 313 3.704413e+06 ns/op 283.06 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 338 3.637324e+06 ns/op 288.28 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 336 3.811741e+06 ns/op 275.09 MB/s 4.120731e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 310 3.950411e+06 ns/op 265.44 MB/s 4.120726e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 307 3.991823e+06 ns/op 262.68 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 322 3.816813e+06 ns/op 274.73 MB/s 4.120731e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 316 3.700635e+06 ns/op 283.35 MB/s 4.120729e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 327 3.653394e+06 ns/op 287.01 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 319 3.67143e+06 ns/op 285.6 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/quotes/1024KiB-12 309 3.924588e+06 ns/op 267.18 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1KiB-12 328123 3504 ns/op 293.97 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 328660 3521 ns/op 292.51 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 343232 3619 ns/op 284.59 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 333943 3608 ns/op 285.46 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 345814 3675 ns/op 280.28 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 304610 3624 ns/op 284.18 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 318465 3668 ns/op 280.77 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 343503 3787 ns/op 271.98 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 306086 3593 ns/op 286.63 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/1KiB-12 332097 3634 ns/op 283.44 MB/s 4104 B/op 18 allocs/op +BenchmarkParse/escapes/64KiB-12 4210 317885 ns/op 206.18 MB/s 262283 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 5474 208630 ns/op 314.15 MB/s 262282 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 6140 189138 ns/op 346.53 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 6489 182784 ns/op 358.58 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7150 176911 ns/op 370.48 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7638 224640 ns/op 291.76 MB/s 262282 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 6823 174884 ns/op 374.77 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 6592 171656 ns/op 381.82 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 7178 170657 ns/op 384.06 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/64KiB-12 6930 172764 ns/op 379.37 MB/s 262281 B/op 31 allocs/op +BenchmarkParse/escapes/1024KiB-12 460 2.956955e+06 ns/op 354.62 MB/s 4.120734e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 427 2.820456e+06 ns/op 371.78 MB/s 4.120732e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 460 2.609394e+06 ns/op 401.85 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 441 2.845873e+06 ns/op 368.46 MB/s 4.120728e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 463 2.854916e+06 ns/op 367.29 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 454 2.85234e+06 ns/op 367.62 MB/s 4.120729e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 463 2.594006e+06 ns/op 404.23 MB/s 4.120727e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 452 3.07302e+06 ns/op 341.22 MB/s 4.120733e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 427 2.909474e+06 ns/op 360.4 MB/s 4.120729e+06 B/op 41 allocs/op +BenchmarkParse/escapes/1024KiB-12 397 2.931484e+06 ns/op 357.7 MB/s 4.12073e+06 B/op 41 allocs/op +BenchmarkParse/same_key/1KiB-12 62742 18607 ns/op 55.14 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 65608 18856 ns/op 54.41 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 60404 19066 ns/op 53.81 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 65659 18846 ns/op 54.44 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 52842 20331 ns/op 50.47 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 63850 20232 ns/op 50.71 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 62703 20269 ns/op 50.62 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 58990 19755 ns/op 51.94 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 61843 18833 ns/op 54.48 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/1KiB-12 63619 18915 ns/op 54.24 MB/s 48408 B/op 19 allocs/op +BenchmarkParse/same_key/64KiB-12 592 1.862989e+06 ns/op 35.18 MB/s 4.445868e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 678 1.863095e+06 ns/op 35.18 MB/s 4.445867e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 669 1.787368e+06 ns/op 36.67 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 682 1.75479e+06 ns/op 37.35 MB/s 4.445868e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 676 1.814948e+06 ns/op 36.11 MB/s 4.445866e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 688 1.813523e+06 ns/op 36.14 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 691 1.736947e+06 ns/op 37.73 MB/s 4.445866e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 678 1.82226e+06 ns/op 35.97 MB/s 4.445865e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 652 1.824434e+06 ns/op 35.92 MB/s 4.445869e+06 B/op 31 allocs/op +BenchmarkParse/same_key/64KiB-12 686 1.776034e+06 ns/op 36.9 MB/s 4.445868e+06 B/op 31 allocs/op +BenchmarkParse/same_key/1024KiB-12 46 2.5679564e+07 ns/op 40.83 MB/s 7.496272e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 44 2.602143e+07 ns/op 40.3 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 39 2.6262302e+07 ns/op 39.93 MB/s 7.4962594e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 44 2.6871269e+07 ns/op 39.02 MB/s 7.4962602e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 44 2.6421975e+07 ns/op 39.69 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 42 2.8436658e+07 ns/op 36.87 MB/s 7.4962594e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 44 2.7014693e+07 ns/op 38.82 MB/s 7.4962597e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 43 2.7528903e+07 ns/op 38.09 MB/s 7.4962594e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 43 2.7778631e+07 ns/op 37.75 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/same_key/1024KiB-12 40 2.9247706e+07 ns/op 35.85 MB/s 7.4962592e+07 B/op 43 allocs/op +BenchmarkParse/headers/1KiB-12 106965 11083 ns/op 92.39 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 108086 11115 ns/op 92.13 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 106798 11076 ns/op 92.45 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 109377 11075 ns/op 92.46 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 107755 11417 ns/op 89.69 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 108224 11284 ns/op 90.75 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 107266 11174 ns/op 91.64 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 105124 11317 ns/op 90.49 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 107433 11168 ns/op 91.69 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/1KiB-12 105373 11297 ns/op 90.64 MB/s 12688 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1796 672041 ns/op 97.52 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1818 655212 ns/op 100.02 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1800 663163 ns/op 98.82 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1797 672167 ns/op 97.5 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1686 664834 ns/op 98.57 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1806 664166 ns/op 98.67 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1774 661822 ns/op 99.02 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1696 666669 ns/op 98.3 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1711 659590 ns/op 99.36 MB/s 729873 B/op 8 allocs/op +BenchmarkParse/headers/64KiB-12 1777 655237 ns/op 100.02 MB/s 729872 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 115 1.0406017e+07 ns/op 100.77 MB/s 1.1543313e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0062324e+07 ns/op 104.21 MB/s 1.1543314e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0241278e+07 ns/op 102.39 MB/s 1.1543313e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0133456e+07 ns/op 103.48 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0093323e+07 ns/op 103.89 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0283257e+07 ns/op 101.97 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0202408e+07 ns/op 102.78 MB/s 1.1543315e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0395811e+07 ns/op 100.87 MB/s 1.1543314e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0061598e+07 ns/op 104.22 MB/s 1.1543314e+07 B/op 8 allocs/op +BenchmarkParse/headers/1024KiB-12 100 1.0019518e+07 ns/op 104.65 MB/s 1.1543312e+07 B/op 8 allocs/op +BenchmarkParse/long_subsection/1KiB-12 545482 2018 ns/op 508.9 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 647542 2078 ns/op 494.12 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 573482 2066 ns/op 497.05 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 591535 1995 ns/op 514.8 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 592261 2031 ns/op 505.6 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 555657 2022 ns/op 507.83 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 563414 2037 ns/op 504.08 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 582015 2100 ns/op 489.1 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 579451 2023 ns/op 507.66 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/1KiB-12 589474 2047 ns/op 501.61 MB/s 3648 B/op 13 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 104220 ns/op 628.85 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12189 98446 ns/op 665.74 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12008 99512 ns/op 658.61 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 104011 ns/op 630.11 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 102161 ns/op 641.52 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 100613 ns/op 651.39 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 102659 ns/op 638.42 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 100171 ns/op 654.27 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 11875 100985 ns/op 649 MB/s 261825 B/op 26 allocs/op +BenchmarkParse/long_subsection/64KiB-12 12190 99074 ns/op 661.51 MB/s 261824 B/op 26 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 615 1.91745e+06 ns/op 546.86 MB/s 4.120276e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 627 1.906956e+06 ns/op 549.87 MB/s 4.120278e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 567 1.984071e+06 ns/op 528.5 MB/s 4.120274e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 627 1.919475e+06 ns/op 546.28 MB/s 4.120276e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 640 1.897154e+06 ns/op 552.71 MB/s 4.120277e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 619 1.963785e+06 ns/op 533.96 MB/s 4.120276e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 588 1.932142e+06 ns/op 542.7 MB/s 4.120277e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 608 1.984127e+06 ns/op 528.48 MB/s 4.120275e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 613 2.00698e+06 ns/op 522.47 MB/s 4.120275e+06 B/op 36 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 578 1.959753e+06 ns/op 535.06 MB/s 4.120275e+06 B/op 36 allocs/op +BenchmarkParse/comments/1KiB-12 384217 3124 ns/op 328.7 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 397478 3080 ns/op 333.47 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 392512 3066 ns/op 335 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 401457 3094 ns/op 331.96 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 343982 3133 ns/op 327.75 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 392181 3120 ns/op 329.17 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 388008 3124 ns/op 328.75 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 338295 3163 ns/op 324.7 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 387487 3008 ns/op 341.41 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/1KiB-12 422719 3111 ns/op 330.17 MB/s 7968 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7417 161752 ns/op 405.23 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7168 162647 ns/op 402.99 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 6481 165445 ns/op 396.18 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7027 163774 ns/op 400.22 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 6954 163152 ns/op 401.75 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7471 170575 ns/op 384.26 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 6968 184831 ns/op 354.63 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7239 170400 ns/op 384.66 MB/s 483617 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 6801 172054 ns/op 380.96 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/64KiB-12 7296 165157 ns/op 396.87 MB/s 483616 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 448 2.520214e+06 ns/op 416.07 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 475 2.505116e+06 ns/op 418.58 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 472 2.518064e+06 ns/op 416.42 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 486 2.496114e+06 ns/op 420.09 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 471 2.497548e+06 ns/op 419.84 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 472 2.542276e+06 ns/op 412.46 MB/s 7.512354e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 477 2.502418e+06 ns/op 419.03 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 463 2.531251e+06 ns/op 414.25 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 472 2.518997e+06 ns/op 416.27 MB/s 7.512353e+06 B/op 6 allocs/op +BenchmarkParse/comments/1024KiB-12 476 2.512886e+06 ns/op 417.28 MB/s 7.512352e+06 B/op 6 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24416 49078 ns/op 20.91 MB/s 116801 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 23944 50426 ns/op 20.35 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24201 49564 ns/op 20.7 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24566 49238 ns/op 20.84 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 25104 49030 ns/op 20.93 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24901 47897 ns/op 21.42 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24788 48121 ns/op 21.32 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24578 50152 ns/op 20.46 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24750 48820 ns/op 21.02 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 24697 49711 ns/op 20.64 MB/s 116800 B/op 45 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 210 5.624253e+06 ns/op 11.65 MB/s 9.285314e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 217 5.538302e+06 ns/op 11.83 MB/s 9.285347e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 218 5.423618e+06 ns/op 12.08 MB/s 9.285331e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 229 5.505822e+06 ns/op 11.9 MB/s 9.285334e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 220 5.271599e+06 ns/op 12.43 MB/s 9.285331e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 229 5.50148e+06 ns/op 11.91 MB/s 9.285339e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 211 5.594794e+06 ns/op 11.71 MB/s 9.285335e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 218 5.449454e+06 ns/op 12.03 MB/s 9.285335e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 211 5.524356e+06 ns/op 11.86 MB/s 9.285332e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 213 5.543786e+06 ns/op 11.82 MB/s 9.285342e+06 B/op 107 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 18 7.4107434e+07 ns/op 14.15 MB/s 1.64574853e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.6939121e+07 ns/op 13.63 MB/s 1.64574515e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.0118687e+07 ns/op 13.09 MB/s 1.64574558e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 7.9396842e+07 ns/op 13.21 MB/s 1.64574533e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.0425569e+07 ns/op 13.04 MB/s 1.64574541e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.1728653e+07 ns/op 12.83 MB/s 1.64574549e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 8.1735177e+07 ns/op 12.83 MB/s 1.64574508e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.0531444e+07 ns/op 13.02 MB/s 1.64574533e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8696242e+07 ns/op 13.32 MB/s 1.64574531e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 7.9835963e+07 ns/op 13.13 MB/s 1.64574517e+08 B/op 622 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22503 52724 ns/op 19.61 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23161 53267 ns/op 19.41 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22696 54191 ns/op 19.08 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 21891 53393 ns/op 19.37 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22026 53599 ns/op 19.29 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22737 53115 ns/op 19.47 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 21966 54045 ns/op 19.13 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22215 54201 ns/op 19.08 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22311 55738 ns/op 18.55 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 21316 54472 ns/op 18.98 MB/s 92608 B/op 43 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 279 4.406251e+06 ns/op 14.87 MB/s 5.893478e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 273 4.378019e+06 ns/op 14.97 MB/s 5.893479e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 276 4.271998e+06 ns/op 15.34 MB/s 5.893476e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 285 4.228529e+06 ns/op 15.5 MB/s 5.893478e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 276 4.431388e+06 ns/op 14.79 MB/s 5.89348e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 274 4.427621e+06 ns/op 14.8 MB/s 5.893481e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 273 4.264495e+06 ns/op 15.37 MB/s 5.893482e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 297 4.114335e+06 ns/op 15.93 MB/s 5.893475e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 282 4.249894e+06 ns/op 15.42 MB/s 5.893477e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 270 4.294874e+06 ns/op 15.26 MB/s 5.89348e+06 B/op 84 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3955365e+07 ns/op 16.4 MB/s 1.10928092e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3867034e+07 ns/op 16.42 MB/s 1.10928061e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3293301e+07 ns/op 16.57 MB/s 1.10928072e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3122281e+07 ns/op 16.61 MB/s 1.10928073e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3179136e+07 ns/op 16.6 MB/s 1.10928093e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.432926e+07 ns/op 16.3 MB/s 1.10928086e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3214335e+07 ns/op 16.59 MB/s 1.10928121e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3417434e+07 ns/op 16.53 MB/s 1.10928067e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3391137e+07 ns/op 16.54 MB/s 1.10928079e+08 B/op 359 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.2512064e+07 ns/op 16.77 MB/s 1.10928067e+08 B/op 359 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19615 61085 ns/op 16.91 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19327 62647 ns/op 16.49 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19471 61292 ns/op 16.85 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19258 62149 ns/op 16.62 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 18934 62228 ns/op 16.6 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19300 61862 ns/op 16.7 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19381 61134 ns/op 16.9 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19339 62614 ns/op 16.5 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19650 61289 ns/op 16.85 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 19312 64292 ns/op 16.07 MB/s 91472 B/op 53 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 207 5.727538e+06 ns/op 11.44 MB/s 1.0203052e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 196 5.930894e+06 ns/op 11.05 MB/s 1.0203054e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 199 5.97393e+06 ns/op 10.97 MB/s 1.0203057e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 204 6.043115e+06 ns/op 10.85 MB/s 1.0203055e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 190 6.378874e+06 ns/op 10.28 MB/s 1.020305e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 194 6.209061e+06 ns/op 10.56 MB/s 1.0203052e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 192 6.16533e+06 ns/op 10.63 MB/s 1.0203054e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 192 6.2297e+06 ns/op 10.52 MB/s 1.0203053e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 192 6.247756e+06 ns/op 10.49 MB/s 1.0203057e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 195 6.169213e+06 ns/op 10.62 MB/s 1.0203054e+07 B/op 103 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.1323978e+07 ns/op 12.89 MB/s 1.75181733e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.0046083e+07 ns/op 13.1 MB/s 1.75181782e+08 B/op 151 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.1019413e+07 ns/op 12.94 MB/s 1.75181717e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.1187236e+07 ns/op 12.92 MB/s 1.75181693e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.0527948e+07 ns/op 13.02 MB/s 1.75181747e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.3236348e+07 ns/op 12.6 MB/s 1.7518179e+08 B/op 151 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.2176743e+07 ns/op 12.76 MB/s 1.75181764e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.0932106e+07 ns/op 12.96 MB/s 1.75181756e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.235478e+07 ns/op 12.73 MB/s 1.75181713e+08 B/op 150 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 8.3229086e+07 ns/op 12.6 MB/s 1.75181773e+08 B/op 151 allocs/op +BenchmarkFile/Set/n=10-12 30895 37357 ns/op 79156 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 35222 36026 ns/op 79156 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 31662 38499 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 29900 38924 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 31828 37913 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 29434 38475 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 31032 37017 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 33693 42788 ns/op 79156 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 33193 34223 ns/op 79155 B/op 161 allocs/op +BenchmarkFile/Set/n=10-12 35308 34704 ns/op 79156 B/op 162 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2074623 595.5 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2074032 579 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2051220 586.1 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2030247 585.3 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2056933 586.7 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2077357 581 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2052550 584.7 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2043246 586.2 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2066917 582.6 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 2043548 581.9 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22312 53506 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22234 54093 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22412 53512 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22231 53811 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 21938 56133 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 21019 55154 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22291 54227 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 20970 56859 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 21548 56258 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/SetNewSection/n=10-12 20793 55855 ns/op 112072 B/op 296 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22939 52691 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22183 52748 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22447 53262 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22678 52941 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 21838 55029 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 21415 55100 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 21428 55302 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 21957 54722 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22525 53152 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 22014 54701 ns/op 110456 B/op 239 allocs/op +BenchmarkFile/Bytes/n=10-12 3867669 303.9 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4109804 297.8 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3951469 298.5 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4004516 303.6 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3907399 310.3 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4051450 297.2 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4044996 294.8 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4081429 301.3 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4085800 303 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3632848 307.2 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Set/n=1000-12 262 4.63775e+06 ns/op 9.080756e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 254 4.577455e+06 ns/op 9.080775e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 265 4.548097e+06 ns/op 9.080754e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 264 4.532329e+06 ns/op 9.080748e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 267 4.496907e+06 ns/op 9.080755e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 266 4.611598e+06 ns/op 9.080752e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 262 4.524577e+06 ns/op 9.080753e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 262 4.524664e+06 ns/op 9.080751e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 267 4.485563e+06 ns/op 9.080753e+06 B/op 8158 allocs/op +BenchmarkFile/Set/n=1000-12 267 4.526553e+06 ns/op 9.080748e+06 B/op 8158 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29433 40547 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29688 40248 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29122 40642 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29416 40816 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29971 40231 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29672 40278 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29830 40457 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29086 40866 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 29964 40343 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 30118 40082 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 175 6.798115e+06 ns/op 1.2362806e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 175 6.887704e+06 ns/op 1.236281e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 172 6.861706e+06 ns/op 1.2362796e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 174 6.892087e+06 ns/op 1.2362812e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 170 6.960185e+06 ns/op 1.236281e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 177 6.753263e+06 ns/op 1.2362817e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 172 6.876183e+06 ns/op 1.2362809e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 171 7.024126e+06 ns/op 1.2362811e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 168 6.993871e+06 ns/op 1.2362802e+07 B/op 16254 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 176 6.841669e+06 ns/op 1.2362803e+07 B/op 16254 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 172 6.841966e+06 ns/op 1.292618e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 172 6.825954e+06 ns/op 1.2926181e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 163 7.246492e+06 ns/op 1.2926193e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 174 6.980143e+06 ns/op 1.2926184e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 158 7.295322e+06 ns/op 1.2926181e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 166 6.954853e+06 ns/op 1.2926182e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 166 7.153265e+06 ns/op 1.2926188e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 169 6.962722e+06 ns/op 1.2926181e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 174 6.870898e+06 ns/op 1.2926187e+07 B/op 12280 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 168 7.047383e+06 ns/op 1.2926195e+07 B/op 12280 allocs/op +BenchmarkFile/Bytes/n=1000-12 44668 28972 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 42621 30060 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 36308 30783 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 37310 30085 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 40918 31566 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 38487 31343 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 39242 31834 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 35860 32289 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 36783 32320 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 36924 31025 ns/op 155648 B/op 1 allocs/op diff --git "a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" new file mode 100644 index 0000000..1d4c7d4 Binary files /dev/null and "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkEdit.profile" differ diff --git "a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkFile.profile" "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkFile.profile" new file mode 100644 index 0000000..514746f Binary files /dev/null and "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkFile.profile" differ diff --git "a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkParse.profile" "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkParse.profile" new file mode 100644 index 0000000..730353b Binary files /dev/null and "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/cpu/\341\232\227\341\232\227BenchmarkParse.profile" differ diff --git a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/git.diff b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/git.diff new file mode 100644 index 0000000..eea72da --- /dev/null +++ b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/git.diff @@ -0,0 +1,680 @@ +diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff +new file mode 100644 +index 0000000..b6e6218 +--- /dev/null ++++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/git.diff +@@ -0,0 +1,69 @@ ++diff --git a/env_test.go b/env_test.go ++index c4f3ce1..780217e 100644 ++--- a/env_test.go +++++ b/env_test.go ++@@ -1697,6 +1697,16 @@ func BenchmarkEnv(b *testing.B) { ++ _, _ = env.LoadProtected() ++ } ++ }) +++ b.Run("LoadFileManyGitdirConditions", func(b *testing.B) { +++ // The git directory's real path is resolved once per load, not +++ // once per condition. +++ path := filepath.Join(tempDir(b), "config") +++ writeFile(b, path, strings.Repeat("[includeIf \"gitdir:/nope/\"]\n\tpath = x\n", 1000)) +++ b.ReportAllocs() +++ for b.Loop() { +++ _, _ = env.LoadFile(path) +++ } +++ }) ++ b.Run("UpdateFile", func(b *testing.B) { ++ path := filepath.Join(env.GitDir, "config") ++ i := 0 ++diff --git a/file_test.go b/file_test.go ++index 1415a54..cb6a960 100644 ++--- a/file_test.go +++++ b/file_test.go ++@@ -1063,6 +1063,43 @@ func BenchmarkParse(b *testing.B) { ++ } ++ } ++ +++// BenchmarkEdit measures edits that touch many lines at once, on inputs +++// built to hit their slow paths; each iteration parses the file first. +++// Throughput (MB/s) must not fall as the size grows: these edits are linear. +++func BenchmarkEdit(b *testing.B) { +++ edits := []struct { +++ name string +++ gen func(n int) []byte +++ edit func(f *gitconfig.File) error +++ }{ +++ {"UnsetAll one section", func(n int) []byte { return sized("[a]\n", "\tk = v\n", n) }, func(f *gitconfig.File) error { +++ _, err := f.UnsetAll("a.k") +++ return err +++ }}, +++ {"UnsetAll section per value", func(n int) []byte { return sized("", "[a]\n\tk = v\n", n) }, func(f *gitconfig.File) error { +++ _, err := f.UnsetAll("a.k") +++ return err +++ }}, +++ {"RenameSection one line", func(n int) []byte { return append(sized("", "[a]", n), " k = v\n"...) }, func(f *gitconfig.File) error { +++ return f.RenameSection("a", "b") +++ }}, +++ } +++ for _, e := range edits { +++ for _, n := range []int{1 << 10, 1 << 16, 1 << 20} { +++ data := e.gen(n) +++ b.Run(fmt.Sprintf("%s/%dKiB", e.name, n>>10), func(b *testing.B) { +++ b.SetBytes(int64(len(data))) +++ b.ReportAllocs() +++ for b.Loop() { +++ f, err := gitconfig.Parse("f", data) +++ require.NoError(b, err) +++ require.NoError(b, e.edit(f)) +++ } +++ }) +++ } +++ } +++} +++ ++ func BenchmarkFile(b *testing.B) { ++ for _, n := range []int{10, 1000} { ++ data := []byte(bigConfig(n)) +diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json +new file mode 100644 +index 0000000..d9b3e36 +--- /dev/null ++++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/meta.json +@@ -0,0 +1,24 @@ ++{ ++ "name": "edit-benches", ++ "benchs": [ ++ "BenchmarkEnv/Load", ++ "BenchmarkEnv/LoadProtected", ++ "BenchmarkEnv/LoadFileManyGitdirConditions", ++ "BenchmarkEnv/UpdateFile", ++ "BenchmarkEnv/UpdateFileUnchanged", ++ "BenchmarkEdit" ++ ], ++ "git_commit": "9f097cd6b40f09dd20e5e00b92bd6ded3ca0f281", ++ "machine": { ++ "goos": "linux", ++ "goarch": "amd64", ++ "num_cpu": 12, ++ "physical_cores": 6, ++ "threads_per_core": 2, ++ "cpu": "AMD Ryzen 5 3600 6-Core Processor", ++ "cache_l1d": 32768, ++ "cache_l2": 524288, ++ "cache_l3": 16777216 ++ }, ++ "go_version": "go1.27.1" ++} +diff --git a/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench +new file mode 100644 +index 0000000..df8598f +--- /dev/null ++++ b/.benchspotter/sessions/01a120a7-af08-711e-9648-85d370040ba2/results.bench +@@ -0,0 +1,145 @@ ++goos: linux ++goarch: amd64 ++pkg: github.com/git-bug/gitconfig ++cpu: AMD Ryzen 5 3600 6-Core Processor ++ ++BenchmarkEnv/Load-12 5012 241503 ns/op 332510 B/op 932 allocs/op ++BenchmarkEnv/Load-12 5055 237011 ns/op 332506 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4833 247790 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4948 262598 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4952 247394 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4938 237301 ns/op 332506 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4702 242795 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4986 237644 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 5089 232608 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/Load-12 4891 234684 ns/op 332507 B/op 932 allocs/op ++BenchmarkEnv/LoadProtected-12 18220 65050 ns/op 62041 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18184 65361 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18378 65616 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18579 64420 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18400 64840 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18554 64231 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18663 64566 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18583 64480 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18693 64180 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadProtected-12 18700 64691 ns/op 62040 B/op 219 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1105 1.123003e+06 ns/op 1.881754e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1070 1.101759e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1053 1.101034e+06 ns/op 1.881737e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1146 1.094549e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1125 1.082766e+06 ns/op 1.881735e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1135 1.108419e+06 ns/op 1.881735e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1063 1.09794e+06 ns/op 1.881737e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1086 1.10521e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 1094 1.171132e+06 ns/op 1.881737e+06 B/op 12101 allocs/op ++BenchmarkEnv/LoadFileManyGitdirConditions-12 986 1.125506e+06 ns/op 1.881736e+06 B/op 12101 allocs/op ++BenchmarkEnv/UpdateFile-12 4190 271686 ns/op 460668 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4456 274471 ns/op 460667 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4729 275723 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4462 271889 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4380 270158 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4204 277096 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4135 278751 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4646 270537 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4436 272529 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFile-12 4336 272191 ns/op 460666 B/op 993 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 107961 ns/op 136545 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 107468 ns/op 136545 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 108063 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 109785 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 108510 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 108888 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 9543 112027 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 110303 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 110549 ns/op 136544 B/op 480 allocs/op ++BenchmarkEnv/UpdateFileUnchanged-12 10000 109377 ns/op 136544 B/op 480 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 24992 48458 ns/op 21.17 MB/s 116801 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25394 48523 ns/op 21.14 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 23578 48785 ns/op 21.03 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25435 47119 ns/op 21.77 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25606 47586 ns/op 21.56 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25616 47607 ns/op 21.55 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25425 47311 ns/op 21.69 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25312 47771 ns/op 21.48 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25722 47106 ns/op 21.78 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1KiB-12 25611 46886 ns/op 21.88 MB/s 116800 B/op 45 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 229 5.223861e+06 ns/op 12.55 MB/s 9.285321e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 232 5.112092e+06 ns/op 12.82 MB/s 9.28532e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 238 5.116727e+06 ns/op 12.81 MB/s 9.285327e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 224 5.389997e+06 ns/op 12.16 MB/s 9.285338e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 218 5.208605e+06 ns/op 12.58 MB/s 9.285335e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 238 4.952627e+06 ns/op 13.23 MB/s 9.285355e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 236 4.986969e+06 ns/op 13.14 MB/s 9.285334e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 236 5.071369e+06 ns/op 12.92 MB/s 9.285339e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 235 5.001953e+06 ns/op 13.1 MB/s 9.285328e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/64KiB-12 247 4.941395e+06 ns/op 13.26 MB/s 9.285326e+06 B/op 107 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 18 7.4505016e+07 ns/op 14.07 MB/s 1.64574835e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.4735616e+07 ns/op 14.03 MB/s 1.64574529e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.5954921e+07 ns/op 13.81 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 16 7.7238758e+07 ns/op 13.58 MB/s 1.64574515e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.7903187e+07 ns/op 13.46 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 8.0328085e+07 ns/op 13.05 MB/s 1.64574541e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8560708e+07 ns/op 13.35 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.6633014e+07 ns/op 13.68 MB/s 1.64574917e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 15 7.8626452e+07 ns/op 13.34 MB/s 1.64574538e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_one_section/1024KiB-12 14 7.7921155e+07 ns/op 13.46 MB/s 1.64574525e+08 B/op 622 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23151 52263 ns/op 19.78 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 22872 51578 ns/op 20.05 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23139 52356 ns/op 19.75 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 20388 55894 ns/op 18.5 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23473 51433 ns/op 20.1 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23798 50874 ns/op 20.32 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23052 53274 ns/op 19.41 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23304 51407 ns/op 20.11 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23028 52248 ns/op 19.79 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 23043 52120 ns/op 19.84 MB/s 92608 B/op 43 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 285 4.233862e+06 ns/op 15.48 MB/s 5.893476e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 279 4.254785e+06 ns/op 15.4 MB/s 5.893476e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.177645e+06 ns/op 15.69 MB/s 5.893478e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 283 4.225172e+06 ns/op 15.51 MB/s 5.893476e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.190192e+06 ns/op 15.64 MB/s 5.893479e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 284 4.19033e+06 ns/op 15.64 MB/s 5.893482e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 267 4.401359e+06 ns/op 14.89 MB/s 5.893483e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 289 4.19691e+06 ns/op 15.62 MB/s 5.89348e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 271 4.336991e+06 ns/op 15.11 MB/s 5.893481e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 282 4.261353e+06 ns/op 15.38 MB/s 5.893478e+06 B/op 84 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.5209017e+07 ns/op 16.08 MB/s 1.10928098e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4119783e+07 ns/op 16.35 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.3904657e+07 ns/op 16.41 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.3818774e+07 ns/op 16.43 MB/s 1.10928051e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4315706e+07 ns/op 16.3 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4330359e+07 ns/op 16.3 MB/s 1.10928072e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4296497e+07 ns/op 16.31 MB/s 1.10928086e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4794364e+07 ns/op 16.18 MB/s 1.10928079e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 16 6.4290026e+07 ns/op 16.31 MB/s 1.10928107e+08 B/op 359 allocs/op ++BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 18 6.5052421e+07 ns/op 16.12 MB/s 1.10928073e+08 B/op 359 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19653 60606 ns/op 17.04 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19838 60903 ns/op 16.96 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19718 61200 ns/op 16.88 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19851 60693 ns/op 17.02 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19701 60974 ns/op 16.94 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19495 60933 ns/op 16.95 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19958 60714 ns/op 17.01 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19819 60609 ns/op 17.04 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19388 61425 ns/op 16.82 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/1KiB-12 19386 61863 ns/op 16.7 MB/s 91472 B/op 53 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 200 5.925565e+06 ns/op 11.06 MB/s 1.0203044e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 202 5.820491e+06 ns/op 11.26 MB/s 1.0203058e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 206 5.833726e+06 ns/op 11.24 MB/s 1.0203054e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 198 5.979629e+06 ns/op 10.96 MB/s 1.0203056e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 200 6.009091e+06 ns/op 10.91 MB/s 1.0203047e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 198 5.995473e+06 ns/op 10.93 MB/s 1.0203043e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 199 5.953213e+06 ns/op 11.01 MB/s 1.0203049e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 194 6.095935e+06 ns/op 10.75 MB/s 1.0203063e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 201 5.925326e+06 ns/op 11.06 MB/s 1.0203056e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/64KiB-12 205 5.881998e+06 ns/op 11.14 MB/s 1.0203051e+07 B/op 103 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.003303e+07 ns/op 13.1 MB/s 1.75181749e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.0758246e+07 ns/op 12.98 MB/s 1.75181781e+08 B/op 151 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.8895688e+07 ns/op 13.29 MB/s 1.75181799e+08 B/op 151 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.8704744e+07 ns/op 13.32 MB/s 1.75181749e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.959157e+07 ns/op 13.17 MB/s 1.75181717e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.955495e+07 ns/op 13.18 MB/s 1.75181733e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 7.9595676e+07 ns/op 13.17 MB/s 1.75181733e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.9909298e+07 ns/op 13.12 MB/s 1.7518173e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 14 8.0221386e+07 ns/op 13.07 MB/s 1.75181749e+08 B/op 150 allocs/op ++BenchmarkEdit/RenameSection_one_line/1024KiB-12 13 7.9051805e+07 ns/op 13.26 MB/s 1.7518179e+08 B/op 151 allocs/op +diff --git a/edit.go b/edit.go +index 3862ece..da3e2e2 100644 +--- a/edit.go ++++ b/edit.go +@@ -92,23 +92,61 @@ type flatEntry struct { + implicit bool + } + +-func flatten(p parsed) []flatEntry { +- var out []flatEntry +- for _, it := range p.items { +- if it.kind == itemEntry { +- out = append(out, flatEntry{it.inSection, p.secOf(it), p.nameOf(it), p.valueOf(it), it.implicit}) ++func (e flatEntry) is(k Key) bool { ++ return e.inSection && e.sec == k.sectionKey() && e.name == k.Name ++} ++ ++// flatCursor walks the entries of a parsed file, without copying them out: ++// the checks of an edit compare two whole files. ++type flatCursor struct { ++ p *parsed ++ i int ++ keep func(flatEntry) bool ++} ++ ++// next returns the next entry keep accepts, or false at the end. ++func (c *flatCursor) next() (flatEntry, bool) { ++ for c.i < len(c.p.items) { ++ it := c.p.items[c.i] ++ c.i++ ++ if it.kind != itemEntry { ++ continue ++ } ++ e := flatEntry{it.inSection, c.p.secOf(it), c.p.nameOf(it), c.p.valueOf(it), it.implicit} ++ if c.keep == nil || c.keep(e) { ++ return e, true + } + } +- return out ++ return flatEntry{}, false + } + +-func (e flatEntry) is(k Key) bool { +- return e.inSection && e.sec == k.sectionKey() && e.name == k.Name ++// sameEntries reports whether the entries of before that keepBefore accepts, ++// changed by conv, are the entries of after that keepAfter accepts. A nil ++// function keeps every entry, or changes none. ++func sameEntries(before, after *parsed, keepBefore, keepAfter func(flatEntry) bool, conv func(flatEntry) flatEntry) bool { ++ b := flatCursor{p: before, keep: keepBefore} ++ a := flatCursor{p: after, keep: keepAfter} ++ for { ++ eb, okB := b.next() ++ ea, okA := a.next() ++ if okB != okA { ++ return false ++ } ++ if !okB { ++ return true ++ } ++ if conv != nil { ++ eb = conv(eb) ++ } ++ if eb != ea { ++ return false ++ } ++ } + } + + // apply splices the bytes of the file, parses the result, and keeps it if + // check accepts the entries before and after the edit. +-func (f *File) apply(key string, splices []splice, check func(before, after []flatEntry) bool) error { ++func (f *File) apply(key string, splices []splice, check func(before, after *parsed) bool) error { + slices.SortFunc(splices, func(a, b splice) int { return a.start - b.start }) + data := make([]byte, 0, len(f.data)+64) + at := 0 +@@ -119,7 +157,7 @@ func (f *File) apply(key string, splices []splice, check func(before, after []fl + } + data = append(data, f.data[at:]...) + p, err := parse(f.name, data) +- if err != nil || !check(flatten(f.parsed), flatten(p)) { ++ if err != nil || !check(&f.parsed, &p) { + return &KeyError{Key: key, Err: errUnsafeEdit} + } + f.data = data +@@ -129,34 +167,25 @@ func (f *File) apply(key string, splices []splice, check func(before, after []fl + } + + // checkKey returns a check that the entries of other keys are unchanged and +-// that k has the values want after the edit. +-func checkKey(k Key, want []string, implicitAllowed bool) func(before, after []flatEntry) bool { +- return func(before, after []flatEntry) bool { ++// that k has the values want after the edit. Only the first implicitOK of ++// them may be implicit. ++func checkKey(k Key, want []string, implicitOK int) func(before, after *parsed) bool { ++ return func(before, after *parsed) bool { + other := func(e flatEntry) bool { return !e.is(k) } +- if !slices.Equal(filter(before, other), filter(after, other)) { +- return false +- } +- got := filter(after, func(e flatEntry) bool { return e.is(k) }) +- if len(got) != len(want) { ++ if !sameEntries(before, after, other, other, nil) { + return false + } +- for i, e := range got { +- if e.value != want[i] || e.implicit && !implicitAllowed { ++ got := flatCursor{p: after, keep: func(e flatEntry) bool { return e.is(k) }} ++ for i := 0; ; i++ { ++ e, ok := got.next() ++ if !ok { ++ return i == len(want) ++ } ++ if i == len(want) || e.value != want[i] || e.implicit && i >= implicitOK { + return false + } + } +- return true +- } +-} +- +-func filter(entries []flatEntry, keep func(flatEntry) bool) []flatEntry { +- var out []flatEntry +- for _, e := range entries { +- if keep(e) { +- out = append(out, e) +- } + } +- return out + } + + // Layout helpers. A token is a header or an entry; the whitespace before a +@@ -489,8 +518,8 @@ func (f *File) removeSection(name string, sk sectionKey) (bool, error) { + } + splices = append(splices, splice{f.ownStart(h), end, ""}) + } +- err := f.apply(name, splices, func(before, after []flatEntry) bool { +- return slices.Equal(filter(before, func(e flatEntry) bool { return !e.inSection || e.sec != sk }), after) ++ err := f.apply(name, splices, func(before, after *parsed) bool { ++ return sameEntries(before, after, func(e flatEntry) bool { return !e.inSection || e.sec != sk }, nil, nil) + }) + return err == nil, err + } +@@ -537,12 +566,12 @@ func (f *File) renameSection(name string, sk, to sectionKey, header string) erro + splices = append(splices, splice{f.ownStart(h), f.lineEnd(h), text.String()}) + done = end - 1 + } +- return f.apply(name, splices, func(before, after []flatEntry) bool { +- for i := range before { +- if before[i].inSection && before[i].sec == sk { +- before[i].sec = to ++ return f.apply(name, splices, func(before, after *parsed) bool { ++ return sameEntries(before, after, nil, nil, func(e flatEntry) flatEntry { ++ if e.inSection && e.sec == sk { ++ e.sec = to + } +- } +- return slices.Equal(before, after) ++ return e ++ }) + }) + } +diff --git a/env_test.go b/env_test.go +index c4f3ce1..780217e 100644 +--- a/env_test.go ++++ b/env_test.go +@@ -1697,6 +1697,16 @@ func BenchmarkEnv(b *testing.B) { + _, _ = env.LoadProtected() + } + }) ++ b.Run("LoadFileManyGitdirConditions", func(b *testing.B) { ++ // The git directory's real path is resolved once per load, not ++ // once per condition. ++ path := filepath.Join(tempDir(b), "config") ++ writeFile(b, path, strings.Repeat("[includeIf \"gitdir:/nope/\"]\n\tpath = x\n", 1000)) ++ b.ReportAllocs() ++ for b.Loop() { ++ _, _ = env.LoadFile(path) ++ } ++ }) + b.Run("UpdateFile", func(b *testing.B) { + path := filepath.Join(env.GitDir, "config") + i := 0 +diff --git a/file.go b/file.go +index 19d352d..a0484be 100644 +--- a/file.go ++++ b/file.go +@@ -102,9 +102,9 @@ func (f *File) Set(key, value string) error { + if it := f.items[idx[0]]; !it.implicit && f.valueOf(it) == value { + return nil + } +- return f.apply(key, []splice{f.replaceEntry(idx[0], name, value)}, checkKey(k, []string{value}, false)) ++ return f.apply(key, []splice{f.replaceEntry(idx[0], name, value)}, checkKey(k, []string{value}, 0)) + } +- return f.apply(key, []splice{f.insert(k, section, sub, name, value)}, checkKey(k, []string{value}, false)) ++ return f.apply(key, []splice{f.insert(k, section, sub, name, value)}, checkKey(k, []string{value}, 0)) + } + + // ReplaceAll replaces all values of key with a single value, or adds it if +@@ -116,7 +116,7 @@ func (f *File) ReplaceAll(key, value string) error { + } + idx := f.entriesOf(k) + if len(idx) == 0 { +- return f.apply(key, []splice{f.insert(k, section, sub, name, value)}, checkKey(k, []string{value}, false)) ++ return f.apply(key, []splice{f.insert(k, section, sub, name, value)}, checkKey(k, []string{value}, 0)) + } + // The values collapse into one, at the place of the last. + var splices []splice +@@ -124,7 +124,7 @@ func (f *File) ReplaceAll(key, value string) error { + splices = append(splices, f.removeEntry(i)) + } + splices = append(splices, f.replaceEntry(idx[len(idx)-1], name, value)) +- return f.apply(key, splices, checkKey(k, []string{value}, false)) ++ return f.apply(key, splices, checkKey(k, []string{value}, 0)) + } + + // Add adds a value to key, keeping existing ones, like `git config --add`. +@@ -138,11 +138,8 @@ func (f *File) Add(key, value string) error { + want = append(want, f.valueOf(f.items[i])) + } + want = append(want, value) +- return f.apply(key, []splice{f.insert(k, section, sub, name, value)}, func(before, after []flatEntry) bool { +- // Implicit values are kept: only the new one must not be. +- got := filter(after, func(e flatEntry) bool { return e.is(k) }) +- return checkKey(k, want, true)(before, after) && !got[len(got)-1].implicit +- }) ++ // Implicit values are kept: only the new one must not be. ++ return f.apply(key, []splice{f.insert(k, section, sub, name, value)}, checkKey(k, want, len(want)-1)) + } + + // Unset removes the value of key, like `git config --unset `, and +@@ -158,7 +155,7 @@ func (f *File) Unset(key string) (removed bool, err error) { + case 0: + return false, nil + case 1: +- err := f.apply(key, f.unsetEntries(idx), checkKey(k, nil, false)) ++ err := f.apply(key, f.unsetEntries(idx), checkKey(k, nil, 0)) + return err == nil, err + } + return false, &KeyError{Key: key, Err: ErrMultipleValues} +@@ -175,7 +172,7 @@ func (f *File) UnsetAll(key string) (removed int, err error) { + if len(idx) == 0 { + return 0, nil + } +- if err := f.apply(key, f.unsetEntries(idx), checkKey(k, nil, false)); err != nil { ++ if err := f.apply(key, f.unsetEntries(idx), checkKey(k, nil, 0)); err != nil { + return 0, err + } + return len(idx), nil +diff --git a/file_test.go b/file_test.go +index 1415a54..cb6a960 100644 +--- a/file_test.go ++++ b/file_test.go +@@ -1063,6 +1063,43 @@ func BenchmarkParse(b *testing.B) { + } + } + ++// BenchmarkEdit measures edits that touch many lines at once, on inputs ++// built to hit their slow paths; each iteration parses the file first. ++// Throughput (MB/s) must not fall as the size grows: these edits are linear. ++func BenchmarkEdit(b *testing.B) { ++ edits := []struct { ++ name string ++ gen func(n int) []byte ++ edit func(f *gitconfig.File) error ++ }{ ++ {"UnsetAll one section", func(n int) []byte { return sized("[a]\n", "\tk = v\n", n) }, func(f *gitconfig.File) error { ++ _, err := f.UnsetAll("a.k") ++ return err ++ }}, ++ {"UnsetAll section per value", func(n int) []byte { return sized("", "[a]\n\tk = v\n", n) }, func(f *gitconfig.File) error { ++ _, err := f.UnsetAll("a.k") ++ return err ++ }}, ++ {"RenameSection one line", func(n int) []byte { return append(sized("", "[a]", n), " k = v\n"...) }, func(f *gitconfig.File) error { ++ return f.RenameSection("a", "b") ++ }}, ++ } ++ for _, e := range edits { ++ for _, n := range []int{1 << 10, 1 << 16, 1 << 20} { ++ data := e.gen(n) ++ b.Run(fmt.Sprintf("%s/%dKiB", e.name, n>>10), func(b *testing.B) { ++ b.SetBytes(int64(len(data))) ++ b.ReportAllocs() ++ for b.Loop() { ++ f, err := gitconfig.Parse("f", data) ++ require.NoError(b, err) ++ require.NoError(b, e.edit(f)) ++ } ++ }) ++ } ++ } ++} ++ + func BenchmarkFile(b *testing.B) { + for _, n := range []int{10, 1000} { + data := []byte(bigConfig(n)) +diff --git a/parse.go b/parse.go +index 0ddcfba..06030b0 100644 +--- a/parse.go ++++ b/parse.go +@@ -93,14 +93,15 @@ type parser struct { + sec int32 // current section + inSection bool + secIndex map[sectionKey]int32 ++ headers map[string]int32 // the section of each header, by its bytes + names map[string]string // key names, interned +- buf []byte // scratch for values ++ buf []byte // scratch for values and subsections + } + + // parse splits data into items, or returns the *SyntaxError git would + // report. + func parse(name string, data []byte) (parsed, error) { +- p := &parser{name: name, data: data, line: 1, secIndex: make(map[sectionKey]int32), names: make(map[string]string)} ++ p := &parser{name: name, data: data, line: 1, secIndex: make(map[sectionKey]int32), headers: make(map[string]int32), names: make(map[string]string)} + p.items = make([]item, 0, 2*bytes.Count(data, []byte{'\n'})+4) + p.secs = []sectionKey{{}} + if err := p.run(); err != nil { +@@ -131,16 +132,15 @@ func (p *parser) addEntry(start, end int, name []byte, value string) *item { + return it + } + +-// setSection makes sec the current section. +-func (p *parser) setSection(sec sectionKey) { ++// section returns the index of sec in p.secs, adding it if absent. ++func (p *parser) section(sec sectionKey) int32 { + i, ok := p.secIndex[sec] + if !ok { + i = int32(len(p.secs)) + p.secs = append(p.secs, sec) + p.secIndex[sec] = i + } +- p.sec = i +- p.inSection = true ++ return i + } + + // isSpace reports whether data[i] is whitespace between tokens: a space, a +@@ -219,22 +219,19 @@ func (p *parser) header() error { + for i < len(d) && (isKeyChar(d[i]) || d[i] == '.') { + i++ + } +- name := lowerASCII(string(d[start+1 : i])) ++ nameEnd := i + if i == len(d) { + return p.errorAt(p.line+1, "unterminated section header") + } + +- var sec sectionKey ++ sub := p.buf[:0] ++ defer func() { p.buf = sub[:0] }() ++ hasSub := false + switch { + case d[i] == ']': +- if name == "" { ++ if nameEnd == start+1 { + return p.errorAt(p.line, "empty section name") + } +- sec.section = name +- if dot := strings.IndexByte(name, '.'); dot >= 0 { +- // Legacy header [a.B]: subsection "b". +- sec = sectionKey{name[:dot], name[dot+1:], true} +- } + i++ + + case isSpace(d, i): +@@ -245,7 +242,6 @@ func (p *parser) header() error { + return p.errorAt(p.line, "invalid section header") + } + i++ +- var sub []byte + for { + if i == len(d) || d[i] == '\n' { + return p.errorAt(p.line, "unterminated subsection") +@@ -277,22 +273,43 @@ func (p *parser) header() error { + return p.errorAt(p.line, "invalid section header") + } + i++ +- sec = sectionKey{name, string(sub), true} +- if dot := strings.IndexByte(name, '.'); dot >= 0 { +- // [a.b "x"]: git reads the keys as a.b.x.. +- sec = sectionKey{name[:dot], name[dot+1:] + "." + string(sub), true} +- } ++ hasSub = true + + default: + return p.errorAt(p.line, "invalid section header") + } + +- p.setSection(sec) ++ // The section is decoded once for each spelling of a header: files ++ // repeat their headers. ++ sec, ok := p.headers[string(d[start:i])] ++ if !ok { ++ sec = p.section(headerKey(lowerASCII(string(d[start+1:nameEnd])), sub, hasSub)) ++ p.headers[string(d[start:i])] = sec ++ } ++ p.sec = sec ++ p.inSection = true + p.pos = i + p.add(itemHeader, start, i) + return nil + } + ++// headerKey returns the section of a header with the lowered name, and the ++// subsection sub if hasSub. ++func headerKey(name string, sub []byte, hasSub bool) sectionKey { ++ dot := strings.IndexByte(name, '.') ++ switch { ++ case !hasSub && dot < 0: ++ return sectionKey{section: name} ++ case !hasSub: ++ // Legacy header [a.B]: subsection "b". ++ return sectionKey{name[:dot], name[dot+1:], true} ++ case dot < 0: ++ return sectionKey{name, string(sub), true} ++ } ++ // [a.b "x"]: git reads the keys as a.b.x.. ++ return sectionKey{name[:dot], name[dot+1:] + "." + string(sub), true} ++} ++ + func (p *parser) entry() error { + d := p.data + start := p.pos diff --git "a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkEdit.profile" "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkEdit.profile" new file mode 100644 index 0000000..f16e513 Binary files /dev/null and "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkEdit.profile" differ diff --git "a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkFile.profile" "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkFile.profile" new file mode 100644 index 0000000..0b7dfc5 Binary files /dev/null and "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkFile.profile" differ diff --git "a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkParse.profile" "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkParse.profile" new file mode 100644 index 0000000..4be7075 Binary files /dev/null and "b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/mem/\341\232\227\341\232\227BenchmarkParse.profile" differ diff --git a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/meta.json b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/meta.json new file mode 100644 index 0000000..851ce9d --- /dev/null +++ b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/meta.json @@ -0,0 +1,21 @@ +{ + "name": "alloc-AB", + "benchs": [ + "BenchmarkParse", + "BenchmarkEdit", + "BenchmarkFile" + ], + "git_commit": "9f097cd6b40f09dd20e5e00b92bd6ded3ca0f281", + "machine": { + "goos": "linux", + "goarch": "amd64", + "num_cpu": 12, + "physical_cores": 6, + "threads_per_core": 2, + "cpu": "AMD Ryzen 5 3600 6-Core Processor", + "cache_l1d": 32768, + "cache_l2": 524288, + "cache_l3": 16777216 + }, + "go_version": "go1.27.1" +} diff --git a/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/results.bench b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/results.bench new file mode 100644 index 0000000..600c57d --- /dev/null +++ b/.benchspotter/sessions/01a120b8-cecb-7ce8-8e6b-119d836f987e/results.bench @@ -0,0 +1,465 @@ +goos: linux +goarch: amd64 +pkg: github.com/git-bug/gitconfig +cpu: AMD Ryzen 5 3600 6-Core Processor + +BenchmarkParse/realistic/1KiB-12 98496 12082 ns/op 94.77 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 101469 12106 ns/op 94.58 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 99706 12085 ns/op 94.74 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 101668 12170 ns/op 94.08 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 104539 12012 ns/op 95.32 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 98101 12146 ns/op 94.27 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 100449 12077 ns/op 94.8 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 99398 12106 ns/op 94.58 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 98299 12120 ns/op 94.47 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/1KiB-12 99037 12132 ns/op 94.38 MB/s 23800 B/op 85 allocs/op +BenchmarkParse/realistic/64KiB-12 2174 810528 ns/op 81.09 MB/s 1.396506e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1722 816902 ns/op 80.45 MB/s 1.396503e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1850 643690 ns/op 102.1 MB/s 1.396499e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1858 633611 ns/op 103.73 MB/s 1.396502e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1756 630631 ns/op 104.22 MB/s 1.396499e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1914 636040 ns/op 103.33 MB/s 1.396498e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1900 634445 ns/op 103.59 MB/s 1.396498e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1982 620801 ns/op 105.87 MB/s 1.396498e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1916 765270 ns/op 85.88 MB/s 1.396502e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/64KiB-12 1982 599231 ns/op 109.68 MB/s 1.396495e+06 B/op 2066 allocs/op +BenchmarkParse/realistic/1024KiB-12 128 9.216998e+06 ns/op 113.77 MB/s 2.3731898e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 100 1.103815e+07 ns/op 95 MB/s 2.3731982e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 120 9.878692e+06 ns/op 106.15 MB/s 2.3731896e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 123 9.716865e+06 ns/op 107.91 MB/s 2.3731901e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 123 9.780195e+06 ns/op 107.22 MB/s 2.3731902e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 91 1.1581071e+07 ns/op 90.54 MB/s 2.3731925e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 100 1.0990645e+07 ns/op 95.41 MB/s 2.3731923e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 121 9.874891e+06 ns/op 106.19 MB/s 2.37319e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 128 9.467725e+06 ns/op 110.75 MB/s 2.3731905e+07 B/op 32121 allocs/op +BenchmarkParse/realistic/1024KiB-12 98 1.1610986e+07 ns/op 90.31 MB/s 2.3731923e+07 B/op 32121 allocs/op +BenchmarkParse/one_long_value/1KiB-12 168272 7172 ns/op 142.93 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 184128 5902 ns/op 173.67 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 212677 5542 ns/op 184.95 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 214167 5601 ns/op 183 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 214615 5571 ns/op 183.98 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 211438 5613 ns/op 182.61 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 207934 5539 ns/op 185.05 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 217893 5701 ns/op 179.79 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 204313 5804 ns/op 176.59 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/1KiB-12 223256 5609 ns/op 182.75 MB/s 7184 B/op 23 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4531 265462 ns/op 246.88 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4632 263493 ns/op 248.72 MB/s 426384 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4358 277081 ns/op 236.53 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4620 398611 ns/op 164.41 MB/s 426386 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 3921 293000 ns/op 223.68 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4423 273972 ns/op 239.21 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4557 264860 ns/op 247.44 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4539 271160 ns/op 241.69 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4245 280616 ns/op 233.55 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/64KiB-12 4124 297314 ns/op 220.43 MB/s 426385 B/op 36 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 198 5.843213e+06 ns/op 179.45 MB/s 7.348683e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 212 5.765323e+06 ns/op 181.88 MB/s 7.34868e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 204 6.48352e+06 ns/op 161.73 MB/s 7.348681e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 204 6.04976e+06 ns/op 173.33 MB/s 7.348675e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 152 7.178469e+06 ns/op 146.07 MB/s 7.348686e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 202 5.752349e+06 ns/op 182.29 MB/s 7.34868e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 206 5.716846e+06 ns/op 183.42 MB/s 7.348675e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 204 5.72527e+06 ns/op 183.15 MB/s 7.348684e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 212 5.686385e+06 ns/op 184.4 MB/s 7.348684e+06 B/op 47 allocs/op +BenchmarkParse/one_long_value/1024KiB-12 216 5.684373e+06 ns/op 184.47 MB/s 7.348677e+06 B/op 47 allocs/op +BenchmarkParse/continuations/1KiB-12 159111 7556 ns/op 135.78 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 144363 8105 ns/op 126.59 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 142924 7600 ns/op 135.01 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 156525 7539 ns/op 136.1 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 147247 7918 ns/op 129.57 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 154094 7837 ns/op 130.92 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 161887 7763 ns/op 132.17 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 171967 7040 ns/op 145.74 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 172468 6988 ns/op 146.82 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/1KiB-12 175572 6930 ns/op 148.06 MB/s 25808 B/op 21 allocs/op +BenchmarkParse/continuations/64KiB-12 3117 382878 ns/op 171.17 MB/s 1.581137e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 3019 389403 ns/op 168.3 MB/s 1.581137e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 2731 396542 ns/op 165.27 MB/s 1.581138e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 3147 395942 ns/op 165.52 MB/s 1.581138e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 3140 383666 ns/op 170.82 MB/s 1.581137e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 2457 410520 ns/op 159.65 MB/s 1.581138e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 2727 420864 ns/op 155.72 MB/s 1.581138e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 2930 441887 ns/op 148.31 MB/s 1.581138e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 2593 409242 ns/op 160.14 MB/s 1.581138e+06 B/op 34 allocs/op +BenchmarkParse/continuations/64KiB-12 3240 408440 ns/op 160.46 MB/s 1.581139e+06 B/op 34 allocs/op +BenchmarkParse/continuations/1024KiB-12 241 4.841404e+06 ns/op 216.59 MB/s 2.5100371e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 246 4.831505e+06 ns/op 217.03 MB/s 2.5100369e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 244 4.839587e+06 ns/op 216.67 MB/s 2.5100369e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 243 4.901073e+06 ns/op 213.95 MB/s 2.5100369e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 248 4.811825e+06 ns/op 217.92 MB/s 2.5100369e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 240 4.9261e+06 ns/op 212.86 MB/s 2.5100368e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 240 4.876976e+06 ns/op 215.01 MB/s 2.5100369e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 240 4.889801e+06 ns/op 214.44 MB/s 2.510037e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 238 4.876571e+06 ns/op 215.02 MB/s 2.5100369e+07 B/op 44 allocs/op +BenchmarkParse/continuations/1024KiB-12 248 4.849401e+06 ns/op 216.23 MB/s 2.5100371e+07 B/op 44 allocs/op +BenchmarkParse/quotes/1KiB-12 244732 4676 ns/op 219.4 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 249798 4837 ns/op 212.13 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 254726 4707 ns/op 217.98 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 255338 4688 ns/op 218.84 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 258063 4867 ns/op 210.8 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 243198 4726 ns/op 217.1 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 256525 4647 ns/op 220.8 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 252800 4651 ns/op 220.62 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 262669 4774 ns/op 214.94 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/1KiB-12 232527 4775 ns/op 214.86 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/quotes/64KiB-12 5299 223110 ns/op 293.75 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5137 219847 ns/op 298.11 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5241 227202 ns/op 288.46 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5202 229687 ns/op 285.34 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5478 233586 ns/op 280.57 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5484 224393 ns/op 292.07 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5113 230155 ns/op 284.76 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5166 228993 ns/op 286.2 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5275 228425 ns/op 286.91 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/64KiB-12 5264 226237 ns/op 289.69 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/quotes/1024KiB-12 340 3.524691e+06 ns/op 297.5 MB/s 4.120981e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 340 3.58415e+06 ns/op 292.56 MB/s 4.120984e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 338 3.565965e+06 ns/op 294.05 MB/s 4.120986e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 326 3.580807e+06 ns/op 292.83 MB/s 4.120982e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 338 3.495076e+06 ns/op 300.02 MB/s 4.120983e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 321 3.68879e+06 ns/op 284.26 MB/s 4.120986e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 332 3.759898e+06 ns/op 278.88 MB/s 4.120989e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 333 3.529801e+06 ns/op 297.06 MB/s 4.120983e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 334 3.558531e+06 ns/op 294.67 MB/s 4.120986e+06 B/op 44 allocs/op +BenchmarkParse/quotes/1024KiB-12 301 3.861701e+06 ns/op 271.53 MB/s 4.120989e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1KiB-12 323125 3564 ns/op 289 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 339340 3565 ns/op 288.88 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 322316 3563 ns/op 289.08 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 337710 3549 ns/op 290.25 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 333478 3573 ns/op 288.27 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 324721 3548 ns/op 290.29 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 341749 3628 ns/op 283.94 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 332493 3504 ns/op 293.93 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 356616 3551 ns/op 290.04 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/1KiB-12 328112 3550 ns/op 290.12 MB/s 4368 B/op 21 allocs/op +BenchmarkParse/escapes/64KiB-12 8210 148062 ns/op 442.67 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 7800 148624 ns/op 440.99 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 7808 148647 ns/op 440.92 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 8032 153218 ns/op 427.77 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 8584 150255 ns/op 436.21 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 8349 149158 ns/op 439.41 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 7939 148068 ns/op 442.65 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 8061 149061 ns/op 439.7 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 8014 150630 ns/op 435.12 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/64KiB-12 8048 148891 ns/op 440.2 MB/s 262544 B/op 34 allocs/op +BenchmarkParse/escapes/1024KiB-12 495 2.552001e+06 ns/op 410.89 MB/s 4.120986e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 422 2.692705e+06 ns/op 389.42 MB/s 4.120988e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 441 2.563992e+06 ns/op 408.96 MB/s 4.120985e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 432 2.677018e+06 ns/op 391.7 MB/s 4.120989e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 459 2.570174e+06 ns/op 407.98 MB/s 4.120988e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 486 2.589066e+06 ns/op 405 MB/s 4.120989e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 457 2.431313e+06 ns/op 431.28 MB/s 4.120985e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 484 2.465482e+06 ns/op 425.31 MB/s 4.12099e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 502 2.388281e+06 ns/op 439.05 MB/s 4.120985e+06 B/op 44 allocs/op +BenchmarkParse/escapes/1024KiB-12 523 2.378019e+06 ns/op 440.95 MB/s 4.120985e+06 B/op 44 allocs/op +BenchmarkParse/same_key/1KiB-12 71072 16630 ns/op 61.7 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 72723 16650 ns/op 61.62 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 73953 16546 ns/op 62.01 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 67302 17913 ns/op 57.28 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 66685 18020 ns/op 56.94 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 72214 16651 ns/op 61.62 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 72188 16496 ns/op 62.2 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 68553 16908 ns/op 60.68 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 70267 16939 ns/op 60.57 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/1KiB-12 70260 16728 ns/op 61.33 MB/s 48672 B/op 22 allocs/op +BenchmarkParse/same_key/64KiB-12 664 1.826244e+06 ns/op 35.89 MB/s 4.446123e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 650 1.891215e+06 ns/op 34.65 MB/s 4.44612e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 621 1.851504e+06 ns/op 35.4 MB/s 4.446119e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 654 1.818073e+06 ns/op 36.05 MB/s 4.446118e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 658 1.824682e+06 ns/op 35.92 MB/s 4.446117e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 662 1.845494e+06 ns/op 35.51 MB/s 4.446119e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 640 1.853216e+06 ns/op 35.36 MB/s 4.446119e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 637 1.851019e+06 ns/op 35.41 MB/s 4.446118e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 675 1.807891e+06 ns/op 36.25 MB/s 4.446119e+06 B/op 34 allocs/op +BenchmarkParse/same_key/64KiB-12 684 1.7897e+06 ns/op 36.62 MB/s 4.446118e+06 B/op 34 allocs/op +BenchmarkParse/same_key/1024KiB-12 45 2.6231202e+07 ns/op 39.97 MB/s 7.4962855e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 46 2.5111723e+07 ns/op 41.76 MB/s 7.4962848e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 48 2.4809864e+07 ns/op 42.26 MB/s 7.4962968e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 48 2.475029e+07 ns/op 42.37 MB/s 7.4962848e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 48 2.506669e+07 ns/op 41.83 MB/s 7.4962848e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 45 2.5566657e+07 ns/op 41.01 MB/s 7.4962848e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 46 2.5403104e+07 ns/op 41.28 MB/s 7.4962848e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 46 2.5523669e+07 ns/op 41.08 MB/s 7.496285e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 45 2.5917124e+07 ns/op 40.46 MB/s 7.4962848e+07 B/op 46 allocs/op +BenchmarkParse/same_key/1024KiB-12 43 2.6897904e+07 ns/op 38.98 MB/s 7.4962853e+07 B/op 46 allocs/op +BenchmarkParse/headers/1KiB-12 167066 6818 ns/op 150.19 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 178939 6829 ns/op 149.94 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 179001 6842 ns/op 149.67 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 177456 6766 ns/op 151.35 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 180487 6843 ns/op 149.64 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 172934 6784 ns/op 150.94 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 156220 6976 ns/op 146.79 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 183199 6850 ns/op 149.5 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 174961 6834 ns/op 149.83 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/1KiB-12 178612 6906 ns/op 148.28 MB/s 12960 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 3112 379353 ns/op 172.76 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 2973 398466 ns/op 164.47 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 3016 399233 ns/op 164.15 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 2869 399890 ns/op 163.88 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 3040 408991 ns/op 160.24 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 2791 394505 ns/op 166.12 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 3133 387418 ns/op 169.16 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 2839 391336 ns/op 167.47 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 3094 400192 ns/op 163.76 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/64KiB-12 2900 391480 ns/op 167.41 MB/s 730145 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 199 5.828334e+06 ns/op 179.91 MB/s 1.1543585e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 210 5.630595e+06 ns/op 186.23 MB/s 1.1543584e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 210 5.630526e+06 ns/op 186.23 MB/s 1.1543584e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 212 5.609366e+06 ns/op 186.93 MB/s 1.1543584e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 212 5.713623e+06 ns/op 183.52 MB/s 1.1543586e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 207 5.744549e+06 ns/op 182.53 MB/s 1.1543585e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 208 5.702827e+06 ns/op 183.87 MB/s 1.1543584e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 202 5.809036e+06 ns/op 180.51 MB/s 1.1543585e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 208 5.727577e+06 ns/op 183.07 MB/s 1.1543584e+07 B/op 12 allocs/op +BenchmarkParse/headers/1024KiB-12 207 5.781065e+06 ns/op 181.38 MB/s 1.1543585e+07 B/op 12 allocs/op +BenchmarkParse/long_subsection/1KiB-12 471932 2516 ns/op 408.11 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 476712 2505 ns/op 410.02 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 486252 2597 ns/op 395.47 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 456918 2651 ns/op 387.38 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 454606 2737 ns/op 375.24 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 452276 2603 ns/op 394.62 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 477220 2542 ns/op 404 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 488622 2545 ns/op 403.54 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 494998 2500 ns/op 410.75 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/1KiB-12 474442 2553 ns/op 402.24 MB/s 5112 B/op 19 allocs/op +BenchmarkParse/long_subsection/64KiB-12 9476 110812 ns/op 591.45 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 109076 ns/op 600.85 MB/s 335866 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 106781 ns/op 613.77 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 107454 ns/op 609.93 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 105492 ns/op 621.27 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 111444 ns/op 588.09 MB/s 335866 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 105963 ns/op 618.51 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 104771 ns/op 625.54 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 106865 ns/op 613.29 MB/s 335865 B/op 32 allocs/op +BenchmarkParse/long_subsection/64KiB-12 10000 105530 ns/op 621.04 MB/s 335866 B/op 32 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 556 2.266373e+06 ns/op 462.67 MB/s 5.177367e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 537 2.258514e+06 ns/op 464.28 MB/s 5.17736e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 536 2.227851e+06 ns/op 470.67 MB/s 5.177365e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 549 2.281338e+06 ns/op 459.63 MB/s 5.177361e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 537 2.283466e+06 ns/op 459.21 MB/s 5.177366e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 526 2.257562e+06 ns/op 464.47 MB/s 5.177362e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 543 2.185419e+06 ns/op 479.81 MB/s 5.177367e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 506 2.240107e+06 ns/op 468.09 MB/s 5.177365e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 534 2.256651e+06 ns/op 464.66 MB/s 5.177367e+06 B/op 42 allocs/op +BenchmarkParse/long_subsection/1024KiB-12 501 2.249869e+06 ns/op 466.06 MB/s 5.177366e+06 B/op 42 allocs/op +BenchmarkParse/comments/1KiB-12 413898 2862 ns/op 358.85 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 396931 2908 ns/op 353.22 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 384942 2944 ns/op 348.79 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 396912 2855 ns/op 359.76 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 408728 2854 ns/op 359.8 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 412206 2929 ns/op 350.64 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 420957 2859 ns/op 359.21 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 423242 2846 ns/op 360.92 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 433494 2887 ns/op 355.73 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/1KiB-12 449575 2870 ns/op 357.85 MB/s 8016 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 7530 163541 ns/op 400.79 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 7156 161723 ns/op 405.3 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 6975 163812 ns/op 400.13 MB/s 483665 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 7422 161467 ns/op 405.94 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 6914 161553 ns/op 405.72 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 6943 164399 ns/op 398.7 MB/s 483665 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 6032 166916 ns/op 392.69 MB/s 483665 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 7501 164736 ns/op 397.89 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 6940 160267 ns/op 408.98 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/64KiB-12 6241 161307 ns/op 406.34 MB/s 483664 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 464 2.567127e+06 ns/op 408.46 MB/s 7.512401e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 470 2.521007e+06 ns/op 415.94 MB/s 7.5124e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 477 2.497534e+06 ns/op 419.85 MB/s 7.5124e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 469 2.523982e+06 ns/op 415.45 MB/s 7.5124e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 465 2.53307e+06 ns/op 413.96 MB/s 7.512401e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 482 2.515405e+06 ns/op 416.86 MB/s 7.5124e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 484 2.492833e+06 ns/op 420.64 MB/s 7.5124e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 477 2.504671e+06 ns/op 418.65 MB/s 7.5124e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 465 2.534522e+06 ns/op 413.72 MB/s 7.512401e+06 B/op 7 allocs/op +BenchmarkParse/comments/1024KiB-12 452 2.562974e+06 ns/op 409.13 MB/s 7.512401e+06 B/op 7 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30248 39691 ns/op 25.85 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 28885 40981 ns/op 25.04 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 29887 40633 ns/op 25.25 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30684 39624 ns/op 25.89 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30318 39602 ns/op 25.91 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30070 39310 ns/op 26.1 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30463 39687 ns/op 25.85 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30253 40485 ns/op 25.34 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 29690 39938 ns/op 25.69 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/1KiB-12 30445 39678 ns/op 25.86 MB/s 68184 B/op 41 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 400 3.018787e+06 ns/op 21.71 MB/s 5.705927e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 394 3.031728e+06 ns/op 21.62 MB/s 5.70593e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 397 3.02041e+06 ns/op 21.7 MB/s 5.705932e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 384 3.098119e+06 ns/op 21.15 MB/s 5.705945e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 393 2.987426e+06 ns/op 21.94 MB/s 5.705935e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 391 3.022324e+06 ns/op 21.68 MB/s 5.705932e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 394 3.134989e+06 ns/op 20.91 MB/s 5.70593e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 378 3.147148e+06 ns/op 20.82 MB/s 5.705931e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 397 3.075578e+06 ns/op 21.31 MB/s 5.70593e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/64KiB-12 405 3.012771e+06 ns/op 21.75 MB/s 5.705927e+06 B/op 93 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 21 5.3726348e+07 ns/op 19.52 MB/s 9.5762504e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 21 5.3193404e+07 ns/op 19.71 MB/s 9.5762238e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 20 5.5104985e+07 ns/op 19.03 MB/s 9.576225e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 21 5.7249859e+07 ns/op 18.32 MB/s 9.5762243e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 22 5.4863962e+07 ns/op 19.11 MB/s 9.5762242e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 21 5.5631806e+07 ns/op 18.85 MB/s 9.5762243e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 22 5.6294417e+07 ns/op 18.63 MB/s 9.5762237e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 21 5.4814864e+07 ns/op 19.13 MB/s 9.5762238e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 22 5.4993133e+07 ns/op 19.07 MB/s 9.5762232e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_one_section/1024KiB-12 20 5.6212895e+07 ns/op 18.65 MB/s 9.5762239e+07 B/op 596 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27382 44019 ns/op 23.49 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27558 43466 ns/op 23.79 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27434 43873 ns/op 23.57 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27482 43902 ns/op 23.55 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27218 43620 ns/op 23.7 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27433 43651 ns/op 23.69 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 27334 43785 ns/op 23.62 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 26629 44409 ns/op 23.28 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 26890 43757 ns/op 23.63 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1KiB-12 26859 44192 ns/op 23.4 MB/s 68568 B/op 40 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 368 3.25052e+06 ns/op 20.16 MB/s 3.903353e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 368 3.239066e+06 ns/op 20.23 MB/s 3.903355e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 372 3.214841e+06 ns/op 20.39 MB/s 3.903355e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 369 3.231707e+06 ns/op 20.28 MB/s 3.903357e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 340 3.431382e+06 ns/op 19.1 MB/s 3.903358e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 373 3.245817e+06 ns/op 20.19 MB/s 3.903354e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 380 3.183671e+06 ns/op 20.59 MB/s 3.903353e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 370 3.21263e+06 ns/op 20.4 MB/s 3.903355e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 357 3.297649e+06 ns/op 19.87 MB/s 3.903356e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/64KiB-12 372 3.301616e+06 ns/op 19.85 MB/s 3.903353e+06 B/op 72 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 25 4.7101973e+07 ns/op 22.26 MB/s 6.7551927e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 24 4.6501323e+07 ns/op 22.55 MB/s 6.7551928e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 25 4.6851508e+07 ns/op 22.38 MB/s 6.7551927e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 25 4.6525166e+07 ns/op 22.54 MB/s 6.7551927e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 25 4.7196917e+07 ns/op 22.22 MB/s 6.7551927e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 22 4.7113476e+07 ns/op 22.26 MB/s 6.7551928e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 25 4.6917263e+07 ns/op 22.35 MB/s 6.7551927e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 22 4.7983891e+07 ns/op 21.85 MB/s 6.7551938e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 24 4.6804266e+07 ns/op 22.4 MB/s 6.7551928e+07 B/op 335 allocs/op +BenchmarkEdit/UnsetAll_section_per_value/1024KiB-12 25 4.7128275e+07 ns/op 22.25 MB/s 6.7551927e+07 B/op 335 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26847 44797 ns/op 23.06 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 27152 44660 ns/op 23.13 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26806 45758 ns/op 22.58 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26992 44653 ns/op 23.13 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26252 45162 ns/op 22.87 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26881 45251 ns/op 22.83 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26350 45127 ns/op 22.89 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26959 45181 ns/op 22.86 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26979 45261 ns/op 22.82 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/1KiB-12 26582 44233 ns/op 23.35 MB/s 91888 B/op 58 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 256 4.717275e+06 ns/op 13.89 MB/s 1.0203484e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 249 4.882828e+06 ns/op 13.42 MB/s 1.0203494e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 254 4.741235e+06 ns/op 13.82 MB/s 1.0203489e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 252 4.919881e+06 ns/op 13.32 MB/s 1.0203484e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 248 4.822133e+06 ns/op 13.59 MB/s 1.0203494e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 240 4.947286e+06 ns/op 13.25 MB/s 1.0203484e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 238 4.965071e+06 ns/op 13.2 MB/s 1.0203492e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 213 5.277208e+06 ns/op 12.42 MB/s 1.0203491e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 234 5.035859e+06 ns/op 13.02 MB/s 1.0203492e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/64KiB-12 241 4.964547e+06 ns/op 13.2 MB/s 1.0203485e+07 B/op 108 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 20 5.7212119e+07 ns/op 18.33 MB/s 1.75182164e+08 B/op 155 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.6285053e+07 ns/op 18.63 MB/s 1.75182156e+08 B/op 155 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.6764819e+07 ns/op 18.47 MB/s 1.75182175e+08 B/op 156 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.6595931e+07 ns/op 18.53 MB/s 1.75182182e+08 B/op 156 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.8090739e+07 ns/op 18.05 MB/s 1.751822e+08 B/op 156 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.7314467e+07 ns/op 18.3 MB/s 1.75182168e+08 B/op 155 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.715157e+07 ns/op 18.35 MB/s 1.7518215e+08 B/op 155 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 18 5.6573782e+07 ns/op 18.53 MB/s 1.751822e+08 B/op 156 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 20 5.7670295e+07 ns/op 18.18 MB/s 1.75182208e+08 B/op 156 allocs/op +BenchmarkEdit/RenameSection_one_line/1024KiB-12 19 5.865655e+07 ns/op 17.88 MB/s 1.75182204e+08 B/op 156 allocs/op +BenchmarkFile/Set/n=10-12 41673 29335 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 41652 29408 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 40716 29653 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 41114 29747 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 41599 29386 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 39153 31075 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 38833 29808 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 41344 29403 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 40138 29783 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/Set/n=10-12 41134 29993 ns/op 32534 B/op 162 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1728762 705.7 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1631811 713.9 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1771570 677.3 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1767153 684.1 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1771474 681.1 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1770790 676.5 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1751448 685.8 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1748499 682.4 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1740174 686.2 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=10-12 1764769 678.5 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23562 50788 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23940 50323 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23473 50955 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23218 51762 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23976 50369 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23823 49981 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23792 50717 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22773 52963 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 22906 51646 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/SetNewSection/n=10-12 23438 51483 ns/op 67432 B/op 327 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25704 46585 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25952 46559 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25489 46513 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 26023 46133 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 26163 46769 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25068 47073 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25882 46493 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25860 46505 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25404 47202 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/ConfigAfterEdit/n=10-12 25201 46701 ns/op 63839 B/op 240 allocs/op +BenchmarkFile/Bytes/n=10-12 4164099 297.8 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4033780 298.3 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4128052 296.8 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4124798 298.6 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4043148 293.2 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3803600 301.5 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 3759801 300.2 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4021701 293.5 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4109550 290.4 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Bytes/n=10-12 4009456 297.6 ns/op 1536 B/op 1 allocs/op +BenchmarkFile/Set/n=1000-12 411 2.868195e+06 ns/op 3.53158e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 441 2.743965e+06 ns/op 3.531581e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 415 2.805316e+06 ns/op 3.53158e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 429 2.816515e+06 ns/op 3.531581e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 409 2.87294e+06 ns/op 3.531581e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 420 2.834364e+06 ns/op 3.53158e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 393 2.830313e+06 ns/op 3.531579e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 417 2.895135e+06 ns/op 3.531581e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 418 2.908667e+06 ns/op 3.53158e+06 B/op 10127 allocs/op +BenchmarkFile/Set/n=1000-12 418 2.89274e+06 ns/op 3.531581e+06 B/op 10127 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23536 50099 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23934 50065 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24116 49964 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23734 50294 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23936 50395 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24082 49991 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24067 49918 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 24110 49165 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 23938 49023 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetUnchanged/n=1000-12 22592 49977 ns/op 8 B/op 1 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 195 5.912392e+06 ns/op 7.063893e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 213 5.713267e+06 ns/op 7.063893e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 210 5.759223e+06 ns/op 7.063892e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 206 5.808959e+06 ns/op 7.063891e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 207 5.749402e+06 ns/op 7.063895e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 205 5.814124e+06 ns/op 7.063893e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 207 5.723606e+06 ns/op 7.06389e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 206 5.800222e+06 ns/op 7.063892e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 202 5.852603e+06 ns/op 7.063894e+06 B/op 20257 allocs/op +BenchmarkFile/SetNewSection/n=1000-12 211 5.762766e+06 ns/op 7.063896e+06 B/op 20257 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 192 6.223014e+06 ns/op 7.377055e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 195 6.133095e+06 ns/op 7.377056e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 194 6.205278e+06 ns/op 7.377058e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 193 6.143424e+06 ns/op 7.376123e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 196 6.170515e+06 ns/op 7.377063e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 190 6.191896e+06 ns/op 7.377058e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 192 6.199909e+06 ns/op 7.377057e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 193 6.404785e+06 ns/op 7.377065e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 188 6.32039e+06 ns/op 7.377065e+06 B/op 14249 allocs/op +BenchmarkFile/ConfigAfterEdit/n=1000-12 192 6.210494e+06 ns/op 7.377059e+06 B/op 14249 allocs/op +BenchmarkFile/Bytes/n=1000-12 36672 32750 ns/op 155649 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 40292 31666 ns/op 155650 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 40029 30171 ns/op 155650 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 42747 30375 ns/op 155650 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 40814 31550 ns/op 155649 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 35986 31921 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 38697 31148 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 36982 31782 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 38062 31989 ns/op 155648 B/op 1 allocs/op +BenchmarkFile/Bytes/n=1000-12 39504 31617 ns/op 155648 B/op 1 allocs/op diff --git a/config.go b/config.go index 49bcb0f..31fc6ca 100644 --- a/config.go +++ b/config.go @@ -2,6 +2,7 @@ package gitconfig import ( "iter" + "slices" ) // Config is an immutable, ordered set of entries, from a single file or from @@ -14,40 +15,103 @@ import ( // treats it as absent. Keys are usually constants in the caller's code; // check them once with ParseKey, for example in a test. // -// Methods returning a small result the caller owns return a slice (GetAll, -// Subsections); methods walking the configuration return an iterator (All, -// Section, Subsection). +// When a key has several values, the last one wins, as in git: Get, Value, +// Bool, Int and Path use it, GetAll returns them all. type Config struct { - // unexported + entries []Entry + index map[Key][]int // positions in entries, by key + + // subsections lists the distinct subsections of each section, in order + // of first appearance. + subsections map[string][]string +} + +// newConfig returns a Config of entries. It takes ownership of the slice. +func newConfig(entries []Entry) *Config { + c := &Config{ + entries: entries, + index: make(map[Key][]int), + subsections: make(map[string][]string), + } + seen := make(map[sectionKey]bool) + for i, e := range entries { + c.index[e.Key] = append(c.index[e.Key], i) + if sk := e.Key.sectionKey(); sk.hasSub && !seen[sk] { + seen[sk] = true + c.subsections[sk.section] = append(c.subsections[sk.section], sk.subsection) + } + } + return c +} + +// lookup returns the positions of the entries of key. +func (c *Config) lookup(key string) []int { + if c == nil { + return nil + } + k, ok := parseKey(key) + if !ok { + return nil + } + return c.index[k] +} + +// filter returns the entries of one section or subsection. +func (c *Config) filter(sk sectionKey) iter.Seq[Entry] { + return func(yield func(Entry) bool) { + if c == nil { + return + } + for _, e := range c.entries { + if e.Key.sectionKey() == sk && !yield(e) { + return + } + } + } } // Get returns the last entry for key, as `git config --get` does. func (c *Config) Get(key string) (Entry, bool) { - panic("not implemented") + idx := c.lookup(key) + if len(idx) == 0 { + return Entry{}, false + } + return c.entries[idx[len(idx)-1]], true } // GetAll returns every entry for key, in order. The returned slice is owned // by the caller. func (c *Config) GetAll(key string) []Entry { - panic("not implemented") + idx := c.lookup(key) + if len(idx) == 0 { + return nil + } + out := make([]Entry, len(idx)) + for i, j := range idx { + out[i] = c.entries[j] + } + return out } // All returns every entry, in order. func (c *Config) All() iter.Seq[Entry] { - panic("not implemented") + if c == nil { + return slices.Values([]Entry(nil)) + } + return slices.Values(c.entries) } // Section returns every entry of section [section] itself, in order; entries // of its subsections are not included. section is case-insensitive. func (c *Config) Section(section string) iter.Seq[Entry] { - panic("not implemented") + return c.filter(sectionKey{section: lowerASCII(section)}) } // Subsection returns every entry of subsection [section "sub"], in order. // section is case-insensitive, sub is case-sensitive. sub "" is the empty // subsection [section ""], distinct from [section]. func (c *Config) Subsection(section, sub string) iter.Seq[Entry] { - panic("not implemented") + return c.filter(sectionKey{lowerASCII(section), sub, true}) } // Subsections returns the distinct subsections of section, in order of first @@ -55,34 +119,74 @@ func (c *Config) Subsection(section, sub string) iter.Seq[Entry] { // Entries of [section] itself have no subsection and are not listed; "" // stands for [section ""]. func (c *Config) Subsections(section string) []string { - panic("not implemented") + if c == nil { + return nil + } + return slices.Clone(c.subsections[lowerASCII(section)]) } // Value returns the last value for key, or def if key is absent. func (c *Config) Value(key string, def string) string { - panic("not implemented") + if e, ok := c.Get(key); ok { + return e.Value + } + return def } // Bool returns the last value for key as a boolean, or def if key is absent. // A value that is not a valid boolean is a *ValueError; the result is then // def. To tell an absent key from one set to def, use Get and Entry.Bool. func (c *Config) Bool(key string, def bool) (bool, error) { - panic("not implemented") + e, ok := c.Get(key) + if !ok { + return def, nil + } + b, err := e.Bool() + if err != nil { + return def, err + } + return b, nil } // Int returns the last value for key as an integer, or def if key is absent. // A value that is not a valid integer is a *ValueError; the result is then // def. func (c *Config) Int(key string, def int64) (int64, error) { - panic("not implemented") + e, ok := c.Get(key) + if !ok { + return def, nil + } + n, err := e.Int() + if err != nil { + return def, err + } + return n, nil } -// Path returns the last value for key as a path, as Entry.Path does with -// home, usually Env.Home. If key is absent, def is expanded the same way. A -// value or a def that cannot be expanded is a *ValueError (for def, its Entry -// has the key, Value def and a zero Origin); the result is then def as given. +// Path returns the last value for key expanded as a path by Entry.Path, or +// def if key is absent, expanded the same way. home is the home directory, +// usually Env.Home. +// +// A value that cannot be expanded is a *ValueError, and the result is then +// def, unexpanded. This also holds for def itself: its error reports it +// with an empty Origin, and a zero Key if key is invalid. func (c *Config) Path(key, home, def string) (string, error) { - panic("not implemented") + e, ok := c.Get(key) + if !ok { + // Expand def as an entry of key, so that an error names the key. + k, _ := parseKey(key) + e = Entry{Key: k, Value: def} + } + p, err := e.Path(home) + if err != nil { + return def, err + } + return p, nil +} + +// error returns a *ValueError for e read as typ. +func (e Entry) error(typ string, err error) error { + return &ValueError{Entry: e, Type: typ, Err: err} } // Entry is one occurrence of a key in a configuration file. It is plain @@ -111,11 +215,11 @@ type Origin struct { // Env.UpdateScope has that scope. Scope Scope - // File is the file that holds the entry, possibly an included one, as - // git opened it: an include's relative path is joined to the including - // file's directory and not cleaned. "" for git -c. On Windows, the - // separators are those of the paths given, where git writes some - // paths with '/'. + // File is the file that holds the entry, possibly an included one, or + // "" for the command line. Like git, an included file's relative path + // is appended to the including file's directory without cleaning it: + // "/home/u/.config/git/../work.inc". On Windows the separators are + // kept as given, where git shows some paths with '/'. File string Line int // 1-based; 0 for git -c @@ -125,24 +229,49 @@ type Origin struct { // are true; false, no, off, 0 and "" are false; an implicit value is true. // Case is ignored. Anything else is a *ValueError. func (e Entry) Bool() (bool, error) { - panic("not implemented") + if e.Implicit { + return true, nil + } + b, err := parseBool(e.Value) + if err != nil { + return false, e.error("bool", err) + } + return b, nil } -// Int interprets the value as git does, including the k, m and g suffixes. -// An invalid unit, an out-of-range value or an implicit value is a -// *ValueError. +// Int interprets the value as git does: a decimal number, or octal with a +// leading "0" and hexadecimal with a leading "0x" as in C (so "010" is 8), +// optionally followed by a unit k, m or g (×1024, ×1024², ×1024³), case +// ignored. An invalid number or unit, a value out of the int64 range or an +// implicit value is a *ValueError. func (e Entry) Int() (int64, error) { - panic("not implemented") + if e.Implicit { + return 0, e.error("int", nil) + } + n, err := parseInt(e.Value) + if err != nil { + return 0, e.error("int", err) + } + return n, nil } // Path interprets the value as a path, as git does for `--type=path` and for -// include paths: a leading "~/" is replaced by home, usually Env.Home. A "~/" -// with an empty home, a leading "%(prefix)/" (the prefix of the git binary, -// which depends on how it was built) and an implicit value are a -// *ValueError. Config.Path -// does the same for a key. +// include paths: a leading "~/", or a value of "~" alone, is replaced by +// home, usually Env.Home. Other values are returned unchanged; relative +// paths are not resolved. +// +// These are a *ValueError: "~" or "~/" with an empty home, "~user/" (not +// supported), a leading "%(prefix)/" (the install prefix of the git binary, +// unknown to this package) and an implicit value. func (e Entry) Path(home string) (string, error) { - panic("not implemented") + if e.Implicit { + return "", e.error("path", nil) + } + p, err := expandPath(e.Value, home) + if err != nil { + return "", e.error("path", err) + } + return p, nil } // Key is the canonical form of a key, as printed by `git config --list`: diff --git a/config_test.go b/config_test.go index 5a03fdc..4b48a58 100644 --- a/config_test.go +++ b/config_test.go @@ -611,6 +611,15 @@ func TestConfigTypedGetters(t *testing.T) { assert.Equal(t, gitconfig.Key{Section: "a", Name: "missing"}, ve.Entry.Key) assert.Equal(t, gitconfig.Origin{}, ve.Entry.Origin) }) + + t.Run("Path with an invalid key and a default that cannot be expanded", func(t *testing.T) { + got, err := c.Path("a.k_y", "", "~/d") + var ve *gitconfig.ValueError + require.ErrorAs(t, err, &ve) + assert.Equal(t, "~/d", got) + assert.Equal(t, gitconfig.Key{}, ve.Entry.Key) + assert.Equal(t, `gitconfig: bad path value "~/d": home directory unknown`, err.Error()) + }) } // bigConfig returns a configuration of n remotes and n branches, the shape diff --git a/discover.go b/discover.go new file mode 100644 index 0000000..c97fb54 --- /dev/null +++ b/discover.go @@ -0,0 +1,220 @@ +package gitconfig + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" +) + +// environ is the process environment, as Discover reads it. +type environ interface { + Lookup(key string) (string, bool) +} + +type osEnviron struct{} + +func (osEnviron) Lookup(key string) (string, bool) { return os.LookupEnv(key) } + +func get(env environ, key string) string { + v, _ := env.Lookup(key) + return v +} + +func discover(gitDir string, env environ) (Env, error) { + var e Env + + e.Home = get(env, "HOME") + if e.Home == "" && runtime.GOOS == "windows" { + e.Home = get(env, "USERPROFILE") + } + + if g, ok := env.Lookup("GIT_CONFIG_GLOBAL"); ok { + // Set but empty: no global file at all. + if g != "" { + e.GlobalConfig = []string{g} + } + } else { + if xdg := get(env, "XDG_CONFIG_HOME"); xdg != "" { + e.GlobalConfig = append(e.GlobalConfig, filepath.Join(xdg, "git", "config")) + } else if e.Home != "" { + e.GlobalConfig = append(e.GlobalConfig, filepath.Join(e.Home, ".config", "git", "config")) + } + if e.Home != "" { + e.GlobalConfig = append(e.GlobalConfig, filepath.Join(e.Home, ".gitconfig")) + } + } + + noSystem := false + if v, ok := env.Lookup("GIT_CONFIG_NOSYSTEM"); ok { + b, err := parseBool(v) + if err != nil { + return Env{}, &EnvError{Var: "GIT_CONFIG_NOSYSTEM", Err: fmt.Errorf("bad boolean value %q", v)} + } + noSystem = b + } + switch s, ok := env.Lookup("GIT_CONFIG_SYSTEM"); { + case noSystem: + case ok: + e.SystemConfig = s + case runtime.GOOS != "windows": + e.SystemConfig = "/etc/gitconfig" + } + // On Windows the system file is under git's installation directory, + // which this package cannot know: there is none unless + // GIT_CONFIG_SYSTEM names it. + + cmd, err := countEntries(env) + if err != nil { + return Env{}, err + } + params, err := parseParameters(get(env, "GIT_CONFIG_PARAMETERS")) + if err != nil { + return Env{}, &EnvError{Var: "GIT_CONFIG_PARAMETERS", Err: err} + } + e.Command = append(cmd, params...) + + if gitDir != "" { + e.GitDir, err = resolveGitDir(gitDir) + if err != nil { + return Env{}, err + } + } + return e, nil +} + +// resolveGitDir makes gitDir absolute and follows a "gitdir: " file. +func resolveGitDir(gitDir string) (string, error) { + gitDir, err := filepath.Abs(gitDir) + if err != nil { + return "", err + } + // A directory is the git directory itself. One that does not exist (yet) + // is not an error here: loading finds no files in it. + fi, err := os.Stat(gitDir) + if err != nil || !fi.Mode().IsRegular() { + return gitDir, nil + } + data, err := readSmall(gitDir) + if err != nil { + return "", &FileError{Path: gitDir, Err: err} + } + path, ok := strings.CutPrefix(strings.TrimRight(string(data), "\r\n"), "gitdir: ") + if !ok || path == "" { + return "", &FileError{Path: gitDir, Err: errors.New("invalid gitfile format")} + } + if !filepath.IsAbs(path) { + if path, err = joinReal(filepath.Dir(gitDir), path); err != nil { + return "", &FileError{Path: gitDir, Err: err} + } + } + return path, nil +} + +// countEntries reads GIT_CONFIG_COUNT, GIT_CONFIG_KEY_ and +// GIT_CONFIG_VALUE_. The count is digits only, where git also accepts a +// sign and leading whitespace. +func countEntries(env environ) ([]CommandEntry, error) { + count := get(env, "GIT_CONFIG_COUNT") + if count == "" { + return nil, nil + } + n, err := strconv.ParseInt(count, 10, 32) + if err != nil || count[0] < '0' || count[0] > '9' { + return nil, &EnvError{Var: "GIT_CONFIG_COUNT", Err: fmt.Errorf("bogus count %q", count)} + } + var entries []CommandEntry + for i := range int(n) { + keyVar, valueVar := "GIT_CONFIG_KEY_"+strconv.Itoa(i), "GIT_CONFIG_VALUE_"+strconv.Itoa(i) + key, ok := env.Lookup(keyVar) + if !ok { + return nil, &EnvError{Var: keyVar, Err: errMissingVar} + } + value, ok := env.Lookup(valueVar) + if !ok { + return nil, &EnvError{Var: valueVar, Err: errMissingVar} + } + entries = append(entries, CommandEntry{Key: key, Value: value}) + } + return entries, nil +} + +var ( + errMissingVar = errors.New("missing") + errBogusParameters = errors.New("bogus format") +) + +// parseParameters parses GIT_CONFIG_PARAMETERS, where git passes its -c +// options to the commands it runs: items separated by whitespace, each a +// single-quoted key, then "=" and a single-quoted value, or "=" alone for +// an option without a value. Inside a quoted string, a quote is written as +// a backslash and a quote between two quoted strings, and so may be an +// exclamation mark: +// +// 'e.f'='x'\''y' 'g.h'=''\!'z' +// +// The older form of an item, a single-quoted "key=value", is accepted too. +func parseParameters(s string) ([]CommandEntry, error) { + var entries []CommandEntry + for s != "" { + key, rest, ok := unquote(s) + if !ok { + return nil, errBogusParameters + } + var c CommandEntry + if v, ok := strings.CutPrefix(rest, "="); ok { + // The current form: 'key'='value', or 'key'= for no value. + c.Key = key + if v == "" || isParamSpace(v[0]) { + c.Implicit = true + rest = v + } else { + c.Value, rest, ok = unquote(v) + if !ok { + return nil, errBogusParameters + } + } + } else if k, v, ok := strings.Cut(key, "="); ok { + // The older form: 'key=value'. + c.Key, c.Value = k, v + } else { + c.Key, c.Implicit = key, true + } + if rest != "" && !isParamSpace(rest[0]) { + return nil, errBogusParameters + } + entries = append(entries, c) + s = strings.TrimLeft(rest, " \t\n\v\f\r") + } + return entries, nil +} + +func isParamSpace(c byte) bool { + return strings.IndexByte(" \t\n\v\f\r", c) >= 0 +} + +// unquote reads a single-quoted string at the start of s. +func unquote(s string) (value, rest string, ok bool) { + if s == "" || s[0] != '\'' { + return "", "", false + } + var sb strings.Builder + s = s[1:] + for { + i := strings.IndexByte(s, '\'') + if i < 0 { + return "", "", false + } + sb.WriteString(s[:i]) + s = s[i+1:] + if len(s) >= 3 && s[0] == '\\' && (s[1] == '\'' || s[1] == '!') && s[2] == '\'' { + sb.WriteByte(s[1]) + s = s[3:] + continue + } + return sb.String(), s, true + } +} diff --git a/doc.go b/doc.go index 7cd8eca..d69dfaa 100644 --- a/doc.go +++ b/doc.go @@ -8,7 +8,8 @@ // - [File] is the lossless, editable form of a single file. // - [Env] says where git's configuration comes from, and is the only way // to the disk: it loads scopes or files (following includes) and -// edits them under git's lock. +// edits them with git's lock file protocol, so that git and this +// package can safely write the same file. // // Reading the effective configuration of a repository and changing a value: // @@ -26,8 +27,13 @@ // }) // // Nothing in this package reads the process environment or the home -// directory, except [Discover], and only the methods of [Env] touch the -// filesystem. An Env built by hand is fully described by its fields; -// [Parse], [File] and [Config] do no I/O, for callers that store +// directory, except [Discover], and only [Discover] and the methods of [Env] +// touch the filesystem. An Env built by hand is fully described by its +// fields. [Parse], [File] and [Config] do no I/O, for callers that store // configuration their own way. +// +// Errors wrap the sentinel errors of this package (ErrLocked, +// ErrInvalidKey, ...) in a typed error giving the key, file or value +// concerned; the documentation of the error variables says which function +// returns which type. package gitconfig diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md index 255eab6..12e6838 100644 --- a/docs/REQUIREMENTS.md +++ b/docs/REQUIREMENTS.md @@ -77,7 +77,7 @@ from git, and the principles that decide it, are in | N5 | Go version no newer than the oldest of git-bug's and go-git's `go.mod` (both `go 1.26.0` today). | MUST | | N6 | Works on Linux, macOS and Windows (path handling, CRLF, rename-over-existing). | MUST | | N7 | MIT licence, compatible with go-git (Apache-2.0) and git-bug. No code derived from git (GPL-2.0-only): see [CLEAN-ROOM.md](CLEAN-ROOM.md). | MUST | -| N8 | Errors are sentinel values wrapped in typed errors (`KeyError`, `FileError`, `SyntaxError`, `ValueError`) usable with `errors.Is`/`errors.As`, and carry file/line/key context like git's messages. Which method returns which is listed in `errors.go`. | MUST | +| N8 | Errors are sentinel values wrapped in typed errors (`KeyError`, `FileError`, `SyntaxError`, `ValueError`, `EnvError`) usable with `errors.Is`/`errors.As`, and carry file/line/key context like git's messages. Which method returns which is listed in `errors.go`. | MUST | ## 6. Security diff --git a/edit.go b/edit.go new file mode 100644 index 0000000..88cf669 --- /dev/null +++ b/edit.go @@ -0,0 +1,598 @@ +package gitconfig + +import ( + "errors" + "slices" + "strings" +) + +// Edits are computed on the items of the file as splices of its bytes, in +// the places and the format git's own edits use. The result is parsed again +// and checked against the expected entries before it replaces the file, so +// that no edit can change anything but what it was asked to. + +// errUnsafeEdit is returned, wrapped in a *KeyError, if an edit would have +// changed other entries than its own. It is a bug of this package if it +// ever happens. +var errUnsafeEdit = errors.New("gitconfig: internal error: the edit would change other entries") + +// splice replaces data[start:end] with text. +type splice struct { + start, end int + text string +} + +// prepare checks that the file is open and validates key for an edit. It +// returns the key in canonical form, with its section and name as the +// caller spelled them, for new lines and headers; the subsection of the +// canonical form is already spelled as given. +func (f *File) prepare(key string) (k Key, section, name string, err error) { + if err := f.checkOpen(key); err != nil { + return Key{}, "", "", err + } + k, ok := parseKey(key) + if !ok || k.Section == "" { + return Key{}, "", "", &KeyError{Key: key, Err: ErrInvalidKey} + } + section = key[:strings.IndexByte(key, '.')] + name = key[strings.LastIndexByte(key, '.')+1:] + return k, section, name, nil +} + +// prepareValue is prepare for an edit that writes value. +func (f *File) prepareValue(key, value string) (k Key, section, name string, err error) { + k, section, name, err = f.prepare(key) + if err != nil { + return Key{}, "", "", err + } + if strings.IndexByte(value, 0) >= 0 { + return Key{}, "", "", &KeyError{Key: key, Err: ErrInvalidValue} + } + return k, section, name, nil +} + +func (f *File) checkOpen(key string) error { + if f.closed { + return &KeyError{Key: key, Err: ErrClosed} + } + return nil +} + +// entriesOf returns the indexes of the entries of key. +func (f *File) entriesOf(k Key) []int { + var idx []int + sk := k.sectionKey() + for i, it := range f.items { + if it.kind == itemEntry && f.secOf(it) == sk && f.nameOf(it) == k.Name { + idx = append(idx, i) + } + } + return idx +} + +// flatEntry is an entry as the checks of an edit compare them, origin left +// out. Unlike Config, the checks include the entries before any header, +// with the zero sec: an edit must not change them either. They never match +// the edited key or section, which always has a section name. +type flatEntry struct { + sec sectionKey + name string + value string + implicit bool +} + +func (e flatEntry) is(k Key) bool { + return e.sec == k.sectionKey() && e.name == k.Name +} + +// flatCursor walks the entries of a parsed file, without copying them out: +// the checks of an edit compare two whole files. +type flatCursor struct { + p *parsed + i int + keep func(flatEntry) bool +} + +// next returns the next entry keep accepts, or false at the end. +func (c *flatCursor) next() (flatEntry, bool) { + for c.i < len(c.p.items) { + it := c.p.items[c.i] + c.i++ + if it.kind != itemEntry { + continue + } + e := flatEntry{c.p.secOf(it), c.p.nameOf(it), c.p.valueOf(it), it.implicit} + if c.keep == nil || c.keep(e) { + return e, true + } + } + return flatEntry{}, false +} + +// sameEntries reports whether the entries of before that keepBefore accepts, +// changed by conv, are the entries of after that keepAfter accepts. A nil +// function keeps every entry, or changes none. +func sameEntries(before, after *parsed, keepBefore, keepAfter func(flatEntry) bool, conv func(flatEntry) flatEntry) bool { + b := flatCursor{p: before, keep: keepBefore} + a := flatCursor{p: after, keep: keepAfter} + for { + eb, okB := b.next() + ea, okA := a.next() + if okB != okA { + return false + } + if !okB { + return true + } + if conv != nil { + eb = conv(eb) + } + if eb != ea { + return false + } + } +} + +// apply splices the bytes of the file, parses the result, and keeps it if +// check accepts the entries before and after the edit. +func (f *File) apply(key string, splices []splice, check func(before, after *parsed) bool) error { + // The splices never overlap; they are applied in order of position. + slices.SortFunc(splices, func(a, b splice) int { return a.start - b.start }) + data := make([]byte, 0, len(f.data)+64) + at := 0 + for _, s := range splices { + data = append(data, f.data[at:s.start]...) + data = append(data, s.text...) + at = s.end + } + data = append(data, f.data[at:]...) + p, err := parse(f.name, data) + if err != nil || !check(&f.parsed, &p) { + return &KeyError{Key: key, Err: errUnsafeEdit} + } + f.data = data + f.parsed = p + f.snap = nil + return nil +} + +// checkKey returns a check that the entries of other keys are unchanged and +// that k has the values want after the edit. Only the first implicitOK of +// them may be implicit: those an edit keeps (Add) may be, a value it writes +// never is. +func checkKey(k Key, want []string, implicitOK int) func(before, after *parsed) bool { + return func(before, after *parsed) bool { + other := func(e flatEntry) bool { return !e.is(k) } + if !sameEntries(before, after, other, other, nil) { + return false + } + got := flatCursor{p: after, keep: func(e flatEntry) bool { return e.is(k) }} + for i := 0; ; i++ { + e, ok := got.next() + if !ok { + return i == len(want) + } + if i == len(want) || e.value != want[i] || e.implicit && i >= implicitOK { + return false + } + } + } +} + +// Layout helpers. A token is a header or an entry; the whitespace before a +// token on its line is its own. + +// ownStart returns where the token at i starts, its whitespace included. +func (f *File) ownStart(i int) int { + if i > 0 && f.items[i-1].kind == itemSpace { + return f.items[i-1].start + } + return f.items[i].start +} + +// firstOnLine reports whether the token at i is the first of its line. +func (f *File) firstOnLine(i int) bool { + j := i - 1 + if j >= 0 && f.items[j].kind == itemSpace { + j-- + } + return j < 0 || f.items[j].kind == itemNewline || f.items[j].kind == itemBOM +} + +// newlineAfter returns the index of the first newline after item i, or -1 +// if the file ends first. +func (f *File) newlineAfter(i int) int { + for j := i + 1; j < len(f.items); j++ { + if f.items[j].kind == itemNewline { + return j + } + } + return -1 +} + +// lineEnd returns the end of the line of item i, its newline included. +func (f *File) lineEnd(i int) int { + if n := f.newlineAfter(i); n >= 0 { + return f.items[n].end + } + return len(f.data) +} + +// terminator returns what must be written at the end of the file before a +// new line can start there. +func (f *File) terminator() string { + if len(f.items) == 0 { + return "" + } + last := f.items[len(f.items)-1] + switch { + case last.kind == itemNewline || last.kind == itemBOM: + return "" + case last.kind == itemEntry && last.open == continued: + // A blank line ends the value. + return "\n" + case last.kind == itemEntry && last.open == backslashEOF: + // The first newline completes the continuation, the blank line + // after it ends the value. + return "\n\n" + } + return "\n" +} + +// formatEntry returns the line git writes for an entry. +func formatEntry(name, value string) string { + return "\t" + name + " = " + quoteValue(value) + "\n" +} + +// quoteValue quotes and escapes a value as git writes it: in quotes if it +// starts or ends with a space or contains '#', ';' or a CR; backslash, +// double quote, tab and newline escaped. +// +// Unquoted, an edge space would be trimmed, '#' and ';' would start a +// comment, and a CR could be trimmed or read as part of a CRLF. Tabs need +// no quotes: they are escaped. +func quoteValue(v string) string { + var sb strings.Builder + quote := strings.HasPrefix(v, " ") || strings.HasSuffix(v, " ") || strings.ContainsAny(v, "#;\r") + if quote { + sb.WriteByte('"') + } + for i := 0; i < len(v); i++ { + switch c := v[i]; c { + case '\\', '"': + sb.WriteByte('\\') + sb.WriteByte(c) + case '\t': + sb.WriteString(`\t`) + case '\n': + sb.WriteString(`\n`) + default: + sb.WriteByte(c) + } + } + if quote { + sb.WriteByte('"') + } + return sb.String() +} + +// formatHeader returns a section header as git writes it. +func formatHeader(section, sub string, hasSub bool) string { + if !hasSub { + return "[" + section + "]" + } + r := strings.NewReplacer(`\`, `\\`, `"`, `\"`) + return "[" + section + " \"" + r.Replace(sub) + "\"]" +} + +// replaceEntry returns the splice that rewrites the entry at i. +func (f *File) replaceEntry(i int, name, value string) splice { + line := formatEntry(name, value) + if !f.firstOnLine(i) { + // After a header on the same line: the line is split. + line = "\n" + line + } + return splice{f.ownStart(i), f.entryLineEnd(i), line} +} + +// removeEntry returns the splice that removes the entry at i: its line, or +// the rest of the line after a header, which then ends with a newline. +func (f *File) removeEntry(i int) splice { + text := "" + if !f.firstOnLine(i) { + text = "\n" + } + return splice{f.ownStart(i), f.entryLineEnd(i), text} +} + +// entryLineEnd returns where the line of the entry at i ends for git when it +// removes or rewrites it: after its newline, and after the CR of the next +// line if that line is a blank CRLF line (git turns it into a blank LF line). +func (f *File) entryLineEnd(i int) int { + n := f.newlineAfter(i) + if n < 0 { + return len(f.data) + } + end := f.items[n].end + if n+1 < len(f.items) && f.items[n+1].kind == itemNewline && f.items[n+1].end-f.items[n+1].start == 2 { + end++ + } + return end +} + +// unsetEntries returns the splices that remove the entries at idx, all of +// the same key, with the headers of their section where nothing remains in +// them, as git does. git decides for each entry on the file as it is before +// the edit: +// +// - Headers of the same section that follow each other count as one. +// - Looking back from the entry, the headers go only if one of them is +// reached before any other entry or a header of another section. +// - Looking forward, past the other removed entries, an entry before the +// next header of another section keeps them. +// - A comment between the previous entry or header of another section and +// that next header keeps them too, except the comments at the end of the +// removed entries' lines. +// +// The region removed then starts after the previous entry's line, or right +// after the previous header or a BOM (the rest of its line becomes a +// newline), and ends at the next header, its indentation included. +func (f *File) unsetEntries(idx []int) []splice { + // Comments at the end of a removed entry's line play no part. + trailing := make(map[int]bool) + for _, i := range idx { + for j := i + 1; j < len(f.items) && f.items[j].kind != itemNewline; j++ { + if f.items[j].kind == itemComment { + trailing[j] = true + } + } + } + // comments[j] counts the comments of items[:j] that play a part. + comments := make([]int, len(f.items)+1) + for j, it := range f.items { + comments[j+1] = comments[j] + if it.kind == itemComment && !trailing[j] { + comments[j+1]++ + } + } + + // From the last entry back, so that a forward scan reaching a removed + // entry can stop where that entry's own scan did. + forward := make(map[int]int, len(idx)) + splices := make([]splice, len(idx)) + for n := len(idx) - 1; n >= 0; n-- { + splices[n] = f.unsetEntry(idx[n], forward, comments) + } + + // Merge the regions that overlap: a region may hold other entries. + slices.SortFunc(splices, func(a, b splice) int { return a.start - b.start }) + var merged []splice + for _, sp := range splices { + if n := len(merged); n > 0 && sp.start < merged[n-1].end { + merged[n-1].end = max(merged[n-1].end, sp.end) + continue + } + merged = append(merged, sp) + } + return merged +} + +// unsetEntry returns the splice that removes the entry at i. forward holds +// where the forward scan of each removed entry after i stopped; the scan of +// i is added to it. comments counts the comments that play a part, as +// unsetEntries computes it. +func (f *File) unsetEntry(i int, forward map[int]int, comments []int) splice { + sec := f.items[i].sec + sameHeader := func(it item) bool { return it.kind == itemHeader && f.secOf(it) == f.secs[sec] } + + // Scan back to the previous entry or header of another section, b; + // headers tells whether a header of the entry's section was passed. + headers := false + b := i - 1 + for ; b >= 0; b-- { + it := f.items[b] + if it.kind == itemEntry || it.kind == itemHeader && !sameHeader(it) { + break + } + if sameHeader(it) { + headers = true + } + } + // Scan forward to the next entry or header of another section, fw. + fw := i + 1 + for ; fw < len(f.items); fw++ { + if end, ok := forward[fw]; ok { + // A removed entry: the scan goes on as its own did. + fw = end + break + } + it := f.items[fw] + if it.kind == itemEntry || it.kind == itemHeader && !sameHeader(it) { + break + } + } + forward[i] = fw + if !headers || fw < len(f.items) && f.items[fw].kind == itemEntry { + // The section keeps other entries: remove the entry's line only. + return f.removeEntry(i) + } + + // The section's headers go too: remove from after b up to fw. + start, text := 0, "" + switch { + case b < 0: + if len(f.items) > 0 && f.items[0].kind == itemBOM { + // git ends the line after a BOM as after a header. + start, text = f.items[0].end, "\n" + } + case f.items[b].kind == itemEntry: + start = f.lineEnd(b) + default: + start, text = f.items[b].end, "\n" + } + end := len(f.data) + if fw < len(f.items) { + end = f.items[fw].start + } + // The region is items[from:fw]: start and end are item boundaries. + from, _ := slices.BinarySearchFunc(f.items, start, func(it item, start int) int { return it.start - start }) + if comments[fw]-comments[from] > 0 { + return f.removeEntry(i) + } + return splice{start, end, text} +} + +// insert returns the splice that adds a new entry for k, at the end of the +// last section that matches, or in a new section at the end of the file. +func (f *File) insert(k Key, section, name, value string) splice { + line := formatEntry(name, value) + headers := f.headersOf(k.sectionKey()) + if len(headers) == 0 { + return splice{len(f.data), len(f.data), f.terminator() + formatHeader(section, k.Subsection, k.HasSubsection) + "\n" + line} + } + + // Insert after the last entry of the last matching section, or after + // its header if it has none. + h := headers[len(headers)-1] + last := h + for i := h + 1; i < len(f.items) && f.items[i].kind != itemHeader; i++ { + if f.items[i].kind == itemEntry { + last = i + } + } + if f.items[last].kind == itemEntry { + if n := f.newlineAfter(last); n >= 0 { + end := f.items[n].end + if f.entryLineEnd(last) != end { + // git's end of the entry takes the CR of a blank CRLF + // line: the new line goes after that blank line. + end = f.items[n+1].end + } + return splice{end, end, line} + } + return splice{len(f.data), len(f.data), f.terminator() + line} + } + // After a header: right after its newline, or splitting its line. + if last+1 < len(f.items) && f.items[last+1].kind == itemNewline { + end := f.items[last+1].end + return splice{end, end, line} + } + end := f.items[last].end + return splice{end, end, "\n" + line} +} + +// prepareSection checks that the file is open and validates the name of a +// section given to a section edit, and its subsections. +func (f *File) prepareSection(section string, subs ...string) error { + if err := f.checkOpen(section); err != nil { + return err + } + if err := sectionArg(section); err != nil { + return err + } + for _, sub := range subs { + if !validSubsection(sub) { + return &KeyError{Key: sub, Err: ErrInvalidKey} + } + } + return nil +} + +// sectionArg validates the name of a section given to a section edit. +func sectionArg(section string) error { + if section == "" || !validSection(section) { + return &KeyError{Key: section, Err: ErrInvalidKey} + } + return nil +} + +// headersOf returns the indexes of the headers of sk. +func (f *File) headersOf(sk sectionKey) []int { + var idx []int + for i, it := range f.items { + if it.kind == itemHeader && f.secOf(it) == sk { + idx = append(idx, i) + } + } + return idx +} + +// removeSection removes every header of sk and what follows it up to the +// next header, whose own whitespace is kept. +func (f *File) removeSection(name string, sk sectionKey) (bool, error) { + headers := f.headersOf(sk) + if len(headers) == 0 { + return false, nil + } + var splices []splice + for _, h := range headers { + end := len(f.data) + for j := h + 1; j < len(f.items); j++ { + if f.items[j].kind == itemHeader { + end = f.ownStart(j) + break + } + } + splices = append(splices, splice{f.ownStart(h), end, ""}) + } + err := f.apply(name, splices, func(before, after *parsed) bool { + return sameEntries(before, after, func(e flatEntry) bool { return e.sec != sk }, nil, nil) + }) + return err == nil, err +} + +// renameSection rewrites every header of sk as header, and checks that the +// entries of sk now have section to. +func (f *File) renameSection(name string, sk, to sectionKey, header string) error { + headers := f.headersOf(sk) + if len(headers) == 0 { + return &KeyError{Key: name, Err: ErrNotFound} + } + // A header that does not start its line follows another header on the + // same line ("[b] [a]") and is rewritten in place. A header that starts + // its line has the whole line rewritten, the other headers of sk on it + // included. + var splices []splice + done := -1 // headers up to this index are rewritten with their line + for _, h := range headers { + if h <= done { + continue + } + if !f.firstOnLine(h) { + splices = append(splices, splice{f.items[h].start, f.items[h].end, header}) + continue + } + // git rewrites the line: the header alone, then the rest of the + // line, if any, on a line of its own. + rest := h + 1 + for rest < len(f.items) && f.items[rest].kind == itemSpace { + rest++ + } + end := len(f.items) + if n := f.newlineAfter(h); n >= 0 { + end = n + 1 + } + var text strings.Builder + text.WriteString(header + "\n") + if rest < end && f.items[rest].kind != itemNewline { + text.WriteByte('\t') + for _, it := range f.items[rest:end] { + if it.kind == itemHeader && f.secOf(it) == sk { + text.WriteString(header) + } else { + text.Write(f.data[it.start:it.end]) + } + } + } + splices = append(splices, splice{f.ownStart(h), f.lineEnd(h), text.String()}) + done = end - 1 + } + return f.apply(name, splices, func(before, after *parsed) bool { + return sameEntries(before, after, nil, nil, func(e flatEntry) flatEntry { + if e.sec == sk { + e.sec = to + } + return e + }) + }) +} diff --git a/env.go b/env.go index 76e0ddf..c7ec6d4 100644 --- a/env.go +++ b/env.go @@ -1,7 +1,11 @@ package gitconfig import ( + "os" + "path/filepath" + "slices" "strconv" + "strings" ) // Scope is one layer of git's configuration. Values from later scopes take @@ -63,9 +67,11 @@ type Env struct { // GitDir is the repository's git directory, "" outside a repository. // It must be absolute: includeIf "gitdir:" matches absolute paths, and // a relative GitDir makes Load fail with ErrNotAbsolute rather than - // silently not match. The common directory and the worktree - // configuration are derived from it. includeIf "onbranch:" reads - // /HEAD when such a condition is met. + // silently not match. For a linked worktree, this is the worktree's + // own git directory (.git/worktrees/): the shared config is found + // through its commondir file, and config.worktree is read from it. + // includeIf "onbranch:" reads /HEAD when such a condition is + // met. GitDir string // SystemConfig is the system configuration file, "" for none. @@ -109,30 +115,86 @@ type CommandEntry struct { // Without a home directory, Home is "" and the global files are those git // reads then: the XDG file if XDG_CONFIG_HOME is set, no ~/.gitconfig. // -// It fails if a gitdir file is malformed, or if GIT_CONFIG_COUNT or -// GIT_CONFIG_PARAMETERS is malformed, as git does. +// On Windows, SystemConfig is "" unless GIT_CONFIG_SYSTEM is set: the +// system file lives in git's installation directory, which Discover does +// not look for. Set the field to read it. +// +// It fails if a gitdir file is malformed (a *FileError), or if +// GIT_CONFIG_NOSYSTEM, GIT_CONFIG_COUNT or GIT_CONFIG_PARAMETERS is +// malformed (an *EnvError), as git does. // // It is the only function of this package that reads the process // environment. func Discover(gitDir string) (Env, error) { - panic("not implemented") + return discover(gitDir, osEnviron{}) } -// Load returns the effective configuration: every scope, in git's order of -// precedence, with includes followed. This is what git itself uses. The view -// of one scope is the entries with that Origin.Scope. +// Load returns the effective configuration: system, global, local, worktree +// and command-line scopes, in that order of precedence, with includes +// followed. This is what git itself uses. Each entry's Origin.Scope tells +// which scope it comes from. Missing files are skipped. // -// Includes are followed as git does (depth limit 10, ErrIncludeDepth), with -// two safety bounds git lacks: an include must be a regular file -// (ErrNotRegular, so a FIFO or a device cannot hang or exhaust the process), -// and one load reads at most 1000 files and 64 MiB in total (ErrLoadLimit, so -// a file including itself many times cannot multiply the work exponentially). -// These are reached only by files built to reach them. +// Includes are followed as git does (include.path and includeIf with the +// gitdir:, gitdir/i: and onbranch: conditions; hasconfig: is never +// true), up to 10 levels deep (ErrIncludeDepth). Two safety bounds git lacks +// are added: an include must be a regular file (ErrNotRegular, so a FIFO or a +// device cannot hang or exhaust the process), and one load reads at most +// 1000 files and 64 MiB in total (ErrLoadLimit, so a file including itself +// many times cannot multiply the work exponentially). Real configurations +// never come near these bounds. // // A global file that cannot be read for lack of permission is skipped, as // git does; LoadProtected skips such a system file too. func (e Env) Load() (*Config, error) { - panic("not implemented") + l, err := e.newLoader() + if err != nil { + return nil, err + } + if err := l.system(false); err != nil { + return nil, err + } + if err := l.global(); err != nil { + return nil, err + } + if l.repo != nil { + start := len(l.entries) + if err := l.loadFile(l.repo.localConfig(), ScopeLocal, false); err != nil { + return nil, err + } + ok, err := worktreeConfigIn(l.entries[start:], l.repo.localConfig()) + if err != nil { + return nil, err + } + if ok { + if err := l.loadFile(l.repo.worktreeConfig(), ScopeWorktree, false); err != nil { + return nil, err + } + } + } + if err := l.command(); err != nil { + return nil, err + } + return l.config(), nil +} + +// system loads the system scope, if any. If skipUnreadable, a file that +// cannot be read for lack of permission is skipped. +func (l *loader) system(skipUnreadable bool) error { + if l.env.SystemConfig == "" { + return nil + } + return l.loadFile(l.env.SystemConfig, ScopeSystem, skipUnreadable) +} + +// global loads the global scope, skipping the files that cannot be read for +// lack of permission, as git does. +func (l *loader) global() error { + for _, path := range l.env.GlobalConfig { + if err := l.loadFile(path, ScopeGlobal, true); err != nil { + return err + } + } + return nil } // LoadProtected returns the configuration of the protected scopes only, with @@ -141,20 +203,84 @@ func (e Env) Load() (*Config, error) { // safe.directory or safe.bareRepository. The repository's configuration is // not read, so this is safe to call before SafeDirectory. func (e Env) LoadProtected() (*Config, error) { - panic("not implemented") + l, err := e.newLoader() + if err != nil { + return nil, err + } + if err := l.system(true); err != nil { + return nil, err + } + if err := l.global(); err != nil { + return nil, err + } + if err := l.command(); err != nil { + return nil, err + } + return l.config(), nil } // SafeDirectory reports whether dir is allowed by the safe.directory values // of LoadProtected, with git's matching rules ("*", a trailing "/*", an empty // value resetting the list). dir is the repository's working tree, or its -// git directory for a bare repository, as git checks. +// git directory for a bare repository, as git checks. It may be relative to +// the working directory; symbolic links are resolved on both sides. // // The repository's configuration is not read. It does not check who owns // dir: git trusts a repository owned by the current user without consulting // safe.directory, and that check is platform specific and left to the // caller. A false result therefore means "not listed", not "unsafe". func (e Env) SafeDirectory(dir string) (bool, error) { - panic("not implemented") + c, err := e.LoadProtected() + if err != nil { + return false, err + } + dir = realPath(dir) + if !filepath.IsAbs(dir) { + // The entries that count are absolute. Joining the real working + // directory drops a leading ".." correctly: it has no links. + if wd, err := os.Getwd(); err == nil { + dir = filepath.Join(realPath(wd), dir) + } + } + safe := false + for _, entry := range c.GetAll("safe.directory") { + switch v := entry.Value; { + case v == "": + safe = false + case v == "*": + safe = true + case strings.HasSuffix(v, "/*"): + prefix, err := expandPath(strings.TrimSuffix(v, "*"), e.Home) + if err != nil || !filepath.IsAbs(prefix) { + // As for exact entries, relative ones are skipped. + continue + } + if p, err := filepath.EvalSymlinks(prefix); err == nil { + if strings.HasPrefix(dir, strings.TrimSuffix(p, string(filepath.Separator))+string(filepath.Separator)) { + safe = true + } + } + default: + p, err := expandPath(v, e.Home) + if err != nil || !filepath.IsAbs(p) { + // "." and unexpandable values are skipped. + continue + } + if p, err := filepath.EvalSymlinks(p); err == nil && p == dir { + safe = true + } + } + } + return safe, nil +} + +// realPath returns path with symbolic links resolved, or cleaned if it +// cannot be resolved. +func realPath(path string) string { + if p, err := filepath.EvalSymlinks(path); err == nil { + return p + } + return filepath.Clean(path) } // LoadScope returns the configuration of exactly one scope, with includes @@ -173,7 +299,31 @@ func (e Env) SafeDirectory(dir string) (bool, error) { // It fails with ErrNoScope for ScopeFile, and for ScopeLocal and // ScopeWorktree where WritePath does. func (e Env) LoadScope(s Scope) (*Config, error) { - panic("not implemented") + l, err := e.newLoader() + if err != nil { + return nil, err + } + switch s { + case ScopeSystem: + err = l.system(false) + case ScopeGlobal: + err = l.global() + case ScopeLocal, ScopeWorktree: + var path string + var scope Scope + path, scope, err = scopeFile(l.repo, s) + if err == nil { + err = l.loadFile(path, scope, false) + } + case ScopeCommand: + err = l.command() + default: + return nil, ErrNoScope + } + if err != nil { + return nil, err + } + return l.config(), nil } // LoadFile returns the configuration of the file at path, with includes @@ -181,7 +331,14 @@ func (e Env) LoadScope(s Scope) (*Config, error) { // includes there only with --includes). Its entries have ScopeFile. A // missing file is empty. func (e Env) LoadFile(path string) (*Config, error) { - panic("not implemented") + l, err := e.newLoader() + if err != nil { + return nil, err + } + if err := l.loadFile(path, ScopeFile, false); err != nil { + return nil, err + } + return l.config(), nil } // WritePath returns the file that `git config --` writes to. For @@ -193,14 +350,72 @@ func (e Env) LoadFile(path string) (*Config, error) { // ScopeLocal and ScopeWorktree outside a repository, ScopeWorktree in a // repository with several worktrees and no extensions.worktreeConfig. func (e Env) WritePath(s Scope) (string, error) { - panic("not implemented") + path, _, err := e.writeTarget(s) + return path, err } -// UpdateScope edits the file of scope s, as UpdateFile does; the File's -// entries have the scope LoadScope gives them. Writes never go into an -// included file. +// writeTarget returns the file WritePath returns, and the scope its entries +// have. +func (e Env) writeTarget(s Scope) (string, Scope, error) { + switch s { + case ScopeSystem: + if e.SystemConfig == "" { + return "", 0, ErrNoScope + } + return e.SystemConfig, s, nil + case ScopeGlobal: + if len(e.GlobalConfig) == 0 { + return "", 0, ErrNoScope + } + for _, v := range slices.Backward(e.GlobalConfig) { + if _, err := os.Lstat(v); err == nil { + return v, s, nil + } + } + return e.GlobalConfig[len(e.GlobalConfig)-1], s, nil + case ScopeLocal, ScopeWorktree: + r, err := e.repo() + if err != nil { + return "", 0, err + } + return scopeFile(r, s) + } + return "", 0, ErrNoScope +} + +// scopeFile returns the file of the local or worktree scope of r, nil +// outside a repository, and the scope its entries have: without +// extensions.worktreeConfig, the worktree scope is the local config, if the +// repository has a single worktree. +func scopeFile(r *repo, s Scope) (string, Scope, error) { + if r == nil { + return "", 0, ErrNoScope + } + if s == ScopeLocal { + return r.localConfig(), ScopeLocal, nil + } + ok, err := r.worktreeConfigEnabled() + switch { + case err != nil: + return "", 0, err + case ok: + return r.worktreeConfig(), ScopeWorktree, nil + case r.severalWorktrees(): + return "", 0, ErrNoScope + } + return r.localConfig(), ScopeLocal, nil +} + +// UpdateScope edits the file of scope s, which WritePath gives, as +// UpdateFile does; it fails with ErrNoScope where WritePath does. The +// File's entries have the scope LoadScope gives them. Writes never go into +// an included file. func (e Env) UpdateScope(s Scope, fn func(*File) error) error { - panic("not implemented") + path, scope, err := e.writeTarget(s) + if err != nil { + return err + } + return updateFile(path, scope, fn) } // UpdateFile edits the file at path with git's lock protocol, like @@ -224,13 +439,14 @@ func (e Env) UpdateScope(s Scope, fn func(*File) error) error { // for a moment. // // If fn returns an error or panics, the lock is released and nothing is -// written; the panic then continues. Readers see either the old or the new file, never a partial one. -// After fn returns, edits of the File fail with ErrClosed. +// written; the panic then continues. Readers see either the old or the new +// file, never a partial one. After fn returns, edits of the File fail with +// ErrClosed. // // Following the symbolic link in step 1 is what git does, and what dotfile // managers rely on. It also means that whoever controls the directory of // path controls which file is written: do not edit the configuration of a // repository you have not checked with SafeDirectory and an ownership check. func (e Env) UpdateFile(path string, fn func(*File) error) error { - panic("not implemented") + return updateFile(path, ScopeFile, fn) } diff --git a/env_test.go b/env_test.go index 37f397d..c8a5e7f 100644 --- a/env_test.go +++ b/env_test.go @@ -158,6 +158,16 @@ func TestEnvLoad(t *testing.T) { assert.Empty(t, slices.Collect(c.All())) }) + t.Run("file in place of a directory is missing", func(t *testing.T) { + // A file ~/.config/git hides the XDG file, as for git. + dir := tempDir(t) + writeFile(t, filepath.Join(dir, "git"), "") + env := gitconfig.Env{GlobalConfig: []string{filepath.Join(dir, "git", "config")}} + c, err := env.Load() + require.NoError(t, err) + assert.Empty(t, slices.Collect(c.All())) + }) + t.Run("outside a repository", func(t *testing.T) { env := fullEnv(t) env.GitDir = "" @@ -297,7 +307,7 @@ func TestEnvIncludes(t *testing.T) { t.Run("missing file ignored", func(t *testing.T) { _, c, err := load(t, map[string]string{ - "main": "[include]\n\tpath = missing\n\tpath = ~/missing\n[a]\n\tk = v\n", + "main": "[include]\n\tpath = missing\n\tpath = ~/missing\n\tpath = main/missing\n[a]\n\tk = v\n", }) require.NoError(t, err) assert.Equal(t, "v", c.Value("a.k", "")) @@ -454,6 +464,17 @@ func TestEnvLoadLimits(t *testing.T) { require.NoError(t, err) } }) + + t.Run("repeated includeIf paths", func(t *testing.T) { + // One costly pattern, then many paths under it: the condition is + // evaluated once, not once per path. + dir := tempDir(t) + writeFile(t, filepath.Join(dir, "main"), "[includeIf \"gitdir:"+strings.Repeat("a*", 1<<17)+"\"]\n"+strings.Repeat("path =\n", 1<<16)) + initRepo(t, filepath.Join(dir, ".git"), "") + + _, err := gitconfig.Env{GitDir: filepath.Join(dir, ".git"), GlobalConfig: []string{filepath.Join(dir, "main")}}.Load() + require.NoError(t, err) + }) } func TestEnvIncludeIf(t *testing.T) { @@ -519,6 +540,73 @@ func TestEnvIncludeIf(t *testing.T) { assert.True(t, included(t, env, "gitdir:"+s+"/link/"), "path as given") }) + t.Run("gitdir:./ from a relative file", func(t *testing.T) { + // "./" is the absolute directory of the file: loaded as "config" + // from cfg, "gitdir:./repo/" is cfg/repo/, not any repo/. + cfg := filepath.Join(tempDir(t), "cfg") + writeFile(t, filepath.Join(cfg, "config"), "[includeIf \"gitdir:./repo/\"]\n\tpath = "+s+"/inc\n") + t.Chdir(cfg) + for _, tc := range []struct { + gitDir string + want bool + }{ + {filepath.Join(cfg, "repo", ".git"), true}, + {filepath.Join(dir, "other", "repo", ".git"), false}, + } { + c, err := gitconfig.Env{GitDir: tc.gitDir}.LoadFile("config") + require.NoError(t, err) + _, ok := c.Get("a.k") + assert.Equal(t, tc.want, ok, tc.gitDir) + } + }) + + t.Run("gitdir classes", func(t *testing.T) { + // Checked against git 2.56. A malformed class does not fall back + // to literal characters: the pattern matches nothing. + for _, tc := range []struct { + name string // of the directory holding .git + cond string + want bool + }{ + {"x1", "gitdir:" + s + "/x[[:digit:]]/", true}, + {"x1", "gitdir:" + s + "/[[:lower:]][[:digit:]]/", true}, + {"x1", "gitdir:" + s + "/[[:upper:]]1/", false}, + {"x1", "gitdir/i:" + s + "/[[:upper:]]1/", true}, + {"x1", "gitdir:" + s + "/x[[:alpha:][:digit:]]/", true}, + {"x1", "gitdir:" + s + "/x[![:alpha:]]/", true}, + {"x1", "gitdir:" + s + "/x[[:punct:]]/", false}, + {"x1", "gitdir:" + s + "/x[[:digit:]a-]/", true}, + {"x1", "gitdir:" + s + "/x[[:bogus:]]/", false}, + {"x1", "gitdir:" + s + "/x[![:bogus:]]/", false}, + {"x1", "gitdir:" + s + "/x[1[:bogus:]]/", false}, + {"x1", "gitdir:" + s + "/x[[:DIGIT:]]/", false}, + {"x1", "gitdir:" + s + "/x[[::]1]/", false}, + {"x1", "gitdir:" + s + "/x[[:digit:]/", false}, + {"x1", "gitdir:" + s + "/x1/[a", false}, + {"x-", "gitdir:" + s + "/x[[:digit:]-z]/", true}, + {"xd", "gitdir:" + s + "/x[[:digit]/", true}, // no ":]": '[', ':', 'd', ... + {"a[b", "gitdir:" + s + "/a[b/", false}, + {"a[b", "gitdir:" + s + "/a[[]b/", true}, + {"a[b", "gitdir:" + s + "/[[:alpha:]][[]b/", true}, + } { + t.Run(tc.name+" "+tc.cond, func(t *testing.T) { + gitDir := filepath.Join(dir, tc.name, ".git") + initRepo(t, gitDir, "") + assert.Equal(t, tc.want, included(t, gitconfig.Env{GitDir: gitDir}, tc.cond)) + }) + } + }) + + t.Run("malformed patterns take linear time", func(t *testing.T) { + // Each '[' would otherwise scan the rest of the pattern. A final + // ":]" makes each "[:" look for the end of a class name. + for _, p := range []string{"[", "[\\\\]", "[:", "[[:"} { + cond := "gitdir:" + strings.Repeat(p, 1<<18/len(p)) + assert.False(t, included(t, env, cond), "%q", p) + assert.False(t, included(t, env, cond+":]"), "%q, then \":]\"", p) + } + }) + t.Run("onbranch", func(t *testing.T) { for _, tc := range []struct { head string @@ -681,6 +769,24 @@ func TestEnvLoadScope(t *testing.T) { require.NoError(t, err) assert.Equal(t, []scoped{{gitconfig.ScopeWorktree, "linked"}}, scopedValues(c.GetAll("a.k"))) }) + + t.Run("linked worktree through a link", func(t *testing.T) { + // commondir's ".." apply to the real directory, as the system + // resolves them, not to the link's parent. + env := fullEnv(t) + local := filepath.Join(env.GitDir, "config") + wt := addWorktree(t, env.GitDir, "wt", "other") + link := filepath.Join(tempDir(t), "wt") + symlink(t, wt, link) + env.GitDir = link + + path, err := env.WritePath(gitconfig.ScopeLocal) + require.NoError(t, err) + assert.Equal(t, local, path) + c, err := env.LoadScope(gitconfig.ScopeLocal) + require.NoError(t, err) + assert.Equal(t, "local", c.Value("a.k", "")) + }) } func TestEnvLoadFile(t *testing.T) { @@ -766,6 +872,32 @@ func TestEnvSafeDirectory(t *testing.T) { }) } + t.Run("relative directory", func(t *testing.T) { + // Checked against git 2.56: "../*" trusts nothing, as "../repo" + // does not. A relative dir is taken from the working directory. + t.Chdir(a) + for _, tc := range []struct { + entry string + dir string + want bool + }{ + {s + "/a", ".", true}, + {s + "/a/b", "b", true}, + {s + "/ab", "../ab", true}, + {s + "/*", "../ab", true}, + {"../*", "../ab", false}, + {"../ab", "../ab", false}, + } { + t.Run(tc.entry+" "+tc.dir, func(t *testing.T) { + global := filepath.Join(tempDir(t), "global") + writeFile(t, global, "[safe]\n\tdirectory = "+tc.entry+"\n") + got, err := gitconfig.Env{GlobalConfig: []string{global}}.SafeDirectory(tc.dir) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } + }) + t.Run("listed through an include", func(t *testing.T) { dir := tempDir(t) writeFile(t, filepath.Join(dir, "global"), "[include]\n\tpath = inc\n") @@ -951,23 +1083,25 @@ func TestDiscover(t *testing.T) { } }) - t.Run("malformed command line", func(t *testing.T) { + t.Run("malformed environment", func(t *testing.T) { for _, tc := range []struct { - name string - vars map[string]string + name string + vars map[string]string + envVar string // EnvError.Var }{ - {"bogus count", map[string]string{"GIT_CONFIG_COUNT": "x"}}, - {"negative count", map[string]string{"GIT_CONFIG_COUNT": "-1"}}, - {"sign only", map[string]string{"GIT_CONFIG_COUNT": "+"}}, - {"space only", map[string]string{"GIT_CONFIG_COUNT": " "}}, + {"bogus count", map[string]string{"GIT_CONFIG_COUNT": "x"}, "GIT_CONFIG_COUNT"}, + {"negative count", map[string]string{"GIT_CONFIG_COUNT": "-1"}, "GIT_CONFIG_COUNT"}, + {"sign only", map[string]string{"GIT_CONFIG_COUNT": "+"}, "GIT_CONFIG_COUNT"}, + {"space only", map[string]string{"GIT_CONFIG_COUNT": " "}, "GIT_CONFIG_COUNT"}, // Accepted by git. - {"plus sign", map[string]string{"GIT_CONFIG_COUNT": "+1", "GIT_CONFIG_KEY_0": "a.k", "GIT_CONFIG_VALUE_0": "v"}}, - {"leading space", map[string]string{"GIT_CONFIG_COUNT": " 1", "GIT_CONFIG_KEY_0": "a.k", "GIT_CONFIG_VALUE_0": "v"}}, - {"negative zero", map[string]string{"GIT_CONFIG_COUNT": "-0"}}, - {"too many", map[string]string{"GIT_CONFIG_COUNT": "2147483648"}}, - {"missing key", map[string]string{"GIT_CONFIG_COUNT": "1", "GIT_CONFIG_VALUE_0": "v"}}, - {"missing value", map[string]string{"GIT_CONFIG_COUNT": "1", "GIT_CONFIG_KEY_0": "a.k"}}, - {"bad parameters", map[string]string{"GIT_CONFIG_PARAMETERS": "'a.k"}}, + {"plus sign", map[string]string{"GIT_CONFIG_COUNT": "+1", "GIT_CONFIG_KEY_0": "a.k", "GIT_CONFIG_VALUE_0": "v"}, "GIT_CONFIG_COUNT"}, + {"leading space", map[string]string{"GIT_CONFIG_COUNT": " 1", "GIT_CONFIG_KEY_0": "a.k", "GIT_CONFIG_VALUE_0": "v"}, "GIT_CONFIG_COUNT"}, + {"negative zero", map[string]string{"GIT_CONFIG_COUNT": "-0"}, "GIT_CONFIG_COUNT"}, + {"too many", map[string]string{"GIT_CONFIG_COUNT": "2147483648"}, "GIT_CONFIG_COUNT"}, + {"missing key", map[string]string{"GIT_CONFIG_COUNT": "1", "GIT_CONFIG_VALUE_0": "v"}, "GIT_CONFIG_KEY_0"}, + {"missing value", map[string]string{"GIT_CONFIG_COUNT": "1", "GIT_CONFIG_KEY_0": "a.k"}, "GIT_CONFIG_VALUE_0"}, + {"bad parameters", map[string]string{"GIT_CONFIG_PARAMETERS": "'a.k"}, "GIT_CONFIG_PARAMETERS"}, + {"bad nosystem", map[string]string{"GIT_CONFIG_NOSYSTEM": "maybe"}, "GIT_CONFIG_NOSYSTEM"}, } { t.Run(tc.name, func(t *testing.T) { cleanGitEnv(t) @@ -975,7 +1109,9 @@ func TestDiscover(t *testing.T) { t.Setenv(k, v) } _, err := gitconfig.Discover("") - require.Error(t, err) + envErr, ok := errors.AsType[*gitconfig.EnvError](err) + require.True(t, ok, "got %v", err) + assert.Equal(t, tc.envVar, envErr.Var) }) } }) @@ -1009,6 +1145,20 @@ func TestDiscover(t *testing.T) { assert.Equal(t, wt, filepath.Clean(env.GitDir)) }) + t.Run("gitdir file through a link", func(t *testing.T) { + // The ".." of the gitdir file apply to the real directory that + // holds it, not to the link's parent. + home := cleanGitEnv(t) + gitDir := filepath.Join(home, "real", "target", ".git") + initRepo(t, gitDir, "") + writeFile(t, filepath.Join(home, "real", "proj", ".git"), "gitdir: ../target/.git\n") + symlink(t, filepath.Join(home, "real", "proj"), filepath.Join(home, "proj")) + + env, err := gitconfig.Discover(filepath.Join(home, "proj", ".git")) + require.NoError(t, err) + assert.Equal(t, gitDir, env.GitDir) + }) + t.Run("malformed gitdir file", func(t *testing.T) { home := cleanGitEnv(t) writeFile(t, filepath.Join(home, "wt", ".git"), "garbage\n") @@ -1323,6 +1473,26 @@ func TestEnvUpdateFileSymlinks(t *testing.T) { assertLink(t, filepath.Join(dir, "d", "l2"), "../real") }) + t.Run("relative target through a directory link", func(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Windows removes \"..\" from a path before following links") + } + // cfg/link -> d/../real, with cfg/d -> ../store/sub: the ".." is + // that of store/sub, so the file is store/real, not cfg/real. + dir := tempDir(t) + target := filepath.Join(dir, "store", "real") + decoy := filepath.Join(dir, "cfg", "real") + writeFile(t, target, "[a]\n\tk = v\n") + writeFile(t, decoy, "[a]\n\tk = v\n") + require.NoError(t, os.Mkdir(filepath.Join(dir, "store", "sub"), 0o755)) + symlink(t, "../store/sub", filepath.Join(dir, "cfg", "d")) + symlink(t, "d/../real", filepath.Join(dir, "cfg", "link")) + + require.NoError(t, gitconfig.Env{}.UpdateFile(filepath.Join(dir, "cfg", "link"), set)) + assert.Equal(t, "[a]\n\tk = w\n", readFile(t, target)) + assert.Equal(t, "[a]\n\tk = v\n", readFile(t, decoy)) + }) + t.Run("dangling link creates the target", func(t *testing.T) { dir := tempDir(t) link := filepath.Join(dir, "link") @@ -1586,6 +1756,23 @@ func TestEnvUpdateFilePermissions(t *testing.T) { }) } + t.Run("lock private until written", func(t *testing.T) { + path := filepath.Join(tempDir(t), "config") + writeFile(t, path, "[a]\n\tk = v\n") + require.NoError(t, os.Chmod(path, 0o644)) + + err := gitconfig.Env{}.UpdateFile(path, func(f *gitconfig.File) error { + fi, err := os.Stat(path + ".lock") + require.NoError(t, err) + assert.Equal(t, os.FileMode(0o600), fi.Mode().Perm()) + return f.Set("a.k", "w") + }) + require.NoError(t, err) + fi, err := os.Stat(path) + require.NoError(t, err) + assert.Equal(t, os.FileMode(0o644), fi.Mode().Perm()) + }) + t.Run("new file", func(t *testing.T) { // As any new file: 0666 minus the umask. dir := tempDir(t) @@ -1687,6 +1874,16 @@ func BenchmarkEnv(b *testing.B) { _, _ = env.LoadProtected() } }) + b.Run("LoadFileManyGitdirConditions", func(b *testing.B) { + // The git directory's real path is resolved once per load, not + // once per condition. + path := filepath.Join(tempDir(b), "config") + writeFile(b, path, strings.Repeat("[includeIf \"gitdir:/nope/\"]\n\tpath = x\n", 1000)) + b.ReportAllocs() + for b.Loop() { + _, _ = env.LoadFile(path) + } + }) b.Run("UpdateFile", func(b *testing.B) { path := filepath.Join(env.GitDir, "config") i := 0 diff --git a/errors.go b/errors.go index dc7260c..7161373 100644 --- a/errors.go +++ b/errors.go @@ -17,6 +17,8 @@ import ( // writing), a *KeyError for an invalid CommandEntry key, and ErrNoScope // unwrapped, as no file is concerned. The error returned by the // callback of Env.UpdateFile or Env.UpdateScope is returned as is. +// - Discover also returns an *EnvError for an environment variable it +// cannot interpret. var ( // ErrNotFound is returned when the section or subsection to rename is // absent. Removing something absent is not an error. @@ -88,8 +90,8 @@ type FileError struct { } func (e *FileError) Error() string { - var se *SyntaxError - if errors.As(e.Err, &se) && se.File == e.Path { + // A syntax error already names the file: don't repeat it. + if se, ok := errors.AsType[*SyntaxError](e.Err); ok && se.File == e.Path { return se.Error() } return fmt.Sprintf("gitconfig: %s: %v", e.Path, e.Err) @@ -99,6 +101,23 @@ func (e *FileError) Unwrap() error { return e.Err } +// EnvError reports an environment variable Discover cannot interpret, as +// git refuses it: a malformed GIT_CONFIG_NOSYSTEM, GIT_CONFIG_COUNT or +// GIT_CONFIG_PARAMETERS, or a missing GIT_CONFIG_KEY_ or +// GIT_CONFIG_VALUE_. +type EnvError struct { + Var string // the variable's name, e.g. "GIT_CONFIG_COUNT" + Err error // the reason +} + +func (e *EnvError) Error() string { + return fmt.Sprintf("gitconfig: %s: %v", e.Var, e.Err) +} + +func (e *EnvError) Unwrap() error { + return e.Err +} + // SyntaxError reports a file git refuses to parse. Msg describes the problem // but never quotes the file's content, which may be a file that is not a // configuration file at all, reached through an include. @@ -126,7 +145,11 @@ func (e *ValueError) Error() string { if e.Entry.Implicit { msg = fmt.Sprintf("gitconfig: missing %s value for %q", e.Type, e.Entry.Key.String()) } else { - msg = fmt.Sprintf("gitconfig: bad %s value %q for %q", e.Type, e.Entry.Value, e.Entry.Key.String()) + msg = fmt.Sprintf("gitconfig: bad %s value %q", e.Type, e.Entry.Value) + // No key for the default of Config.Path given an invalid key. + if e.Entry.Key != (Key{}) { + msg += fmt.Sprintf(" for %q", e.Entry.Key.String()) + } } if e.Entry.Origin.File != "" { msg += fmt.Sprintf(" in file %s", e.Entry.Origin.File) diff --git a/errors_test.go b/errors_test.go index 44e10cc..fb1ff0e 100644 --- a/errors_test.go +++ b/errors_test.go @@ -57,6 +57,18 @@ func TestErrorMessagesEscapeControlCharacters(t *testing.T) { assert.NotContains(t, err.Error(), "\x1b") assert.NotContains(t, err.Error(), "\r") }) + + t.Run("EnvError", func(t *testing.T) { + for _, name := range []string{"GIT_CONFIG_COUNT", "GIT_CONFIG_NOSYSTEM"} { + cleanGitEnv(t) + t.Setenv(name, evil) + _, err := gitconfig.Discover("") + require.Error(t, err) + assert.NotContains(t, err.Error(), "\x1b") + assert.NotContains(t, err.Error(), "\r") + assert.Contains(t, err.Error(), name) + } + }) } func TestErrorMessagesHaveContext(t *testing.T) { diff --git a/file.go b/file.go index 373e157..b35a608 100644 --- a/file.go +++ b/file.go @@ -1,18 +1,21 @@ package gitconfig +import ( + "bytes" +) + // File is the lossless, editable form of one configuration file. Edits // change only the lines `git config` would change, and produce the same -// bytes; everything else is kept byte for byte. -// -// A File is mutable and must not be used by several goroutines at once. Use -// Config to take an immutable snapshot. The zero value is an empty file. -// -// A File knows the scope it was opened for (Env.UpdateScope); a File from -// Parse or Env.UpdateFile has ScopeFile. +// bytes; everything else (comments, blank lines, indentation) is kept byte +// for byte. // // A File comes from Parse, for content that is not on disk (tests, in-memory // repositories, a .gitmodules blob), or from Env.UpdateScope and -// Env.UpdateFile, to edit a file on disk. +// Env.UpdateFile, to edit a file on disk. The zero value is an empty file. +// +// A File is mutable and must not be used by several goroutines at once. Use +// Config to take an immutable snapshot; its entries have the scope the File +// was opened for by Env.UpdateScope, and ScopeFile otherwise. // // Edits find keys, sections and subsections as Config does: section and name // case-insensitive, subsection exact, a legacy header [a.B] holding @@ -21,17 +24,27 @@ package gitconfig // lowered, and it removes or renames a section only if the header is // spelled as given. // -// Edits fail with ErrInvalidKey if a key or section name is one git rejects, -// or a key with an empty section (".sub.name", which git reads from a -// header [ "sub"]), and with ErrInvalidValue if a value contains a NUL byte. Removing something -// that is absent is not an error: the methods report what they removed. +// A new key goes at the end of the last section that holds its section or +// subsection, or into a new section at the end of the file, as git does. +// +// Edits return a *KeyError wrapping ErrInvalidKey if a key or section name +// is one git rejects, or a key has an empty section (".sub.name", which git +// reads from a header [ "sub"]), and wrapping ErrInvalidValue if a value +// contains a NUL byte. Removing something that is absent is not an error: +// the methods report what they removed. // // An edit cannot inject anything: whatever the key, subsection and value, // the edited file parses to the entries before the edit with exactly that // one change. Values are quoted and escaped as git does, and a subsection // that cannot be written in a header (newline, NUL) is ErrInvalidKey. type File struct { - // unexported + name string + scope Scope + data []byte + parsed + + closed bool // edits fail with ErrClosed + snap *Config // Config of data, nil until asked for } // Parse parses a configuration file. name is used in errors and in the @@ -43,13 +56,20 @@ type File struct { // data may come from anyone (a .gitmodules blob from a remote): Parse never // panics, and its time and memory are linear in len(data). func Parse(name string, data []byte) (*File, error) { - panic("not implemented") + // The File keeps data, which its items index: the caller must stay free + // to reuse its slice. + data = bytes.Clone(data) + p, err := parse(name, data) + if err != nil { + return nil, err + } + return &File{name: name, data: data, parsed: p}, nil } // Bytes returns the content of the file. It is identical to the parsed data // if the file was not edited. func (f *File) Bytes() []byte { - panic("not implemented") + return bytes.Clone(f.data) } // Config returns an immutable snapshot of the entries of this file. Include @@ -61,7 +81,10 @@ func (f *File) Bytes() []byte { // The snapshot is kept until the next edit, so calling Config repeatedly, // for example to read values inside an Env.UpdateFile callback, is cheap. func (f *File) Config() *Config { - panic("not implemented") + if f.snap == nil { + f.snap = newConfig(f.entries(f.scope, f.name, nil)) + } + return f.snap } // Set sets the value of key, like `git config `, adding it if @@ -72,45 +95,116 @@ func (f *File) Config() *Config { // difference with git's edits. An implicit entry ("[a]\n\tk") is never // already set, even for value "": it reads as true, and "k = " as false. func (f *File) Set(key, value string) error { - panic("not implemented") + k, section, name, err := f.prepareValue(key, value) + if err != nil { + return err + } + var sp splice + switch idx := f.entriesOf(k); len(idx) { + case 0: + sp = f.insert(k, section, name, value) + case 1: + if it := f.items[idx[0]]; !it.implicit && f.valueOf(it) == value { + return nil + } + sp = f.replaceEntry(idx[0], name, value) + default: + return &KeyError{Key: key, Err: ErrMultipleValues} + } + return f.apply(key, []splice{sp}, checkKey(k, []string{value}, 0)) } // ReplaceAll replaces all values of key with a single value, or adds it if -// key is absent, like `git config --replace-all `. +// key is absent, like `git config --replace-all `. The new +// value takes the place of the last old one. func (f *File) ReplaceAll(key, value string) error { - panic("not implemented") + k, section, name, err := f.prepareValue(key, value) + if err != nil { + return err + } + var splices []splice + if idx := f.entriesOf(k); len(idx) == 0 { + splices = append(splices, f.insert(k, section, name, value)) + } else { + // The values collapse into one, at the place of the last. + for _, i := range idx[:len(idx)-1] { + splices = append(splices, f.removeEntry(i)) + } + splices = append(splices, f.replaceEntry(idx[len(idx)-1], name, value)) + } + return f.apply(key, splices, checkKey(k, []string{value}, 0)) } // Add adds a value to key, keeping existing ones, like `git config --add`. +// The new value is read last, so it is the one Config.Get returns. func (f *File) Add(key, value string) error { - panic("not implemented") + k, section, name, err := f.prepareValue(key, value) + if err != nil { + return err + } + var want []string + for _, i := range f.entriesOf(k) { + want = append(want, f.valueOf(f.items[i])) + } + want = append(want, value) + // Implicit values are kept: only the new one must not be. + return f.apply(key, []splice{f.insert(k, section, name, value)}, checkKey(k, want, len(want)-1)) } // Unset removes the value of key, like `git config --unset `, and // reports whether there was one. It fails with ErrMultipleValues if key has // several values. func (f *File) Unset(key string) (removed bool, err error) { - panic("not implemented") + k, _, _, err := f.prepare(key) + if err != nil { + return false, err + } + idx := f.entriesOf(k) + switch len(idx) { + case 0: + return false, nil + case 1: + err := f.apply(key, f.unsetEntries(idx), checkKey(k, nil, 0)) + return err == nil, err + } + return false, &KeyError{Key: key, Err: ErrMultipleValues} } // UnsetAll removes all values of key, like `git config --unset-all `, // and returns how many were removed. func (f *File) UnsetAll(key string) (removed int, err error) { - panic("not implemented") + k, _, _, err := f.prepare(key) + if err != nil { + return 0, err + } + idx := f.entriesOf(k) + if len(idx) == 0 { + return 0, nil + } + if err := f.apply(key, f.unsetEntries(idx), checkKey(k, nil, 0)); err != nil { + return 0, err + } + return len(idx), nil } // RemoveSection removes section [section] and all its entries, like // `git config --remove-section
`, and reports whether it was // present. Its subsections are kept. func (f *File) RemoveSection(section string) (removed bool, err error) { - panic("not implemented") + if err := f.prepareSection(section); err != nil { + return false, err + } + return f.removeSection(section, sectionKey{section: lowerASCII(section)}) } // RemoveSubsection removes subsection [section "sub"] and all its entries, // like `git config --remove-section
.`, and reports whether // it was present. sub "" is the empty subsection [section ""]. func (f *File) RemoveSubsection(section, sub string) (removed bool, err error) { - panic("not implemented") + if err := f.prepareSection(section, sub); err != nil { + return false, err + } + return f.removeSection(section, sectionKey{lowerASCII(section), sub, true}) } // RenameSection renames section [oldName] to [newName], like @@ -121,7 +215,14 @@ func (f *File) RemoveSubsection(section, sub string) (removed bool, err error) { // it does, both sections remain and read as one, as with git. To refuse // instead, check f.Config() first, in the same Env.UpdateFile callback. func (f *File) RenameSection(oldName, newName string) error { - panic("not implemented") + if err := f.prepareSection(oldName); err != nil { + return err + } + if err := sectionArg(newName); err != nil { + return err + } + to := sectionKey{section: lowerASCII(newName)} + return f.renameSection(oldName, sectionKey{section: lowerASCII(oldName)}, to, formatHeader(newName, "", false)) } // RenameSubsection renames subsection [section "oldSub"] to @@ -131,5 +232,9 @@ func (f *File) RenameSection(oldName, newName string) error { // rename` refuses one, and a caller doing the same checks // f.Config().Subsections first. func (f *File) RenameSubsection(section, oldSub, newSub string) error { - panic("not implemented") + if err := f.prepareSection(section, oldSub, newSub); err != nil { + return err + } + sec := lowerASCII(section) + return f.renameSection(section, sectionKey{sec, oldSub, true}, sectionKey{sec, newSub, true}, formatHeader(section, newSub, true)) } diff --git a/file_test.go b/file_test.go index 1599747..cb6a960 100644 --- a/file_test.go +++ b/file_test.go @@ -491,6 +491,12 @@ var unsetCases = []editCase{ {"entry before any header kept", "k = v\n[a]\n\tj = w\n", edits(unset("a.j")), "k = v\n"}, + {"only key after a BOM", + "\xef\xbb\xbf[a]\n\tk = v\n", edits(unset("a.k")), + "\xef\xbb\xbf\n"}, + {"section after a BOM removed", + "\xef\xbb\xbf [a]\r\n\tk = v\r\n[b]\r\n\tx = y\r\n", edits(unset("a.k")), + "\xef\xbb\xbf\n[b]\r\n\tx = y\r\n"}, // E5: the emptied section is removed unless a comment lies between // the previous entry or header and the next section. @@ -1057,6 +1063,43 @@ func BenchmarkParse(b *testing.B) { } } +// BenchmarkEdit measures edits that touch many lines at once, on inputs +// built to hit their slow paths; each iteration parses the file first. +// Throughput (MB/s) must not fall as the size grows: these edits are linear. +func BenchmarkEdit(b *testing.B) { + edits := []struct { + name string + gen func(n int) []byte + edit func(f *gitconfig.File) error + }{ + {"UnsetAll one section", func(n int) []byte { return sized("[a]\n", "\tk = v\n", n) }, func(f *gitconfig.File) error { + _, err := f.UnsetAll("a.k") + return err + }}, + {"UnsetAll section per value", func(n int) []byte { return sized("", "[a]\n\tk = v\n", n) }, func(f *gitconfig.File) error { + _, err := f.UnsetAll("a.k") + return err + }}, + {"RenameSection one line", func(n int) []byte { return append(sized("", "[a]", n), " k = v\n"...) }, func(f *gitconfig.File) error { + return f.RenameSection("a", "b") + }}, + } + for _, e := range edits { + for _, n := range []int{1 << 10, 1 << 16, 1 << 20} { + data := e.gen(n) + b.Run(fmt.Sprintf("%s/%dKiB", e.name, n>>10), func(b *testing.B) { + b.SetBytes(int64(len(data))) + b.ReportAllocs() + for b.Loop() { + f, err := gitconfig.Parse("f", data) + require.NoError(b, err) + require.NoError(b, e.edit(f)) + } + }) + } + } +} + func BenchmarkFile(b *testing.B) { for _, n := range []int{10, 1000} { data := []byte(bigConfig(n)) diff --git a/glob.go b/glob.go new file mode 100644 index 0000000..e91f6e5 --- /dev/null +++ b/glob.go @@ -0,0 +1,281 @@ +package gitconfig + +import "strings" + +// The patterns of includeIf "gitdir:" and "onbranch:" are matched as paths: +// '*' and '?' do not match '/', "**/" at the start of a segment matches any +// number of directories, "**" ending the pattern after a '/' (or alone) +// matches anything, "[...]" is a class of characters ("[!...]" or "[^...]" +// negated, "a-z" a range, "[:digit:]" a named class) and '\' quotes the +// next character. A pattern with an unterminated class, or an unknown class +// name, matches nothing, as in git. +// +// The pattern is compiled into tokens and matched by simulating every +// possible position in the pattern at once, so that the time is linear in +// the length of the name for a given pattern: patterns come from +// configuration files someone else may have written. + +type globKind uint8 + +const ( + globLit globKind = iota // one given character + globAny // '?': any character but '/' + globClass // "[...]": a character of the class, never '/' + globStar // '*': any run of characters without '/' + globDirs // "**/": nothing, or anything ending with '/' + globAll // final "**": anything +) + +type globToken struct { + kind globKind + c byte // globLit + ranges [][2]byte // globClass: inclusive ranges + negate bool // globClass +} + +// compileGlob compiles p, or returns false if it is malformed. +func compileGlob(p string) ([]globToken, bool) { + var toks []globToken + // A "[:" starts a named class only if a ":]" follows somewhere. + lastColon := strings.LastIndex(p, ":]") + segStart := true + for i := 0; i < len(p); { + c := p[i] + switch { + case c == '*' && segStart && i+1 < len(p) && p[i+1] == '*' && (i+2 == len(p) || p[i+2] == '/'): + if i+2 == len(p) { + toks = append(toks, globToken{kind: globAll}) + i += 2 + continue + } + if len(toks) == 0 || toks[len(toks)-1].kind != globDirs { + // "**/**/" is "**/". + toks = append(toks, globToken{kind: globDirs}) + } + i += 3 + continue // still at the start of a segment + case c == '*': + for i < len(p) && p[i] == '*' { + i++ + } + toks = append(toks, globToken{kind: globStar}) + case c == '?': + toks = append(toks, globToken{kind: globAny}) + i++ + case c == '[': + tok, end, ok := compileClass(p, i, lastColon) + if !ok { + // Failing at once keeps the work linear: no other '[' + // rescans the rest of the pattern. + return nil, false + } + toks = append(toks, tok) + i = end + case c == '\\' && i+1 < len(p): + toks = append(toks, globToken{kind: globLit, c: p[i+1]}) + i += 2 + default: + toks = append(toks, globToken{kind: globLit, c: c}) + i++ + } + // "**" is special only at the start of a segment: at the start of the + // pattern or after a '/'. + segStart = toks[len(toks)-1].kind == globLit && toks[len(toks)-1].c == '/' + } + return toks, true +} + +// compileClass compiles the class starting at p[start], and returns the +// index past its end, or false if it is not terminated or names an unknown +// class. lastColon is the index of the last ":]" in p. +func compileClass(p string, start, lastColon int) (globToken, int, bool) { + tok := globToken{kind: globClass} + i := start + 1 + if i < len(p) && (p[i] == '!' || p[i] == '^') { + tok.negate = true + i++ + } + first := true // a ']' first in the class is a literal ']' + for i < len(p) { + c := p[i] + if c == ']' && !first { + return tok, i + 1, true + } + first = false + if c == '[' && i+1 < len(p) && p[i+1] == ':' && i+2 <= lastColon { + // Found, as some ":]" starts at or after i+2. A known name + // ends within a few bytes, and an unknown one fails. + n := strings.Index(p[i+2:], ":]") + ranges, ok := namedClasses[p[i+2:i+2+n]] + if !ok { + return globToken{}, 0, false + } + tok.ranges = append(tok.ranges, ranges...) + i += 2 + n + 2 + continue + } + if c == '\\' && i+1 < len(p) { + i++ + c = p[i] + } + // A range "a-z"; a '-' before the closing ']' is a literal '-'. + lo, hi := c, c + if i+2 < len(p) && p[i+1] == '-' && p[i+2] != ']' { + hi = p[i+2] + if hi == '\\' && i+3 < len(p) { + hi = p[i+3] + i++ + } + i += 2 + } + tok.ranges = append(tok.ranges, [2]byte{lo, hi}) + i++ + } + return globToken{}, 0, false +} + +// namedClasses are the ranges of the named classes, in ASCII. +var namedClasses = map[string][][2]byte{ + "alnum": {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}, + "alpha": {{'A', 'Z'}, {'a', 'z'}}, + "blank": {{' ', ' '}, {'\t', '\t'}}, + "cntrl": {{0, 31}, {127, 127}}, + "digit": {{'0', '9'}}, + "graph": {{'!', '~'}}, + "lower": {{'a', 'z'}}, + "print": {{' ', '~'}}, + "punct": {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}, + "space": {{'\t', '\r'}, {' ', ' '}}, + "upper": {{'A', 'Z'}}, + "xdigit": {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}, +} + +func (t globToken) inClass(c byte, fold bool) bool { + for _, r := range t.ranges { + if r[0] <= c && c <= r[1] || fold && r[0] <= swapCase(c) && swapCase(c) <= r[1] { + return !t.negate + } + } + return t.negate +} + +func swapCase(c byte) byte { + if isAlpha(c) { + return c ^ 0x20 + } + return c +} + +func sameByte(a, b byte, fold bool) bool { + return a == b || fold && swapCase(a) == b +} + +// globMatch reports whether name matches pattern. With fold, the case of +// ASCII letters is ignored. +func globMatch(pattern, name string, fold bool) bool { + toks, ok := compileGlob(pattern) + if !ok { + return false + } + n := int32(len(toks)) + // A state is a position in the pattern, 2*s for token s, or 2*s+1 for + // the inside of the directories matched by the globDirs token s. Only + // the states reached are visited. + cur := newStateSet(2*n + 2) + next := newStateSet(2*n + 2) + + // enter adds the state of token s and those reachable without a + // character: past a '*', "**/" or final "**". + enter := func(set *stateSet, s int32) { + for set.add(2*s) && s < n { + if k := toks[s].kind; k != globStar && k != globDirs && k != globAll { + break + } + s++ + } + } + + enter(cur, 0) + for i := 0; i < len(name); i++ { + c := name[i] + next.reset() + for _, id := range cur.list { + s := id / 2 + if id%2 == 1 { + // Inside "**/": anything, and a '/' may end it. + next.add(id) + if c == '/' { + enter(next, s+1) + } + continue + } + if s == n { + continue + } + switch t := toks[s]; t.kind { + case globLit: + if sameByte(c, t.c, fold) { + enter(next, s+1) + } + case globAny: + if c != '/' { + enter(next, s+1) + } + case globClass: + if c != '/' && t.inClass(c, fold) { + enter(next, s+1) + } + case globStar: + if c != '/' { + enter(next, s) + } + case globDirs: + next.add(id + 1) + if c == '/' { + enter(next, s+1) + } + case globAll: + enter(next, s) + } + } + if len(next.list) == 0 { + // No position left: the rest of name cannot match. + return false + } + cur, next = next, cur + } + // Matched if the whole pattern is consumed. + return cur.has(2 * n) +} + +// stateSet is a set of states, listed in the order they were added. reset +// empties it in constant time by moving to a new generation instead of +// clearing mark. +type stateSet struct { + list []int32 + mark []uint32 // mark[id] == gen if id is in the set + gen uint32 +} + +func newStateSet(size int32) *stateSet { + return &stateSet{mark: make([]uint32, size), gen: 1} +} + +func (ss *stateSet) reset() { + ss.list = ss.list[:0] + ss.gen++ +} + +// add adds id and reports whether it was absent. +func (ss *stateSet) add(id int32) bool { + if ss.mark[id] == ss.gen { + return false + } + ss.mark[id] = ss.gen + ss.list = append(ss.list, id) + return true +} + +func (ss *stateSet) has(id int32) bool { + return ss.mark[id] == ss.gen +} diff --git a/interop_test.go b/interop_test.go index 756e0cd..fb45641 100644 --- a/interop_test.go +++ b/interop_test.go @@ -781,6 +781,11 @@ var gitScenarios = []gitScenario{ "onbranch:mai", "onbranch:other", "onbranch:main/", + "onbranch:[[:lower:]]ain", + "onbranch:[[:digit:]]ain", + "onbranch:m[![:digit:]]in", + "onbranch:[[:bogus:]]ain", + "onbranch:ma[in", "nosuch:x", } var global strings.Builder diff --git a/key.go b/key.go index 84b019c..72b3bbd 100644 --- a/key.go +++ b/key.go @@ -1,12 +1,57 @@ package gitconfig +import ( + "strings" +) + // ParseKey parses "section.name" or "section.subsection.name" as git does: // the subsection is everything between the first and the last dot, section -// and name are lowered. A key git rejects is ErrInvalidKey, including a -// subsection containing a newline or a NUL byte. Reads treat an invalid key -// as absent; only ParseKey and edits report it. +// and name are lowered. A key git rejects is a *KeyError wrapping +// ErrInvalidKey: the section must hold only letters, digits and '-', the +// name must also start with a letter, and the subsection may hold anything +// but a newline or a NUL byte. Reads treat an invalid key as absent; only +// ParseKey and edits report it. func ParseKey(s string) (Key, error) { - panic("not implemented") + k, ok := parseKey(s) + if !ok { + return Key{}, &KeyError{Key: s, Err: ErrInvalidKey} + } + return k, nil +} + +// parseKey is ParseKey without the error value, for reads. +// +// A key with an empty section is valid when it has a subsection +// (".sub.name", "..name"): git reads it from a header [ "sub"] or [.], and +// every entry read must have a key ParseKey accepts. Edits refuse it. +func parseKey(s string) (Key, bool) { + // The subsection may itself contain dots: only the first and the last + // dot are separators. + first := strings.IndexByte(s, '.') + last := strings.LastIndexByte(s, '.') + if first < 0 { + return Key{}, false + } + name := s[last+1:] + if !validName(name) { + return Key{}, false + } + k := Key{Name: lowerASCII(name)} + if first == last { + // No subsection: the section may not be empty (".name"). + if !validSection(s[:first]) || first == 0 { + return Key{}, false + } + k.Section = lowerASCII(s[:first]) + return k, true + } + if !validSection(s[:first]) || !validSubsection(s[first+1:last]) { + return Key{}, false + } + k.Section = lowerASCII(s[:first]) + k.Subsection = s[first+1 : last] + k.HasSubsection = true + return k, true } // String returns the key as "section.name" or "section.subsection.name" @@ -17,3 +62,74 @@ func (k Key) String() string { } return k.Section + "." + k.Name } + +// sectionKey identifies a section or subsection in canonical form: the part +// of a Key before its name. +type sectionKey struct { + section string + subsection string + hasSub bool +} + +func (k Key) sectionKey() sectionKey { + return sectionKey{k.Section, k.Subsection, k.HasSubsection} +} + +// isKeyChar reports whether c may appear in a section or key name. +func isKeyChar(c byte) bool { + return isAlpha(c) || '0' <= c && c <= '9' || c == '-' +} + +func isAlpha(c byte) bool { + return 'a' <= c|0x20 && c|0x20 <= 'z' +} + +// validName reports whether s is a valid key name: a letter, then letters, +// digits and dashes. +func validName(s string) bool { + if s == "" || !isAlpha(s[0]) { + return false + } + for i := 1; i < len(s); i++ { + if !isKeyChar(s[i]) { + return false + } + } + return true +} + +// validSection reports whether s holds only the characters of a section +// name. It may be empty. +func validSection(s string) bool { + for i := 0; i < len(s); i++ { + if !isKeyChar(s[i]) { + return false + } + } + return true +} + +// validSubsection reports whether s can be written in a header: anything +// but a newline or a NUL byte. +func validSubsection(s string) bool { + return !strings.ContainsAny(s, "\n\x00") +} + +// lowerASCII lowers ASCII letters only: the format is ASCII, and Unicode +// folding would accept names git rejects. +func lowerASCII(s string) string { + // Most names are already lower case: return s itself unless a letter + // needs lowering, so that the common case does not allocate. + for i := 0; i < len(s); i++ { + if 'A' <= s[i] && s[i] <= 'Z' { + b := []byte(s) + for j := i; j < len(b); j++ { + if 'A' <= b[j] && b[j] <= 'Z' { + b[j] += 'a' - 'A' + } + } + return string(b) + } + } + return s +} diff --git a/load.go b/load.go new file mode 100644 index 0000000..f12e1f6 --- /dev/null +++ b/load.go @@ -0,0 +1,450 @@ +package gitconfig + +import ( + "errors" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "runtime" + "slices" + "strings" + "syscall" +) + +// Limits of one load (requirement X3 of docs/REQUIREMENTS.md): git's +// include depth, and bounds on the work a set of files built to multiply it +// can cause. +const ( + maxIncludeDepth = 10 + maxLoadFiles = 1000 + maxLoadBytes = 64 << 20 +) + +// repo is the repository of an Env. +type repo struct { + gitDir string // as given: the worktree's git directory + commonDir string // the repository's git directory +} + +// repo returns the repository of the Env, or nil outside a repository. +func (e Env) repo() (*repo, error) { + if e.GitDir == "" { + return nil, nil + } + if !filepath.IsAbs(e.GitDir) { + return nil, &FileError{Path: e.GitDir, Err: ErrNotAbsolute} + } + r := &repo{gitDir: e.GitDir, commonDir: e.GitDir} + data, err := readSmall(filepath.Join(e.GitDir, "commondir")) + switch { + case err == nil: + dir := strings.TrimRight(string(data), "\r\n") + if !filepath.IsAbs(dir) { + if dir, err = joinReal(e.GitDir, dir); err != nil { + return nil, &FileError{Path: e.GitDir, Err: err} + } + } + r.commonDir = filepath.Clean(dir) + case !errors.Is(err, fs.ErrNotExist): + return nil, &FileError{Path: filepath.Join(e.GitDir, "commondir"), Err: err} + } + return r, nil +} + +// joinReal joins the relative path rel to the directory dir as the system +// resolves it: the links of dir are followed before the ".." of rel apply, +// where filepath.Join would apply them first. +func joinReal(dir, rel string) (string, error) { + real, err := filepath.EvalSymlinks(dir) + if err != nil { + return "", err + } + return filepath.Join(real, rel), nil +} + +func (r *repo) localConfig() string { + return filepath.Join(r.commonDir, "config") +} + +func (r *repo) worktreeConfig() string { + return filepath.Join(r.gitDir, "config.worktree") +} + +// worktreeConfigEnabled reports whether extensions.worktreeConfig is set +// in the repository's config. +func (r *repo) worktreeConfigEnabled() (bool, error) { + data, err := readConfigFile(r.localConfig(), maxLoadBytes) + if isMissing(err) { + return false, nil + } + if err != nil { + return false, &FileError{Path: r.localConfig(), Err: err} + } + p, err := parse(r.localConfig(), data) + if err != nil { + return false, &FileError{Path: r.localConfig(), Err: err} + } + return worktreeConfigIn(p.entries(ScopeLocal, r.localConfig(), nil), r.localConfig()) +} + +// worktreeConfigIn returns the last value of extensions.worktreeConfig in +// the entries of file path: git does not take it from included files. +func worktreeConfigIn(entries []Entry, path string) (bool, error) { + k := Key{Section: "extensions", Name: "worktreeconfig"} + enabled := false + for _, e := range entries { + if e.Key != k || e.Origin.File != path { + continue + } + b, err := e.Bool() + if err != nil { + return false, &FileError{Path: path, Err: err} + } + enabled = b + } + return enabled, nil +} + +// severalWorktrees reports whether the repository has linked worktrees. +func (r *repo) severalWorktrees() bool { + entries, err := os.ReadDir(filepath.Join(r.commonDir, "worktrees")) + return err == nil && len(entries) > 0 +} + +// branch returns the branch HEAD points to, or "" if it is detached or +// cannot be read. +func (r *repo) branch() string { + data, err := readSmall(filepath.Join(r.gitDir, "HEAD")) + if err != nil { + return "" + } + s, ok := strings.CutPrefix(string(data), "ref:") + if !ok { + return "" + } + s = strings.TrimSpace(s) + name, ok := strings.CutPrefix(s, "refs/heads/") + if !ok { + return "" + } + return name +} + +// readSmall reads a small file of the repository, such as HEAD. +func readSmall(path string) ([]byte, error) { + return readConfigFile(path, 4096) +} + +// readConfigFile reads a regular file, at most limit bytes. A file that is +// not regular is ErrNotRegular, read without blocking: a FIFO would block +// until someone writes to it. os.DevNull, which git documents to skip a +// scope, reads as empty. +func readConfigFile(path string, limit int64) ([]byte, error) { + if path == os.DevNull { + return nil, nil + } + var data []byte + err := retryTransient(func() error { + f, err := openRegular(path) + if err != nil { + return err + } + defer f.Close() + fi, err := f.Stat() + if err != nil { + return err + } + if !fi.Mode().IsRegular() { + return ErrNotRegular + } + if fi.Size() > limit { + return ErrLoadLimit + } + data, err = io.ReadAll(io.LimitReader(f, limit+1)) + if err == nil && int64(len(data)) > limit { + err = ErrLoadLimit + } + return err + }) + return data, err +} + +// isMissing reports whether err means that a file does not exist: also +// when a directory of its path is a file, as git takes it. +func isMissing(err error) bool { + return errors.Is(err, fs.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) +} + +// loader loads files into a list of entries, following includes, within +// the limits of one load. +type loader struct { + env Env + repo *repo + + files int + bytes int64 + entries []Entry + + branch string + branchRead bool + + realGitDir string // with '/', "" if it cannot be resolved + realDirRead bool + + conds map[condKey]bool // includeIf conditions evaluated so far +} + +// condKey is an includeIf condition and the file that holds it, which +// "gitdir:./" depends on. +type condKey struct{ cond, from string } + +func (e Env) newLoader() (*loader, error) { + r, err := e.repo() + if err != nil { + return nil, err + } + return &loader{env: e, repo: r}, nil +} + +func (l *loader) config() *Config { + return newConfig(l.entries) +} + +// loadFile loads the file at path for scope. A missing file is empty. If +// skipUnreadable, a file that cannot be read for lack of permission is +// skipped, as git does for global files. +func (l *loader) loadFile(path string, scope Scope, skipUnreadable bool) error { + err := l.load(path, scope, 0) + // Only path itself is skipped: an unreadable include of it still fails. + if skipUnreadable { + if fe, ok := errors.AsType[*FileError](err); ok && fe.Path == path && errors.Is(fe.Err, fs.ErrPermission) { + return nil + } + } + return err +} + +func (l *loader) load(path string, scope Scope, depth int) error { + l.files++ + if l.files > maxLoadFiles { + return &FileError{Path: path, Err: ErrLoadLimit} + } + // Read at most what remains of the byte budget of the load. + data, err := readConfigFile(path, maxLoadBytes-l.bytes) + if isMissing(err) { + return nil + } + if err != nil { + return &FileError{Path: path, Err: err} + } + l.bytes += int64(len(data)) + + pf, err := parse(path, data) + if err != nil { + return &FileError{Path: path, Err: err} + } + start := len(l.entries) + l.entries = pf.entries(scope, path, l.entries) + // The entries of an include go right after its include.path entry, so + // that later entries of this file take precedence over them. Take the + // entries of this file out of l.entries, which include appends to, and + // put them back one by one. + own := slices.Clone(l.entries[start:]) + l.entries = l.entries[:start] + for _, e := range own { + l.entries = append(l.entries, e) + if err := l.include(e, path, scope, depth); err != nil { + return err + } + } + return nil +} + +// include follows e if it is an include directive. from is the file that +// holds it, "" for the command line. +func (l *loader) include(e Entry, from string, scope Scope, depth int) error { + if e.Key.Name != "path" { + return nil + } + switch { + case e.Key.Section == "include" && !e.Key.HasSubsection: + case e.Key.Section == "includeif" && e.Key.HasSubsection: + ok, err := l.condition(e.Key.Subsection, from) + if err != nil || !ok { + return err + } + default: + return nil + } + + if e.Implicit { + return l.fail(from, e, "missing value for include.path") + } + if e.Value == "" { + // An empty path includes nothing. + return nil + } + path, err := e.Path(l.env.Home) + if err != nil { + return &FileError{Path: from, Err: err} + } + if !filepath.IsAbs(path) { + if from == "" { + return &FileError{Path: path, Err: ErrNotAbsolute} + } + path = dirPrefix(from) + path + } + if depth+1 > maxIncludeDepth { + return &FileError{Path: path, Err: fmt.Errorf("%w while including %s from %s; this might be due to circular includes", ErrIncludeDepth, path, from)} + } + return l.load(path, scope, depth+1) +} + +// fail reports an invalid include directive, as git does: a bad line of +// the file, or a bad command-line entry. +func (l *loader) fail(from string, e Entry, msg string) error { + if from == "" { + return &KeyError{Key: e.Key.String(), Err: errors.New("gitconfig: " + msg)} + } + return &FileError{Path: from, Err: &SyntaxError{File: from, Line: e.Origin.Line, Msg: msg}} +} + +// dirPrefix returns path up to and including its last separator: an +// include's relative path is appended to it as is, not cleaned, as git +// does. +func dirPrefix(path string) string { + i := strings.LastIndexByte(path, '/') + if runtime.GOOS == "windows" { + i = max(i, strings.LastIndexByte(path, '\\')) + } + return path[:i+1] +} + +// condition evaluates the condition of an includeIf, once per condition +// and file: a file can repeat path under one costly gitdir: pattern. +func (l *loader) condition(cond, from string) (bool, error) { + k := condKey{cond, from} + if ok, found := l.conds[k]; found { + return ok, nil + } + ok, err := l.evalCondition(cond, from) + if err != nil { + return false, err + } + if l.conds == nil { + l.conds = make(map[condKey]bool) + } + l.conds[k] = ok + return ok, nil +} + +// evalCondition evaluates the condition of an includeIf. Unknown +// conditions, and those this package does not support (hasconfig:), are +// false. +func (l *loader) evalCondition(cond, from string) (bool, error) { + switch { + case strings.HasPrefix(cond, "gitdir:"): + return l.gitdir(cond[len("gitdir:"):], from, false) + case strings.HasPrefix(cond, "gitdir/i:"): + return l.gitdir(cond[len("gitdir/i:"):], from, true) + case strings.HasPrefix(cond, "onbranch:"): + if l.repo == nil { + return false, nil + } + if !l.branchRead { + l.branch, l.branchRead = l.repo.branch(), true + } + if l.branch == "" { + return false, nil + } + pattern := cond[len("onbranch:"):] + if strings.HasSuffix(pattern, "/") { + pattern += "**" + } + return globMatch(pattern, l.branch, false), nil + } + return false, nil +} + +// gitdir evaluates includeIf "gitdir:": "~/" is the home +// directory, "./" the absolute directory of the including file, a relative +// pattern matches at any depth ("**/" prepended) and a trailing '/' matches +// everything below ("**" appended). The pattern is matched against the git +// directory as given and its real path. +func (l *loader) gitdir(pattern, from string, fold bool) (bool, error) { + switch { + case strings.HasPrefix(pattern, "./"): + if from == "" { + return false, &FileError{Path: pattern, Err: ErrNotAbsolute} + } + dir := dirPrefix(from) + if !filepath.IsAbs(from) { + abs, err := filepath.Abs(from) + if err != nil { + return false, &FileError{Path: from, Err: err} + } + dir = dirPrefix(abs) + } + pattern = filepath.ToSlash(dir) + pattern[2:] + case strings.HasPrefix(pattern, "~/"): + if l.env.Home == "" { + return false, nil + } + pattern = strings.TrimSuffix(filepath.ToSlash(l.env.Home), "/") + pattern[1:] + } + // Checked only now: a "./" pattern on the command line is an error even + // outside a repository. + if l.repo == nil { + return false, nil + } + if !isAbsPattern(pattern) { + pattern = "**/" + pattern + } + if strings.HasSuffix(pattern, "/") { + pattern += "**" + } + dir := filepath.ToSlash(l.repo.gitDir) + if globMatch(pattern, dir, fold) { + return true, nil + } + if !l.realDirRead { + if real, err := filepath.EvalSymlinks(l.repo.gitDir); err == nil { + l.realGitDir = filepath.ToSlash(real) + } + l.realDirRead = true + } + if l.realGitDir != "" && l.realGitDir != dir { + return globMatch(pattern, l.realGitDir, fold), nil + } + return false, nil +} + +// isAbsPattern reports whether a gitdir pattern, written with '/', is +// absolute. +func isAbsPattern(p string) bool { + if strings.HasPrefix(p, "/") { + return true + } + return runtime.GOOS == "windows" && filepath.IsAbs(filepath.FromSlash(p)) +} + +// command loads the entries of the command line. +func (l *loader) command() error { + for _, c := range l.env.Command { + k, err := ParseKey(c.Key) + if err != nil { + return err + } + e := Entry{Key: k, Value: c.Value, Implicit: c.Implicit, Origin: Origin{Scope: ScopeCommand}} + if c.Implicit { + e.Value = "" + } + l.entries = append(l.entries, e) + if err := l.include(e, "", ScopeCommand, 0); err != nil { + return err + } + } + return nil +} diff --git a/parse.go b/parse.go new file mode 100644 index 0000000..d07704f --- /dev/null +++ b/parse.go @@ -0,0 +1,437 @@ +package gitconfig + +import ( + "bytes" + "strings" +) + +// A file is kept as its bytes plus a list of items covering them exactly, +// in order: parsing never drops a byte, and edits splice the bytes, then +// parse them again. + +type itemKind uint8 + +const ( + itemBOM itemKind = iota // the UTF-8 byte order mark, first in the file + itemSpace // spaces, tabs and lone CRs + itemNewline // "\n" or "\r\n" + itemComment // from '#' or ';' to the end of the line, newline excluded + itemHeader // from '[' to ']' + itemEntry // from the key to the end of the value, continuation lines included +) + +// item is one piece of the file. It holds no pointer, so that a large file +// costs the garbage collector nothing to scan: the decoded data of headers +// and entries is in the tables of parsed. +type item struct { + kind itemKind + implicit bool // entries only: no '=' + open openEnd // entries only + sec int32 // the section a header opens or an entry is in: index in parsed.secs, 0 before any header + text int32 // entries only: index in parsed.texts + start int // bytes of the item: data[start:end] + end int + line int // line of start, 1-based +} + +// entryText is the key name and the value of an entry. +type entryText struct { + name string // lowered + value string // decoded +} + +// parsed is a parsed file: its items, and the data they refer to. +type parsed struct { + items []item + // secs[0] is the zero sectionKey, for entries before any header. No + // header has it: a header always has a section name or a subsection. + secs []sectionKey + texts []entryText +} + +func (p *parsed) secOf(it item) sectionKey { return p.secs[it.sec] } +func (p *parsed) nameOf(it item) string { return p.texts[it.text].name } +func (p *parsed) valueOf(it item) string { return p.texts[it.text].value } + +// entries appends the entries of the file, those before any header left +// out, to out. +func (p *parsed) entries(scope Scope, file string, out []Entry) []Entry { + for _, it := range p.items { + if it.kind != itemEntry || it.sec == 0 { + continue + } + sec := p.secs[it.sec] + t := p.texts[it.text] + out = append(out, Entry{ + Key: Key{Section: sec.section, Subsection: sec.subsection, HasSubsection: sec.hasSub, Name: t.name}, + Value: t.value, + Implicit: it.implicit, + Origin: Origin{Scope: scope, File: file, Line: it.line}, + }) + } + return out +} + +// openEnd tells how an entry at the end of the file ends, for an edit that +// adds a line after it. +type openEnd uint8 + +const ( + closed openEnd = iota // the next line is not part of the value + continued // a backslash-newline ends the file: the next line would continue the value + backslashEOF // a backslash ends the file: a newline would continue the value +) + +var bom = []byte("\xef\xbb\xbf") + +type parser struct { + parsed + name string + data []byte + pos int + line int + + sec int32 // current section + secIndex map[sectionKey]int32 + headers map[string]int32 // the section of each header, by its bytes + names map[string]string // key names, interned + buf []byte // scratch for values and subsections +} + +// parse splits data into items, or returns the *SyntaxError git would +// report. +func parse(name string, data []byte) (parsed, error) { + p := &parser{name: name, data: data, line: 1, secIndex: make(map[sectionKey]int32), headers: make(map[string]int32), names: make(map[string]string)} + p.items = make([]item, 0, 2*bytes.Count(data, []byte{'\n'})+4) + p.secs = []sectionKey{{}} + if err := p.run(); err != nil { + return parsed{}, err + } + return p.parsed, nil +} + +func (p *parser) errorAt(line int, msg string) error { + return &SyntaxError{File: p.name, Line: line, Msg: msg} +} + +func (p *parser) add(kind itemKind, start, end int) *item { + p.items = append(p.items, item{kind: kind, start: start, end: end, line: p.line, sec: p.sec}) + return &p.items[len(p.items)-1] +} + +// addEntry adds an entry item. +func (p *parser) addEntry(start, end int, name []byte, value string) *item { + it := p.add(itemEntry, start, end) + n, ok := p.names[string(name)] + if !ok { + n = lowerASCII(string(name)) + p.names[string(name)] = n + } + it.text = int32(len(p.texts)) + p.texts = append(p.texts, entryText{n, value}) + return it +} + +// section returns the index of sec in p.secs, adding it if absent. +func (p *parser) section(sec sectionKey) int32 { + i, ok := p.secIndex[sec] + if !ok { + i = int32(len(p.secs)) + p.secs = append(p.secs, sec) + p.secIndex[sec] = i + } + return i +} + +// isSpace reports whether data[i] is whitespace between tokens: a space, a +// tab, or a CR that does not start a CRLF. +func isSpace(data []byte, i int) bool { + switch data[i] { + case ' ', '\t': + return true + case '\r': + return !isNewline(data, i) + } + return false +} + +// isNewline reports whether a newline, "\n" or "\r\n", starts at data[i]. +func isNewline(data []byte, i int) bool { + return data[i] == '\n' || data[i] == '\r' && i+1 < len(data) && data[i+1] == '\n' +} + +// newlineLen returns the length of the newline at data[i]. +func newlineLen(data []byte, i int) int { + if data[i] == '\r' { + return 2 + } + return 1 +} + +func (p *parser) run() error { + d := p.data + if bytes.HasPrefix(d, bom) { + p.add(itemBOM, 0, len(bom)) + p.pos = len(bom) + } + for p.pos < len(d) { + start := p.pos + c := d[start] + switch { + case isSpace(d, start): + for p.pos < len(d) && isSpace(d, p.pos) { + p.pos++ + } + p.add(itemSpace, start, p.pos) + case isNewline(d, start): + p.pos += newlineLen(d, start) + p.add(itemNewline, start, p.pos) + p.line++ + case c == '#' || c == ';': + end := len(d) + if i := bytes.IndexByte(d[start:], '\n'); i >= 0 { + end = start + i + if d[end-1] == '\r' { + end-- + } + } + p.pos = end + p.add(itemComment, start, end) + case c == '[': + if err := p.header(); err != nil { + return err + } + case isAlpha(c): + if err := p.entry(); err != nil { + return err + } + default: + return p.errorAt(p.line, "invalid line") + } + } + return nil +} + +func (p *parser) header() error { + d := p.data + start := p.pos + i := start + 1 + for i < len(d) && (isKeyChar(d[i]) || d[i] == '.') { + i++ + } + nameEnd := i + if i == len(d) { + // git counts the end of the line it stopped at, and so reports the + // next line for an unterminated header or value. + return p.errorAt(p.line+1, "unterminated section header") + } + + // sub reuses the parser's scratch buffer, and gives it back grown. + sub := p.buf[:0] + defer func() { p.buf = sub[:0] }() + hasSub := false + switch { + case d[i] == ']': + if nameEnd == start+1 { + return p.errorAt(p.line, "empty section name") + } + i++ + + case isSpace(d, i): + for i < len(d) && isSpace(d, i) { + i++ + } + if i == len(d) || d[i] != '"' { + return p.errorAt(p.line, "invalid section header") + } + i++ + for { + if i == len(d) || d[i] == '\n' { + return p.errorAt(p.line, "unterminated subsection") + } + c := d[i] + if c == '"' { + break + } + if c == '\\' { + i++ + if i == len(d) || d[i] == '\n' { + return p.errorAt(p.line, "unterminated subsection") + } + c = d[i] + } + if c == 0 { + // git cuts the subsection at the NUL, reading a key the + // file does not spell. + return p.errorAt(p.line, "NUL byte in subsection") + } + sub = append(sub, c) + i++ + } + i++ + if i == len(d) || isNewline(d, i) { + return p.errorAt(p.line+1, "unterminated section header") + } + if d[i] != ']' { + return p.errorAt(p.line, "invalid section header") + } + i++ + hasSub = true + + default: + return p.errorAt(p.line, "invalid section header") + } + + // The section is decoded once for each spelling of a header: files + // repeat their headers. + sec, ok := p.headers[string(d[start:i])] + if !ok { + sec = p.section(headerKey(lowerASCII(string(d[start+1:nameEnd])), sub, hasSub)) + p.headers[string(d[start:i])] = sec + } + p.sec = sec + p.pos = i + p.add(itemHeader, start, i) + return nil +} + +// headerKey returns the section of a header with the lowered name, and the +// subsection sub if hasSub. +func headerKey(name string, sub []byte, hasSub bool) sectionKey { + dot := strings.IndexByte(name, '.') + switch { + case !hasSub && dot < 0: + return sectionKey{section: name} + case !hasSub: + // Legacy header [a.B]: subsection "b". + return sectionKey{name[:dot], name[dot+1:], true} + case dot < 0: + return sectionKey{name, string(sub), true} + } + // [a.b "x"]: git reads the keys as a.b.x.. + return sectionKey{name[:dot], name[dot+1:] + "." + string(sub), true} +} + +func (p *parser) entry() error { + d := p.data + start := p.pos + i := start + for i < len(d) && isKeyChar(d[i]) { + i++ + } + name := d[start:i] + j := i + for j < len(d) && (d[j] == ' ' || d[j] == '\t') { + j++ + } + if j == len(d) || isNewline(d, j) { + // No '=': an implicit value. The entry ends with its name; the + // whitespace after it is an item of its own. + p.pos = i + p.addEntry(start, i, name, "").implicit = true + return nil + } + if d[j] != '=' { + return p.errorAt(p.line, "invalid key") + } + + value, end, lines, open, err := p.value(j + 1) + if err != nil { + return err + } + p.pos = end + p.addEntry(start, end, name, value).open = open + p.line += lines + return nil +} + +// value decodes the value starting at data[i], up to a comment, the end of +// the line or the end of the file. It returns the value, where it ends and +// how many continuation lines it spans, and how it ends if it ends the file. +// +// Whitespace outside quotes is skipped before the value, and held back +// inside it: an escape, a quote or any other character writes it out first, +// and the end of the value drops it. +func (p *parser) value(i int) (string, int, int, openEnd, error) { + d := p.data + buf := p.buf[:0] + defer func() { p.buf = buf[:0] }() + pending := -1 // start of held-back whitespace, -1 if none + inQuote := false + lines := 0 + open := closed + flush := func(at int) { + if pending >= 0 { + buf = append(buf, d[pending:at]...) + pending = -1 + } + } +loop: + for i < len(d) && !isNewline(d, i) { + c := d[i] + switch { + case c == '\\': + if i+1 == len(d) { + // A backslash ends the file. Outside quotes it is dropped. + if inQuote { + return "", 0, 0, 0, p.errorAt(p.line+lines+1, "unterminated quote") + } + flush(i) + i++ + open = backslashEOF + continue + } + flush(i) + if isNewline(d, i+1) { + i += 1 + newlineLen(d, i+1) + lines++ + if i == len(d) { + open = continued + } + continue + } + switch d[i+1] { + case 't': + buf = append(buf, '\t') + case 'n': + buf = append(buf, '\n') + case 'b': + buf = append(buf, '\b') + case '"', '\\': + buf = append(buf, d[i+1]) + default: + return "", 0, 0, 0, p.errorAt(p.line+lines, "invalid escape") + } + i += 2 + case c == '"': + flush(i) + inQuote = !inQuote + i++ + case inQuote: + buf = append(buf, c) + i++ + case c == ' ' || c == '\t' || c == '\r': + // A CR here is a lone one: the loop stops at a CRLF. + if len(buf) > 0 && pending < 0 { + pending = i + } + i++ + case c == '#' || c == ';': + break loop + default: + flush(i) + buf = append(buf, c) + i++ + } + } + // A comment ends the value only outside quotes: the quote is open only + // at a newline or the end of the file. + if inQuote { + return "", 0, 0, 0, p.errorAt(p.line+lines, "unterminated quote") + } + if n := bytes.IndexByte(buf, 0); n >= 0 { + // git stops reading the value at a NUL byte. + buf = buf[:n] + } + return string(buf), i, lines, open, nil +} diff --git a/platform.go b/platform.go new file mode 100644 index 0000000..2a26e15 --- /dev/null +++ b/platform.go @@ -0,0 +1,54 @@ +package gitconfig + +import ( + "errors" + "os" + "runtime" + "syscall" + "time" +) + +// openRegular opens a file for reading without blocking on a FIFO; the +// caller checks that it is a regular file. Windows has no FIFO to open by +// name. +func openRegular(path string) (*os.File, error) { + flag := os.O_RDONLY + if runtime.GOOS != "windows" { + flag |= syscall.O_NONBLOCK + } + return os.OpenFile(path, flag, 0) +} + +// Another process holding a file for a moment, as a concurrent writer or a +// reader (an editor, an antivirus) does, makes opening or replacing it fail +// on Windows. Such failures are retried for up to a second. +const retryFor = time.Second + +// Windows error codes for a file another process holds. +const ( + errorAccessDenied syscall.Errno = 5 + errorSharingViolation syscall.Errno = 32 + errorLockViolation syscall.Errno = 33 +) + +// transient reports whether err is a Windows failure to retry. +func transient(err error) bool { + return runtime.GOOS == "windows" && + (errors.Is(err, errorAccessDenied) || errors.Is(err, errorSharingViolation) || errors.Is(err, errorLockViolation)) +} + +// retryTransient calls fn until it succeeds, fails for another reason, or +// a second has passed. Only Windows refuses to open or replace a file +// another process holds: elsewhere fn is called once. +func retryTransient(fn func() error) error { + deadline := time.Now().Add(retryFor) + delay := time.Millisecond + for { + err := fn() + if err == nil || !transient(err) || time.Now().After(deadline) { + return err + } + time.Sleep(delay) + delay = min(2*delay, 50*time.Millisecond) + } +} diff --git a/update.go b/update.go new file mode 100644 index 0000000..3d9e833 --- /dev/null +++ b/update.go @@ -0,0 +1,135 @@ +package gitconfig + +import ( + "bytes" + "errors" + "io/fs" + "os" + "path/filepath" + "runtime" +) + +// maxSymlinks is how many symbolic links an update follows to the file it +// edits. +const maxSymlinks = 5 + +var errTooManyLinks = errors.New("too many levels of symbolic links") + +// updateFile edits the file at path under git's lock: see Env.UpdateFile. +func updateFile(path string, scope Scope, fn func(*File) error) (err error) { + target, err := followLinks(path) + if err != nil { + return err + } + + // The lock gets the target's permissions before the new content is + // written: until then, a lock replacing a file that exists is private, + // so that no one can open it and read a private file through it. + perm := os.FileMode(0o666) + if _, err := os.Lstat(target); err == nil { + perm = 0o600 + } + lockPath := target + ".lock" + lock, err := os.OpenFile(lockPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + if errors.Is(err, fs.ErrExist) { + return &FileError{Path: lockPath, Err: ErrLocked} + } + return &FileError{Path: lockPath, Err: err} + } + committed := false + defer func() { + if !committed { + lock.Close() + os.Remove(lockPath) + } + }() + + // Read under the lock: no update made since can be lost. + data, err := readConfigFile(target, maxLoadBytes) + exists := err == nil + switch { + case isMissing(err): + data = nil + case err != nil: + return &FileError{Path: path, Err: err} + } + p, err := parse(path, data) + if err != nil { + return &FileError{Path: path, Err: err} + } + f := &File{name: path, scope: scope, data: data, parsed: p} + + if err := callClosing(f, fn); err != nil { + return err + } + if bytes.Equal(f.data, data) { + // Unchanged: the deferred cleanup drops the lock. + return nil + } + + // Windows has no permission bits to copy. + if exists && runtime.GOOS != "windows" { + fi, err := os.Stat(target) + if err != nil { + return &FileError{Path: target, Err: err} + } + // Exactly the target's bits: chmod is not subject to the umask. + if err := lock.Chmod(fi.Mode().Perm()); err != nil { + return &FileError{Path: lockPath, Err: err} + } + } + if _, err := lock.Write(f.data); err != nil { + return &FileError{Path: lockPath, Err: err} + } + if err := lock.Close(); err != nil { + return &FileError{Path: lockPath, Err: err} + } + if err := retryTransient(func() error { return os.Rename(lockPath, target) }); err != nil { + return &FileError{Path: target, Err: err} + } + committed = true + return nil +} + +// callClosing calls fn and closes f when fn returns or panics. +func callClosing(f *File, fn func(*File) error) error { + defer func() { f.closed = true }() + return fn(f) +} + +// followLinks returns the file path names once symbolic links are followed, +// as git does: up to maxSymlinks of them, a relative target resolved +// against the directory of the link. The file may not exist; if it does, it +// must be a regular file. +func followLinks(path string) (string, error) { + p := path + for n := 0; ; n++ { + fi, err := os.Lstat(p) + if errors.Is(err, fs.ErrNotExist) { + return p, nil + } + if err != nil { + return "", &FileError{Path: p, Err: err} + } + switch { + case fi.Mode().IsRegular(): + return p, nil + case fi.Mode()&fs.ModeSymlink == 0: + return "", &FileError{Path: p, Err: ErrNotRegular} + case n == maxSymlinks: + return "", &FileError{Path: path, Err: errTooManyLinks} + } + link, err := os.Readlink(p) + if err != nil { + return "", &FileError{Path: p, Err: err} + } + if !filepath.IsAbs(link) { + // Not filepath.Join: cleaning would drop a ".." before the + // system follows the links leading to it. + dir, _ := filepath.Split(p) + link = dir + link + } + p = link + } +} diff --git a/value.go b/value.go new file mode 100644 index 0000000..8f28d92 --- /dev/null +++ b/value.go @@ -0,0 +1,160 @@ +package gitconfig + +import ( + "errors" + "math" + "strconv" + "strings" +) + +// errInvalidUnit is the reason of a *ValueError for an integer that is not a +// number followed by an optional unit. +var errInvalidUnit = errors.New("invalid unit") + +// parseBool interprets a non-implicit value as a boolean. +func parseBool(v string) (bool, error) { + switch { + case v == "", equalFoldASCII(v, "false"), equalFoldASCII(v, "no"), equalFoldASCII(v, "off"): + return false, nil + case equalFoldASCII(v, "true"), equalFoldASCII(v, "yes"), equalFoldASCII(v, "on"): + return true, nil + } + n, err := parseInt(v) + if err != nil { + return false, err + } + // As in git, a numeric boolean must fit in 32 bits. + if n < math.MinInt32 || n > math.MaxInt32 { + return false, strconv.ErrRange + } + return n != 0, nil +} + +// parseInt interprets a value as an integer: leading whitespace, an +// optional sign, a decimal, octal ("0" prefix) or hexadecimal ("0x" prefix) +// number, then an optional unit k, m or g, case-insensitive. +func parseInt(v string) (int64, error) { + s := strings.TrimLeft(v, " \t\n\v\f\r") + neg := false + if s != "" && (s[0] == '+' || s[0] == '-') { + neg = s[0] == '-' + s = s[1:] + } + + // "0x" is a prefix only if a hex digit follows: "0x" alone is the octal + // number 0 followed by the invalid unit "x". + base := uint64(10) + switch { + case len(s) > 2 && s[0] == '0' && s[1]|0x20 == 'x' && digitValue(s[2]) < 16: + base = 16 + s = s[2:] + case len(s) > 0 && s[0] == '0': + base = 8 + } + + // On overflow, the scan goes on to find where the digits end: an + // invalid unit after them is reported before the range error. + var mag uint64 + overflow := false + n := 0 + for ; n < len(s); n++ { + d := uint64(digitValue(s[n])) + if d >= base { + break + } + if mag > (math.MaxUint64-d)/base { + overflow = true + } else { + mag = mag*base + d + } + } + if n == 0 { + // No digits; git reports this as an invalid unit too. + return 0, errInvalidUnit + } + + var unit uint64 = 1 + switch rest := s[n:]; { + case rest == "": + case len(rest) == 1 && rest[0]|0x20 == 'k': + unit = 1 << 10 + case len(rest) == 1 && rest[0]|0x20 == 'm': + unit = 1 << 20 + case len(rest) == 1 && rest[0]|0x20 == 'g': + unit = 1 << 30 + default: + return 0, errInvalidUnit + } + + // The magnitude of the number and of the result may reach 1<<63 for a + // negative value. + limit := uint64(math.MaxInt64) + if neg { + limit++ + } + if overflow || mag > limit || mag > limit/unit { + return 0, strconv.ErrRange + } + mag *= unit + if neg { + return int64(-mag), nil + } + return int64(mag), nil +} + +// digitValue returns the value of c as a digit up to base 36, or 36. +func digitValue(c byte) int { + switch { + case '0' <= c && c <= '9': + return int(c - '0') + case 'a' <= c|0x20 && c|0x20 <= 'z': + return int(c|0x20-'a') + 10 + } + return 36 +} + +// errPrefix, errNoHome and errUserHome are the reasons of a *ValueError for +// paths this package cannot expand. +var ( + errPrefix = errors.New("%(prefix)/ is not supported") + errNoHome = errors.New("home directory unknown") + errUserHome = errors.New("~user/ is not supported") +) + +// expandPath interprets a value as a path: "~" and "~/" are replaced by +// home. "~user/" and "%(prefix)/" are refused. +func expandPath(v, home string) (string, error) { + switch { + case strings.HasPrefix(v, "%(prefix)/"): + return "", errPrefix + case v == "~" || strings.HasPrefix(v, "~/"): + if home == "" { + return "", errNoHome + } + return home + v[1:], nil + case strings.HasPrefix(v, "~"): + return "", errUserHome + } + return v, nil +} + +// equalFoldASCII reports whether s and t are equal, ignoring the case of +// ASCII letters only. +func equalFoldASCII(s, t string) bool { + if len(s) != len(t) { + return false + } + for i := 0; i < len(s); i++ { + a, b := s[i], t[i] + if 'A' <= a && a <= 'Z' { + a += 'a' - 'A' + } + if 'A' <= b && b <= 'Z' { + b += 'a' - 'A' + } + if a != b { + return false + } + } + return true +}