Use a sync.Pool to recycle Param slices

Zero allocations (amortized)!
diff --git a/router.go b/router.go
index 155b871..bdee266 100644
--- a/router.go
+++ b/router.go
@@ -78,6 +78,7 @@
 
 import (
 	"net/http"
+	"sync"
 )
 
 // Handle is a function that can be registered to a route to handle HTTP
@@ -112,6 +113,9 @@
 type Router struct {
 	trees map[string]*node
 
+	// pool to recycle Param slices
+	psPool sync.Pool
+
 	// Enables automatic redirection if the current route can't be matched but a
 	// handler for the path with (without) the trailing slash exists.
 	// For example if /foo/ is requested but a route only exists for /foo, the
@@ -168,6 +172,24 @@
 	}
 }
 
+func (r *Router) paramsGet() *Params {
+	if vp := r.psPool.Get(); vp != nil {
+		psp := vp.(*Params)
+		*psp = (*psp)[0:0] // reset slice
+		return psp
+	}
+
+	// Allocate new slice if none is available
+	ps := make(Params, 0, 20) // TODO
+	return &ps
+}
+
+func (r *Router) paramsRecycle(psp *Params) {
+	if psp != nil {
+		r.psPool.Put(psp)
+	}
+}
+
 // GET is a shortcut for router.Handle("GET", path, handle)
 func (r *Router) GET(path string, handle Handle) {
 	r.Handle("GET", path, handle)
@@ -281,7 +303,13 @@
 // the same path with an extra / without the trailing slash should be performed.
 func (r *Router) Lookup(method, path string) (Handle, Params, bool) {
 	if root := r.trees[method]; root != nil {
-		return root.getValue(path)
+		psp := r.paramsGet()
+		h, tsr := root.getValue(path, psp) // TODO
+		if h != nil {
+			return h, *psp, tsr
+		}
+		r.paramsRecycle(psp)
+		return nil, nil, tsr
 	}
 	return nil, nil, false
 }
@@ -294,39 +322,44 @@
 
 	if root := r.trees[req.Method]; root != nil {
 		path := req.URL.Path
-
-		if handle, ps, tsr := root.getValue(path); handle != nil {
-			handle(w, req, ps)
+		psp := r.paramsGet()
+		if handle, tsr := root.getValue(path, psp); handle != nil {
+			handle(w, req, *psp)
+			r.paramsRecycle(psp)
 			return
-		} else if req.Method != "CONNECT" && path != "/" {
-			code := 301 // Permanent redirect, request with GET method
-			if req.Method != "GET" {
-				// Temporary redirect, request with same method
-				// As of Go 1.3, Go does not support status code 308.
-				code = 307
-			}
+		} else {
+			r.paramsRecycle(psp)
 
-			if tsr && r.RedirectTrailingSlash {
-				if len(path) > 1 && path[len(path)-1] == '/' {
-					req.URL.Path = path[:len(path)-1]
-				} else {
-					req.URL.Path = path + "/"
+			if req.Method != "CONNECT" && path != "/" {
+				code := 301 // Permanent redirect, request with GET method
+				if req.Method != "GET" {
+					// Temporary redirect, request with same method
+					// As of Go 1.3, Go does not support status code 308.
+					code = 307
 				}
-				http.Redirect(w, req, req.URL.String(), code)
-				return
-			}
 
-			// Try to fix the request path
-			if r.RedirectFixedPath {
-				fixedPath, found := root.findCaseInsensitivePath(
-					CleanPath(path),
-					r.RedirectTrailingSlash,
-				)
-				if found {
-					req.URL.Path = string(fixedPath)
+				if tsr && r.RedirectTrailingSlash {
+					if len(path) > 1 && path[len(path)-1] == '/' {
+						req.URL.Path = path[:len(path)-1]
+					} else {
+						req.URL.Path = path + "/"
+					}
 					http.Redirect(w, req, req.URL.String(), code)
 					return
 				}
+
+				// Try to fix the request path
+				if r.RedirectFixedPath {
+					fixedPath, found := root.findCaseInsensitivePath(
+						CleanPath(path),
+						r.RedirectTrailingSlash,
+					)
+					if found {
+						req.URL.Path = string(fixedPath)
+						http.Redirect(w, req, req.URL.String(), code)
+						return
+					}
+				}
 			}
 		}
 	}
@@ -339,7 +372,7 @@
 				continue
 			}
 
-			handle, _, _ := r.trees[method].getValue(req.URL.Path)
+			handle, _ := r.trees[method].getValue(req.URL.Path, nil)
 			if handle != nil {
 				if r.MethodNotAllowed != nil {
 					r.MethodNotAllowed(w, req)
diff --git a/router_test.go b/router_test.go
index 9dc6296..867c632 100644
--- a/router_test.go
+++ b/router_test.go
@@ -10,6 +10,7 @@
 	"net/http"
 	"net/http/httptest"
 	"reflect"
+	"runtime"
 	"testing"
 )
 
@@ -49,9 +50,9 @@
 	router := New()
 
 	routed := false
+	want := Params{Param{"name", "gopher"}}
 	router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
 		routed = true
-		want := Params{Param{"name", "gopher"}}
 		if !reflect.DeepEqual(ps, want) {
 			t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
 		}
@@ -67,6 +68,42 @@
 	}
 }
 
+func TestRouterZeroAlloc(t *testing.T) {
+	runs := 1000
+	m := new(runtime.MemStats)
+
+	router := New()
+	router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
+		if val := ps.ByName("name"); val != "gordon" {
+			t.Errorf("Expected param value 'gordon', got '%s'", val)
+		}
+	})
+	r, _ := http.NewRequest("GET", "/user/gordon", nil)
+
+	w := new(mockResponseWriter)
+	u := r.URL
+	rq := u.RawQuery
+	r.RequestURI = u.RequestURI()
+
+	// before
+	runtime.GC()
+	runtime.ReadMemStats(m)
+	mallocs := 0 - m.Mallocs
+
+	for i := 0; i < runs; i++ {
+		u.RawQuery = rq
+		router.ServeHTTP(w, r)
+	}
+
+	// after
+	runtime.ReadMemStats(m)
+	mallocs += m.Mallocs
+
+	if aa := int(mallocs / uint64(runs)); aa > 0 {
+		t.Fatalf("Amortized allocations: %d", aa)
+	}
+}
+
 type handlerStruct struct {
 	handeled *bool
 }
diff --git a/tree.go b/tree.go
index a15bc2c..3ff6394 100644
--- a/tree.go
+++ b/tree.go
@@ -316,7 +316,7 @@
 // If no handle can be found, a TSR (trailing slash redirect) recommendation is
 // made if a handle exists with an extra (without the) trailing slash for the
 // given path.
-func (n *node) getValue(path string) (handle Handle, p Params, tsr bool) {
+func (n *node) getValue(path string, psp *Params) (handle Handle, tsr bool) {
 walk: // Outer loop for walking the tree
 	for {
 		if len(path) > len(n.path) {
@@ -353,14 +353,12 @@
 					}
 
 					// save param value
-					if p == nil {
-						// lazy allocation
-						p = make(Params, 0, n.maxParams)
+					if psp != nil {
+						i := len(*psp)
+						*psp = (*psp)[:i+1] // expand slice within preallocated capacity
+						(*psp)[i].Key = n.path[1:]
+						(*psp)[i].Value = path[:end]
 					}
-					i := len(p)
-					p = p[:i+1] // expand slice within preallocated capacity
-					p[i].Key = n.path[1:]
-					p[i].Value = path[:end]
 
 					// we need to go deeper!
 					if end < len(path) {
@@ -388,14 +386,12 @@
 
 				case catchAll:
 					// save param value
-					if p == nil {
-						// lazy allocation
-						p = make(Params, 0, n.maxParams)
+					if psp != nil {
+						i := len(*psp)
+						*psp = (*psp)[:i+1] // expand slice within preallocated capacity
+						(*psp)[i].Key = n.path[2:]
+						(*psp)[i].Value = path
 					}
-					i := len(p)
-					p = p[:i+1] // expand slice within preallocated capacity
-					p[i].Key = n.path[2:]
-					p[i].Value = path
 
 					handle = n.handle
 					return
diff --git a/tree_test.go b/tree_test.go
index 64f26d1..187b349 100644
--- a/tree_test.go
+++ b/tree_test.go
@@ -38,9 +38,15 @@
 	ps         Params
 }
 
+func getParams() *Params {
+	ps := make(Params, 0, 20)
+	return &ps
+}
+
 func checkRequests(t *testing.T, tree *node, requests testRequests) {
 	for _, request := range requests {
-		handler, ps, _ := tree.getValue(request.path)
+		psp := getParams()
+		handler, _ := tree.getValue(request.path, psp)
 
 		if handler == nil {
 			if !request.nilHandler {
@@ -55,7 +61,7 @@
 			}
 		}
 
-		if !reflect.DeepEqual(ps, request.ps) {
+		if !reflect.DeepEqual(*psp, request.ps) && (len(request.ps) > 0 || len(*psp) > 0) {
 			t.Errorf("Params mismatch for route '%s'", request.path)
 		}
 	}
@@ -426,7 +432,7 @@
 		"/doc/",
 	}
 	for _, route := range tsrRoutes {
-		handler, _, tsr := tree.getValue(route)
+		handler, tsr := tree.getValue(route, nil)
 		if handler != nil {
 			t.Fatalf("non-nil handler for TSR route '%s", route)
 		} else if !tsr {
@@ -443,7 +449,7 @@
 		"/api/world/abc",
 	}
 	for _, route := range noTsrRoutes {
-		handler, _, tsr := tree.getValue(route)
+		handler, tsr := tree.getValue(route, nil)
 		if handler != nil {
 			t.Fatalf("non-nil handler for No-TSR route '%s", route)
 		} else if tsr {
@@ -595,7 +601,7 @@
 
 	// normal lookup
 	recv := catchPanic(func() {
-		tree.getValue("/test")
+		tree.getValue("/test", nil)
 	})
 	if rs, ok := recv.(string); !ok || rs != panicMsg {
 		t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)