Binary string to unicode - string

I'm not 100% sure why my binary string to unicode isn't working..can anyone point out the issue or help me patch it? Also the reason why i chunk out the binary is that it is too large for ParseInt to handle. See the playground link below for an example.
func binToString(s []byte) string {
var counter int
chunk := make([]byte, 7)
var buf bytes.Buffer
for i := range s {
if i%8 == 0 {
counter = 0
if i, err := strconv.ParseInt(string(chunk), 2, 64); err == nil {
buf.WriteString(string(i))
}
} else {
chunk[counter] = s[i] //i know i can use modulus here too but i was testing and an counter was easier to track and test for me
counter++
}
}
return buf.String()
}
It either seems to miss a character or add an character (or two) on conversion.
Here is a playground link showing an example of the function not working as expected.

Your function could be implemented in a simpler, more efficient manner:
func binToString(s []byte) string {
output := make([]byte, len(s)/8)
for i := 0; i < len(output); i++ {
val, err := strconv.ParseInt(string(s[i*8:(i+1)*8]), 2, 64)
if err == nil {
output[i] = byte(val)
}
}
return string(output)
}
https://play.golang.org/p/Fmo7I-rN3c

Related

Efficient way to convert string to string representation of the binary

I'm searching how to convert a string to a string representation in binary with best performance.
So I started with something similar to the following:
func binConvertOrig(s string) string {
var buf bytes.Buffer
for i := 0; i < len(s); i++ {
fmt.Fprintf(&buf, "%08b", s[i])
}
return buf.String()
}
s := "Test"
log.Printf("%s => binConvertOrig => %s", s, binConvertOrig(s))
But it seems that fmt.Fprintf & bytes.Buffer are not very efficient.
Is there a better way to do this?
Thanks
Nothing beats a pre-calculated lookup table, especially if it's stored in a slice or array (and not in a map), and the converter allocates a byte slice for the result with just the right size:
var byteBinaries [256][]byte
func init() {
for i := range byteBinaries {
byteBinaries[i] = []byte(fmt.Sprintf("%08b", i))
}
}
func strToBin(s string) string {
res := make([]byte, len(s)*8)
for i := len(s) - 1; i >= 0; i-- {
copy(res[i*8:], byteBinaries[s[i]])
}
return string(res)
}
Testing it:
fmt.Println(strToBin("\x01\xff"))
Output (try it on the Go Playground):
0000000111111111
Benchmarks
Let's see how fast it can get:
var texts = []string{
"\x00",
"123",
"1234567890",
"asdf;lkjasdf;lkjasdf;lkj108fhq098wf34",
}
func BenchmarkOrig(b *testing.B) {
for n := 0; n < b.N; n++ {
for _, t := range texts {
binConvertOrig(t)
}
}
}
func BenchmarkLookup(b *testing.B) {
for n := 0; n < b.N; n++ {
for _, t := range texts {
strToBin(t)
}
}
}
Results:
BenchmarkOrig-4 200000 8526 ns/op 2040 B/op 12 allocs/op
BenchmarkLookup-4 2000000 781 ns/op 880 B/op 8 allocs/op
The lookup version (strToBin()) is 11 times faster and uses less memory and allocations. Basically it only uses allocation for the result (which is unavoidable).

Go conversion between struct and byte array

