mirror of
https://github.com/miniflux/v2.git
synced 2025-08-16 18:01:37 +00:00
First commit
This commit is contained in:
commit
8ffb773f43
2121 changed files with 1118910 additions and 0 deletions
251
vendor/github.com/tdewolff/parse/strconv/float.go
generated
vendored
Normal file
251
vendor/github.com/tdewolff/parse/strconv/float.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
|||
package strconv // import "github.com/tdewolff/parse/strconv"
|
||||
|
||||
import "math"
|
||||
|
||||
var float64pow10 = []float64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22,
|
||||
}
|
||||
|
||||
// Float parses a byte-slice and returns the float it represents.
|
||||
// If an invalid character is encountered, it will stop there.
|
||||
func ParseFloat(b []byte) (float64, int) {
|
||||
i := 0
|
||||
neg := false
|
||||
if i < len(b) && (b[i] == '+' || b[i] == '-') {
|
||||
neg = b[i] == '-'
|
||||
i++
|
||||
}
|
||||
|
||||
dot := -1
|
||||
trunk := -1
|
||||
n := uint64(0)
|
||||
for ; i < len(b); i++ {
|
||||
c := b[i]
|
||||
if c >= '0' && c <= '9' {
|
||||
if trunk == -1 {
|
||||
if n > math.MaxUint64/10 {
|
||||
trunk = i
|
||||
} else {
|
||||
n *= 10
|
||||
n += uint64(c - '0')
|
||||
}
|
||||
}
|
||||
} else if dot == -1 && c == '.' {
|
||||
dot = i
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
f := float64(n)
|
||||
if neg {
|
||||
f = -f
|
||||
}
|
||||
|
||||
mantExp := int64(0)
|
||||
if dot != -1 {
|
||||
if trunk == -1 {
|
||||
trunk = i
|
||||
}
|
||||
mantExp = int64(trunk - dot - 1)
|
||||
} else if trunk != -1 {
|
||||
mantExp = int64(trunk - i)
|
||||
}
|
||||
expExp := int64(0)
|
||||
if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
|
||||
i++
|
||||
if e, expLen := ParseInt(b[i:]); expLen > 0 {
|
||||
expExp = e
|
||||
i += expLen
|
||||
}
|
||||
}
|
||||
exp := expExp - mantExp
|
||||
|
||||
// copied from strconv/atof.go
|
||||
if exp == 0 {
|
||||
return f, i
|
||||
} else if exp > 0 && exp <= 15+22 { // int * 10^k
|
||||
// If exponent is big but number of digits is not,
|
||||
// can move a few zeros into the integer part.
|
||||
if exp > 22 {
|
||||
f *= float64pow10[exp-22]
|
||||
exp = 22
|
||||
}
|
||||
if f <= 1e15 && f >= -1e15 {
|
||||
return f * float64pow10[exp], i
|
||||
}
|
||||
} else if exp < 0 && exp >= -22 { // int / 10^k
|
||||
return f / float64pow10[-exp], i
|
||||
}
|
||||
f *= math.Pow10(int(-mantExp))
|
||||
return f * math.Pow10(int(expExp)), i
|
||||
}
|
||||
|
||||
const log2 = 0.301029995
|
||||
const int64maxlen = 18
|
||||
|
||||
func float64exp(f float64) int {
|
||||
exp2 := 0
|
||||
if f != 0.0 {
|
||||
x := math.Float64bits(f)
|
||||
exp2 = int(x>>(64-11-1))&0x7FF - 1023 + 1
|
||||
}
|
||||
|
||||
exp10 := float64(exp2) * log2
|
||||
if exp10 < 0 {
|
||||
exp10 -= 1.0
|
||||
}
|
||||
return int(exp10)
|
||||
}
|
||||
|
||||
func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||
if math.IsNaN(f) || math.IsInf(f, 0) {
|
||||
return b, false
|
||||
} else if prec >= int64maxlen {
|
||||
return b, false
|
||||
}
|
||||
|
||||
neg := false
|
||||
if f < 0.0 {
|
||||
f = -f
|
||||
neg = true
|
||||
}
|
||||
if prec == -1 {
|
||||
prec = int64maxlen - 1
|
||||
}
|
||||
prec -= float64exp(f) // number of digits in front of the dot
|
||||
f *= math.Pow10(prec)
|
||||
|
||||
// calculate mantissa and exponent
|
||||
mant := int64(f)
|
||||
mantLen := LenInt(mant)
|
||||
mantExp := mantLen - prec - 1
|
||||
if mant == 0 {
|
||||
return append(b, '0'), true
|
||||
}
|
||||
|
||||
// expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop
|
||||
exp := 0
|
||||
expLen := 0
|
||||
if mantExp > 0 {
|
||||
// positive exponent is determined in the loop below
|
||||
// but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone,
|
||||
// since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros
|
||||
if prec < 0 {
|
||||
exp = mantExp
|
||||
}
|
||||
expLen = 1 + LenInt(int64(exp)) // e + digits
|
||||
} else if mantExp < -3 {
|
||||
exp = mantExp
|
||||
expLen = 2 + LenInt(int64(exp)) // e + minus + digits
|
||||
} else if mantExp < -1 {
|
||||
mantLen += -mantExp - 1 // extra zero between dot and first digit
|
||||
}
|
||||
|
||||
// reserve space in b
|
||||
i := len(b)
|
||||
maxLen := 1 + mantLen + expLen // dot + mantissa digits + exponent
|
||||
if neg {
|
||||
maxLen++
|
||||
}
|
||||
if i+maxLen > cap(b) {
|
||||
b = append(b, make([]byte, maxLen)...)
|
||||
} else {
|
||||
b = b[:i+maxLen]
|
||||
}
|
||||
|
||||
// write to string representation
|
||||
if neg {
|
||||
b[i] = '-'
|
||||
i++
|
||||
}
|
||||
|
||||
// big conversion loop, start at the end and move to the front
|
||||
// initially print trailing zeros and remove them later on
|
||||
// for example if the first non-zero digit is three positions in front of the dot, it will overwrite the zeros with a positive exponent
|
||||
zero := true
|
||||
last := i + mantLen // right-most position of digit that is non-zero + dot
|
||||
dot := last - prec - exp // position of dot
|
||||
j := last
|
||||
for mant > 0 {
|
||||
if j == dot {
|
||||
b[j] = '.'
|
||||
j--
|
||||
}
|
||||
newMant := mant / 10
|
||||
digit := mant - 10*newMant
|
||||
if zero && digit > 0 {
|
||||
// first non-zero digit, if we are still behind the dot we can trim the end to this position
|
||||
// otherwise trim to the dot (including the dot)
|
||||
if j > dot {
|
||||
i = j + 1
|
||||
// decrease negative exponent further to get rid of dot
|
||||
if exp < 0 {
|
||||
newExp := exp - (j - dot)
|
||||
// getting rid of the dot shouldn't lower the exponent to more digits (e.g. -9 -> -10)
|
||||
if LenInt(int64(newExp)) == LenInt(int64(exp)) {
|
||||
exp = newExp
|
||||
dot = j
|
||||
j--
|
||||
i--
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = dot
|
||||
}
|
||||
last = j
|
||||
zero = false
|
||||
}
|
||||
b[j] = '0' + byte(digit)
|
||||
j--
|
||||
mant = newMant
|
||||
}
|
||||
|
||||
if j > dot {
|
||||
// extra zeros behind the dot
|
||||
for j > dot {
|
||||
b[j] = '0'
|
||||
j--
|
||||
}
|
||||
b[j] = '.'
|
||||
} else if last+3 < dot {
|
||||
// add positive exponent because we have 3 or more zeros in front of the dot
|
||||
i = last + 1
|
||||
exp = dot - last - 1
|
||||
} else if j == dot {
|
||||
// handle 0.1
|
||||
b[j] = '.'
|
||||
}
|
||||
|
||||
// exponent
|
||||
if exp != 0 {
|
||||
if exp == 1 {
|
||||
b[i] = '0'
|
||||
i++
|
||||
} else if exp == 2 {
|
||||
b[i] = '0'
|
||||
b[i+1] = '0'
|
||||
i += 2
|
||||
} else {
|
||||
b[i] = 'e'
|
||||
i++
|
||||
if exp < 0 {
|
||||
b[i] = '-'
|
||||
i++
|
||||
exp = -exp
|
||||
}
|
||||
i += LenInt(int64(exp))
|
||||
j := i
|
||||
for exp > 0 {
|
||||
newExp := exp / 10
|
||||
digit := exp - 10*newExp
|
||||
j--
|
||||
b[j] = '0' + byte(digit)
|
||||
exp = newExp
|
||||
}
|
||||
}
|
||||
}
|
||||
return b[:i], true
|
||||
}
|
196
vendor/github.com/tdewolff/parse/strconv/float_test.go
generated
vendored
Normal file
196
vendor/github.com/tdewolff/parse/strconv/float_test.go
generated
vendored
Normal file
|
@ -0,0 +1,196 @@
|
|||
package strconv // import "github.com/tdewolff/parse/strconv"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/tdewolff/test"
|
||||
)
|
||||
|
||||
func TestParseFloat(t *testing.T) {
|
||||
floatTests := []struct {
|
||||
f string
|
||||
expected float64
|
||||
}{
|
||||
{"5", 5},
|
||||
{"5.1", 5.1},
|
||||
{"-5.1", -5.1},
|
||||
{"5.1e-2", 5.1e-2},
|
||||
{"5.1e+2", 5.1e+2},
|
||||
{"0.0e1", 0.0e1},
|
||||
{"18446744073709551620", 18446744073709551620.0},
|
||||
{"1e23", 1e23},
|
||||
// TODO: hard to test due to float imprecision
|
||||
// {"1.7976931348623e+308", 1.7976931348623e+308)
|
||||
// {"4.9406564584124e-308", 4.9406564584124e-308)
|
||||
}
|
||||
for _, tt := range floatTests {
|
||||
f, n := ParseFloat([]byte(tt.f))
|
||||
test.That(t, n == len(tt.f), "parsed", n, "characters instead for", tt.f)
|
||||
test.That(t, f == tt.expected, "return", tt.expected, "for", tt.f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendFloat(t *testing.T) {
|
||||
floatTests := []struct {
|
||||
f float64
|
||||
prec int
|
||||
expected string
|
||||
}{
|
||||
{0, 6, "0"},
|
||||
{1, 6, "1"},
|
||||
{9, 6, "9"},
|
||||
{9.99999, 6, "9.99999"},
|
||||
{123, 6, "123"},
|
||||
{0.123456, 6, ".123456"},
|
||||
{0.066, 6, ".066"},
|
||||
{0.0066, 6, ".0066"},
|
||||
{12e2, 6, "1200"},
|
||||
{12e3, 6, "12e3"},
|
||||
{0.1, 6, ".1"},
|
||||
{0.001, 6, ".001"},
|
||||
{0.0001, 6, "1e-4"},
|
||||
{-1, 6, "-1"},
|
||||
{-123, 6, "-123"},
|
||||
{-123.456, 6, "-123.456"},
|
||||
{-12e3, 6, "-12e3"},
|
||||
{-0.1, 6, "-.1"},
|
||||
{-0.0001, 6, "-1e-4"},
|
||||
{0.000100009, 10, "100009e-9"},
|
||||
{0.0001000009, 10, "1.000009e-4"},
|
||||
{1e18, 0, "1e18"},
|
||||
//{1e19, 0, "1e19"},
|
||||
//{1e19, 18, "1e19"},
|
||||
{1e1, 0, "10"},
|
||||
{1e2, 1, "100"},
|
||||
{1e3, 2, "1e3"},
|
||||
{1e10, -1, "1e10"},
|
||||
{1e15, -1, "1e15"},
|
||||
{1e-5, 6, "1e-5"},
|
||||
{math.NaN(), 0, ""},
|
||||
{math.Inf(1), 0, ""},
|
||||
{math.Inf(-1), 0, ""},
|
||||
{0, 19, ""},
|
||||
{.000923361977200859392, -1, "9.23361977200859392e-4"},
|
||||
}
|
||||
for _, tt := range floatTests {
|
||||
f, _ := AppendFloat([]byte{}, tt.f, tt.prec)
|
||||
test.String(t, string(f), tt.expected, "for", tt.f)
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 22)
|
||||
AppendFloat(b, 12.34, -1)
|
||||
test.String(t, string(b[:5]), "12.34", "in buffer")
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
func TestAppendFloatRandom(t *testing.T) {
|
||||
N := int(1e6)
|
||||
if testing.Short() {
|
||||
N = 0
|
||||
}
|
||||
r := rand.New(rand.NewSource(99))
|
||||
//prec := 10
|
||||
for i := 0; i < N; i++ {
|
||||
f := r.ExpFloat64()
|
||||
//f = math.Floor(f*float64(prec)) / float64(prec)
|
||||
|
||||
b, _ := AppendFloat([]byte{}, f, -1)
|
||||
f2, _ := strconv.ParseFloat(string(b), 64)
|
||||
if math.Abs(f-f2) > 1e-6 {
|
||||
fmt.Println("Bad:", f, "!=", f2, "in", string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFloatToBytes1(b *testing.B) {
|
||||
r := []byte{} //make([]byte, 10)
|
||||
f := 123.456
|
||||
for i := 0; i < b.N; i++ {
|
||||
r = strconv.AppendFloat(r[:0], f, 'g', 6, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFloatToBytes2(b *testing.B) {
|
||||
r := make([]byte, 10)
|
||||
f := 123.456
|
||||
for i := 0; i < b.N; i++ {
|
||||
r, _ = AppendFloat(r[:0], f, 6)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkModf1(b *testing.B) {
|
||||
f := 123.456
|
||||
x := 0.0
|
||||
for i := 0; i < b.N; i++ {
|
||||
a, b := math.Modf(f)
|
||||
x += a + b
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkModf2(b *testing.B) {
|
||||
f := 123.456
|
||||
x := 0.0
|
||||
for i := 0; i < b.N; i++ {
|
||||
a := float64(int64(f))
|
||||
b := f - a
|
||||
x += a + b
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrintInt1(b *testing.B) {
|
||||
X := int64(123456789)
|
||||
n := LenInt(X)
|
||||
r := make([]byte, n)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x := X
|
||||
j := n
|
||||
for x > 0 {
|
||||
j--
|
||||
r[j] = '0' + byte(x%10)
|
||||
x /= 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrintInt2(b *testing.B) {
|
||||
X := int64(123456789)
|
||||
n := LenInt(X)
|
||||
r := make([]byte, n)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x := X
|
||||
j := n
|
||||
for x > 0 {
|
||||
j--
|
||||
newX := x / 10
|
||||
r[j] = '0' + byte(x-10*newX)
|
||||
x = newX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var int64pow10 = []int64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18,
|
||||
}
|
||||
|
||||
func BenchmarkPrintInt3(b *testing.B) {
|
||||
X := int64(123456789)
|
||||
n := LenInt(X)
|
||||
r := make([]byte, n)
|
||||
for i := 0; i < b.N; i++ {
|
||||
x := X
|
||||
j := 0
|
||||
for j < n {
|
||||
pow := int64pow10[n-j-1]
|
||||
tmp := x / pow
|
||||
r[j] = '0' + byte(tmp)
|
||||
j++
|
||||
x -= tmp * pow
|
||||
}
|
||||
}
|
||||
}
|
78
vendor/github.com/tdewolff/parse/strconv/int.go
generated
vendored
Normal file
78
vendor/github.com/tdewolff/parse/strconv/int.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package strconv // import "github.com/tdewolff/parse/strconv"
|
||||
|
||||
import "math"
|
||||
|
||||
// Int parses a byte-slice and returns the integer it represents.
|
||||
// If an invalid character is encountered, it will stop there.
|
||||
func ParseInt(b []byte) (int64, int) {
|
||||
i := 0
|
||||
neg := false
|
||||
if len(b) > 0 && (b[0] == '+' || b[0] == '-') {
|
||||
neg = b[0] == '-'
|
||||
i++
|
||||
}
|
||||
n := uint64(0)
|
||||
for i < len(b) {
|
||||
c := b[i]
|
||||
if n > math.MaxUint64/10 {
|
||||
return 0, 0
|
||||
} else if c >= '0' && c <= '9' {
|
||||
n *= 10
|
||||
n += uint64(c - '0')
|
||||
} else {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
if !neg && n > uint64(math.MaxInt64) || n > uint64(math.MaxInt64)+1 {
|
||||
return 0, 0
|
||||
} else if neg {
|
||||
return -int64(n), i
|
||||
}
|
||||
return int64(n), i
|
||||
}
|
||||
|
||||
func LenInt(i int64) int {
|
||||
if i < 0 {
|
||||
i = -i
|
||||
}
|
||||
switch {
|
||||
case i < 10:
|
||||
return 1
|
||||
case i < 100:
|
||||
return 2
|
||||
case i < 1000:
|
||||
return 3
|
||||
case i < 10000:
|
||||
return 4
|
||||
case i < 100000:
|
||||
return 5
|
||||
case i < 1000000:
|
||||
return 6
|
||||
case i < 10000000:
|
||||
return 7
|
||||
case i < 100000000:
|
||||
return 8
|
||||
case i < 1000000000:
|
||||
return 9
|
||||
case i < 10000000000:
|
||||
return 10
|
||||
case i < 100000000000:
|
||||
return 11
|
||||
case i < 1000000000000:
|
||||
return 12
|
||||
case i < 10000000000000:
|
||||
return 13
|
||||
case i < 100000000000000:
|
||||
return 14
|
||||
case i < 1000000000000000:
|
||||
return 15
|
||||
case i < 10000000000000000:
|
||||
return 16
|
||||
case i < 100000000000000000:
|
||||
return 17
|
||||
case i < 1000000000000000000:
|
||||
return 18
|
||||
}
|
||||
return 19
|
||||
}
|
95
vendor/github.com/tdewolff/parse/strconv/int_test.go
generated
vendored
Normal file
95
vendor/github.com/tdewolff/parse/strconv/int_test.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
package strconv // import "github.com/tdewolff/parse/strconv"
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/tdewolff/test"
|
||||
)
|
||||
|
||||
func TestParseInt(t *testing.T) {
|
||||
intTests := []struct {
|
||||
i string
|
||||
expected int64
|
||||
}{
|
||||
{"5", 5},
|
||||
{"99", 99},
|
||||
{"999", 999},
|
||||
{"-5", -5},
|
||||
{"+5", 5},
|
||||
{"9223372036854775807", 9223372036854775807},
|
||||
{"9223372036854775808", 0},
|
||||
{"-9223372036854775807", -9223372036854775807},
|
||||
{"-9223372036854775808", -9223372036854775808},
|
||||
{"-9223372036854775809", 0},
|
||||
{"18446744073709551620", 0},
|
||||
{"a", 0},
|
||||
}
|
||||
for _, tt := range intTests {
|
||||
i, _ := ParseInt([]byte(tt.i))
|
||||
test.That(t, i == tt.expected, "return", tt.expected, "for", tt.i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLenInt(t *testing.T) {
|
||||
lenIntTests := []struct {
|
||||
number int64
|
||||
expected int
|
||||
}{
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{10, 2},
|
||||
{99, 2},
|
||||
|
||||
// coverage
|
||||
{100, 3},
|
||||
{1000, 4},
|
||||
{10000, 5},
|
||||
{100000, 6},
|
||||
{1000000, 7},
|
||||
{10000000, 8},
|
||||
{100000000, 9},
|
||||
{1000000000, 10},
|
||||
{10000000000, 11},
|
||||
{100000000000, 12},
|
||||
{1000000000000, 13},
|
||||
{10000000000000, 14},
|
||||
{100000000000000, 15},
|
||||
{1000000000000000, 16},
|
||||
{10000000000000000, 17},
|
||||
{100000000000000000, 18},
|
||||
{1000000000000000000, 19},
|
||||
}
|
||||
for _, tt := range lenIntTests {
|
||||
test.That(t, LenInt(tt.number) == tt.expected, "return", tt.expected, "for", tt.number)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
var num []int64
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
for j := 0; j < 1000; j++ {
|
||||
num = append(num, rand.Int63n(1000))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLenIntLog(b *testing.B) {
|
||||
n := 0
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < 1000; j++ {
|
||||
n += int(math.Log10(math.Abs(float64(num[j])))) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLenIntSwitch(b *testing.B) {
|
||||
n := 0
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < 1000; j++ {
|
||||
n += LenInt(num[j])
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue