]> Cypherpunks repositories - gostls13.git/commitdiff
Refactor exvar to use interface types, and add mapVar.
authorDavid Symonds <dsymonds@golang.org>
Tue, 21 Apr 2009 05:38:14 +0000 (22:38 -0700)
committerDavid Symonds <dsymonds@golang.org>
Tue, 21 Apr 2009 05:38:14 +0000 (22:38 -0700)
R=r
APPROVED=r
DELTA=170  (136 added, 6 deleted, 28 changed)
OCL=27628
CL=27652

src/lib/exvar.go
src/lib/exvar_test.go

index 319a0977a2c2bd7eaf2fc6af51d219920918de00..88f36b7bd81d20186d5d40c30c5b7fdecfde0dd4 100644 (file)
@@ -11,44 +11,141 @@ import (
        "sync";
 )
 
+// If mismatched names are used (e.g. calling IncrementInt on a mapVar), the
+// var name is silently mapped to these. We will consider variables starting
+// with reservedPrefix to be reserved by this package, and so we avoid the
+// possibility of a user doing IncrementInt("x-mismatched-map", 1).
+// TODO(dsymonds): Enforce this.
+const (
+       reservedPrefix = "x-";
+       mismatchedInt = reservedPrefix + "mismatched-int";
+       mismatchedMap = reservedPrefix + "mismatched-map";
+)
+
+// exVar is an abstract type for all exported variables.
+type exVar interface {
+       String() string;
+}
+
+// intVar is an integer variable, and satisfies the exVar interface.
+type intVar int;
+
+func (i intVar) String() string {
+       return fmt.Sprint(int(i))
+}
+
+// mapVar is a map variable, and satisfies the exVar interface.
+type mapVar map[string] int;
+
+func (m mapVar) String() string {
+       s := "map:x";  // TODO(dsymonds): the 'x' should be user-specified!
+       for k, v := range m {
+               s += fmt.Sprintf(" %s:%v", k, v)
+       }
+       return s
+}
+
+// TODO(dsymonds):
+// - string-valued vars
+// - dynamic lookup vars (via chan?)
+
 // Global state.
 var (
        mutex sync.Mutex;
-       intVars = make(map[string] int);
-       mapVars = make(map[string] map[string] int);
-       // TODO(dsymonds):
-       // - string-valued vars
-       // - docstrings
-       // - dynamic lookup vars (via chan)
+       vars = make(map[string] exVar);
+       // TODO(dsymonds): docstrings
 )
 
