blob: 73703fddec3829c029c55bfea6025ea692d8090c [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// Package denco provides fast URL router.
2package denco
3
4import (
5 "fmt"
6 "sort"
7 "strings"
8)
9
10const (
11 // ParamCharacter is a special character for path parameter.
12 ParamCharacter = ':'
13
14 // WildcardCharacter is a special character for wildcard path parameter.
15 WildcardCharacter = '*'
16
17 // TerminationCharacter is a special character for end of path.
18 TerminationCharacter = '#'
19
20 // MaxSize is max size of records and internal slice.
21 MaxSize = (1 << 22) - 1
22)
23
24// Router represents a URL router.
25type Router struct {
26 // SizeHint expects the maximum number of path parameters in records to Build.
27 // SizeHint will be used to determine the capacity of the memory to allocate.
28 // By default, SizeHint will be determined from given records to Build.
29 SizeHint int
30
31 static map[string]interface{}
32 param *doubleArray
33}
34
35// New returns a new Router.
36func New() *Router {
37 return &Router{
38 SizeHint: -1,
39 static: make(map[string]interface{}),
40 param: newDoubleArray(),
41 }
42}
43
44// Lookup returns data and path parameters that associated with path.
45// params is a slice of the Param that arranged in the order in which parameters appeared.
46// e.g. when built routing path is "/path/to/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}].
47func (rt *Router) Lookup(path string) (data interface{}, params Params, found bool) {
48 if data, found := rt.static[path]; found {
49 return data, nil, true
50 }
51 if len(rt.param.node) == 1 {
52 return nil, nil, false
53 }
54 nd, params, found := rt.param.lookup(path, make([]Param, 0, rt.SizeHint), 1)
55 if !found {
56 return nil, nil, false
57 }
58 for i := 0; i < len(params); i++ {
59 params[i].Name = nd.paramNames[i]
60 }
61 return nd.data, params, true
62}
63
64// Build builds URL router from records.
65func (rt *Router) Build(records []Record) error {
66 statics, params := makeRecords(records)
67 if len(params) > MaxSize {
68 return fmt.Errorf("denco: too many records")
69 }
70 if rt.SizeHint < 0 {
71 rt.SizeHint = 0
72 for _, p := range params {
73 size := 0
74 for _, k := range p.Key {
75 if k == ParamCharacter || k == WildcardCharacter {
76 size++
77 }
78 }
79 if size > rt.SizeHint {
80 rt.SizeHint = size
81 }
82 }
83 }
84 for _, r := range statics {
85 rt.static[r.Key] = r.Value
86 }
87 if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil {
88 return err
89 }
90 return nil
91}
92
93// Param represents name and value of path parameter.
94type Param struct {
95 Name string
96 Value string
97}
98
99// Params represents the name and value of path parameters.
100type Params []Param
101
102// Get gets the first value associated with the given name.
103// If there are no values associated with the key, Get returns "".
104func (ps Params) Get(name string) string {
105 for _, p := range ps {
106 if p.Name == name {
107 return p.Value
108 }
109 }
110 return ""
111}
112
113type doubleArray struct {
114 bc []baseCheck
115 node []*node
116}
117
118func newDoubleArray() *doubleArray {
119 return &doubleArray{
120 bc: []baseCheck{0},
121 node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
122 }
123}
124
125// baseCheck contains BASE, CHECK and Extra flags.
126// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
127//
128// BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
129// |----------------------|--|--------|
130// 32 10 8 0
131type baseCheck uint32
132
133func (bc baseCheck) Base() int {
134 return int(bc >> 10)
135}
136
137func (bc *baseCheck) SetBase(base int) {
138 *bc |= baseCheck(base) << 10
139}
140
141func (bc baseCheck) Check() byte {
142 return byte(bc)
143}
144
145func (bc *baseCheck) SetCheck(check byte) {
146 *bc |= baseCheck(check)
147}
148
149func (bc baseCheck) IsEmpty() bool {
150 return bc&0xfffffcff == 0
151}
152
153func (bc baseCheck) IsSingleParam() bool {
154 return bc&paramTypeSingle == paramTypeSingle
155}
156
157func (bc baseCheck) IsWildcardParam() bool {
158 return bc&paramTypeWildcard == paramTypeWildcard
159}
160
161func (bc baseCheck) IsAnyParam() bool {
162 return bc&paramTypeAny != 0
163}
164
165func (bc *baseCheck) SetSingleParam() {
166 *bc |= (1 << 8)
167}
168
169func (bc *baseCheck) SetWildcardParam() {
170 *bc |= (1 << 9)
171}
172
173const (
174 paramTypeSingle = 0x0100
175 paramTypeWildcard = 0x0200
176 paramTypeAny = 0x0300
177)
178
179func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) {
180 indices := make([]uint64, 0, 1)
181 for i := 0; i < len(path); i++ {
182 if da.bc[idx].IsAnyParam() {
183 indices = append(indices, (uint64(i)<<32)|(uint64(idx)&0xffffffff))
184 }
185 c := path[i]
186 if idx = nextIndex(da.bc[idx].Base(), c); idx >= len(da.bc) || da.bc[idx].Check() != c {
187 goto BACKTRACKING
188 }
189 }
190 if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter {
191 return da.node[da.bc[next].Base()], params, true
192 }
193BACKTRACKING:
194 for j := len(indices) - 1; j >= 0; j-- {
195 i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
196 if da.bc[idx].IsSingleParam() {
197 idx := nextIndex(da.bc[idx].Base(), ParamCharacter)
198 if idx >= len(da.bc) {
199 break
200 }
201 next := NextSeparator(path, i)
202 params := append(params, Param{Value: path[i:next]})
203 if nd, params, found := da.lookup(path[next:], params, idx); found {
204 return nd, params, true
205 }
206 }
207 if da.bc[idx].IsWildcardParam() {
208 idx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
209 params := append(params, Param{Value: path[i:]})
210 return da.node[da.bc[idx].Base()], params, true
211 }
212 }
213 return nil, nil, false
214}
215
216// build builds double-array from records.
217func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error {
218 sort.Stable(recordSlice(srcs))
219 base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
220 if err != nil {
221 return err
222 }
223 if leaf != nil {
224 nd, err := makeNode(leaf)
225 if err != nil {
226 return err
227 }
228 da.bc[idx].SetBase(len(da.node))
229 da.node = append(da.node, nd)
230 }
231 for _, sib := range siblings {
232 da.setCheck(nextIndex(base, sib.c), sib.c)
233 }
234 for _, sib := range siblings {
235 records := srcs[sib.start:sib.end]
236 switch sib.c {
237 case ParamCharacter:
238 for _, r := range records {
239 next := NextSeparator(r.Key, depth+1)
240 name := r.Key[depth+1 : next]
241 r.paramNames = append(r.paramNames, name)
242 r.Key = r.Key[next:]
243 }
244 da.bc[idx].SetSingleParam()
245 if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
246 return err
247 }
248 case WildcardCharacter:
249 r := records[0]
250 name := r.Key[depth+1 : len(r.Key)-1]
251 r.paramNames = append(r.paramNames, name)
252 r.Key = ""
253 da.bc[idx].SetWildcardParam()
254 if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
255 return err
256 }
257 default:
258 if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil {
259 return err
260 }
261 }
262 }
263 return nil
264}
265
266// setBase sets BASE.
267func (da *doubleArray) setBase(i, base int) {
268 da.bc[i].SetBase(base)
269}
270
271// setCheck sets CHECK.
272func (da *doubleArray) setCheck(i int, check byte) {
273 da.bc[i].SetCheck(check)
274}
275
276// findEmptyIndex returns an index of unused BASE/CHECK node.
277func (da *doubleArray) findEmptyIndex(start int) int {
278 i := start
279 for ; i < len(da.bc); i++ {
280 if da.bc[i].IsEmpty() {
281 break
282 }
283 }
284 return i
285}
286
287// findBase returns good BASE.
288func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
289 for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
290 base = nextIndex(idx, firstChar)
291 if _, used := usedBase[base]; used {
292 continue
293 }
294 i := 0
295 for ; i < len(siblings); i++ {
296 next := nextIndex(base, siblings[i].c)
297 if len(da.bc) <= next {
298 da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
299 }
300 if !da.bc[next].IsEmpty() {
301 break
302 }
303 }
304 if i == len(siblings) {
305 break
306 }
307 }
308 usedBase[base] = struct{}{}
309 return base
310}
311
312func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
313 siblings, leaf, err = makeSiblings(records, depth)
314 if err != nil {
315 return -1, nil, nil, err
316 }
317 if len(siblings) < 1 {
318 return -1, nil, leaf, nil
319 }
320 base = da.findBase(siblings, idx, usedBase)
321 if base > MaxSize {
322 return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice")
323 }
324 da.setBase(idx, base)
325 return base, siblings, leaf, err
326}
327
328// node represents a node of Double-Array.
329type node struct {
330 data interface{}
331
332 // Names of path parameters.
333 paramNames []string
334}
335
336// makeNode returns a new node from record.
337func makeNode(r *record) (*node, error) {
338 dups := make(map[string]bool)
339 for _, name := range r.paramNames {
340 if dups[name] {
341 return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key)
342 }
343 dups[name] = true
344 }
345 return &node{data: r.Value, paramNames: r.paramNames}, nil
346}
347
348// sibling represents an intermediate data of build for Double-Array.
349type sibling struct {
350 // An index of start of duplicated characters.
351 start int
352
353 // An index of end of duplicated characters.
354 end int
355
356 // A character of sibling.
357 c byte
358}
359
360// nextIndex returns a next index of array of BASE/CHECK.
361func nextIndex(base int, c byte) int {
362 return base ^ int(c)
363}
364
365// makeSiblings returns slice of sibling.
366func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) {
367 var (
368 pc byte
369 n int
370 )
371 for i, r := range records {
372 if len(r.Key) <= depth {
373 leaf = r
374 continue
375 }
376 c := r.Key[depth]
377 switch {
378 case pc < c:
379 sib = append(sib, sibling{start: i, c: c})
380 case pc == c:
381 continue
382 default:
383 return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
384 }
385 if n > 0 {
386 sib[n-1].end = i
387 }
388 pc = c
389 n++
390 }
391 if n == 0 {
392 return nil, leaf, nil
393 }
394 sib[n-1].end = len(records)
395 return sib, leaf, nil
396}
397
398// Record represents a record data for router construction.
399type Record struct {
400 // Key for router construction.
401 Key string
402
403 // Result value for Key.
404 Value interface{}
405}
406
407// NewRecord returns a new Record.
408func NewRecord(key string, value interface{}) Record {
409 return Record{
410 Key: key,
411 Value: value,
412 }
413}
414
415// record represents a record that use to build the Double-Array.
416type record struct {
417 Record
418 paramNames []string
419}
420
421// makeRecords returns the records that use to build Double-Arrays.
422func makeRecords(srcs []Record) (statics, params []*record) {
423 spChars := string([]byte{ParamCharacter, WildcardCharacter})
424 termChar := string(TerminationCharacter)
425 for _, r := range srcs {
426 if strings.ContainsAny(r.Key, spChars) {
427 r.Key += termChar
428 params = append(params, &record{Record: r})
429 } else {
430 statics = append(statics, &record{Record: r})
431 }
432 }
433 return statics, params
434}
435
436// recordSlice represents a slice of Record for sort and implements the sort.Interface.
437type recordSlice []*record
438
439// Len implements the sort.Interface.Len.
440func (rs recordSlice) Len() int {
441 return len(rs)
442}
443
444// Less implements the sort.Interface.Less.
445func (rs recordSlice) Less(i, j int) bool {
446 return rs[i].Key < rs[j].Key
447}
448
449// Swap implements the sort.Interface.Swap.
450func (rs recordSlice) Swap(i, j int) {
451 rs[i], rs[j] = rs[j], rs[i]
452}