I am writing a client - server application in Go. I want to perform C-like type casting in Go.
E.g. in Go
type packet struct {
opcode uint16
data [1024]byte
}
var pkt1 packet
...
n, raddr, err := conn.ReadFromUDP(pkt1) // error here
Also I want to perform C-like memcpy(), which will allow me to directly map the network byte stream received to a struct.
e.g. with above received pkt1
type file_info struct {
file_size uint32 // 4 bytes
file_name [1020]byte
}
var file file_info
if (pkt1.opcode == WRITE) {
memcpy(&file, pkt1.data, 1024)
}
unsafe.Pointer is, well, unsafe, and you don't actually need it here. Use encoding/binary package instead:
// Create a struct and write it.
t := T{A: 0xEEFFEEFF, B: 3.14}
buf := &bytes.Buffer{}
err := binary.Write(buf, binary.BigEndian, t)
if err != nil {
panic(err)
}
fmt.Println(buf.Bytes())
// Read into an empty struct.
t = T{}
err = binary.Read(buf, binary.BigEndian, &t)
if err != nil {
panic(err)
}
fmt.Printf("%x %f", t.A, t.B)
Playground
As you can see, it handles sizes and endianness quite neatly.
I've had the same problem and I solved it by using the "encoding/binary" package. Here's an example:
package main
import (
"bytes"
"fmt"
"encoding/binary"
)
func main() {
p := fmt.Println
b := []byte{43, 1, 0}
myStruct := MyStruct{}
err := binary.Read(bytes.NewBuffer(b[:]), binary.BigEndian, &myStruct)
if err != nil {
panic(err)
}
p(myStruct)
}
type MyStruct struct {
Num uint8
Num2 uint16
}
Here's the running example: https://play.golang.org/p/Q3LjaAWDMh
Thank you for answers and I am sure they work perfectly. But in my case I was more interested in parsing the []byte buffer received as network packet. I used following method to parse the buffer.
var data []byte // holds the network packet received
opcode := binary.BigEndian.Uint16(data) // this will get first 2 bytes to be interpreted as uint16 number
raw_data := data[2:len(data)] // this will copy rest of the raw data in to raw_data byte stream
While constructing a []byte stream from a struct, you can use following method
type packet struct {
opcode uint16
blk_no uint16
data string
}
pkt := packet{opcode: 2, blk_no: 1, data: "testing"}
var buf []byte = make([]byte, 50) // make sure the data string is less than 46 bytes
offset := 0
binary.BigEndian.PutUint16(buf[offset:], pkt.opcode)
offset = offset + 2
binary.BigEndian.PutUint16(buf[offset:], pkt.blk_no)
offset = offset + 2
bytes_copied := copy(buf[offset:], pkt.data)
I hope this gives general idea about how to convert []byte stream to struct and struct back to []byte stream.
You'd have to use unsafe, also uint is 8 bytes on 64bit systems, you have to use uint32 if you want 4 bytes.
It's ugly, unsafe and you have to handle endianess yourself.
type packet struct {
opcode uint16
data [1022]byte
}
type file_info struct {
file_size uint32 // 4 bytes
file_name [1018]byte //this struct has to fit in packet.data
}
func makeData() []byte {
fi := file_info{file_size: 1 << 20}
copy(fi.file_name[:], []byte("test.x64"))
p := packet{
opcode: 1,
data: *(*[1022]byte)(unsafe.Pointer(&fi)),
}
mem := *(*[1022]byte)(unsafe.Pointer(&p))
return mem[:]
}
func main() {
data := makeData()
fmt.Println(data)
p := (*packet)(unsafe.Pointer(&data[0]))
if p.opcode == 1 {
fi := (*file_info)(unsafe.Pointer(&p.data[0]))
fmt.Println(fi.file_size, string(fi.file_name[:8]))
}
}
play

How do I dump the struct into the byte array without reflection?

I already found encoding/binary package to deal with it, but it depended on reflect package so it didn't work with uncapitalized(that is, unexported) struct fields. However I spent a week to find that problem out, I still have a question: if struct fields should not be exported, how do I dump them easily into binary data?
EDIT: Here's the example. If you capitalize the name of fields of Data struct, that works properly. But Data struct was intended to be an abstract type, so I don't want to export these fields.
package main
import (
"fmt"
"encoding/binary"
"bytes"
)
type Data struct {
id int32
name [16]byte
}
func main() {
d := Data{Id: 1}
copy(d.Name[:], []byte("tree"))
buffer := new(bytes.Buffer)
binary.Write(buffer, binary.LittleEndian, d)
// d was written properly
fmt.Println(buffer.Bytes())
// try to read...
buffer = bytes.NewBuffer(buffer.Bytes())
var e = new(Data)
err := binary.Read(buffer, binary.LittleEndian, e)
fmt.Println(e, err)
}
Your best option would probably be to use the gob package and let your struct implement the GobDecoder and GobEncoder interfaces in order to serialize and deserialize private fields.
This would be safe, platform independent, and efficient. And you have to add those GobEncode and GobDecode functions only on structs with unexported fields, which means you don't clutter the rest of your code.
func (d *Data) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := encoder.Encode(d.id)
if err!=nil {
return nil, err
}
err = encoder.Encode(d.name)
if err!=nil {
return nil, err
}
return w.Bytes(), nil
}
func (d *Data) GobDecode(buf []byte) error {
r := bytes.NewBuffer(buf)
decoder := gob.NewDecoder(r)
err := decoder.Decode(&d.id)
if err!=nil {
return err
}
return decoder.Decode(&d.name)
}
func main() {
d := Data{id: 7}
copy(d.name[:], []byte("tree"))
buffer := new(bytes.Buffer)
// writing
enc := gob.NewEncoder(buffer)
err := enc.Encode(d)
if err != nil {
log.Fatal("encode error:", err)
}
// reading
buffer = bytes.NewBuffer(buffer.Bytes())
e := new(Data)
dec := gob.NewDecoder(buffer)
err = dec.Decode(e)
fmt.Println(e, err)
}

