]> Cypherpunks repositories - gostls13.git/commitdiff
godoc: don't use quadratic algorithm to filter paths
authorRobert Griesemer <gri@golang.org>
Thu, 16 Sep 2010 17:40:07 +0000 (10:40 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 16 Sep 2010 17:40:07 +0000 (10:40 -0700)
R=rsc
CC=golang-dev
https://golang.org/cl/2212042

src/cmd/godoc/godoc.go

index 59f4a95dbd356fa57b0ddb39bbe3306d1d601425..cd3d14ede4124ba2cf649e789367b71cf3a026cf 100644 (file)
@@ -107,21 +107,44 @@ func isRelated(p, q string) bool {
 }
 
 
+// binarySearch returns an index i such that (a[i] <= s < a[i+1]) || (s is not in a).
+// The slice a must not be empty and sorted in increasing order.
+// (See "A Method of Programming", E.W. Dijkstra).
+//
+func binarySearch(a []string, s string) int {
+       i, j := 0, len(a)
+       // i < j for non-empty a
+       for i+1 < j {
+               // 0 <= i < j <= len(a) && (a[i] <= s < a[j] || (s is not in a))
+               h := i + (j-i)/2 // i < h < j
+               if a[h] <= s {
+                       i = h
+               } else { // s < a[h]
+                       j = h
+               }
+       }
+       // i+1 == j for non-empty a
+       return i
+}
+
+
 func setPathFilter(list []string) {
        if len(list) == 0 {
                pathFilter.set(nil)
                return
        }
 
-       // TODO(gri) This leads to quadratic behavior.
-       //           Need to find a better filter solution.
+       // len(list) > 0
        pathFilter.set(func(path string) bool {
-               for _, p := range list {
-                       if isRelated(path, p) {
-                               return true
-                       }
-               }
-               return false
+               // list is sorted in increasing order and for each path all its children are removed
+               i := binarySearch(list, path)
+               // At this point we have (list[i] <= path < list[i+1]) || (path is not in list),
+               // thus path must be either longer (a child) of list[i], or shorter (a parent)
+               // of list[i+1] - assuming an "infinitely extended" list. However, binarySearch
+               // will return a 0 if path < list[0], so we must be careful in that case.
+               return i == 0 && isParentOf(path, list[0]) ||
+                       isParentOf(list[i], path) ||
+                       i+1 < len(list) && isParentOf(path, list[i+1])
        })
 }
 
@@ -142,12 +165,22 @@ func readDirList(filename string) ([]string, os.Error) {
        if err != nil {
                return nil, err
        }
-       // create list of valid directory names
+       // create a sorted list of valid directory names
        filter := func(path string) bool {
                d, err := os.Lstat(path)
                return err == nil && isPkgDir(d)
        }
-       return canonicalizePaths(strings.Split(string(contents), "\n", -1), filter), nil
+       list := canonicalizePaths(strings.Split(string(contents), "\n", -1), filter)
+       // for each parent path, remove all it's children q
+       // (requirement for binary search to work when filtering)
+       i := 0
+       for _, q := range list {
+               if i == 0 || !isParentOf(list[i-1], q) {
+                       list[i] = q
+                       i++
+               }
+       }
+       return list[0:i], nil
 }