Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare string and byte slice in Go without copy

Tags:

go

What is the best way to check that Go string and a byte slice contain the same bytes? The simplest str == string(byteSlice) is inefficient as it copies byteSlice first.

I was looking for a version of Equal(a, b []byte) that takes a string as its argument, but could not find anything suitable.

like image 448
Igor Bukanov Avatar asked Jul 17 '15 18:07

Igor Bukanov


3 Answers

Starting from Go 1.5 the compiler optimizes string(bytes) when comparing to a string using a stack-allocated temporary. Thus since Go 1.5

str == string(byteSlice)

became a canonical and efficient way to compare string to a byte slice.

like image 98
Igor Bukanov Avatar answered Oct 14 '22 12:10

Igor Bukanov


The Go Programming Language Specification

String types

A string type represents the set of string values. A string value is a (possibly empty) sequence of bytes. The predeclared string type is string.

The length of a string s (its size in bytes) can be discovered using the built-in function len. A string's bytes can be accessed by integer indices 0 through len(s)-1.

For example,

package main

import "fmt"

func equal(s string, b []byte) bool {
    if len(s) != len(b) {
        return false
    }
    for i, x := range b {
        if x != s[i] {
            return false
        }
    }
    return true
}

func main() {
    s := "equal"
    b := []byte(s)
    fmt.Println(equal(s, b))
    s = "not" + s
    fmt.Println(equal(s, b))
}

Output:

true
false
like image 8
peterSO Avatar answered Oct 14 '22 12:10

peterSO


If you're comfortable enough with the fact that this can break on a later release (doubtful though), you can use unsafe:

func unsafeCompare(a string, b []byte) int {
    abp := *(*[]byte)(unsafe.Pointer(&a))
    return bytes.Compare(abp, b)
}

func unsafeEqual(a string, b []byte) bool {
    bbp := *(*string)(unsafe.Pointer(&b))
    return a == bbp
}

playground

Benchmarks:

// using:
//  aaa = strings.Repeat("a", 100)
//  bbb = []byte(strings.Repeat("a", 99) + "b")

// go 1.5
BenchmarkCopy-8         20000000                75.4 ns/op
BenchmarkPetersEqual-8  20000000                83.1 ns/op
BenchmarkUnsafe-8       100000000               12.2 ns/op
BenchmarkUnsafeEqual-8  200000000               8.94 ns/op
// go 1.4
BenchmarkCopy           10000000                233  ns/op
BenchmarkPetersEqual    20000000                72.3 ns/op
BenchmarkUnsafe         100000000               15.5 ns/op
BenchmarkUnsafeEqual    100000000               10.7 ns/op
like image 8
OneOfOne Avatar answered Oct 14 '22 13:10

OneOfOne