lib/stats: Add Incr() to stats.Map
Incr() increments the value associated with a key. It is a no-op for
non-numeric types.
Change-Id: I4d14eb1da7ac045a965bd20c852695c162ec40ec
diff --git a/lib/stats/map.go b/lib/stats/map.go
index 5987430..6e801eb 100644
--- a/lib/stats/map.go
+++ b/lib/stats/map.go
@@ -82,6 +82,39 @@
m.insertMissingNodes()
}
+// Incr increments the value of the given key and returns the new value.
+func (m *Map) Incr(key string, delta int64) interface{} {
+ now := time.Now()
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if _, exists := m.value[key]; !exists {
+ m.value[key] = mapValue{now, int64(0)}
+ oName := path.Join(m.name, key)
+ lock.Lock()
+ if n := findNodeLocked(oName, true); n.object == nil {
+ n.object = &mapValueWrapper{m, key}
+ }
+ lock.Unlock()
+ }
+ var result interface{}
+ switch value := m.value[key].value.(type) {
+ case int64:
+ result = value + delta
+ case uint64:
+ if delta >= 0 {
+ result = value + uint64(delta)
+ } else {
+ result = value - uint64(-delta)
+ }
+ case float64:
+ result = value + float64(delta)
+ default:
+ return nil
+ }
+ m.value[key] = mapValue{now, result}
+ return result
+}
+
// Delete deletes the given keys from the map object.
func (m *Map) Delete(keys []string) {
// The lock order is important.
diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go
index 4115942..605f7c6 100644
--- a/lib/stats/stats_test.go
+++ b/lib/stats/stats_test.go
@@ -279,7 +279,7 @@
func TestMap(t *testing.T) {
m := libstats.NewMap("testing/foo")
- m.Set([]libstats.KeyValue{{"a", 1}, {"b", 2}})
+ m.Set([]libstats.KeyValue{{"a", uint64(1)}, {"b", 2}, {"c", float64(10.0)}})
// Test the Value of the map.
{
@@ -299,13 +299,38 @@
}
expected := []libstats.KeyValue{
libstats.KeyValue{Key: "foo", Value: nil},
- libstats.KeyValue{Key: "foo/a", Value: int64(1)},
+ libstats.KeyValue{Key: "foo/a", Value: uint64(1)},
libstats.KeyValue{Key: "foo/b", Value: int64(2)},
+ libstats.KeyValue{Key: "foo/c", Value: float64(10.0)},
}
if !reflect.DeepEqual(got, expected) {
t.Errorf("unexpected result. Got %#v, want %#v", got, expected)
}
}
+ // Test Incr
+ testcases := []struct {
+ key string
+ incr int64
+ expected interface{}
+ }{
+ {"a", 2, uint64(3)},
+ {"a", -1, uint64(2)},
+ {"b", 5, int64(7)},
+ {"c", -2, float64(8)},
+ {"d", -2, int64(-2)},
+ }
+ for i, tc := range testcases {
+ if got := m.Incr(tc.key, tc.incr); got != tc.expected {
+ t.Errorf("unexpected result for #%d. Got %v, expected %v", got, tc.expected)
+ }
+ got, err := libstats.Value("testing/foo/" + tc.key)
+ if err != nil {
+ t.Errorf("unexpected error for #%d: %v", i, err)
+ }
+ if got != tc.expected {
+ t.Errorf("unexpected result for #%d. Got %v, want %v", i, got, tc.expected)
+ }
+ }
m.Delete([]string{"a"})
@@ -317,7 +342,9 @@
}
expected := []libstats.KeyValue{
libstats.KeyValue{Key: "foo", Value: nil},
- libstats.KeyValue{Key: "foo/b", Value: int64(2)},
+ libstats.KeyValue{Key: "foo/b", Value: int64(7)},
+ libstats.KeyValue{Key: "foo/c", Value: float64(8)},
+ libstats.KeyValue{Key: "foo/d", Value: int64(-2)},
}
if !reflect.DeepEqual(got, expected) {
t.Errorf("unexpected result. Got %#v, want %#v", got, expected)