-// Increment adds inc to the var called name.
-func Increment(name string, inc int) {
+// getOrInitIntVar either gets or initializes an intVar called name.
+// Callers should already be holding the mutex.
+func getOrInitIntVar(name string) *intVar {
+       if v, ok := vars[name]; ok {
+               // Existing var
+               if iv, ok := v.(*intVar); ok {
+                       return iv
+               }
+               // Type mismatch.
+               return getOrInitIntVar(mismatchedInt)
+       }
+       // New var
+       iv := new(intVar);
+       vars[name] = iv;
+       return iv
+}
+
+// getOrInitMapVar either gets or initializes a mapVar called name.
+// Callers should already be holding the mutex.
+func getOrInitMapVar(name string) *mapVar {
+       if v, ok := vars[name]; ok {
+               // Existing var
+               if mv, ok := v.(*mapVar); ok {
+                       return mv
+               }
+               // Type mismatch.
+               return getOrInitMapVar(mismatchedMap)
+       }
+       // New var
+       var m mapVar = make(map[string] int);
+       vars[name] = &m;
+       return &m
+}
+
+// IncrementInt adds inc to the integer-valued var called name.
+func IncrementInt(name string, inc int) {
        mutex.Lock();
        defer mutex.Unlock();
 
-       if x, ok := intVars[name]; ok {
-               intVars[name] += inc
+       *getOrInitIntVar(name) += inc
+}
+
+// IncrementMap adds inc to the keyed value in the map-valued var called name.
+func IncrementMap(name string, key string, inc int) {
+       mutex.Lock();
+       defer mutex.Unlock();
+
+       mv := getOrInitMapVar(name);
+       // TODO(dsymonds): Change this to just mv[key] when bug143 is fixed.
+       if v, ok := (*mv)[key]; ok {
+               mv[key] += inc
        } else {
-               intVars[name] = inc
+               mv[key] = inc
        }
 }
 
-// Set sets the var called name to value.
-func Set(name string, value int) {
+// SetInt sets the integer-valued var called name to value.
+func SetInt(name string, value int) {
+       mutex.Lock();
+       defer mutex.Unlock();
+
+       *getOrInitIntVar(name) = value
+}
+
+// SetMap sets the keyed value in the map-valued var called name.
+func SetMap(name string, key string, value int) {
        mutex.Lock();
        defer mutex.Unlock();
 
-       intVars[name] = value
+       getOrInitMapVar(name)[key] = value
 }
 
-// Get retrieves an integer-valued var called name.
-func Get(name string) (x int, ok bool) {
-       x, ok = intVars[name];
-       return
+// GetInt retrieves an integer-valued var called name.
+func GetInt(name string) int {
+       mutex.Lock();
+       defer mutex.Unlock();
+
+       return *getOrInitIntVar(name)
 }
 
-// TODO(dsymonds): Functions for map-valued vars.
+// GetMap retrieves the keyed value for a map-valued var called name.
+func GetMap(name string, key string) int {
+       mutex.Lock();
+       defer mutex.Unlock();
+
+       // TODO(dsymonds): Change this to just getOrInitMapVar(name)[key] when
+       // bug143 is fixed.
+       x, ok := (*getOrInitMapVar(name))[key];
+       return x
+}
 
 // String produces a string of all the vars in textual format.
 func String() string {
@@ -56,7 +153,7 @@ func String() string {
        defer mutex.Unlock();
 
        s := "";
-       for name, value := range intVars {
+       for name, value := range vars {
                s += fmt.Sprintln(name, value)
        }
        return s
index 2948fc6365579b09e24ad37105048eafbc100eeb..6b309f3a49e2229c2acca1bbe9548ec7d70927ff 100644 (file)
@@ -11,19 +11,17 @@ import (
 )
 
 func TestSimpleCounter(t *testing.T) {
-       // Unknown exvar should be zero, and return !ok.
-       x, ok := Get("requests");
-       if x != 0 || ok {
-               t.Errorf("Get(nonexistent) = (%v, %v), want (%v, %v)",
-                        x, ok, 0, false)
+       // Unknown exvar should be zero.
+       x := GetInt("requests");
+       if x != 0 {
+               t.Errorf("Get(nonexistent) = %v, want 0", x)
        }
 
-       Increment("requests", 1);
-       Increment("requests", 3);
-       x, ok = Get("requests");
-       if x != 4 || !ok {
-               t.Errorf("Get('requests') = (%v, %v), want (%v, %v)",
-                        x, ok, 4, true)
+       IncrementInt("requests", 1);
+       IncrementInt("requests", 3);
+       x = GetInt("requests");
+       if x != 4 {
+               t.Errorf("Get('requests') = %v, want 4", x)
        }
 
        out := String();
@@ -33,22 +31,57 @@ func TestSimpleCounter(t *testing.T) {
        }
 }
 
+func TestMismatchedCounters(t *testing.T) {
+       // Make sure some vars exist.
+       GetInt("requests");
+       GetMap("colours", "red");
+
+       IncrementInt("colours", 1);
+       if x := GetInt("x-mismatched-int"); x != 1 {
+               t.Errorf("GetInt('x-mismatched-int') = %v, want 1", x)
+       }
+
+       IncrementMap("requests", "orange", 1);
+       if x := GetMap("x-mismatched-map", "orange"); x != 1 {
+               t.Errorf("GetMap('x-mismatched-int', 'orange') = %v, want 1", x)
+       }
+}
+
+func TestMapCounter(t *testing.T) {
+       // Unknown exvar should be zero.
+       if x := GetMap("colours", "red"); x != 0 {
+               t.Errorf("GetMap(non, existent) = %v, want 0", x)
+       }
+
+       IncrementMap("colours", "red", 1);
+       IncrementMap("colours", "red", 2);
+       IncrementMap("colours", "blue", 4);
+       if x := GetMap("colours", "red"); x != 3 {
+               t.Errorf("GetMap('colours', 'red') = %v, want 3", x)
+       }
+       if x := GetMap("colours", "blue"); x != 4 {
+               t.Errorf("GetMap('colours', 'blue') = %v, want 4", x)
+       }
+
+       // TODO(dsymonds): Test String()
+}
+
 func hammer(name string, total int, done chan <- int) {
        for i := 0; i < total; i++ {
-               Increment(name, 1)
+               IncrementInt(name, 1)
        }
        done <- 1
 }
 
 func TestHammer(t *testing.T) {
-       Set("hammer-times", 0);
+       SetInt("hammer-times", 0);
        sync := make(chan int);
        hammer_times := int(1e5);
        go hammer("hammer-times", hammer_times, sync);
        go hammer("hammer-times", hammer_times, sync);
        <-sync;
        <-sync;
-       if final, ok := Get("hammer-times"); final != 2 * hammer_times {
+       if final := GetInt("hammer-times"); final != 2 * hammer_times {
                t.Errorf("hammer-times = %v, want %v", final, 2 * hammer_times)
        }
 }