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 b4f7b11
Show file tree
Hide file tree
Showing 7 changed files with 263 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.Delayed(`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
214 changes: 214 additions & 0 deletions pkg/regexp/regexp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package regexp

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

// Regexp is a wrapper struct used for wrapping MustCompile regex expressions
// used as global variables. Using this stucture helps speed the startup time
// of apps that want to use global regex variables. This library initializes them on
// first use as opposed to the start of the executable.
type Regexp struct {
once sync.Once
regexp *regexp.Regexp
val string
}

func Delayed(val string) Regexp {
re := Regexp{
val: val,
}
if precompile {
re.regexp = regexp.MustCompile(re.val)
}
return re
}

func (re *Regexp) compile() {
if precompile {
return
}
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

const 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

const 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 := Delayed(`[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 := Delayed(`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.Delayed("^[a-f0-9]{12}$")
validHex = regexp.Delayed(`^[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 b4f7b11

Please sign in to comment.