replace strings.Split(N) for strings.Cut() or alternatives
Go 1.18 and up now provides a strings.Cut() which is better suited for
splitting key/value pairs (and similar constructs), and performs better:
```go
func BenchmarkSplit(b *testing.B) {
b.ReportAllocs()
data := []string{"12hello=world", "12hello=", "12=hello", "12hello"}
for i := 0; i < b.N; i++ {
for _, s := range data {
_ = strings.SplitN(s, "=", 2)[0]
}
}
}
func BenchmarkCut(b *testing.B) {
b.ReportAllocs()
data := []string{"12hello=world", "12hello=", "12=hello", "12hello"}
for i := 0; i < b.N; i++ {
for _, s := range data {
_, _, _ = strings.Cut(s, "=")
}
}
}
```
BenchmarkSplit
BenchmarkSplit-10 8244206 128.0 ns/op 128 B/op 4 allocs/op
BenchmarkCut
BenchmarkCut-10 54411998 21.80 ns/op 0 B/op 0 allocs/op
While looking at occurrences of `strings.Split()`, I also updated some for alternatives,
or added some constraints; for cases where an specific number of items is expected, I used `strings.SplitN()`
with a suitable limit. This prevents (theoretical) unlimited splits.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -206,13 +206,13 @@ func parseStat(data string) (stat Stat, err error) {
|
||||
return stat, fmt.Errorf("invalid stat data: %q", data)
|
||||
}
|
||||
|
||||
parts := strings.SplitN(data[:i], "(", 2)
|
||||
if len(parts) != 2 {
|
||||
val, name, ok := strings.Cut(data[:i], "(")
|
||||
if !ok {
|
||||
return stat, fmt.Errorf("invalid stat data: %q", data)
|
||||
}
|
||||
|
||||
stat.Name = parts[1]
|
||||
_, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
|
||||
stat.Name = name
|
||||
_, err = fmt.Sscanf(val, "%d", &stat.PID)
|
||||
if err != nil {
|
||||
return stat, err
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func parseStat(data string) (stat Stat, err error) {
|
||||
// parts indexes should be offset by 3 from the field number given
|
||||
// proc(5), because parts is zero-indexed and we've removed fields
|
||||
// one (PID) and two (Name) in the paren-split.
|
||||
parts = strings.Split(data[i+2:], " ")
|
||||
parts := strings.Split(data[i+2:], " ")
|
||||
fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
|
||||
fmt.Sscanf(parts[4-3], "%d", &stat.PPID)
|
||||
return stat, nil
|
||||
|
||||
@@ -230,13 +230,10 @@ func ObjectWithLabelArgs(clicontext *cli.Context) (string, map[string]string) {
|
||||
func LabelArgs(labelStrings []string) map[string]string {
|
||||
labels := make(map[string]string, len(labelStrings))
|
||||
for _, label := range labelStrings {
|
||||
parts := strings.SplitN(label, "=", 2)
|
||||
key := parts[0]
|
||||
value := "true"
|
||||
if len(parts) > 1 {
|
||||
value = parts[1]
|
||||
key, value, ok := strings.Cut(label, "=")
|
||||
if !ok {
|
||||
value = "true"
|
||||
}
|
||||
|
||||
labels[key] = value
|
||||
}
|
||||
|
||||
@@ -247,11 +244,11 @@ func LabelArgs(labelStrings []string) map[string]string {
|
||||
func AnnotationArgs(annoStrings []string) (map[string]string, error) {
|
||||
annotations := make(map[string]string, len(annoStrings))
|
||||
for _, anno := range annoStrings {
|
||||
parts := strings.SplitN(anno, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
key, value, ok := strings.Cut(anno, "=")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid key=value format annotation: %v", anno)
|
||||
}
|
||||
annotations[parts[0]] = parts[1]
|
||||
annotations[key] = value
|
||||
}
|
||||
return annotations, nil
|
||||
}
|
||||
|
||||
@@ -128,12 +128,8 @@ var createCommand = cli.Command{
|
||||
if len(labelstr) > 0 {
|
||||
labels := map[string]string{}
|
||||
for _, lstr := range labelstr {
|
||||
l := strings.SplitN(lstr, "=", 2)
|
||||
if len(l) == 1 {
|
||||
labels[l[0]] = ""
|
||||
} else {
|
||||
labels[l[0]] = l[1]
|
||||
}
|
||||
k, v, _ := strings.Cut(lstr, "=")
|
||||
labels[k] = v
|
||||
}
|
||||
opts = append(opts, leases.WithLabels(labels))
|
||||
}
|
||||
|
||||
@@ -62,13 +62,11 @@ func parseMountFlag(m string) (specs.Mount, error) {
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
v := strings.SplitN(field, "=", 2)
|
||||
if len(v) < 2 {
|
||||
key, val, ok := strings.Cut(field, "=")
|
||||
if !ok {
|
||||
return mount, fmt.Errorf("invalid mount specification: expected key=val")
|
||||
}
|
||||
|
||||
key := v[0]
|
||||
val := v[1]
|
||||
switch key {
|
||||
case "type":
|
||||
mount.Type = val
|
||||
|
||||
@@ -310,16 +310,16 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
||||
|
||||
joinNs := context.StringSlice("with-ns")
|
||||
for _, ns := range joinNs {
|
||||
parts := strings.Split(ns, ":")
|
||||
if len(parts) != 2 {
|
||||
nsType, nsPath, ok := strings.Cut(ns, ":")
|
||||
if !ok {
|
||||
return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'")
|
||||
}
|
||||
if !validNamespace(parts[0]) {
|
||||
return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0])
|
||||
if !validNamespace(nsType) {
|
||||
return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + nsType)
|
||||
}
|
||||
opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{
|
||||
Type: specs.LinuxNamespaceType(parts[0]),
|
||||
Path: parts[1],
|
||||
Type: specs.LinuxNamespaceType(nsType),
|
||||
Path: nsPath,
|
||||
}))
|
||||
}
|
||||
if context.IsSet("gpus") {
|
||||
@@ -457,7 +457,8 @@ func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
|
||||
}
|
||||
|
||||
func parseIDMapping(mapping string) (specs.LinuxIDMapping, error) {
|
||||
parts := strings.Split(mapping, ":")
|
||||
// We expect 3 parts, but limit to 4 to allow detection of invalid values.
|
||||
parts := strings.SplitN(mapping, ":", 4)
|
||||
if len(parts) != 3 {
|
||||
return specs.LinuxIDMapping{}, errors.New("user namespace mappings require the format `container-id:host-id:size`")
|
||||
}
|
||||
|
||||
@@ -152,14 +152,14 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
|
||||
opts = append(opts, oci.WithWindowsCPUMaximum(uint16(cmax)))
|
||||
}
|
||||
for _, dev := range context.StringSlice("device") {
|
||||
parts := strings.Split(dev, "://")
|
||||
if len(parts) != 2 {
|
||||
idType, devID, ok := strings.Cut(dev, "://")
|
||||
if !ok {
|
||||
return nil, errors.New("devices must be in the format IDType://ID")
|
||||
}
|
||||
if parts[0] == "" {
|
||||
if idType == "" {
|
||||
return nil, errors.New("devices must have a non-empty IDType")
|
||||
}
|
||||
opts = append(opts, oci.WithWindowsDevice(parts[0], parts[1]))
|
||||
opts = append(opts, oci.WithWindowsDevice(idType, devID))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,11 +43,10 @@ func run() error {
|
||||
}
|
||||
dir := flag.Arg(1)
|
||||
|
||||
parts := strings.SplitN(flag.Arg(0), ".", 2)
|
||||
if len(parts) != 2 {
|
||||
name, section, ok := strings.Cut(flag.Arg(0), ".")
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid name '%s': name does not contain man page section", flag.Arg(0))
|
||||
}
|
||||
name, section := parts[0], parts[1]
|
||||
|
||||
appName, ok := apps[name]
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user