Convert string to integer type in Go?

I'm trying to convert a string returned from flag.Arg(n) to an int. What is the idiomatic way to do this in Go?
For example strconv.Atoi.
Code:
package main
import (
"fmt"
"strconv"
)
func main() {
s := "123"
// string to int
i, err := strconv.Atoi(s)
if err != nil {
// ... handle error
panic(err)
}
fmt.Println(s, i)
}
Converting Simple strings
The easiest way is to use the strconv.Atoi() function.
Note that there are many other ways. For example fmt.Sscan() and strconv.ParseInt() which give greater flexibility as you can specify the base and bitsize for example. Also as noted in the documentation of strconv.Atoi():
Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
Here's an example using the mentioned functions (try it on the Go Playground):
flag.Parse()
s := flag.Arg(0)
if i, err := strconv.Atoi(s); err == nil {
fmt.Printf("i=%d, type: %T\n", i, i)
}
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
fmt.Printf("i=%d, type: %T\n", i, i)
}
var i int
if _, err := fmt.Sscan(s, &i); err == nil {
fmt.Printf("i=%d, type: %T\n", i, i)
}
Output (if called with argument "123"):
i=123, type: int
i=123, type: int64
i=123, type: int
Parsing Custom strings
There is also a handy fmt.Sscanf() which gives even greater flexibility as with the format string you can specify the number format (like width, base etc.) along with additional extra characters in the input string.
This is great for parsing custom strings holding a number. For example if your input is provided in a form of "id:00123" where you have a prefix "id:" and the number is fixed 5 digits, padded with zeros if shorter, this is very easily parsable like this:
s := "id:00123"
var i int
if _, err := fmt.Sscanf(s, "id:%5d", &i); err == nil {
fmt.Println(i) // Outputs 123
}
Here are three ways to parse strings into integers, from fastest runtime to slowest:
strconv.ParseInt(...) fastest
strconv.Atoi(...) still very fast
fmt.Sscanf(...) not terribly fast but most flexible
Here's a benchmark that shows usage and example timing for each function:
package main
import "fmt"
import "strconv"
import "testing"
var num = 123456
var numstr = "123456"
func BenchmarkStrconvParseInt(b *testing.B) {
num64 := int64(num)
for i := 0; i < b.N; i++ {
x, err := strconv.ParseInt(numstr, 10, 64)
if x != num64 || err != nil {
b.Error(err)
}
}
}
func BenchmarkAtoi(b *testing.B) {
for i := 0; i < b.N; i++ {
x, err := strconv.Atoi(numstr)
if x != num || err != nil {
b.Error(err)
}
}
}
func BenchmarkFmtSscan(b *testing.B) {
for i := 0; i < b.N; i++ {
var x int
n, err := fmt.Sscanf(numstr, "%d", &x)
if n != 1 || x != num || err != nil {
b.Error(err)
}
}
}
You can run it by saving as atoi_test.go and running go test -bench=. atoi_test.go.
goos: darwin
goarch: amd64
BenchmarkStrconvParseInt-8 100000000 17.1 ns/op
BenchmarkAtoi-8 100000000 19.4 ns/op
BenchmarkFmtSscan-8 2000000 693 ns/op
PASS
ok command-line-arguments 5.797s
Try this
import ("strconv")
value := "123"
number,err := strconv.ParseUint(value, 10, 32)
finalIntNum := int(number) //Convert uint64 To int
If you control the input data, you can use the mini version
package main
import (
"testing"
"strconv"
)
func Atoi (s string) int {
var (
n uint64
i int
v byte
)
for ; i < len(s); i++ {
d := s[i]
if '0' <= d && d <= '9' {
v = d - '0'
} else if 'a' <= d && d <= 'z' {
v = d - 'a' + 10
} else if 'A' <= d && d <= 'Z' {
v = d - 'A' + 10
} else {
n = 0; break
}
n *= uint64(10)
n += uint64(v)
}
return int(n)
}
func BenchmarkAtoi(b *testing.B) {
for i := 0; i < b.N; i++ {
in := Atoi("9999")
_ = in
}
}
func BenchmarkStrconvAtoi(b *testing.B) {
for i := 0; i < b.N; i++ {
in, _ := strconv.Atoi("9999")
_ = in
}
}
the fastest option (write your check if necessary). Result :
Path>go test -bench=. atoi_test.go
goos: windows
goarch: amd64
BenchmarkAtoi-2 100000000 14.6 ns/op
BenchmarkStrconvAtoi-2 30000000 51.2 ns/op
PASS
ok path 3.293s

