Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing optimisations #2937

Merged
merged 2 commits into from
Nov 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

This comment was marked as abuse.

This comment was marked as abuse.

}

// 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
}