Skip to content

Commit 2727c14

Browse files
Alessio Tregliafedekunzeodeke-em
authored
types/Coin: compile and reuse Regexps to reduce massive RAM+CPU burn (cosmos#8001)
types/Coin: compile and reuse Regexps to reduce massive RAM+CPU burn (cosmos#7989) From: cosmos#7989 Closes: cosmos#7986 Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Emmanuel T Odeke <emmanuel@orijtech.com>
1 parent 5be42d9 commit 2727c14

File tree

6 files changed

+56
-22
lines changed

6 files changed

+56
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
3939

4040
### Features
4141

42-
* (types/coin.go) [\#6755](https://github.com/cosmos/cosmos-sdk/pull/6755) Add custom regex validation for `Coin` denom by overwriting `CoinDenomRegex` when using `/types/coin.go`.
42+
* (types/coin.go) [\#6755](https://github.com/cosmos/cosmos-sdk/pull/6755) [\#8001](https://github.com/cosmos/cosmos-sdk/pull/8001) Allow custom regex validation for `Coin` denom through `SetCoinDenomRegex()`.
4343
* (version) [\#7835](https://github.com/cosmos/cosmos-sdk/issues/7835) [\#7940](https://github.com/cosmos/cosmos-sdk/issues/7940) The version --long command now shows the list of build dependencies and their versioning information.
4444

4545
### Improvements

RELEASE_NOTES.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ See the [Cosmos SDK 0.39.2 milestone](https://github.com/cosmos/cosmos-sdk/miles
66

77
## Allow ValidateDenom() to be customised per application
88

9-
Applications can now customise `types.Coin` denomination validation by
10-
replacing `types.CoinDenomRegex` with their application-specific validation function.
9+
Applications can now customise `types.Coin` denomination validation by passing
10+
their application-specific validation function to `types.SetCoinDenomRegex()`.
1111

1212
## Upgrade queries don't work after upgrade
1313

types/bench_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package types_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/cosmos/cosmos-sdk/types"
7+
)
8+
9+
var coinStrs = []string{
10+
"2000ATM",
11+
"5000AMX",
12+
"192XXX",
13+
"1e9BTC",
14+
}
15+
16+
func BenchmarkParseCoin(b *testing.B) {
17+
var blankCoin types.Coin
18+
b.ReportAllocs()
19+
for i := 0; i < b.N; i++ {
20+
for _, coinStr := range coinStrs {
21+
coin, err := types.ParseCoin(coinStr)
22+
if err != nil {
23+
b.Fatal(err)
24+
}
25+
if coin == blankCoin {
26+
b.Fatal("Unexpectedly returned a blank coin")
27+
}
28+
}
29+
}
30+
}

types/coin.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -599,33 +599,37 @@ var (
599599
reAmt = `[[:digit:]]+`
600600
reDecAmt = `[[:digit:]]*\.[[:digit:]]+`
601601
reSpc = `[[:space:]]*`
602-
reDnm = returnReDnm
603-
reCoin = returnReCoin
604-
reDecCoin = returnDecCoin
602+
reDnm *regexp.Regexp
603+
reCoin *regexp.Regexp
604+
reDecCoin *regexp.Regexp
605605
)
606606

607-
func returnDecCoin() *regexp.Regexp {
608-
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, CoinDenomRegex()))
609-
}
610-
func returnReCoin() *regexp.Regexp {
611-
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, CoinDenomRegex()))
612-
}
613-
func returnReDnm() *regexp.Regexp {
614-
return regexp.MustCompile(fmt.Sprintf(`^%s$`, CoinDenomRegex()))
607+
func init() {
608+
SetCoinDenomRegex(DefaultCoinDenomRegex)
615609
}
616610

617611
// DefaultCoinDenomRegex returns the default regex string
618612
func DefaultCoinDenomRegex() string {
619613
return reDnmString
620614
}
621615

622-
// CoinDenomRegex returns the current regex string and can be overwritten for custom validation
623-
var CoinDenomRegex = DefaultCoinDenomRegex
616+
// coinDenomRegex returns the current regex string and can be overwritten through the SetCoinDenomRegex accessor.
617+
var coinDenomRegex = DefaultCoinDenomRegex
618+
619+
// SetCoinDenomRegex allows for coin's custom validation by overriding the regular
620+
// expression string used for denom validation.
621+
func SetCoinDenomRegex(reFn func() string) {
622+
coinDenomRegex = reFn
623+
624+
reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, coinDenomRegex()))
625+
reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, coinDenomRegex()))
626+
reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, coinDenomRegex()))
627+
}
624628

625629
// ValidateDenom validates a denomination string returning an error if it is
626630
// invalid.
627631
func ValidateDenom(denom string) error {
628-
if !reDnm().MatchString(denom) {
632+
if !reDnm.MatchString(denom) {
629633
return fmt.Errorf("invalid denom: %s", denom)
630634
}
631635
return nil
@@ -642,7 +646,7 @@ func mustValidateDenom(denom string) {
642646
func ParseCoin(coinStr string) (coin Coin, err error) {
643647
coinStr = strings.TrimSpace(coinStr)
644648

645-
matches := reCoin().FindStringSubmatch(coinStr)
649+
matches := reCoin.FindStringSubmatch(coinStr)
646650
if matches == nil {
647651
return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr)
648652
}

types/coin_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ func TestCoinIsValid(t *testing.T) {
7373
func TestCustomValidation(t *testing.T) {
7474

7575
newDnmRegex := `[\x{1F600}-\x{1F6FF}]`
76-
CoinDenomRegex = func() string {
76+
SetCoinDenomRegex(func() string {
7777
return newDnmRegex
78-
}
78+
})
7979

8080
cases := []struct {
8181
coin Coin
@@ -92,7 +92,7 @@ func TestCustomValidation(t *testing.T) {
9292
for i, tc := range cases {
9393
require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
9494
}
95-
CoinDenomRegex = DefaultCoinDenomRegex
95+
SetCoinDenomRegex(DefaultCoinDenomRegex)
9696
}
9797

9898
func TestAddCoin(t *testing.T) {

types/dec_coin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ func (coins DecCoins) Sort() DecCoins {
600600
func ParseDecCoin(coinStr string) (coin DecCoin, err error) {
601601
coinStr = strings.TrimSpace(coinStr)
602602

603-
matches := reDecCoin().FindStringSubmatch(coinStr)
603+
matches := reDecCoin.FindStringSubmatch(coinStr)
604604
if matches == nil {
605605
return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr)
606606
}

0 commit comments

Comments
 (0)