blob: 3dfc46929fe9f06442c8953730780c3cf15b69c7 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Robin Thellendac59e972014-08-19 18:26:11 -07005package stats
6
7import (
8 "path"
9 "sort"
10 "time"
11
Robin Thellend40c01b22015-07-15 10:44:41 -070012 "v.io/v23/glob"
Mike Burrows2c989372015-03-31 18:06:39 -070013 "v.io/v23/verror"
Robin Thellendac59e972014-08-19 18:26:11 -070014)
15
16// Glob returns the name and (optionally) the value of all the objects that
17// match the given pattern and have been updated since 'updatedSince'. The
18// 'root' argument is the name of the object where the pattern starts.
19// Example:
20// a/b/c
21// a/b/d
22// b/e/f
23// Glob("", "...", time.Time{}, true) will return "a/b/c", "a/b/d", "b/e/f" and
24// their values.
25// Glob("a/b", "*", time.Time{}, true) will return "c", "d" and their values.
26func Glob(root string, pattern string, updatedSince time.Time, includeValues bool) *GlobIterator {
27 g, err := glob.Parse(pattern)
28 if err != nil {
29 return &GlobIterator{err: err}
30 }
31 lock.RLock()
32 defer lock.RUnlock()
33 node := findNodeLocked(root, false)
34 if node == nil {
Mike Burrows2c989372015-03-31 18:06:39 -070035 return &GlobIterator{err: verror.New(verror.ErrNoExist, nil, root)}
Robin Thellendac59e972014-08-19 18:26:11 -070036 }
37 var out []KeyValue
38 globStepLocked("", g, node, updatedSince, includeValues, &out)
39 sort.Sort(keyValueSort(out))
40 return &GlobIterator{results: out}
41}
42
43// globStepLocked applies a glob recursively.
44func globStepLocked(prefix string, g *glob.Glob, n *node, updatedSince time.Time, includeValues bool, result *[]KeyValue) {
Robin Thellend14a09ef2014-08-29 13:55:19 -070045 if g.Len() == 0 {
46 if updatedSince.IsZero() || (n.object != nil && !n.object.LastUpdate().Before(updatedSince)) {
47 var v interface{}
48 if includeValues && n.object != nil {
49 v = n.object.Value()
50 }
51 *result = append(*result, KeyValue{prefix, v})
Robin Thellendac59e972014-08-19 18:26:11 -070052 }
Robin Thellendac59e972014-08-19 18:26:11 -070053 }
Robin Thellend2f1878c2015-07-14 14:18:47 -070054 if g.Empty() {
Robin Thellendac59e972014-08-19 18:26:11 -070055 return
56 }
Robin Thellend2f1878c2015-07-14 14:18:47 -070057 matcher, left := g.Head(), g.Tail()
Robin Thellendac59e972014-08-19 18:26:11 -070058 for name, child := range n.children {
Robin Thellend2f1878c2015-07-14 14:18:47 -070059 if matcher.Match(name) {
Robin Thellendac59e972014-08-19 18:26:11 -070060 globStepLocked(path.Join(prefix, name), left, child, updatedSince, includeValues, result)
61 }
62 }
63}
64
65// KeyValue stores a Key and a Value.
66type KeyValue struct {
67 Key string
68 Value interface{}
69}
70
71// keyValueSort is used to sort a slice of KeyValue objects.
72type keyValueSort []KeyValue
73
74func (s keyValueSort) Len() int {
75 return len(s)
76}
77
78func (s keyValueSort) Less(i, j int) bool {
79 return s[i].Key < s[j].Key
80}
81
82func (s keyValueSort) Swap(i, j int) {
83 s[i], s[j] = s[j], s[i]
84}
85
86type GlobIterator struct {
87 results []KeyValue
88 next KeyValue
89 err error
90}
91
92// Advance stages the next element so that the client can retrieve it with
93// Value(). It returns true iff there is an element to retrieve. The client
94// must call Advance() before calling Value(). Advance may block if an element
95// is not immediately available.
96func (i *GlobIterator) Advance() bool {
97 if len(i.results) == 0 {
98 return false
99 }
100 i.next = i.results[0]
101 i.results = i.results[1:]
102 return true
103}
104
105// Value returns the element that was staged by Advance. Value does not block.
106func (i GlobIterator) Value() KeyValue {
107 return i.next
108}
109
110// Err returns a non-nil error iff the stream encountered any errors. Err does
111// not block.
112func (i GlobIterator) Err() error {
113 return i.err
114}