]> Cypherpunks repositories - gostls13.git/commitdiff
bytes: use Rabin-Karp algorithm for LastIndex
authorTobias Klauser <tklauser@distanz.ch>
Fri, 8 Mar 2019 10:39:08 +0000 (11:39 +0100)
committerTobias Klauser <tobias.klauser@gmail.com>
Fri, 8 Mar 2019 13:46:43 +0000 (13:46 +0000)
Implement LastIndex using the Rabin-Karp algorithm akin to
strings.LastIndex

name              old time/op  new time/op  delta
LastIndexHard1-8  3.16ms ± 1%  1.44ms ± 0%  -54.35%  (p=0.008 n=5+5)
LastIndexHard2-8  3.17ms ± 1%  1.45ms ± 0%  -54.27%  (p=0.008 n=5+5)
LastIndexHard3-8  3.05ms ± 1%  1.44ms ± 1%  -52.58%  (p=0.008 n=5+5)

Change-Id: Ie8ddd179cd84dfa00e3e4e2327ef932975c88670
Reviewed-on: https://go-review.googlesource.com/c/go/+/166258
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/bytes/bytes.go

index daf4a32f26f29e25159ba1ec1c64f785536f1835..f65bf214cc55ddab5cf84c9ffb03cc799f5c7af1 100644 (file)
@@ -114,12 +114,34 @@ func indexBytePortable(s []byte, c byte) int {
 // LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
 func LastIndex(s, sep []byte) int {
        n := len(sep)
-       if n == 0 {
+       switch {
+       case n == 0:
                return len(s)
+       case n == 1:
+               return LastIndexByte(s, sep[0])
+       case n == len(s):
+               if Equal(s, sep) {
+                       return 0
+               }
+               return -1
+       case n > len(s):
+               return -1
        }
-       c := sep[0]
-       for i := len(s) - n; i >= 0; i-- {
-               if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) {
+       // Rabin-Karp search from the end of the string
+       hashss, pow := hashStrRev(sep)
+       last := len(s) - n
+       var h uint32
+       for i := len(s) - 1; i >= last; i-- {
+               h = h*primeRK + uint32(s[i])
+       }
+       if h == hashss && Equal(s[last:], sep) {
+               return last
+       }
+       for i := last - 1; i >= 0; i-- {
+               h *= primeRK
+               h += uint32(s[i])
+               h -= pow * uint32(s[i+n])
+               if h == hashss && Equal(s[i:i+n], sep) {
                        return i
                }
        }
@@ -987,3 +1009,20 @@ func hashStr(sep []byte) (uint32, uint32) {
        }
        return hash, pow
 }
+
+// hashStrRev returns the hash of the reverse of sep and the
+// appropriate multiplicative factor for use in Rabin-Karp algorithm.
+func hashStrRev(sep []byte) (uint32, uint32) {
+       hash := uint32(0)
+       for i := len(sep) - 1; i >= 0; i-- {
+               hash = hash*primeRK + uint32(sep[i])
+       }
+       var pow, sq uint32 = 1, primeRK
+       for i := len(sep); i > 0; i >>= 1 {
+               if i&1 != 0 {
+                       pow *= sq
+               }
+               sq *= sq
+       }
+       return hash, pow
+}