Skip to content

Commit 9e59832

Browse files
authored
Merge pull request #1465 from rhatdan/regexp
Create pkg/regexp to better handle init regex
2 parents 7967d4b + b4f7b11 commit 9e59832

File tree

7 files changed

+263
-23
lines changed

7 files changed

+263
-23
lines changed

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ lint_task:
116116
echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/backports.list
117117
apt-get update
118118
apt-get install -y libbtrfs-dev libdevmapper-dev
119-
test_script: make local-validate && make lint
119+
test_script: make TAGS=regex_precompile local-validate && make lint && make clean
120120

121121

122122
# Update metadata on VM images referenced by this repository state

pkg/idtools/usergroupadd_linux.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package idtools
22

33
import (
44
"fmt"
5-
"regexp"
65
"sort"
76
"strconv"
87
"strings"
98
"sync"
9+
10+
"github.com/containers/storage/pkg/regexp"
1011
)
1112

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

27-
idOutOnce sync.Once
28-
idOutRegexp *regexp.Regexp
28+
idOutRegexp = regexp.Delayed(`uid=([0-9]+).*gid=([0-9]+)`)
2929
// default length for a UID/GID subordinate range
3030
defaultRangeLen = 65536
3131
defaultRangeStart = 100000
@@ -37,10 +37,6 @@ var (
3737
// /etc/sub{uid,gid} ranges which will be used for user namespace
3838
// mapping ranges in containers.
3939
func AddNamespaceRangesUser(name string) (int, int, error) {
40-
idOutOnce.Do(func() {
41-
idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`)
42-
})
43-
4440
if err := addUser(name); err != nil {
4541
return -1, -1, fmt.Errorf("adding user %q: %w", name, err)
4642
}

pkg/regexp/regexp.go

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package regexp
2+
3+
import (
4+
"io"
5+
"regexp"
6+
"sync"
7+
)
8+
9+
// Regexp is a wrapper struct used for wrapping MustCompile regex expressions
10+
// used as global variables. Using this stucture helps speed the startup time
11+
// of apps that want to use global regex variables. This library initializes them on
12+
// first use as opposed to the start of the executable.
13+
type Regexp struct {
14+
once sync.Once
15+
regexp *regexp.Regexp
16+
val string
17+
}
18+
19+
func Delayed(val string) Regexp {
20+
re := Regexp{
21+
val: val,
22+
}
23+
if precompile {
24+
re.regexp = regexp.MustCompile(re.val)
25+
}
26+
return re
27+
}
28+
29+
func (re *Regexp) compile() {
30+
if precompile {
31+
return
32+
}
33+
re.once.Do(func() {
34+
re.regexp = regexp.MustCompile(re.val)
35+
})
36+
}
37+
38+
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte {
39+
re.compile()
40+
return re.regexp.Expand(dst, template, src, match)
41+
}
42+
43+
func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte {
44+
re.compile()
45+
return re.regexp.ExpandString(dst, template, src, match)
46+
}
47+
func (re *Regexp) Find(b []byte) []byte {
48+
re.compile()
49+
return re.regexp.Find(b)
50+
}
51+
52+
func (re *Regexp) FindAll(b []byte, n int) [][]byte {
53+
re.compile()
54+
return re.regexp.FindAll(b, n)
55+
}
56+
57+
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int {
58+
re.compile()
59+
return re.regexp.FindAllIndex(b, n)
60+
}
61+
62+
func (re *Regexp) FindAllString(s string, n int) []string {
63+
re.compile()
64+
return re.regexp.FindAllString(s, n)
65+
}
66+
67+
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int {
68+
re.compile()
69+
return re.regexp.FindAllStringIndex(s, n)
70+
}
71+
72+
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string {
73+
re.compile()
74+
return re.regexp.FindAllStringSubmatch(s, n)
75+
}
76+
77+
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int {
78+
re.compile()
79+
return re.regexp.FindAllStringSubmatchIndex(s, n)
80+
}
81+
82+
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte {
83+
re.compile()
84+
return re.regexp.FindAllSubmatch(b, n)
85+
}
86+
87+
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int {
88+
re.compile()
89+
return re.regexp.FindAllSubmatchIndex(b, n)
90+
}
91+
92+
func (re *Regexp) FindIndex(b []byte) (loc []int) {
93+
re.compile()
94+
return re.regexp.FindIndex(b)
95+
}
96+
97+
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
98+
re.compile()
99+
return re.regexp.FindReaderIndex(r)
100+
}
101+
102+
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int {
103+
re.compile()
104+
return re.regexp.FindReaderSubmatchIndex(r)
105+
}
106+
107+
func (re *Regexp) FindString(s string) string {
108+
re.compile()
109+
return re.regexp.FindString(s)
110+
}
111+
112+
func (re *Regexp) FindStringIndex(s string) (loc []int) {
113+
re.compile()
114+
return re.regexp.FindStringIndex(s)
115+
}
116+
117+
func (re *Regexp) FindStringSubmatch(s string) []string {
118+
re.compile()
119+
return re.regexp.FindStringSubmatch(s)
120+
}
121+
122+
func (re *Regexp) FindStringSubmatchIndex(s string) []int {
123+
re.compile()
124+
return re.regexp.FindStringSubmatchIndex(s)
125+
}
126+
127+
func (re *Regexp) FindSubmatch(b []byte) [][]byte {
128+
re.compile()
129+
return re.regexp.FindSubmatch(b)
130+
}
131+
132+
func (re *Regexp) FindSubmatchIndex(b []byte) []int {
133+
re.compile()
134+
return re.regexp.FindSubmatchIndex(b)
135+
}
136+
137+
func (re *Regexp) LiteralPrefix() (prefix string, complete bool) {
138+
re.compile()
139+
return re.regexp.LiteralPrefix()
140+
}
141+
142+
func (re *Regexp) Longest() {
143+
re.compile()
144+
re.regexp.Longest()
145+
}
146+
147+
func (re *Regexp) Match(b []byte) bool {
148+
re.compile()
149+
return re.regexp.Match(b)
150+
}
151+
152+
func (re *Regexp) MatchReader(r io.RuneReader) bool {
153+
re.compile()
154+
return re.regexp.MatchReader(r)
155+
}
156+
func (re *Regexp) MatchString(s string) bool {
157+
re.compile()
158+
return re.regexp.MatchString(s)
159+
}
160+
161+
func (re *Regexp) NumSubexp() int {
162+
re.compile()
163+
return re.regexp.NumSubexp()
164+
}
165+
166+
func (re *Regexp) ReplaceAll(src, repl []byte) []byte {
167+
re.compile()
168+
return re.regexp.ReplaceAll(src, repl)
169+
}
170+
171+
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte {
172+
re.compile()
173+
return re.regexp.ReplaceAllFunc(src, repl)
174+
}
175+
176+
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte {
177+
re.compile()
178+
return re.regexp.ReplaceAllLiteral(src, repl)
179+
}
180+
181+
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string {
182+
re.compile()
183+
return re.regexp.ReplaceAllLiteralString(src, repl)
184+
}
185+
186+
func (re *Regexp) ReplaceAllString(src, repl string) string {
187+
re.compile()
188+
return re.regexp.ReplaceAllString(src, repl)
189+
}
190+
191+
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string {
192+
re.compile()
193+
return re.regexp.ReplaceAllStringFunc(src, repl)
194+
}
195+
196+
func (re *Regexp) Split(s string, n int) []string {
197+
re.compile()
198+
return re.regexp.Split(s, n)
199+
}
200+
201+
func (re *Regexp) String() string {
202+
re.compile()
203+
return re.regexp.String()
204+
}
205+
206+
func (re *Regexp) SubexpIndex(name string) int {
207+
re.compile()
208+
return re.regexp.SubexpIndex(name)
209+
}
210+
211+
func (re *Regexp) SubexpNames() []string {
212+
re.compile()
213+
return re.regexp.SubexpNames()
214+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//go:build !regexp_precompile
2+
// +build !regexp_precompile
3+
4+
package regexp
5+
6+
const precompile = false

pkg/regexp/regexp_precompile.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//go:build regexp_precompile
2+
// +build regexp_precompile
3+
4+
package regexp
5+
6+
const precompile = true

pkg/regexp/regexp_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package regexp
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestMatchString(t *testing.T) {
8+
r := Delayed(`[0-9]+`)
9+
10+
if !r.MatchString("100") {
11+
t.Fatalf("Should have matched")
12+
}
13+
14+
if r.MatchString("test") {
15+
t.Fatalf("Should not have matched")
16+
}
17+
}
18+
19+
func TestFindStringSubmatch(t *testing.T) {
20+
r := Delayed(`a=([0-9]+).*b=([0-9]+)`)
21+
22+
if len(r.FindStringSubmatch("a=1,b=2")) != 3 {
23+
t.Fatalf("Should have matched 3")
24+
}
25+
26+
if len(r.FindStringSubmatch("a=1")) != 0 {
27+
t.Fatalf("Should not have matched 0")
28+
}
29+
}

pkg/stringid/stringid.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,26 @@ import (
99
"math"
1010
"math/big"
1111
"math/rand"
12-
"regexp"
1312
"strconv"
1413
"strings"
1514
"sync"
1615
"time"
16+
17+
"github.com/containers/storage/pkg/regexp"
1718
)
1819

1920
const shortLen = 12
2021

2122
var (
22-
validShortID *regexp.Regexp
23-
validHex *regexp.Regexp
24-
onceRegex sync.Once
23+
validShortID = regexp.Delayed("^[a-f0-9]{12}$")
24+
validHex = regexp.Delayed(`^[a-f0-9]{64}$`)
2525

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

30-
func initRegex() {
31-
onceRegex.Do(func() {
32-
validShortID = regexp.MustCompile("^[a-f0-9]{12}$")
33-
validHex = regexp.MustCompile(`^[a-f0-9]{64}$`)
34-
})
35-
}
36-
3730
// IsShortID determines if an arbitrary string *looks like* a short ID.
3831
func IsShortID(id string) bool {
39-
initRegex()
40-
4132
return validShortID.MatchString(id)
4233
}
4334

@@ -88,8 +79,6 @@ func GenerateNonCryptoID() string {
8879

8980
// ValidateID checks whether an ID string is a valid image ID.
9081
func ValidateID(id string) error {
91-
initRegex()
92-
9382
if ok := validHex.MatchString(id); !ok {
9483
return fmt.Errorf("image ID %q is invalid", id)
9584
}

0 commit comments

Comments
 (0)