Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AppendInt function with positive/negative number convertion to string #90

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pkg: github.com/gofiber/utils
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz

```go
// go test -benchmem -run=^$ -bench=Benchmark_ -count=2
// go test -benchmem -run=^$ -bench=Benchmark_ -count=2

Benchmark_ToLowerBytes/fiber-12 29715831 36.44 ns/op 0 B/op 0 allocs/op
Benchmark_ToLowerBytes/fiber-12 33316479 36.28 ns/op 0 B/op 0 allocs/op
Expand Down Expand Up @@ -60,6 +60,19 @@ Benchmark_UnsafeBytes/default-12 52615048 22.33 ns/op
Benchmark_ToString-12 22981430 51.72 ns/op 40 B/op 2 allocs/op
Benchmark_ToString-12 22956476 52.93 ns/op 40 B/op 2 allocs/op

Benchmark_ItoA/fiber (pos_num)-12 186969812 6.25 ns/op 0 B/op 0 allocs/op
Benchmark_ItoA/fiber (pos_num)-12 193965686 6.16 ns/op 0 B/op 0 allocs/op
Benchmark_ItoA/strconv.Itoa (pos_num)-12 80716807 14.42 ns/op 4 B/op 1 allocs/op
Benchmark_ItoA/strconv.Itoa (pos_num)-12 80445802 14.85 ns/op 4 B/op 1 allocs/op
Benchmark_ItoA/strconv.FormatInt_(pos_num)-12 81137728 14.52 ns/op 4 B/op 1 allocs/op
Benchmark_ItoA/strconv.FormatInt_(pos_num)-12 81345360 14.51 ns/op 4 B/op 1 allocs/op
Benchmark_ItoA/fiber (neg_num)-12 192902048 6.18 ns/op 0 B/op 0 allocs/op
Benchmark_ItoA/fiber (neg_num)-12 194245189 6.14 ns/op 0 B/op 0 allocs/op
Benchmark_ItoA/strconv.Itoa (neg_num)-12 84505304 13.55 ns/op 5 B/op 1 allocs/op
Benchmark_ItoA/strconv.Itoa (neg_num)-12 82524801 13.54 ns/op 5 B/op 1 allocs/op
Benchmark_ItoA/strconv.FormatInt (neg_num)-12 84884136 13.63 ns/op 5 B/op 1 allocs/op
Benchmark_ItoA/strconv.FormatInt (neg_num)-12 85829492 13.63 ns/op 5 B/op 1 allocs/op

Benchmark_GetMIME/fiber-12 15782622 74.99 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/fiber-12 13992375 93.13 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/default-12 6825952 147.0 ns/op 0 B/op 0 allocs/op
Expand Down Expand Up @@ -100,6 +113,7 @@ Benchmark_CalculateTimestamp/fiber-12 1000000000 0.2634 ns/op
Benchmark_CalculateTimestamp/fiber-12 1000000000 0.2935 ns/op 0 B/op 0 allocs/op
Benchmark_CalculateTimestamp/default-12 15740576 73.79 ns/op 0 B/op 0 allocs/op
Benchmark_CalculateTimestamp/default-12 15789036 71.12 ns/op 0 B/op 0 allocs/op

```

See all the benchmarks under https://gofiber.github.io/utils/
31 changes: 31 additions & 0 deletions convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,34 @@ func ToString(arg any, timeFormat ...string) string {
return fmt.Sprint(arg)
}
}

// AppendInt appends the string representation of the int n to dst and returns the extended buffer.
func AppendInt(dst []byte, n int) []byte {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a separate function for AppendUint that uses uint as param, else we won't be able to handle values bigger than 2,147,483,647

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The math module in Golang defines these:

MaxUint32 = 1<<32 - 1           // 4294967295
MaxUint64 = 1<<64 - 1           // 18446744073709551615

isNegative := n < 0
if isNegative {
// Convert the number to positive
n = -n
}
Comment on lines +156 to +161
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness: Handle integer overflow for negative values.

When converting a negative integer to positive, be cautious of integer overflow. The edge case for n = math.MinInt should be handled.

if isNegative {
	if n == math.MinInt {
		// Handle the edge case for the smallest possible integer
		dst = append(dst, "-9223372036854775808"...)
		return dst
	}
	n = -n
}


var b [20]byte
buf := b[:]
i := len(buf)
var q int
for n >= 10 {
i--
q = n / 10
buf[i] = '0' + byte(n-q*10)
n = q
}
i--
buf[i] = '0' + byte(n)

if isNegative {
// add '-' in front of the number
dst = append(dst, '-')
}

dst = append(dst, buf[i:]...)

return dst
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
}
81 changes: 81 additions & 0 deletions convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package utils

import (
"reflect"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -232,6 +233,19 @@ func TestByteSize(t *testing.T) {
}
}

func Test_AppendInt(t *testing.T) {
t.Parallel()

dst := make([]byte, 0)

require.Equal(t, []byte("42"), AppendInt(dst, 42))
require.Equal(t, []byte("1500"), AppendInt(dst, 1500))
require.Equal(t, []byte("0"), AppendInt(dst, 0))
require.Equal(t, []byte("-1"), AppendInt(dst, -1))
require.Equal(t, []byte("-2"), AppendInt(dst, -2))
require.Equal(t, []byte("-4500"), AppendInt(dst, -4500))
}

// go test -v -run=^$ -bench=ToString -benchmem -count=4
func Benchmark_ToString(b *testing.B) {
for _, value := range dataTypeExamples {
Expand Down Expand Up @@ -295,3 +309,70 @@ func Benchmark_UnsafeString(b *testing.B) {
require.Equal(b, "Hello, World!", res)
})
}

// go test -v -run=^$ -bench=ItoA -benchmem -count=4
func Benchmark_ItoA(b *testing.B) {
number := 4242
number64 := int64(number)
numberString := "4242"
numberN := -4242
number64N := int64(numberN)
numberNString := "-4242"

var resB []byte
var resS string
b.Run("fiber (positiv number)", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()

for n := 0; n < b.N; n++ {
resB = AppendInt(resB[:0], number)
}
require.Equal(b, []byte(numberString), resB)
})

b.Run("default - strconv.Itoa (positiv number)", func(b *testing.B) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

positive*

b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
resS = strconv.Itoa(number)
}
require.Equal(b, numberString, resS)
})

b.Run("default - strconv.FormatInt (positiv number)", func(b *testing.B) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

positive*

b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
resS = strconv.FormatInt(number64, 10)
}
require.Equal(b, numberString, resS)
})

b.Run("fiber (negative number)", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
resB = AppendInt(resB[:0], numberN)
}
require.Equal(b, []byte(numberNString), resB)
})

b.Run("default - strconv.Itoa (negative number)", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
resS = strconv.Itoa(numberN)
}
require.Equal(b, numberNString, resS)
})

b.Run("default - strconv.FormatInt (negative number)", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
resS = strconv.FormatInt(number64N, 10)
}
require.Equal(b, numberNString, resS)
})
}
Loading