podman
460 строк · 11.3 Кб
1// Package denco provides fast URL router.
2package denco3
4import (5"fmt"6"sort"7"strings"8)
9
10const (11// ParamCharacter is a special character for path parameter.12ParamCharacter = ':'13
14// WildcardCharacter is a special character for wildcard path parameter.15WildcardCharacter = '*'16
17// TerminationCharacter is a special character for end of path.18TerminationCharacter = '#'19
20// SeparatorCharacter separates path segments.21SeparatorCharacter = '/'22
23// PathParamCharacter indicates a RESTCONF path param24PathParamCharacter = '='25
26// MaxSize is max size of records and internal slice.27MaxSize = (1 << 22) - 128)
29
30// Router represents a URL router.
31type Router struct {32// SizeHint expects the maximum number of path parameters in records to Build.33// SizeHint will be used to determine the capacity of the memory to allocate.34// By default, SizeHint will be determined from given records to Build.35SizeHint int36
37static map[string]interface{}38param *doubleArray39}
40
41// New returns a new Router.
42func New() *Router {43return &Router{44SizeHint: -1,45static: make(map[string]interface{}),46param: newDoubleArray(),47}48}
49
50// Lookup returns data and path parameters that associated with path.
51// params is a slice of the Param that arranged in the order in which parameters appeared.
52// 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"}].
53func (rt *Router) Lookup(path string) (data interface{}, params Params, found bool) {54if data, found := rt.static[path]; found {55return data, nil, true56}57if len(rt.param.node) == 1 {58return nil, nil, false59}60nd, params, found := rt.param.lookup(path, make([]Param, 0, rt.SizeHint), 1)61if !found {62return nil, nil, false63}64for i := 0; i < len(params); i++ {65params[i].Name = nd.paramNames[i]66}67return nd.data, params, true68}
69
70// Build builds URL router from records.
71func (rt *Router) Build(records []Record) error {72statics, params := makeRecords(records)73if len(params) > MaxSize {74return fmt.Errorf("denco: too many records")75}76if rt.SizeHint < 0 {77rt.SizeHint = 078for _, p := range params {79size := 080for _, k := range p.Key {81if k == ParamCharacter || k == WildcardCharacter {82size++83}84}85if size > rt.SizeHint {86rt.SizeHint = size87}88}89}90for _, r := range statics {91rt.static[r.Key] = r.Value92}93if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil {94return err95}96return nil97}
98
99// Param represents name and value of path parameter.
100type Param struct {101Name string102Value string103}
104
105// Params represents the name and value of path parameters.
106type Params []Param107
108// Get gets the first value associated with the given name.
109// If there are no values associated with the key, Get returns "".
110func (ps Params) Get(name string) string {111for _, p := range ps {112if p.Name == name {113return p.Value114}115}116return ""117}
118
119type doubleArray struct {120bc []baseCheck121node []*node122}
123
124func newDoubleArray() *doubleArray {125return &doubleArray{126bc: []baseCheck{0},127node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.128}129}
130
131// baseCheck contains BASE, CHECK and Extra flags.
132// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
133//
134// BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
135// |----------------------|--|--------|
136// 32 10 8 0
137type baseCheck uint32138
139func (bc baseCheck) Base() int {140return int(bc >> 10)141}
142
143func (bc *baseCheck) SetBase(base int) {144*bc |= baseCheck(base) << 10145}
146
147func (bc baseCheck) Check() byte {148return byte(bc)149}
150
151func (bc *baseCheck) SetCheck(check byte) {152*bc |= baseCheck(check)153}
154
155func (bc baseCheck) IsEmpty() bool {156return bc&0xfffffcff == 0157}
158
159func (bc baseCheck) IsSingleParam() bool {160return bc¶mTypeSingle == paramTypeSingle161}
162
163func (bc baseCheck) IsWildcardParam() bool {164return bc¶mTypeWildcard == paramTypeWildcard165}
166
167func (bc baseCheck) IsAnyParam() bool {168return bc¶mTypeAny != 0169}
170
171func (bc *baseCheck) SetSingleParam() {172*bc |= (1 << 8)173}
174
175func (bc *baseCheck) SetWildcardParam() {176*bc |= (1 << 9)177}
178
179const (180paramTypeSingle = 0x0100181paramTypeWildcard = 0x0200182paramTypeAny = 0x0300183)
184
185func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) {186indices := make([]uint64, 0, 1)187for i := 0; i < len(path); i++ {188if da.bc[idx].IsAnyParam() {189indices = append(indices, (uint64(i)<<32)|(uint64(idx)&0xffffffff))190}191c := path[i]192if idx = nextIndex(da.bc[idx].Base(), c); idx >= len(da.bc) || da.bc[idx].Check() != c {193goto BACKTRACKING194}195}196if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter {197return da.node[da.bc[next].Base()], params, true198}199BACKTRACKING:200for j := len(indices) - 1; j >= 0; j-- {201i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)202if da.bc[idx].IsSingleParam() {203idx := nextIndex(da.bc[idx].Base(), ParamCharacter)204if idx >= len(da.bc) {205break206}207next := NextSeparator(path, i)208params := append(params, Param{Value: path[i:next]})209if nd, params, found := da.lookup(path[next:], params, idx); found {210return nd, params, true211}212}213if da.bc[idx].IsWildcardParam() {214idx := nextIndex(da.bc[idx].Base(), WildcardCharacter)215params := append(params, Param{Value: path[i:]})216return da.node[da.bc[idx].Base()], params, true217}218}219return nil, nil, false220}
221
222// build builds double-array from records.
223func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error {224sort.Stable(recordSlice(srcs))225base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)226if err != nil {227return err228}229if leaf != nil {230nd, err := makeNode(leaf)231if err != nil {232return err233}234da.bc[idx].SetBase(len(da.node))235da.node = append(da.node, nd)236}237for _, sib := range siblings {238da.setCheck(nextIndex(base, sib.c), sib.c)239}240for _, sib := range siblings {241records := srcs[sib.start:sib.end]242switch sib.c {243case ParamCharacter:244for _, r := range records {245next := NextSeparator(r.Key, depth+1)246name := r.Key[depth+1 : next]247r.paramNames = append(r.paramNames, name)248r.Key = r.Key[next:]249}250da.bc[idx].SetSingleParam()251if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {252return err253}254case WildcardCharacter:255r := records[0]256name := r.Key[depth+1 : len(r.Key)-1]257r.paramNames = append(r.paramNames, name)258r.Key = ""259da.bc[idx].SetWildcardParam()260if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {261return err262}263default:264if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil {265return err266}267}268}269return nil270}
271
272// setBase sets BASE.
273func (da *doubleArray) setBase(i, base int) {274da.bc[i].SetBase(base)275}
276
277// setCheck sets CHECK.
278func (da *doubleArray) setCheck(i int, check byte) {279da.bc[i].SetCheck(check)280}
281
282// findEmptyIndex returns an index of unused BASE/CHECK node.
283func (da *doubleArray) findEmptyIndex(start int) int {284i := start285for ; i < len(da.bc); i++ {286if da.bc[i].IsEmpty() {287break288}289}290return i291}
292
293// findBase returns good BASE.
294func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {295for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {296base = nextIndex(idx, firstChar)297if _, used := usedBase[base]; used {298continue299}300i := 0301for ; i < len(siblings); i++ {302next := nextIndex(base, siblings[i].c)303if len(da.bc) <= next {304da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)305}306if !da.bc[next].IsEmpty() {307break308}309}310if i == len(siblings) {311break312}313}314usedBase[base] = struct{}{}315return base316}
317
318func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {319siblings, leaf, err = makeSiblings(records, depth)320if err != nil {321return -1, nil, nil, err322}323if len(siblings) < 1 {324return -1, nil, leaf, nil325}326base = da.findBase(siblings, idx, usedBase)327if base > MaxSize {328return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice")329}330da.setBase(idx, base)331return base, siblings, leaf, err332}
333
334// node represents a node of Double-Array.
335type node struct {336data interface{}337
338// Names of path parameters.339paramNames []string340}
341
342// makeNode returns a new node from record.
343func makeNode(r *record) (*node, error) {344dups := make(map[string]bool)345for _, name := range r.paramNames {346if dups[name] {347return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key)348}349dups[name] = true350}351return &node{data: r.Value, paramNames: r.paramNames}, nil352}
353
354// sibling represents an intermediate data of build for Double-Array.
355type sibling struct {356// An index of start of duplicated characters.357start int358
359// An index of end of duplicated characters.360end int361
362// A character of sibling.363c byte364}
365
366// nextIndex returns a next index of array of BASE/CHECK.
367func nextIndex(base int, c byte) int {368return base ^ int(c)369}
370
371// makeSiblings returns slice of sibling.
372func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) {373var (374pc byte375n int376)377for i, r := range records {378if len(r.Key) <= depth {379leaf = r380continue381}382c := r.Key[depth]383switch {384case pc < c:385sib = append(sib, sibling{start: i, c: c})386case pc == c:387continue388default:389return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")390}391if n > 0 {392sib[n-1].end = i393}394pc = c395n++396}397if n == 0 {398return nil, leaf, nil399}400sib[n-1].end = len(records)401return sib, leaf, nil402}
403
404// Record represents a record data for router construction.
405type Record struct {406// Key for router construction.407Key string408
409// Result value for Key.410Value interface{}411}
412
413// NewRecord returns a new Record.
414func NewRecord(key string, value interface{}) Record {415return Record{416Key: key,417Value: value,418}419}
420
421// record represents a record that use to build the Double-Array.
422type record struct {423Record
424paramNames []string425}
426
427// makeRecords returns the records that use to build Double-Arrays.
428func makeRecords(srcs []Record) (statics, params []*record) {429termChar := string(TerminationCharacter)430paramPrefix := string(SeparatorCharacter) + string(ParamCharacter)431wildcardPrefix := string(SeparatorCharacter) + string(WildcardCharacter)432restconfPrefix := string(PathParamCharacter) + string(ParamCharacter)433for _, r := range srcs {434if strings.Contains(r.Key, paramPrefix) || strings.Contains(r.Key, wildcardPrefix) ||strings.Contains(r.Key, restconfPrefix){435r.Key += termChar436params = append(params, &record{Record: r})437} else {438statics = append(statics, &record{Record: r})439}440}441return statics, params442}
443
444// recordSlice represents a slice of Record for sort and implements the sort.Interface.
445type recordSlice []*record446
447// Len implements the sort.Interface.Len.
448func (rs recordSlice) Len() int {449return len(rs)450}
451
452// Less implements the sort.Interface.Less.
453func (rs recordSlice) Less(i, j int) bool {454return rs[i].Key < rs[j].Key455}
456
457// Swap implements the sort.Interface.Swap.
458func (rs recordSlice) Swap(i, j int) {459rs[i], rs[j] = rs[j], rs[i]460}
461