diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s index f3391740bed..e7833de0c81 100644 --- a/src/pkg/bytes/asm_386.s +++ b/src/pkg/bytes/asm_386.s @@ -15,3 +15,19 @@ TEXT ·IndexByte(SB),7,$0 SUBL $1, DI MOVL DI, ret+16(FP) RET + +TEXT ·Equal(SB),7,$0 + MOVL len+4(FP), BX + MOVL len1+16(FP), CX + MOVL $0, AX + CMPL BX, CX + JNE eqret + MOVL p+0(FP), SI + MOVL q+12(FP), DI + CLD + REP; CMPSB + JNE eqret + MOVL $1, AX +eqret: + MOVB AX, ret+24(FP) + RET diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s index c6793cbdcc7..bc6e886bda1 100644 --- a/src/pkg/bytes/asm_amd64.s +++ b/src/pkg/bytes/asm_amd64.s @@ -90,3 +90,19 @@ success: MOVL DI, ret+24(FP) RET +TEXT ·Equal(SB),7,$0 + MOVL len+8(FP), BX + MOVL len1+24(FP), CX + MOVL $0, AX + MOVL $1, DX + CMPL BX, CX + JNE eqret + MOVQ p+0(FP), SI + MOVQ q+16(FP), DI + CLD + REP; CMPSB + CMOVLEQ DX, AX +eqret: + MOVB AX, ret+32(FP) + RET + diff --git a/src/pkg/bytes/asm_arm.s b/src/pkg/bytes/asm_arm.s index f32fca13666..4ed0c1580a8 100644 --- a/src/pkg/bytes/asm_arm.s +++ b/src/pkg/bytes/asm_arm.s @@ -6,3 +6,6 @@ TEXT ·IndexByte(SB),7,$0 B ·indexBytePortable(SB) +// no memcmp implementation on arm yet +TEXT ·Equal(SB),7,$0 + B ·equalPortable(SB) diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index 9bfd88fa398..307c89aa3d6 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -37,7 +37,9 @@ func Compare(a, b []byte) int { } // Equal returns a boolean reporting whether a == b. -func Equal(a, b []byte) bool { +func Equal(a, b []byte) bool + +func equalPortable(a, b []byte) bool { if len(a) != len(b) { return false } @@ -74,18 +76,33 @@ func explode(s []byte, n int) [][]byte { // Count counts the number of non-overlapping instances of sep in s. func Count(s, sep []byte) int { - if len(sep) == 0 { + n := len(sep) + if n == 0 { return utf8.RuneCount(s) + 1 } - c := sep[0] - n := 0 - for i := 0; i+len(sep) <= len(s); i++ { - if s[i] == c && (len(sep) == 1 || Equal(s[i:i+len(sep)], sep)) { - n++ - i += len(sep) - 1 - } + if n > len(s) { + return 0 } - return n + count := 0 + c := sep[0] + i := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if n == 1 || Equal(s[i:i+n], sep) { + count++ + i += n + continue + } + i++ + } + return count } // Contains returns whether subslice is within b. @@ -99,11 +116,27 @@ func Index(s, sep []byte) int { if n == 0 { return 0 } + if n > len(s) { + return -1 + } c := sep[0] - for i := 0; i+n <= len(s); i++ { - if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) { + if n == 1 { + return IndexByte(s, c) + } + i := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if Equal(s[i:i+n], sep) { return i } + i++ } return -1 } diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 829ef05319c..a2a08c20db0 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -64,13 +64,17 @@ func TestCompare(t *testing.T) { a := []byte(tt.a) b := []byte(tt.b) cmp := Compare(a, b) - eql := Equal(a, b) if cmp != tt.i { t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) } + eql := Equal(a, b) if eql != (tt.i == 0) { t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) } + eql = EqualPortable(a, b) + if eql != (tt.i == 0) { + t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql) + } } } @@ -264,27 +268,18 @@ func TestIndexRune(t *testing.T) { } } -func BenchmarkIndexByte4K(b *testing.B) { bmIndex(b, IndexByte, 4<<10) } - -func BenchmarkIndexByte4M(b *testing.B) { bmIndex(b, IndexByte, 4<<20) } - -func BenchmarkIndexByte64M(b *testing.B) { bmIndex(b, IndexByte, 64<<20) } - -func BenchmarkIndexBytePortable4K(b *testing.B) { - bmIndex(b, IndexBytePortable, 4<<10) -} - -func BenchmarkIndexBytePortable4M(b *testing.B) { - bmIndex(b, IndexBytePortable, 4<<20) -} - -func BenchmarkIndexBytePortable64M(b *testing.B) { - bmIndex(b, IndexBytePortable, 64<<20) -} - var bmbuf []byte -func bmIndex(b *testing.B, index func([]byte, byte) int, n int) { +func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) } +func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) } +func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) } +func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) } +func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) } +func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) } +func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) } +func BenchmarkIndexBytePortable64M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 64<<20) } + +func bmIndexByte(b *testing.B, index func([]byte, byte) int, n int) { if len(bmbuf) < n { bmbuf = make([]byte, n) } @@ -298,7 +293,127 @@ func bmIndex(b *testing.B, index func([]byte, byte) int, n int) { panic("bad index") } } - buf[n-1] = '0' + buf[n-1] = '\x00' +} + +func BenchmarkEqual32(b *testing.B) { bmEqual(b, Equal, 32) } +func BenchmarkEqual4K(b *testing.B) { bmEqual(b, Equal, 4<<10) } +func BenchmarkEqual4M(b *testing.B) { bmEqual(b, Equal, 4<<20) } +func BenchmarkEqual64M(b *testing.B) { bmEqual(b, Equal, 64<<20) } +func BenchmarkEqualPort32(b *testing.B) { bmEqual(b, EqualPortable, 32) } +func BenchmarkEqualPort4K(b *testing.B) { bmEqual(b, EqualPortable, 4<<10) } +func BenchmarkEqualPortable4M(b *testing.B) { bmEqual(b, EqualPortable, 4<<20) } +func BenchmarkEqualPortable64M(b *testing.B) { bmEqual(b, EqualPortable, 64<<20) } + +func bmEqual(b *testing.B, equal func([]byte, []byte) bool, n int) { + if len(bmbuf) < 2*n { + bmbuf = make([]byte, 2*n) + } + b.SetBytes(int64(n)) + buf1 := bmbuf[0:n] + buf2 := bmbuf[n : 2*n] + buf1[n-1] = 'x' + buf2[n-1] = 'x' + for i := 0; i < b.N; i++ { + eq := equal(buf1, buf2) + if !eq { + panic("bad equal") + } + } + buf1[n-1] = '\x00' + buf2[n-1] = '\x00' +} + +func BenchmarkIndex32(b *testing.B) { bmIndex(b, Index, 32) } +func BenchmarkIndex4K(b *testing.B) { bmIndex(b, Index, 4<<10) } +func BenchmarkIndex4M(b *testing.B) { bmIndex(b, Index, 4<<20) } +func BenchmarkIndex64M(b *testing.B) { bmIndex(b, Index, 64<<20) } + +func bmIndex(b *testing.B, index func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, buf[n-7:]) + if j != n-7 { + println("bad index", j) + panic("bad index") + } + } + buf[n-1] = '\x00' +} + +func BenchmarkIndexEasy32(b *testing.B) { bmIndexEasy(b, Index, 32) } +func BenchmarkIndexEasy4K(b *testing.B) { bmIndexEasy(b, Index, 4<<10) } +func BenchmarkIndexEasy4M(b *testing.B) { bmIndexEasy(b, Index, 4<<20) } +func BenchmarkIndexEasy64M(b *testing.B) { bmIndexEasy(b, Index, 64<<20) } + +func bmIndexEasy(b *testing.B, index func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + buf[n-7] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, buf[n-7:]) + if j != n-7 { + println("bad index", j) + panic("bad index") + } + } + buf[n-1] = '\x00' + buf[n-7] = '\x00' +} + +func BenchmarkCount32(b *testing.B) { bmCount(b, Count, 32) } +func BenchmarkCount4K(b *testing.B) { bmCount(b, Count, 4<<10) } +func BenchmarkCount4M(b *testing.B) { bmCount(b, Count, 4<<20) } +func BenchmarkCount64M(b *testing.B) { bmCount(b, Count, 64<<20) } + +func bmCount(b *testing.B, count func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := count(buf, buf[n-7:]) + if j != 1 { + println("bad count", j) + panic("bad count") + } + } + buf[n-1] = '\x00' +} + +func BenchmarkCountEasy32(b *testing.B) { bmCountEasy(b, Count, 32) } +func BenchmarkCountEasy4K(b *testing.B) { bmCountEasy(b, Count, 4<<10) } +func BenchmarkCountEasy4M(b *testing.B) { bmCountEasy(b, Count, 4<<20) } +func BenchmarkCountEasy64M(b *testing.B) { bmCountEasy(b, Count, 64<<20) } + +func bmCountEasy(b *testing.B, count func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + buf[n-7] = 'x' + for i := 0; i < b.N; i++ { + j := count(buf, buf[n-7:]) + if j != 1 { + println("bad count", j) + panic("bad count") + } + } + buf[n-1] = '\x00' + buf[n-7] = '\x00' } type ExplodeTest struct { diff --git a/src/pkg/bytes/export_test.go b/src/pkg/bytes/export_test.go index b65428d9ce8..f61523e60bb 100644 --- a/src/pkg/bytes/export_test.go +++ b/src/pkg/bytes/export_test.go @@ -6,3 +6,4 @@ package bytes // Export func for testing var IndexBytePortable = indexBytePortable +var EqualPortable = equalPortable