libct/user: use []byte more, avoid allocations

Every []byte to string conversion results in a new allocation.
Avoid some by using []byte more.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
(cherry picked from commit 120e3a77d8)
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin
2021-07-02 12:12:25 -07:00
parent 8fad08a3f2
commit 0025bf6827
2 changed files with 27 additions and 26 deletions
+19 -18
View File
@@ -2,6 +2,7 @@ package user
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
@@ -55,11 +56,11 @@ type IDMap struct {
Count int64
}
func parseLine(line string, v ...interface{}) {
parseParts(strings.Split(line, ":"), v...)
func parseLine(line []byte, v ...interface{}) {
parseParts(bytes.Split(line, []byte(":")), v...)
}
func parseParts(parts []string, v ...interface{}) {
func parseParts(parts [][]byte, v ...interface{}) {
if len(parts) == 0 {
return
}
@@ -75,16 +76,16 @@ func parseParts(parts []string, v ...interface{}) {
// This is legit.
switch e := v[i].(type) {
case *string:
*e = p
*e = string(p)
case *int:
// "numbers", with conversion errors ignored because of some misbehaving configuration files.
*e, _ = strconv.Atoi(p)
*e, _ = strconv.Atoi(string(p))
case *int64:
*e, _ = strconv.ParseInt(p, 10, 64)
*e, _ = strconv.ParseInt(string(p), 10, 64)
case *[]string:
// Comma-separated lists.
if p != "" {
*e = strings.Split(p, ",")
if len(p) != 0 {
*e = strings.Split(string(p), ",")
} else {
*e = []string{}
}
@@ -128,8 +129,8 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
line := bytes.TrimSpace(s.Bytes())
if len(line) == 0 {
continue
}
@@ -186,8 +187,8 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
)
for s.Scan() {
text := s.Text()
if text == "" {
text := s.Bytes()
if len(text) == 0 {
continue
}
@@ -278,7 +279,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
// Allow for userArg to have either "user" syntax, or optionally "user:group" syntax
var userArg, groupArg string
parseLine(userSpec, &userArg, &groupArg)
parseLine([]byte(userSpec), &userArg, &groupArg)
// Convert userArg and groupArg to be numeric, so we don't have to execute
// Atoi *twice* for each iteration over lines.
@@ -496,8 +497,8 @@ func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
line := bytes.TrimSpace(s.Bytes())
if len(line) == 0 {
continue
}
@@ -549,14 +550,14 @@ func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) {
)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" {
line := bytes.TrimSpace(s.Bytes())
if len(line) == 0 {
continue
}
// see: man 7 user_namespaces
p := IDMap{}
parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count)
parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count)
if filter == nil || filter(p) {
out = append(out, p)
+8 -8
View File
@@ -16,42 +16,42 @@ func TestUserParseLine(t *testing.T) {
d int
)
parseLine("", &a, &b)
parseLine([]byte(""), &a, &b)
if a != "" || b != "" {
t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
}
parseLine("a", &a, &b)
parseLine([]byte("a"), &a, &b)
if a != "a" || b != "" {
t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
}
parseLine("bad boys:corny cows", &a, &b)
parseLine([]byte("bad boys:corny cows"), &a, &b)
if a != "bad boys" || b != "corny cows" {
t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
}
parseLine("", &c)
parseLine([]byte(""), &c)
if len(c) != 0 {
t.Fatalf("c should be empty (%#v)", c)
}
parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c)
parseLine([]byte("d,e,f:g:h:i,j,k"), &c, &a, &b, &c)
if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
}
parseLine("::::::::::", &a, &b, &c)
parseLine([]byte("::::::::::"), &a, &b, &c)
if a != "" || b != "" || len(c) != 0 {
t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
}
parseLine("not a number", &d)
parseLine([]byte("not a number"), &d)
if d != 0 {
t.Fatalf("d should be 0 (%v)", d)
}
parseLine("b:12:c", &a, &d, &b)
parseLine([]byte("b:12:c"), &a, &d, &b)
if a != "b" || b != "c" || d != 12 {
t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
}