Skip to content

Commit

Permalink
Merge pull request #2937 from weaveworks/parse-optimisations
Browse files Browse the repository at this point in the history
Parsing optimisations
  • Loading branch information
bboreham authored Nov 16, 2017
2 parents 2bda044 + b989006 commit a375891
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 25 deletions.
5 changes: 3 additions & 2 deletions render/id.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package render

import (
"net"
"strings"

"github.com/weaveworks/scope/probe/endpoint"
Expand Down Expand Up @@ -66,7 +65,9 @@ func externalNodeID(n report.Node, addr string, local report.Networks) (string,

// If the dstNodeAddr is not in a network local to this report, we emit an
// internet pseudoNode
if ip := net.ParseIP(addr); ip != nil && !local.Contains(ip) {
// Create a buffer on the stack of this function, so we don't need to allocate in ParseIP
var into [5]byte // one extra byte to save a memory allocation in critbitgo
if ip := report.ParseIP([]byte(addr), into[:4]); ip != nil && !local.Contains(ip) {
// emit one internet node for incoming, one for outgoing
if len(n.Adjacency) > 0 {
return IncomingInternetID, true
Expand Down
52 changes: 29 additions & 23 deletions report/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,11 @@ func makeSingleComponentID(tag string) func(string) string {
// parseSingleComponentID makes a single-component node id decoder
func parseSingleComponentID(tag string) func(string) (string, bool) {
return func(id string) (string, bool) {
fields := strings.SplitN(id, ScopeDelim, 2)
if len(fields) != 2 || fields[1] != "<"+tag+">" {
field0, field1, ok := split2(id, ScopeDelim)
if !ok || field1 != "<"+tag+">" {
return "", false
}
return fields[0], true
return field0, true
}
}

Expand All @@ -197,48 +197,54 @@ func ParseOverlayNodeID(id string) (overlayPrefix string, peerName string) {
return WeaveOverlayPeerPrefix, id
}

// Split a string s into to parts separated by sep.
func split2(s, sep string) (s1, s2 string, ok bool) {
// Not using strings.SplitN() to avoid a heap allocation
pos := strings.Index(s, sep)
if pos == -1 {
return "", "", false
}
return s[:pos], s[pos+1:], true
}

// ParseNodeID produces the host ID and remainder (typically an address) from
// a node ID. Note that hostID may be blank.
func ParseNodeID(nodeID string) (hostID string, remainder string, ok bool) {
fields := strings.SplitN(nodeID, ScopeDelim, 2)
if len(fields) != 2 {
return "", "", false
}
return fields[0], fields[1], true
return split2(nodeID, ScopeDelim)
}

// ParseEndpointNodeID produces the scope, address, and port and remainder.
// Note that hostID may be blank.
// Note that scope may be blank.
func ParseEndpointNodeID(endpointNodeID string) (scope, address, port string, ok bool) {
fields := strings.SplitN(endpointNodeID, ScopeDelim, 3)
if len(fields) != 3 {
// Not using strings.SplitN() to avoid a heap allocation
first := strings.Index(endpointNodeID, ScopeDelim)
if first == -1 {
return "", "", "", false
}

return fields[0], fields[1], fields[2], true
second := strings.Index(endpointNodeID[first+1:], ScopeDelim)
if second == -1 {
return "", "", "", false
}
return endpointNodeID[:first], endpointNodeID[first+1 : first+1+second], endpointNodeID[first+1+second+1:], true
}

// ParseAddressNodeID produces the host ID, address from an address node ID.
func ParseAddressNodeID(addressNodeID string) (hostID, address string, ok bool) {
fields := strings.SplitN(addressNodeID, ScopeDelim, 2)
if len(fields) != 2 {
return "", "", false
}
return fields[0], fields[1], true
return split2(addressNodeID, ScopeDelim)
}

// ParseECSServiceNodeID produces the cluster, service name from an ECS Service node ID
func ParseECSServiceNodeID(ecsServiceNodeID string) (cluster, serviceName string, ok bool) {
fields := strings.SplitN(ecsServiceNodeID, ScopeDelim, 2)
if len(fields) != 2 {
cluster, serviceName, ok = split2(ecsServiceNodeID, ScopeDelim)
if !ok {
return "", "", false
}
// In previous versions, ECS Service node IDs were of form serviceName + "<ecs_service>".
// For backwards compatibility, we should still return a sensical serviceName for these cases.
if fields[1] == "<ecs_service>" {
return "unknown", fields[0], true
if serviceName == "<ecs_service>" {
return "unknown", cluster, true
}
return fields[0], fields[1], true
return cluster, serviceName, true
}

// ExtractHostID extracts the host id from Node
Expand Down
67 changes: 67 additions & 0 deletions report/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,70 @@ func commonIPv4PrefixLen(a, b net.IP) int {
}
return cpl
}

// ParseIP parses s as an IP address into a byte slice if supplied, returning the result.
// (mostly copied from net.ParseIP, modified to save memory allocations)
func ParseIP(s []byte, into []byte) net.IP {
for i := 0; i < len(s); i++ {
switch s[i] {
case '.':
return parseIPv4(s, into)
case ':':
return net.ParseIP(string(s)) // leave IPv6 to the original code since we don't see many of those
}
}
return nil
}

// Parse IPv4 address (d.d.d.d).
// (mostly copied from net.parseIPv4, modified to save memory allocations)
func parseIPv4(s []byte, into []byte) net.IP {
var p []byte
if len(into) >= net.IPv4len { // check if we can use the supplied slice
p = into[:net.IPv4len]
} else {
p = make([]byte, net.IPv4len)
}
for i := 0; i < net.IPv4len; i++ {
if len(s) == 0 {
// Missing octets.
return nil
}
if i > 0 {
if s[0] != '.' {
return nil
}
s = s[1:]
}
n, c, ok := dtoi(s)
if !ok || n > 0xFF {
return nil
}
s = s[c:]
p[i] = byte(n)
}
if len(s) != 0 {
return nil
}
return p
}

// Bigger than we need, not too big to worry about overflow
const big = 0xFFFFFF

// Decimal to integer.
// Returns number, characters consumed, success.
// (completely copied from net.dtoi, just because it wasn't exported)
func dtoi(s []byte) (n int, i int, ok bool) {
n = 0
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
n = n*10 + int(s[i]-'0')
if n >= big {
return big, i, false
}
}
if i == 0 {
return 0, 0, false
}
return n, i, true
}

0 comments on commit a375891

Please sign in to comment.