How to efficiently concatenate strings in go

In Go, a string is a primitive type, which means it is read-only, and every manipulation of it will create a new string.
So if I want to concatenate strings many times without knowing the length of the resulting string, what's the best way to do it?
The naive way would be:
var s string
for i := 0; i < 1000; i++ {
s += getShortStringFromSomewhere()
}
return s
but that does not seem very efficient.
New Way:
From Go 1.10 there is a strings.Builder type, please take a look at this answer for more detail.
Old Way:
Use the bytes package. It has a Buffer type which implements io.Writer.
package main
import (
"bytes"
"fmt"
)
func main() {
var buffer bytes.Buffer
for i := 0; i < 1000; i++ {
buffer.WriteString("a")
}
fmt.Println(buffer.String())
}
This does it in O(n) time.
In Go 1.10+ there is strings.Builder, here.
A Builder is used to efficiently build a string using Write methods. It minimizes memory copying. The zero value is ready to use.
Example
It's almost the same with bytes.Buffer.
package main
import (
"strings"
"fmt"
)
func main() {
// ZERO-VALUE:
//
// It's ready to use from the get-go.
// You don't need to initialize it.
var sb strings.Builder
for i := 0; i < 1000; i++ {
sb.WriteString("a")
}
fmt.Println(sb.String())
}
Click to see this on the playground.
Supported Interfaces
StringBuilder's methods are being implemented with the existing interfaces in mind. So that you can switch to the new Builder type easily in your code.
Grow(int) -> bytes.Buffer#Grow
Len() int -> bytes.Buffer#Len
Reset() -> bytes.Buffer#Reset
String() string -> fmt.Stringer
Write([]byte) (int, error) -> io.Writer
WriteByte(byte) error -> io.ByteWriter
WriteRune(rune) (int, error) -> bufio.Writer#WriteRune - bytes.Buffer#WriteRune
WriteString(string) (int, error) -> io.stringWriter
Differences from bytes.Buffer
It can only grow or reset.
It has a copyCheck mechanism built-in that prevents accidentially copying it:
func (b *Builder) copyCheck() { ... }
In bytes.Buffer, one can access the underlying bytes like this: (*Buffer).Bytes().
strings.Builder prevents this problem.
Sometimes, this is not a problem though and desired instead.
For example: For the peeking behavior when the bytes are passed to an io.Reader etc.
bytes.Buffer.Reset() rewinds and reuses the underlying buffer whereas the strings.Builder.Reset() does not, it detaches the buffer.
Note
Do not copy a StringBuilder value as it caches the underlying data.
If you want to share a StringBuilder value, use a pointer to it.
Check out its source code for more details, here.
If you know the total length of the string that you're going to preallocate then the most efficient way to concatenate strings may be using the builtin function copy. If you don't know the total length before hand, do not use copy, and read the other answers instead.
In my tests, that approach is ~3x faster than using bytes.Buffer and much much faster (~12,000x) than using the operator +. Also, it uses less memory.
I've created a test case to prove this and here are the results:
BenchmarkConcat 1000000 64497 ns/op 502018 B/op 0 allocs/op
BenchmarkBuffer 100000000 15.5 ns/op 2 B/op 0 allocs/op
BenchmarkCopy 500000000 5.39 ns/op 0 B/op 0 allocs/op
Below is code for testing:
package main
import (
"bytes"
"strings"
"testing"
)
func BenchmarkConcat(b *testing.B) {
var str string
for n := 0; n < b.N; n++ {
str += "x"
}
b.StopTimer()
if s := strings.Repeat("x", b.N); str != s {
b.Errorf("unexpected result; got=%s, want=%s", str, s)
}
}
func BenchmarkBuffer(b *testing.B) {
var buffer bytes.Buffer
for n := 0; n < b.N; n++ {
buffer.WriteString("x")
}
b.StopTimer()
if s := strings.Repeat("x", b.N); buffer.String() != s {
b.Errorf("unexpected result; got=%s, want=%s", buffer.String(), s)
}
}
func BenchmarkCopy(b *testing.B) {
bs := make([]byte, b.N)
bl := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
bl += copy(bs[bl:], "x")
}
b.StopTimer()
if s := strings.Repeat("x", b.N); string(bs) != s {
b.Errorf("unexpected result; got=%s, want=%s", string(bs), s)
}
}
// Go 1.10
func BenchmarkStringBuilder(b *testing.B) {
var strBuilder strings.Builder
b.ResetTimer()
for n := 0; n < b.N; n++ {
strBuilder.WriteString("x")
}
b.StopTimer()
if s := strings.Repeat("x", b.N); strBuilder.String() != s {
b.Errorf("unexpected result; got=%s, want=%s", strBuilder.String(), s)
}
}
If you have a string slice that you want to efficiently convert to a string then you can use this approach. Otherwise, take a look at the other answers.
There is a library function in the strings package called Join:
http://golang.org/pkg/strings/#Join
A look at the code of Join shows a similar approach to Append function Kinopiko wrote: https://golang.org/src/strings/strings.go#L420
Usage:
import (
"fmt";
"strings";
)
func main() {
s := []string{"this", "is", "a", "joined", "string\n"};
fmt.Printf(strings.Join(s, " "));
}
$ ./test.bin
this is a joined string
I just benchmarked the top answer posted above in my own code (a recursive tree walk) and the simple concat operator is actually faster than the BufferString.
func (r *record) String() string {
buffer := bytes.NewBufferString("");
fmt.Fprint(buffer,"(",r.name,"[")
for i := 0; i < len(r.subs); i++ {
fmt.Fprint(buffer,"\t",r.subs[i])
}
fmt.Fprint(buffer,"]",r.size,")\n")
return buffer.String()
}
This took 0.81 seconds, whereas the following code:
func (r *record) String() string {
s := "(\"" + r.name + "\" ["
for i := 0; i < len(r.subs); i++ {
s += r.subs[i].String()
}
s += "] " + strconv.FormatInt(r.size,10) + ")\n"
return s
}
only took 0.61 seconds. This is probably due to the overhead of creating the new BufferString.
Update: I also benchmarked the join function and it ran in 0.54 seconds.
func (r *record) String() string {
var parts []string
parts = append(parts, "(\"", r.name, "\" [" )
for i := 0; i < len(r.subs); i++ {
parts = append(parts, r.subs[i].String())
}
parts = append(parts, strconv.FormatInt(r.size,10), ")\n")
return strings.Join(parts,"")
}
package main
import (
"fmt"
)
func main() {
var str1 = "string1"
var str2 = "string2"
out := fmt.Sprintf("%s %s ",str1, str2)
fmt.Println(out)
}
This is the fastest solution that does not require
you to know or calculate the overall buffer size first:
var data []byte
for i := 0; i < 1000; i++ {
data = append(data, getShortStringFromSomewhere()...)
}
return string(data)
By my benchmark, it's 20% slower than the copy solution (8.1ns per
append rather than 6.72ns) but still 55% faster than using bytes.Buffer.
You could create a big slice of bytes and copy the bytes of the short strings into it using string slices. There is a function given in "Effective Go":
func Append(slice, data[]byte) []byte {
l := len(slice);
if l + len(data) > cap(slice) { // reallocate
// Allocate double what's needed, for future growth.
newSlice := make([]byte, (l+len(data))*2);
// Copy data (could use bytes.Copy()).
for i, c := range slice {
newSlice[i] = c
}
slice = newSlice;
}
slice = slice[0:l+len(data)];
for i, c := range data {
slice[l+i] = c
}
return slice;
}
Then when the operations are finished, use string ( ) on the big slice of bytes to convert it into a string again.
Note added in 2018
From Go 1.10 there is a strings.Builder type, please take a look at this answer for more detail.
Pre-201x answer
The benchmark code of #cd1 and other answers are wrong. b.N is not supposed to be set in benchmark function. It's set by the go test tool dynamically to determine if the execution time of the test is stable.
A benchmark function should run the same test b.N times and the test inside the loop should be the same for each iteration. So I fix it by adding an inner loop. I also add benchmarks for some other solutions:
package main
import (
"bytes"
"strings"
"testing"
)
const (
sss = "xfoasneobfasieongasbg"
cnt = 10000
)
var (
bbb = []byte(sss)
expected = strings.Repeat(sss, cnt)
)
func BenchmarkCopyPreAllocate(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
bs := make([]byte, cnt*len(sss))
bl := 0
for i := 0; i < cnt; i++ {
bl += copy(bs[bl:], sss)
}
result = string(bs)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkAppendPreAllocate(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, cnt*len(sss))
for i := 0; i < cnt; i++ {
data = append(data, sss...)
}
result = string(data)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkBufferPreAllocate(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
buf := bytes.NewBuffer(make([]byte, 0, cnt*len(sss)))
for i := 0; i < cnt; i++ {
buf.WriteString(sss)
}
result = buf.String()
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkCopy(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
for i := 0; i < cnt; i++ {
off := len(data)
if off+len(sss) > cap(data) {
temp := make([]byte, 2*cap(data)+len(sss))
copy(temp, data)
data = temp
}
data = data[0 : off+len(sss)]
copy(data[off:], sss)
}
result = string(data)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkAppend(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
data := make([]byte, 0, 64)
for i := 0; i < cnt; i++ {
data = append(data, sss...)
}
result = string(data)
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkBufferWrite(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
var buf bytes.Buffer
for i := 0; i < cnt; i++ {
buf.Write(bbb)
}
result = buf.String()
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkBufferWriteString(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
var buf bytes.Buffer
for i := 0; i < cnt; i++ {
buf.WriteString(sss)
}
result = buf.String()
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
func BenchmarkConcat(b *testing.B) {
var result string
for n := 0; n < b.N; n++ {
var str string
for i := 0; i < cnt; i++ {
str += sss
}
result = str
}
b.StopTimer()
if result != expected {
b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
}
}
Environment is OS X 10.11.6, 2.2 GHz Intel Core i7
Test results:
BenchmarkCopyPreAllocate-8 20000 84208 ns/op 425984 B/op 2 allocs/op
BenchmarkAppendPreAllocate-8 10000 102859 ns/op 425984 B/op 2 allocs/op
BenchmarkBufferPreAllocate-8 10000 166407 ns/op 426096 B/op 3 allocs/op
BenchmarkCopy-8 10000 160923 ns/op 933152 B/op 13 allocs/op
BenchmarkAppend-8 10000 175508 ns/op 1332096 B/op 24 allocs/op
BenchmarkBufferWrite-8 10000 239886 ns/op 933266 B/op 14 allocs/op
BenchmarkBufferWriteString-8 10000 236432 ns/op 933266 B/op 14 allocs/op
BenchmarkConcat-8 10 105603419 ns/op 1086685168 B/op 10000 allocs/op
Conclusion:
CopyPreAllocate is the fastest way; AppendPreAllocate is pretty close to No.1, but it's easier to write the code.
Concat has really bad performance both for speed and memory usage. Don't use it.
Buffer#Write and Buffer#WriteString are basically the same in speed, contrary to what #Dani-Br said in the comment. Considering string is indeed []byte in Go, it makes sense.
bytes.Buffer basically use the same solution as Copy with extra book keeping and other stuff.
Copy and Append use a bootstrap size of 64, the same as bytes.Buffer
Append use more memory and allocs, I think it's related to the grow algorithm it use. It's not growing memory as fast as bytes.Buffer
Suggestion:
For simple task such as what OP wants, I would use Append or AppendPreAllocate. It's fast enough and easy to use.
If need to read and write the buffer at the same time, use bytes.Buffer of course. That's what it's designed for.
My original suggestion was
s12 := fmt.Sprint(s1,s2)
But above answer using bytes.Buffer - WriteString() is the most efficient way.
My initial suggestion uses reflection and a type switch. See (p *pp) doPrint and (p *pp) printArg
There is no universal Stringer() interface for basic types, as I had naively thought.
At least though, Sprint() internally uses a bytes.Buffer. Thus
`s12 := fmt.Sprint(s1,s2,s3,s4,...,s1000)`
is acceptable in terms of memory allocations.
=> Sprint() concatenation can be used for quick debug output.
=> Otherwise use bytes.Buffer ... WriteString
Expanding on cd1's answer:
You might use append() instead of copy().
append() makes ever bigger advance provisions, costing a little more memory, but saving time.
I added two more benchmarks at the top of yours.
Run locally with
go test -bench=. -benchtime=100ms
On my thinkpad T400s it yields:
BenchmarkAppendEmpty 50000000 5.0 ns/op
BenchmarkAppendPrealloc 50000000 3.5 ns/op
BenchmarkCopy 20000000 10.2 ns/op
This is actual version of benchmark provided by #cd1 (Go 1.8, linux x86_64) with the fixes of bugs mentioned by #icza and #PickBoy.
Bytes.Buffer is only 7 times faster than direct string concatenation via + operator.
package performance_test
import (
"bytes"
"fmt"
"testing"
)
const (
concatSteps = 100
)
func BenchmarkConcat(b *testing.B) {
for n := 0; n < b.N; n++ {
var str string
for i := 0; i < concatSteps; i++ {
str += "x"
}
}
}
func BenchmarkBuffer(b *testing.B) {
for n := 0; n < b.N; n++ {
var buffer bytes.Buffer
for i := 0; i < concatSteps; i++ {
buffer.WriteString("x")
}
}
}
Timings:
BenchmarkConcat-4 300000 6869 ns/op
BenchmarkBuffer-4 1000000 1186 ns/op
goutils.JoinBetween
func JoinBetween(in []string, separator string, startIndex, endIndex int) string {
if in == nil {
return ""
}
noOfItems := endIndex - startIndex
if noOfItems <= 0 {
return EMPTY
}
var builder strings.Builder
for i := startIndex; i < endIndex; i++ {
if i > startIndex {
builder.WriteString(separator)
}
builder.WriteString(in[i])
}
return builder.String()
}
I do it using the following :-
package main
import (
"fmt"
"strings"
)
func main (){
concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator.
fmt.Println(concatenation) //abc
}
package main
import (
"fmt"
)
func main() {
var str1 = "string1"
var str2 = "string2"
result := make([]byte, 0)
result = append(result, []byte(str1)...)
result = append(result, []byte(str2)...)
result = append(result, []byte(str1)...)
result = append(result, []byte(str2)...)
fmt.Println(string(result))
}
Simple and easy to digest solution. Details in the comments.
Copy overwrites the elements of slice. We are slicing single-single element and overwriting it.
package main
import (
"fmt"
)
var N int = 100000
func main() {
slice1 := make([]rune, N, N)
//Efficient with fast performance, Need pre-allocated memory
//We can add a check if we reached the limit then increase capacity
//using append, but would be fined for data copying to new array. Also append happens after the length of current slice.
for i := 0; i < N; i++ {
copy(slice1[i:i+1], []rune{'N'})
}
fmt.Println(slice1)
//Simple but fast solution, Every time the slice capacity is reached we get a fine of effort that goes
//in copying data to new array
slice2 := []rune{}
for i := 0; i <= N; i++ {
slice2 = append(slice2, 'N')
}
fmt.Println(slice2)
}
benchmark result with memory allocation statistics. check benchmark code at github.
use strings.Builder to optimize performance.
go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/hechen0/goexp/exps
BenchmarkConcat-8 1000000 60213 ns/op 503992 B/op 1 allocs/op
BenchmarkBuffer-8 100000000 11.3 ns/op 2 B/op 0 allocs/op
BenchmarkCopy-8 300000000 4.76 ns/op 0 B/op 0 allocs/op
BenchmarkStringBuilder-8 1000000000 4.14 ns/op 6 B/op 0 allocs/op
PASS
ok github.com/hechen0/goexp/exps 70.071s
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))
strings.Join() from the "strings" package
If you have a type mismatch(like if you are trying to join an int and a string), you do RANDOMTYPE (thing you want to change)
EX:
package main
import (
"fmt"
"strings"
)
var intEX = 0
var stringEX = "hello all you "
var stringEX2 = "people in here"
func main() {
s := []string{stringEX, stringEX2}
fmt.Println(strings.Join(s, ""))
}
Output :
hello all you people in here

Resources