mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
small string split join optimization
This commit is contained in:
parent
654194b274
commit
e7be27413c
20 changed files with 160 additions and 50 deletions
|
@ -33,8 +33,8 @@ func (c containerHelper) getName() string {
|
|||
}
|
||||
|
||||
func (c containerHelper) getImageName() string {
|
||||
colonSep := strings.Split(c.Image, ":")
|
||||
slashSep := strings.Split(colonSep[0], "/")
|
||||
colonSep := strutils.SplitRune(c.Image, ':')
|
||||
slashSep := strutils.SplitRune(colonSep[0], '/')
|
||||
return slashSep[len(slashSep)-1]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type LabelMap = map[string]any
|
||||
|
@ -13,7 +12,7 @@ func ParseLabels(labels map[string]string) (LabelMap, E.Error) {
|
|||
errs := E.NewBuilder("labels error")
|
||||
|
||||
for lbl, value := range labels {
|
||||
parts := strings.Split(lbl, ".")
|
||||
parts := strutils.SplitRune(lbl, '.')
|
||||
if parts[0] != NSProxy {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
var findRouteFunc = findRouteAnyDomain
|
||||
|
@ -124,7 +125,7 @@ func Handler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func findRouteAnyDomain(host string) (route.HTTPRoute, error) {
|
||||
hostSplit := strings.Split(host, ".")
|
||||
hostSplit := strutils.SplitRune(host, '.')
|
||||
n := len(hostSplit)
|
||||
switch {
|
||||
case n == 3:
|
||||
|
|
|
@ -3,7 +3,8 @@ package err
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
//nolint:recvcheck
|
||||
|
@ -78,7 +79,7 @@ func (err *nestedError) Error() string {
|
|||
if extras := makeLines(err.Extras, 1); len(extras) > 0 {
|
||||
lines = append(lines, extras...)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
return strutils.JoinLines(lines)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
var logger zerolog.Logger
|
||||
|
@ -39,14 +40,14 @@ func init() {
|
|||
FieldsExclude: exclude,
|
||||
FormatMessage: func(msgI interface{}) string { // pad spaces for each line
|
||||
msg := msgI.(string)
|
||||
lines := strings.Split(msg, "\n")
|
||||
lines := strutils.SplitRune(msg, '\n')
|
||||
if len(lines) == 1 {
|
||||
return msg
|
||||
}
|
||||
for i := 1; i < len(lines); i++ {
|
||||
lines[i] = prefix + lines[i]
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
return strutils.JoinRune(lines, '\n')
|
||||
},
|
||||
},
|
||||
).Level(level).With().Timestamp().Logger()
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -48,8 +49,9 @@ func (method HTTPMethod) Fulfill(req *http.Request, res *http.Response) bool {
|
|||
return req.Method == string(method)
|
||||
}
|
||||
|
||||
// Parse implements strutils.Parser.
|
||||
func (k *HTTPHeader) Parse(v string) error {
|
||||
split := strings.Split(v, "=")
|
||||
split := strutils.SplitRune(v, '=')
|
||||
switch len(split) {
|
||||
case 1:
|
||||
split = append(split, "")
|
||||
|
|
|
@ -2,9 +2,9 @@ package accesslog
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type StatusCodeRange struct {
|
||||
|
@ -18,8 +18,9 @@ func (r *StatusCodeRange) Includes(code int) bool {
|
|||
return r.Start <= code && code <= r.End
|
||||
}
|
||||
|
||||
// Parse implements strutils.Parser.
|
||||
func (r *StatusCodeRange) Parse(v string) error {
|
||||
split := strings.Split(v, "-")
|
||||
split := strutils.SplitRune(v, '-')
|
||||
switch len(split) {
|
||||
case 1:
|
||||
start, err := strconv.Atoi(split[0])
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -113,7 +112,7 @@ func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*types.CIDR) error {
|
|||
return err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(body), "\n") {
|
||||
for _, line := range strutils.SplitLine(string(body)) {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
|
@ -528,7 +529,7 @@ func UpgradeType(h http.Header) string {
|
|||
func RemoveHopByHopHeaders(h http.Header) {
|
||||
// RFC 7230, section 6.1: Remove headers listed in the "Connection" header.
|
||||
for _, f := range h["Connection"] {
|
||||
for _, sf := range strings.Split(f, ",") {
|
||||
for _, sf := range strutils.SplitComma(f) {
|
||||
if sf = textproto.TrimString(sf); sf != "" {
|
||||
h.Del(sf)
|
||||
}
|
||||
|
|
|
@ -2,17 +2,17 @@ package types
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
func ValidateHTTPHeaders(headers map[string]string) (http.Header, E.Error) {
|
||||
h := make(http.Header)
|
||||
for k, v := range headers {
|
||||
vSplit := strings.Split(v, ",")
|
||||
vSplit := strutils.CommaSeperatedList(v)
|
||||
for _, header := range vSplit {
|
||||
h.Add(k, strings.TrimSpace(header))
|
||||
h.Add(k, header)
|
||||
}
|
||||
}
|
||||
return h, nil
|
||||
|
|
|
@ -173,14 +173,14 @@ func (e *RawEntry) Finalize() {
|
|||
}
|
||||
|
||||
func (e *RawEntry) splitPorts() (lp string, pp string, extra string) {
|
||||
portSplit := strings.Split(e.Port, ":")
|
||||
portSplit := strutils.SplitRune(e.Port, ':')
|
||||
if len(portSplit) == 1 {
|
||||
pp = portSplit[0]
|
||||
} else {
|
||||
lp = portSplit[0]
|
||||
pp = portSplit[1]
|
||||
if len(portSplit) > 2 {
|
||||
extra = strings.Join(portSplit[2:], ":")
|
||||
extra = strutils.JoinRune(portSplit[2:], ':')
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -197,7 +197,7 @@ func joinPorts(lp string, pp string, extra string) string {
|
|||
if extra != "" {
|
||||
s = append(s, extra)
|
||||
}
|
||||
return strings.Join(s, ":")
|
||||
return strutils.JoinRune(s, ':')
|
||||
}
|
||||
|
||||
func lowestPort(ports map[string]types.Port) string {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type StreamPort struct {
|
||||
|
@ -14,7 +13,7 @@ type StreamPort struct {
|
|||
var ErrStreamPortTooManyColons = E.New("too many colons")
|
||||
|
||||
func ValidateStreamPort(p string) (StreamPort, error) {
|
||||
split := strings.Split(p, ":")
|
||||
split := strutils.SplitRune(p, ':')
|
||||
|
||||
switch len(split) {
|
||||
case 1:
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type StreamScheme struct {
|
||||
|
@ -14,7 +12,7 @@ type StreamScheme struct {
|
|||
|
||||
func ValidateStreamScheme(s string) (*StreamScheme, error) {
|
||||
ss := &StreamScheme{}
|
||||
parts := strings.Split(s, ":")
|
||||
parts := strutils.SplitRune(s, ':')
|
||||
if len(parts) == 1 {
|
||||
parts = []string{s, s}
|
||||
} else if len(parts) != 2 {
|
||||
|
@ -33,7 +31,7 @@ func ValidateStreamScheme(s string) (*StreamScheme, error) {
|
|||
}
|
||||
|
||||
func (s StreamScheme) String() string {
|
||||
return fmt.Sprintf("%s -> %s", s.ListeningScheme, s.ProxyScheme)
|
||||
return string(s.ListeningScheme) + " -> " + string(s.ProxyScheme)
|
||||
}
|
||||
|
||||
// IsCoherent checks if the ListeningScheme and ProxyScheme of the StreamScheme are equal.
|
||||
|
|
|
@ -193,7 +193,7 @@ func Deserialize(src SerializedObject, dst any) E.Error {
|
|||
for _, field := range fields {
|
||||
var key string
|
||||
if jsonTag, ok := field.Tag.Lookup("json"); ok {
|
||||
key = strings.Split(jsonTag, ",")[0]
|
||||
key = strutils.CommaSeperatedList(jsonTag)[0]
|
||||
} else {
|
||||
key = field.Name
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ func Deserialize(src SerializedObject, dst any) E.Error {
|
|||
|
||||
aliases, ok := field.Tag.Lookup("aliases")
|
||||
if ok {
|
||||
for _, alias := range strings.Split(aliases, ",") {
|
||||
for _, alias := range strutils.CommaSeperatedList(aliases) {
|
||||
mapping[alias] = dstV.FieldByName(field.Name)
|
||||
fieldName[field.Name] = alias
|
||||
}
|
||||
|
@ -425,7 +425,7 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E
|
|||
lines := []string{}
|
||||
src = strings.TrimSpace(src)
|
||||
if src != "" {
|
||||
lines = strings.Split(src, "\n")
|
||||
lines = strutils.SplitLine(src)
|
||||
for i := range lines {
|
||||
lines[i] = strings.TrimSpace(lines[i])
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ package strutils
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
|
@ -22,7 +20,7 @@ func Parse[T Parser](from string) (t T, err error) {
|
|||
func MustParse[T Parser](from string) T {
|
||||
t, err := Parse[T](from)
|
||||
if err != nil {
|
||||
logging.Panic().Err(err).Msg("must failed")
|
||||
panic("must failed: " + err.Error())
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
|
81
internal/utils/strutils/split_join.go
Normal file
81
internal/utils/strutils/split_join.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package strutils
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SplitRune is like strings.Split but takes a rune as separator.
|
||||
func SplitRune(s string, sep rune) []string {
|
||||
if sep == 0 {
|
||||
return strings.Split(s, "")
|
||||
}
|
||||
n := strings.Count(s, string(sep)) + 1
|
||||
if n > len(s)+1 {
|
||||
n = len(s) + 1
|
||||
}
|
||||
a := make([]string, n)
|
||||
n--
|
||||
i := 0
|
||||
for i < n {
|
||||
m := strings.IndexRune(s, sep)
|
||||
if m < 0 {
|
||||
break
|
||||
}
|
||||
a[i] = s[:m]
|
||||
s = s[m+1:]
|
||||
i++
|
||||
}
|
||||
a[i] = s
|
||||
return a[:i+1]
|
||||
}
|
||||
|
||||
// SplitComma is a wrapper around SplitRune(s, ',').
|
||||
func SplitComma(s string) []string {
|
||||
return SplitRune(s, ',')
|
||||
}
|
||||
|
||||
// SplitLine is a wrapper around SplitRune(s, '\n').
|
||||
func SplitLine(s string) []string {
|
||||
return SplitRune(s, '\n')
|
||||
}
|
||||
|
||||
// SplitSpace is a wrapper around SplitRune(s, ' ').
|
||||
func SplitSpace(s string) []string {
|
||||
return SplitRune(s, ' ')
|
||||
}
|
||||
|
||||
// JoinRune is like strings.Join but takes a rune as separator.
|
||||
func JoinRune(elems []string, sep rune) string {
|
||||
switch len(elems) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return elems[0]
|
||||
}
|
||||
if sep == 0 {
|
||||
return strings.Join(elems, "")
|
||||
}
|
||||
|
||||
var n int
|
||||
for _, elem := range elems {
|
||||
if len(elem) > math.MaxInt-n {
|
||||
panic("strings: Join output length overflow")
|
||||
}
|
||||
n += len(elem)
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.Grow(n)
|
||||
b.WriteString(elems[0])
|
||||
for _, s := range elems[1:] {
|
||||
b.WriteRune(sep)
|
||||
b.WriteString(s)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// JoinLines is a wrapper around JoinRune(elems, '\n').
|
||||
func JoinLines(elems []string) string {
|
||||
return JoinRune(elems, '\n')
|
||||
}
|
38
internal/utils/strutils/split_join_test.go
Normal file
38
internal/utils/strutils/split_join_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package strutils_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
var alphaNumeric = func() string {
|
||||
var s strings.Builder
|
||||
for i := range 'z' - 'a' + 1 {
|
||||
s.WriteRune('a' + i)
|
||||
s.WriteRune('A' + i)
|
||||
s.WriteRune(',')
|
||||
}
|
||||
for i := range '9' - '0' + 1 {
|
||||
s.WriteRune('0' + i)
|
||||
s.WriteRune(',')
|
||||
}
|
||||
return s.String()
|
||||
}()
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
tests := map[string]rune{
|
||||
"": 0,
|
||||
"1": '1',
|
||||
",": ',',
|
||||
}
|
||||
for sep, rsep := range tests {
|
||||
t.Run(sep, func(t *testing.T) {
|
||||
expected := strings.Split(alphaNumeric, sep)
|
||||
ExpectDeepEqual(t, SplitRune(alphaNumeric, rsep), expected)
|
||||
ExpectEqual(t, JoinRune(expected, rsep), alphaNumeric)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,17 +1,7 @@
|
|||
package strutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
func Atoi(s string) (int, E.Error) {
|
||||
val, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return val, E.From(errors.Unwrap(err)).Subject(s)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
var Atoi = strconv.Atoi
|
||||
|
|
|
@ -8,8 +8,10 @@ import (
|
|||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// CommaSeperatedList returns a list of strings split by commas,
|
||||
// then trim spaces from each element.
|
||||
func CommaSeperatedList(s string) []string {
|
||||
res := strings.Split(s, ",")
|
||||
res := SplitComma(s)
|
||||
for i, part := range res {
|
||||
res[i] = strings.TrimSpace(part)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
|
@ -141,7 +140,7 @@ func (mon *monitor) Uptime() time.Duration {
|
|||
|
||||
// Name implements HealthMonitor.
|
||||
func (mon *monitor) Name() string {
|
||||
parts := strings.Split(mon.service, "/")
|
||||
parts := strutils.SplitRune(mon.service, '/')
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue