Skip to content

Commit

Permalink
Create pkg/regexp to better handle init regex
Browse files Browse the repository at this point in the history
The new Reexp structure, will handle the initialization of regexp
and then will do the MustCompile within a sync.Once field.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
  • Loading branch information
rhatdan committed Jan 12, 2023
1 parent 7967d4b commit f7291f7
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ lint_task:
echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/backports.list
apt-get update
apt-get install -y libbtrfs-dev libdevmapper-dev
test_script: make local-validate && make lint
test_script: make TAGS=regex_precompile local-validate && make lint && make clean


# Update metadata on VM images referenced by this repository state
Expand Down
10 changes: 3 additions & 7 deletions pkg/idtools/usergroupadd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package idtools

import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"sync"

"github.com/containers/storage/pkg/regexp"
)

// add a user and/or group to Linux /etc/passwd, /etc/group using standard
Expand All @@ -24,8 +25,7 @@ var (
"usermod": "-%s %d-%d %s",
}

idOutOnce sync.Once
idOutRegexp *regexp.Regexp
idOutRegexp = regexp.New(`uid=([0-9]+).*gid=([0-9]+)`)
// default length for a UID/GID subordinate range
defaultRangeLen = 65536
defaultRangeStart = 100000
Expand All @@ -37,10 +37,6 @@ var (
// /etc/sub{uid,gid} ranges which will be used for user namespace
// mapping ranges in containers.
func AddNamespaceRangesUser(name string) (int, int, error) {
idOutOnce.Do(func() {
idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`)
})

if err := addUser(name); err != nil {
return -1, -1, fmt.Errorf("adding user %q: %w", name, err)
}
Expand Down
210 changes: 210 additions & 0 deletions pkg/regexp/regexp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package regexp

import (
"io"
"regexp"
"sync"
)

// Regexp is a wrapper struct used for wrapping MustCompile regex expressions
// used init. Using this stucture should help speed up startup time of apps
// that want to initialize their regex at init time.
type Regexp struct {
once sync.Once
regexp *regexp.Regexp
val string
}

func New(val string) Regexp {
re := Regexp{
val: val,
}
if precompile {
re.compile()
}
return re
}

func (re *Regexp) compile() {
re.once.Do(func() {
re.regexp = regexp.MustCompile(re.val)
})
}

func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte {
re.compile()
return re.regexp.Expand(dst, template, src, match)
}

func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte {
re.compile()
return re.regexp.ExpandString(dst, template, src, match)
}
func (re *Regexp) Find(b []byte) []byte {
re.compile()
return re.regexp.Find(b)
}

func (re *Regexp) FindAll(b []byte, n int) [][]byte {
re.compile()
return re.regexp.FindAll(b, n)
}

func (re *Regexp) FindAllIndex(b []byte, n int) [][]int {
re.compile()
return re.regexp.FindAllIndex(b, n)
}

func (re *Regexp) FindAllString(s string, n int) []string {
re.compile()
return re.regexp.FindAllString(s, n)
}

func (re *Regexp) FindAllStringIndex(s string, n int) [][]int {
re.compile()
return re.regexp.FindAllStringIndex(s, n)
}

func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string {
re.compile()
return re.regexp.FindAllStringSubmatch(s, n)
}

func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int {
re.compile()
return re.regexp.FindAllStringSubmatchIndex(s, n)
}

func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte {
re.compile()
return re.regexp.FindAllSubmatch(b, n)
}

func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int {
re.compile()
return re.regexp.FindAllSubmatchIndex(b, n)
}

func (re *Regexp) FindIndex(b []byte) (loc []int) {
re.compile()
return re.regexp.FindIndex(b)
}

func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
re.compile()
return re.regexp.FindReaderIndex(r)
}

func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int {
re.compile()
return re.regexp.FindReaderSubmatchIndex(r)
}

func (re *Regexp) FindString(s string) string {
re.compile()
return re.regexp.FindString(s)
}

func (re *Regexp) FindStringIndex(s string) (loc []int) {
re.compile()
return re.regexp.FindStringIndex(s)
}

func (re *Regexp) FindStringSubmatch(s string) []string {
re.compile()
return re.regexp.FindStringSubmatch(s)
}

func (re *Regexp) FindStringSubmatchIndex(s string) []int {
re.compile()
return re.regexp.FindStringSubmatchIndex(s)
}

func (re *Regexp) FindSubmatch(b []byte) [][]byte {
re.compile()
return re.regexp.FindSubmatch(b)
}

func (re *Regexp) FindSubmatchIndex(b []byte) []int {
re.compile()
return re.regexp.FindSubmatchIndex(b)
}

func (re *Regexp) LiteralPrefix() (prefix string, complete bool) {
re.compile()
return re.regexp.LiteralPrefix()
}

func (re *Regexp) Longest() {
re.compile()
re.regexp.Longest()
}

func (re *Regexp) Match(b []byte) bool {
re.compile()
return re.regexp.Match(b)
}

func (re *Regexp) MatchReader(r io.RuneReader) bool {
re.compile()
return re.regexp.MatchReader(r)
}
func (re *Regexp) MatchString(s string) bool {
re.compile()
return re.regexp.MatchString(s)
}

func (re *Regexp) NumSubexp() int {
re.compile()
return re.regexp.NumSubexp()
}

func (re *Regexp) ReplaceAll(src, repl []byte) []byte {
re.compile()
return re.regexp.ReplaceAll(src, repl)
}

func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte {
re.compile()
return re.regexp.ReplaceAllFunc(src, repl)
}

func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte {
re.compile()
return re.regexp.ReplaceAllLiteral(src, repl)
}

func (re *Regexp) ReplaceAllLiteralString(src, repl string) string {
re.compile()
return re.regexp.ReplaceAllLiteralString(src, repl)
}

func (re *Regexp) ReplaceAllString(src, repl string) string {
re.compile()
return re.regexp.ReplaceAllString(src, repl)
}

func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string {
re.compile()
return re.regexp.ReplaceAllStringFunc(src, repl)
}

func (re *Regexp) Split(s string, n int) []string {
re.compile()
return re.regexp.Split(s, n)
}

func (re *Regexp) String() string {
re.compile()
return re.regexp.String()
}

func (re *Regexp) SubexpIndex(name string) int {
re.compile()
return re.regexp.SubexpIndex(name)
}

func (re *Regexp) SubexpNames() []string {
re.compile()
return re.regexp.SubexpNames()
}
6 changes: 6 additions & 0 deletions pkg/regexp/regexp_dontprecompile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//go:build !regexp_precompile
// +build !regexp_precompile

package regexp

var precompile = false
6 changes: 6 additions & 0 deletions pkg/regexp/regexp_precompile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//go:build regexp_precompile
// +build regexp_precompile

package regexp

var precompile = true
29 changes: 29 additions & 0 deletions pkg/regexp/regexp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package regexp

import (
"testing"
)

func TestMatchString(t *testing.T) {
r := New(`[0-9]+`)

if !r.MatchString("100") {
t.Fatalf("Should have matched")
}

if r.MatchString("test") {
t.Fatalf("Should not have matched")
}
}

func TestFindStringSubmatch(t *testing.T) {
r := New(`a=([0-9]+).*b=([0-9]+)`)

if len(r.FindStringSubmatch("a=1,b=2")) != 3 {
t.Fatalf("Should have matched 3")
}

if len(r.FindStringSubmatch("a=1")) != 0 {
t.Fatalf("Should not have matched 0")
}
}
19 changes: 4 additions & 15 deletions pkg/stringid/stringid.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,26 @@ import (
"math"
"math/big"
"math/rand"
"regexp"
"strconv"
"strings"
"sync"
"time"

"github.com/containers/storage/pkg/regexp"
)

const shortLen = 12

var (
validShortID *regexp.Regexp
validHex *regexp.Regexp
onceRegex sync.Once
validShortID = regexp.New("^[a-f0-9]{12}$")
validHex = regexp.New(`^[a-f0-9]{64}$`)

rngLock sync.Mutex
rng *rand.Rand // A RNG with seeding properties we control. It can only be accessed with randLock held.
)

func initRegex() {
onceRegex.Do(func() {
validShortID = regexp.MustCompile("^[a-f0-9]{12}$")
validHex = regexp.MustCompile(`^[a-f0-9]{64}$`)
})
}

// IsShortID determines if an arbitrary string *looks like* a short ID.
func IsShortID(id string) bool {
initRegex()

return validShortID.MatchString(id)
}

Expand Down Expand Up @@ -88,8 +79,6 @@ func GenerateNonCryptoID() string {

// ValidateID checks whether an ID string is a valid image ID.
func ValidateID(id string) error {
initRegex()

if ok := validHex.MatchString(id); !ok {
return fmt.Errorf("image ID %q is invalid", id)
}
Expand Down

0 comments on commit f7291f7

Please sign in to comment.