diff --git a/backend/api/deck.go b/backend/api/deck.go index af69498..7fd5e83 100644 --- a/backend/api/deck.go +++ b/backend/api/deck.go @@ -1,10 +1,10 @@ package api import ( - "encoding/json" "errors" "fmt" "regexp" + "strconv" "strings" "github.com/gin-gonic/gin" @@ -13,6 +13,19 @@ import ( const WILDCARDS = "0123456789abcdefghijklmnopqrstvwxyzABCDEFGHIJKLMNOPQRSTVWXYZ_`[]/^%?@><=-+*:;,.()#$!'{}~" +var compressionWildcardRegex []*regexp.Regexp + +func init() { + escapeRegex := regexp.MustCompile(`[-\/\\^$*+?.()|[\]{}]`) + + compressionWildcardRegex = make([]*regexp.Regexp, 0, len(WILDCARDS)) + for i := range WILDCARDS { + wildCard := fmt.Sprintf("&%c", WILDCARDS[i]) + escaped := escapeRegex.ReplaceAllString(wildCard, "\\$&") + compressionWildcardRegex = append(compressionWildcardRegex, regexp.MustCompile(escaped)) + } +} + func (a *API) getDeckHandler(c *gin.Context) { name := c.Param("name") name = strings.ToLower(name) @@ -60,11 +73,6 @@ func (a *API) getDeckHandler(c *gin.Context) { c.Data(200, "text/plain", []byte(result.String())) } -func escapeRegexp(s string) string { - r := regexp.MustCompile(`[-\/\\^$*+?.()|[\]{}]`) - return r.ReplaceAllString(s, "\\$&") -} - func decompress(s string) (string, error) { parts := strings.Split(s, "||") if len(parts) < 2 { @@ -76,51 +84,58 @@ func decompress(s string) (string, error) { for i := len(compressionDict) - 1; i >= 0; i-- { word := compressionDict[i] - wildCard := fmt.Sprintf("&%c", WILDCARDS[i]) - r, err := regexp.Compile(escapeRegexp(wildCard)) - if err != nil { - return "", err - } - text = r.ReplaceAllString(text, word) + text = compressionWildcardRegex[i].ReplaceAllString(text, word) } return text, nil } func parseCommaDelimitedIntegerArray(s string) ([]int, error) { if s == "-" { - return make([]int, 0), nil + return nil, nil + } + + currIndex := 0 + result := make([]int, 0, strings.Count(s, ",")+1) + for currIndex < len(s) { + nextIndex := currIndex + strings.Index(s[currIndex:], ",") + if nextIndex < currIndex { + nextIndex = len(s) + } + + resultVal, err := strconv.ParseInt(s[currIndex:nextIndex], 10, 64) + if err != nil { + return nil, err + } + + currIndex = nextIndex + 1 + result = append(result, int(resultVal)) } - //nolint:prealloc - var result []int - err := json.Unmarshal([]byte(fmt.Sprintf("[%s]", s)), &result) - return result, err + return result, nil } -func splitSemicolonDelimited2DArray(s string) [][]string { +func splitDoubleSemicolonArray(s string) []string { if s == "-" { - return make([][]string, 0) + return nil } - //nolint:prealloc - var result [][]string - split := strings.Split(s, ";;") - for _, element := range split { - result = append(result, strings.Split(element, ";")) - } - return result + return strings.Split(s, ";;") } -func parseCards(cards [][]string) []string { - //nolint:prealloc - var result []string - for _, card := range cards { - result = append(result, parseCard(card)) +func parseCards(cardSections []string) []string { + result := make([]string, 0, len(cardSections)) + for _, cardSection := range cardSections { + result = append(result, parseCard(cardSection)) } return result } -func parseCard(c []string) string { - return c[0] +func parseCard(cardSection string) string { + sectionEnd := strings.Index(cardSection, ";") + if sectionEnd == -1 { + return cardSection + } + + return cardSection[:sectionEnd] } func decompressDeck(deck string) (map[string]int, error) { @@ -134,10 +149,14 @@ func decompressDeck(deck string) (map[string]int, error) { if err != nil { return nil, err } - cards := parseCards(splitSemicolonDelimited2DArray(parts[1])) + cards := parseCards(splitDoubleSemicolonArray(parts[1])) - deckDict := make(map[string]int) + deckDict := make(map[string]int, len(cards)) for _, idx := range d { + if idx < 0 || idx >= len(cards) { + return nil, errors.New("card index out of bounds") + } + name := cards[idx] deckDict[name]++ } diff --git a/backend/api/deck_test.go b/backend/api/deck_test.go new file mode 100644 index 0000000..70ccd84 --- /dev/null +++ b/backend/api/deck_test.go @@ -0,0 +1,264 @@ +package api + +import ( + "fmt" + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +func TestDecompress(t *testing.T) { + testCases := []struct { + desc string + input string + output string + shouldError bool + }{ + { + desc: "Empty", + input: "", + output: "", + shouldError: true, + }, + { + desc: "Single |", + input: "|", + output: "", + shouldError: true, + }, + { + desc: "Minimal Valid", + input: "||", + output: "", + shouldError: false, + }, + { + desc: "No compression", + input: "||I love love slay the relics and slay the spire", + output: "I love love slay the relics and slay the spire", + shouldError: false, + }, + { + desc: "Unused compression", + input: "Foo|Bar||I love love slay the relics and slay the spire", + output: "I love love slay the relics and slay the spire", + shouldError: false, + }, + { + desc: "Basic compression", + input: "love|slay the||I &0 &0 &1 relics and &1 spire", + output: "I love love slay the relics and slay the spire", + shouldError: false, + }, + { + desc: "Basic compression", + input: "love|slay the||I &0 &0 &1 relics and &1 spire", + output: "I love love slay the relics and slay the spire", + shouldError: false, + }, + { + desc: "Small Deck", + input: "card|junk||0,1,1,0,2,0;;;&01;&1;x;;&02;&1;y;;&03;&1;z", + output: "0,1,1,0,2,0;;;card1;junk;x;;card2;junk;y;;card3;junk;z", + shouldError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + actualOutput, err := decompress(tc.input) + if tc.shouldError { + assert.Equal(t, true, err != nil) + return + } + + assert.NilError(t, err) + assert.Equal(t, actualOutput, tc.output) + }) + } +} + +func TestParseCommaDelimitedIntegerArray(t *testing.T) { + testCases := []struct { + desc string + input string + output []int + shouldError bool + }{ + { + desc: "Empty", + input: "", + output: []int{}, + shouldError: false, + }, + { + desc: "Dash", + input: "-", + output: []int{}, + shouldError: false, + }, + { + desc: "Invalid", + input: "{", + output: []int{}, + shouldError: true, + }, + { + desc: "Single", + input: "19", + output: []int{19}, + shouldError: false, + }, + { + desc: "Many", + input: "1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9", + output: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + shouldError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + actualOutput, err := parseCommaDelimitedIntegerArray(tc.input) + if tc.shouldError { + assert.Equal(t, true, err != nil) + return + } + + assert.NilError(t, err) + assert.Equal(t, len(actualOutput), len(tc.output)) + for i := range actualOutput { + assert.Equal(t, actualOutput[i], tc.output[i]) + } + }) + } +} + +func TestDecompressDeck(t *testing.T) { + testCases := []struct { + desc string + input string + output map[string]int + shouldError bool + }{ + { + desc: "Empty", + input: "", + output: map[string]int{}, + shouldError: true, + }, + { + desc: "Invalid card index", + input: "card|junk||};;;&01;&1;x;;&02;&1;y;;&03;&1;z", + output: map[string]int{}, + shouldError: true, + }, + { + desc: "Negative card index", + input: "card|junk||-1;;;&01;&1;x;;&02;&1;y;;&03;&1;z", + output: map[string]int{}, + shouldError: true, + }, + { + desc: "Out of bounds card index", + input: "card|junk||3;;;&01;&1;x;;&02;&1;y;;&03;&1;z", + output: map[string]int{}, + shouldError: true, + }, + { + desc: "Simple deck", + input: "card|junk||0,1,1,0,2,0;;;&01;&1;x;;&02;&1;y;;&03;&1;z", + output: map[string]int{ + "card1": 3, + "card2": 2, + "card3": 1, + }, + shouldError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + actualOutput, err := decompressDeck(tc.input) + if tc.shouldError { + assert.Equal(t, true, err != nil) + return + } + + assert.NilError(t, err) + assert.Equal(t, len(actualOutput), len(tc.output)) + for i := range actualOutput { + assert.Equal(t, actualOutput[i], tc.output[i]) + } + }) + } +} + +// getBigDeckString makes us a 52 card compressed deck string for testing with. +func getBigDeckString() string { + compressionDict := make([]string, 0, 128) + + // Generate cards. + compressedCards := make([]string, 0, 52) + for i := 0; i < 52; i++ { + compressionDict = append(compressionDict, fmt.Sprintf("Card Name %d;other details;junk", i)) + compressedCards = append(compressionDict, fmt.Sprintf("%c", WILDCARDS[i])) + } + cardsPart := strings.Join(compressedCards, ";;") + + // Generate deck. + deckIndices := make([]string, 0, 100) + for i := 0; i < 52; i++ { + deckIndices = append(deckIndices, fmt.Sprintf("%d", i)) + } + for i := 0; i < 48; i++ { + deckIndices = append(deckIndices, "17") + } + deckPart := strings.Join(deckIndices, ",") + + // Put it all together. + compressionPart := strings.Join(compressionDict, "|") + compressedPart := fmt.Sprintf("%s;;;%s", deckPart, cardsPart) + return fmt.Sprintf("%s||%s", compressionPart, compressedPart) +} + +func TestDecompressBigDeck(t *testing.T) { + output, err := decompressDeck(getBigDeckString()) + assert.NilError(t, err) + + assert.Equal(t, len(output), 52) + for card, count := range output { + switch card { + case "Card Name 17": + assert.Equal(t, count, 49, card) + default: + assert.Equal(t, count, 1, card) + } + } +} + +func BenchmarkDecompressDeck(b *testing.B) { + testCases := []struct { + desc string + input string + }{ + { + desc: "Simple deck", + input: "card|junk||0,1,1,0,2,0;;;&01;&1;x;;&02;&1;y;;&03;&1;z", + }, + { + desc: "Big deck", + input: getBigDeckString(), + }, + } + + b.ResetTimer() + for _, tc := range testCases { + b.Run(tc.desc, func(b *testing.B) { + for i := 0; i < b.N; i++ { + decompressDeck(tc.input) + } + }) + } +} diff --git a/backend/tools/go.mod b/backend/tools/go.mod index 6837819..07e18a3 100644 --- a/backend/tools/go.mod +++ b/backend/tools/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/golangci/golangci-lint v1.51.2 github.com/rinchsan/gosimports v0.3.7 + golang.org/x/perf v0.0.0-20241004173025-94b0db8a2472 gotest.tools/gotestsum v1.9.0 ) @@ -19,6 +20,7 @@ require ( github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/OpenPeeDeeP/depguard v1.1.1 // indirect + github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/ashanbrown/forbidigo v1.4.0 // indirect @@ -56,7 +58,7 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.8.1 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect @@ -66,7 +68,7 @@ require ( github.com/golangci/misspell v0.4.0 // indirect github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect @@ -166,14 +168,14 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.17.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/backend/tools/go.sum b/backend/tools/go.sum index bff9f51..24b7702 100644 --- a/backend/tools/go.sum +++ b/backend/tools/go.sum @@ -58,6 +58,8 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/OpenPeeDeeP/depguard v1.1.1 h1:TSUznLjvp/4IUP+OQ0t/4jF4QUyxIcVX8YnghZdunyA= github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= +github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 h1:xlwdaKcTNVW4PtpQb8aKA4Pjy0CdJHEqvFbAnvR5m2g= +github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -200,8 +202,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= @@ -235,8 +238,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -582,8 +585,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9 h1:6WHiuFL9FNjg8RljAaT7FNUuKDbvMqS1i5cr2OE2sLQ= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -616,8 +619,8 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -659,7 +662,7 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -670,6 +673,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/perf v0.0.0-20241004173025-94b0db8a2472 h1:kifBcAOhV9fBI1RN0vai5zSvvOjhBTjvymGbRIKB0M4= +golang.org/x/perf v0.0.0-20241004173025-94b0db8a2472/go.mod h1:wLQChX6XSStqGCueXQW/40U3ucTK44jnINeZ0omqPIQ= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -683,8 +688,9 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -746,8 +752,8 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -766,8 +772,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -846,8 +853,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/backend/tools/tools.go b/backend/tools/tools.go index a6cedad..8c517f5 100644 --- a/backend/tools/tools.go +++ b/backend/tools/tools.go @@ -5,5 +5,6 @@ package tools import ( _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/rinchsan/gosimports/cmd/gosimports" + _ "golang.org/x/perf/cmd/benchstat" _ "gotest.tools/gotestsum" )