From e88b7661bde062e9c654c4c5037fb6449442835d Mon Sep 17 00:00:00 2001 From: Carl Jackson Date: Mon, 28 Apr 2014 20:04:42 +0100 Subject: [PATCH] Sort routes Partially sort the routes on insertion. We're doing this so we can do more efficient things to routes later. The sorting rules are a bit subtle since we aren't allowed to rearrange routes in a way that would cause the semantics to differ from the dumb linear scan. --- web/router.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/web/router.go b/web/router.go index e3805ff..3bf384d 100644 --- a/web/router.go +++ b/web/router.go @@ -184,17 +184,35 @@ func (rt *router) handleUntyped(p interface{}, m method, h interface{}) { } func (rt *router) handle(p Pattern, m method, h Handler) { - // We're being a little sloppy here: we assume that pointer assignments - // are atomic, and that there is no way a locked append here can affect - // another goroutine which looked at rt.routes without a lock. rt.lock.Lock() defer rt.lock.Unlock() - rt.routes = append(rt.routes, route{ - prefix: p.Prefix(), + + // Calculate the sorted insertion point, because there's no reason to do + // swapping hijinks if we're already making a copy. We need to use + // bubble sort because we can only compare adjacent elements. + pp := p.Prefix() + var i int + for i = len(rt.routes); i > 0; i-- { + rip := rt.routes[i-1].prefix + if rip <= pp || strings.HasPrefix(rip, pp) { + break + } + } + + newRoutes := make([]route, len(rt.routes)+1) + copy(newRoutes, rt.routes[:i]) + newRoutes[i] = route{ + prefix: pp, method: m, pattern: p, handler: h, - }) + } + copy(newRoutes[i+1:], rt.routes[i:]) + + // We're being a bit sloppy here: we assume that pointer assignment is + // atomic with respect to other agents that don't acquire the lock. We + // should really just give up and use sync/atomic for this. + rt.routes = newRoutes } // This is a bit silly, but I've renamed the method receivers in the public