From a3092bf2252eb2c47df246210c1a48c2b01d8a5f Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Wed, 4 Sep 2024 22:41:16 +0200 Subject: [PATCH 1/2] add lazy loading --- pkg/rules/regex.go | 34 ++++++++++++++++++++++++++++++++++ pkg/rules/string.go | 9 ++------- 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 pkg/rules/regex.go diff --git a/pkg/rules/regex.go b/pkg/rules/regex.go new file mode 100644 index 0000000..ebcf9f1 --- /dev/null +++ b/pkg/rules/regex.go @@ -0,0 +1,34 @@ +package rules + +import ( + "regexp" + "sync" +) + +// nolint: lll +// Define all regular expressions here: +var ( + validUUIDRegexp = lazyRegexCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) + asciiRegexp = lazyRegexCompile(`^[\x00-\x7F]*$`) +) + +// lazyRegexCompile returns a function that compiles the regular expression +// once, when the function is called for the first time. +// If the function is never called, the regular expression is never compiled, +// thus saving on performance. +// +// All regular expression literals should be compiled using this function. +// +// Credits: https://github.com/go-playground/validator/commit/2e1df48b5ab876bdd461bdccc51d109389e7572f +func lazyRegexCompile(str string) func() *regexp.Regexp { + var ( + regex *regexp.Regexp + once sync.Once + ) + return func() *regexp.Regexp { + once.Do(func() { + regex = regexp.MustCompile(str) + }) + return regex + } +} diff --git a/pkg/rules/string.go b/pkg/rules/string.go index 9354305..3a7fe45 100644 --- a/pkg/rules/string.go +++ b/pkg/rules/string.go @@ -73,12 +73,9 @@ func StringDNSLabel() govy.RuleSet[string] { ).WithErrorCode(ErrorCodeStringDNSLabel) } -var validUUIDRegexp = regexp. - MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") - // StringUUID ensures property's value is a valid UUID string. func StringUUID() govy.Rule[string] { - return StringMatchRegexp(validUUIDRegexp, + return StringMatchRegexp(validUUIDRegexp(), "00000000-0000-0000-0000-000000000000", "e190c630-8873-11ee-b9d1-0242ac120002", "79258D24-01A7-47E5-ACBB-7E762DE52298"). @@ -86,11 +83,9 @@ func StringUUID() govy.Rule[string] { WithErrorCode(ErrorCodeStringUUID) } -var asciiRegexp = regexp.MustCompile("^[\x00-\x7F]*$") - // StringASCII ensures property's value contains only ASCII characters. func StringASCII() govy.Rule[string] { - return StringMatchRegexp(asciiRegexp).WithErrorCode(ErrorCodeStringASCII) + return StringMatchRegexp(asciiRegexp()).WithErrorCode(ErrorCodeStringASCII) } // StringURL ensures property's value is a valid URL as defined by [url.Parse] function. From 5b7d57e4e8cc58ffd634fd84ca827d150b01cbe4 Mon Sep 17 00:00:00 2001 From: Mateusz Hawrus Date: Thu, 19 Sep 2024 13:07:15 +0200 Subject: [PATCH 2/2] add tests --- pkg/rules/regex_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 pkg/rules/regex_test.go diff --git a/pkg/rules/regex_test.go b/pkg/rules/regex_test.go new file mode 100644 index 0000000..1a50cd8 --- /dev/null +++ b/pkg/rules/regex_test.go @@ -0,0 +1,18 @@ +package rules + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLazyRegexCompile(t *testing.T) { + lazyRegexp := lazyRegexCompile("^test$") + + re1 := lazyRegexp() + assert.True(t, re1.MatchString("test")) + re2 := lazyRegexp() + assert.True(t, re2.MatchString("test")) + + assert.True(t, re1 == re2, "both regular expression must be the same pointer") +}