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

feat: native bigint using math/big.Int #764

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions examples/gno.land/p/demo/ufmt/ufmt.gno
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ func Sprintf(format string, args ...interface{}) string {
buf += v.String()
case string:
buf += v
case bigint:
buf += string(v)
default:
buf += "(unhandled)"
}
Expand All @@ -70,6 +72,8 @@ func Sprintf(format string, args ...interface{}) string {
buf += strconv.FormatUint(uint64(v), 10)
case uint64:
buf += strconv.FormatUint(v, 10)
case bigint:
buf += string(v)
default:
buf += "(unhandled)"
}
Expand Down
1 change: 1 addition & 0 deletions examples/gno.land/p/demo/ufmt/ufmt_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestSprintf(t *testing.T) {
{"uint [%d]", []interface{}{uint(42)}, "uint [42]"},
{"int64 [%d]", []interface{}{int64(42)}, "int64 [42]"},
{"uint64 [%d]", []interface{}{uint64(42)}, "uint64 [42]"},
{"bigint [%d]", []interface{}{bigint(42)}, "bigint [42]"},
{"bool [%t]", []interface{}{true}, "bool [true]"},
{"bool [%t]", []interface{}{false}, "bool [false]"},
{"invalid bool [%t]", []interface{}{"invalid"}, "invalid bool [(unhandled)]"},
Expand Down
13 changes: 13 additions & 0 deletions gno.land/pkg/sdk/vm/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import (
"encoding/base64"
"fmt"
"math/big"
"strconv"

gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
Expand Down Expand Up @@ -153,6 +154,18 @@
}
tv.SetUint64(u64)
return
case gno.BigintType:
if arg[0] == '+' {
panic("numbers cannot start with +")

Check warning on line 159 in gno.land/pkg/sdk/vm/convert.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/convert.go#L157-L159

Added lines #L157 - L159 were not covered by tests
}
bi, ok := big.NewInt(0).SetString(arg, 10)
if !ok {
panic(fmt.Sprintf(
"error parsing bigint %v", arg))

Check warning on line 164 in gno.land/pkg/sdk/vm/convert.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/convert.go#L161-L164

Added lines #L161 - L164 were not covered by tests
}

tv.SetBigInt(bi)
return

Check warning on line 168 in gno.land/pkg/sdk/vm/convert.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/convert.go#L167-L168

Added lines #L167 - L168 were not covered by tests
default:
panic(fmt.Sprintf("unexpected primitive type %s", bt.String()))
}
Expand Down
9 changes: 7 additions & 2 deletions gnovm/pkg/gnolang/gonative.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gnolang

import (
"fmt"
"math/big"
"reflect"
)

Expand Down Expand Up @@ -508,7 +509,9 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv
tv.SetFloat64(rv.Float())
}
case BigintKind:
panic("not yet implemented")
if lvl != 0 {
tv.SetBigInt(rv.Interface().(*big.Int))
}
case BigdecKind:
panic("not yet implemented")
case ArrayKind:
Expand Down Expand Up @@ -828,7 +831,7 @@ func gno2GoType(t Type) reflect.Type {
case Float64Type:
return reflect.TypeOf(float64(0))
case BigintType, UntypedBigintType:
panic("not yet implemented")
return reflect.TypeOf(big.NewInt(0))
case BigdecType, UntypedBigdecType:
panic("not yet implemented")
default:
Expand Down Expand Up @@ -1113,6 +1116,8 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) {
rv.SetFloat(float64(tv.GetFloat32()))
case Float64Type:
rv.SetFloat(tv.GetFloat64())
case BigintType, UntypedBigintType:
rv.Set(reflect.ValueOf(tv.GetBigInt()))
default:
panic(fmt.Sprintf(
"unexpected type %s",
Expand Down
6 changes: 3 additions & 3 deletions gnovm/pkg/gnolang/op_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -1109,9 +1109,9 @@ func shlAssign(lv, rv *TypedValue) {
case Uint64Type:
lv.SetUint64(lv.GetUint64() << rv.GetUint())
case BigintType, UntypedBigintType:
lb := lv.GetBigInt()
lb = big.NewInt(0).Lsh(lb, rv.GetUint())
lv.V = BigintValue{V: lb}
z := big.NewInt(0)
z.Lsh(lv.GetBigInt(), rv.GetUint())
lv.V = BigintValue{V: z}
default:
panic(fmt.Sprintf(
"operators << and <<= not defined for %s",
Expand Down
16 changes: 16 additions & 0 deletions gnovm/pkg/gnolang/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ func (tv TypedValue) Copy(alloc *Allocator) (cp TypedValue) {
case BigintValue:
cp.T = tv.T
cp.V = cv.Copy(alloc)
cp.N = tv.N
case *ArrayValue:
cp.T = tv.T
cp.V = cv.Copy(alloc)
Expand Down Expand Up @@ -1432,6 +1433,17 @@ func (tv *TypedValue) GetFloat64() float64 {
return *(*float64)(unsafe.Pointer(&tv.N))
}

func (tv *TypedValue) SetBigInt(bi *big.Int) {
if debug {
if tv.T.Kind() != BigintKind || isNative(tv.T) {
panic(fmt.Sprintf(
"TypedValue.SetBigInt() on type %s",
tv.T.String()))
}
}
tv.V = BigintValue{bi}
}

func (tv *TypedValue) GetBigInt() *big.Int {
if debug {
if tv.T != nil && tv.T.Kind() != BigintKind {
Expand Down Expand Up @@ -2439,6 +2451,10 @@ func defaultValue(alloc *Allocator, t Type) Value {
)
}
default:
switch t.Kind() {
case BigintKind:
return BigintValue{V: big.NewInt(0)}
}
return nil
}
}
Expand Down
63 changes: 63 additions & 0 deletions gnovm/pkg/gnolang/values_conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ GNO_CASE:
x := float64(tv.GetInt()) // XXX determinism?
tv.T = t
tv.SetFloat64(x)
case BigintKind:
tv.V = BigintValue{V: big.NewInt(tv.GetInt64())}
tv.T = t
case StringKind:
tv.V = alloc.NewString(string(rune(tv.GetInt())))
tv.T = t
Expand Down Expand Up @@ -315,6 +318,9 @@ GNO_CASE:
x := float64(tv.GetInt32()) // XXX determinism?
tv.T = t
tv.SetFloat64(x)
case BigintKind:
tv.V = BigintValue{V: big.NewInt(tv.GetInt64())}
tv.T = t
case StringKind:
tv.V = alloc.NewString(string(tv.GetInt32()))
tv.T = t
Expand Down Expand Up @@ -374,6 +380,9 @@ GNO_CASE:
x := float64(tv.GetInt64()) // XXX determinism?
tv.T = t
tv.SetFloat64(x)
case BigintKind:
tv.V = BigintValue{V: big.NewInt(tv.GetInt64())}
tv.T = t
case StringKind:
tv.V = alloc.NewString(string(rune(tv.GetInt64())))
tv.T = t
Expand Down Expand Up @@ -433,6 +442,9 @@ GNO_CASE:
x := float64(tv.GetUint()) // XXX determinism?
tv.T = t
tv.SetFloat64(x)
case BigintKind:
tv.V = BigintValue{V: big.NewInt(tv.GetInt64())}
tv.T = t
case StringKind:
tv.V = alloc.NewString(string(rune(tv.GetUint())))
tv.T = t
Expand Down Expand Up @@ -669,6 +681,9 @@ GNO_CASE:
x := float64(tv.GetUint64()) // XXX determinism?
tv.T = t
tv.SetFloat64(x)
case BigintKind:
tv.V = BigintValue{V: big.NewInt(tv.GetInt64())}
tv.T = t
case StringKind:
tv.V = alloc.NewString(string(rune(tv.GetUint64())))
tv.T = t
Expand Down Expand Up @@ -788,6 +803,40 @@ GNO_CASE:
"cannot convert %s to %s",
tvk.String(), k.String()))
}
case BigintKind:
switch k {
case IntKind, Int8Kind, Int16Kind, Int32Kind, UintKind, Uint8Kind, Uint16Kind, Uint32Kind:
panic(fmt.Sprintf(
"cannot convert %s to %s",
tvk.String(), k.String()))
case Int64Kind:
x := tv.GetBigInt()
if x.IsInt64() {
tv.T = t
tv.SetInt64(x.Int64())
} else {
panic(fmt.Sprintf(
"cannot convert %s to %s",
tvk.String(), k.String()))
}
case Uint64Kind:
x := tv.GetBigInt()
if x.IsUint64() {
tv.T = t
tv.SetUint64(x.Uint64())
} else {
panic(fmt.Sprintf(
"cannot convert %s to %s",
tvk.String(), k.String()))
}
case StringKind:
tv.T = t
tv.SetString(StringValue(tv.GetBigInt().String()))
default:
panic(fmt.Sprintf(
"cannot convert %s to %s",
tvk.String(), k.String()))
}
case StringKind:
bt := baseOf(t)
switch cbt := bt.(type) {
Expand All @@ -809,6 +858,20 @@ GNO_CASE:
"cannot convert %s to %s",
tvk.String(), t.String()))
}
case PrimitiveType:
switch k {
case BigintKind:
bi := new(big.Int)
bi, ok := bi.SetString(tv.GetString(), 10)
if !ok {
panic(fmt.Sprintf(
"cannot convert %s to %s",
tvk.String(), k.String(),
))
}
tv.V = BigintValue{V: bi}
tv.T = t
}
default:
panic(fmt.Sprintf(
"cannot convert %s to %s",
Expand Down
61 changes: 61 additions & 0 deletions gnovm/tests/files/type40.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

var (
mi64 int64 = 123
mui64 uint64 = 123

mbi bigint = 123

max_uint64_plus_1 bigint = 18446744073709551616
max_int64_plus_1 bigint = 9223372036854775808
)

func main() {
// default value
var _zeroBig bigint
shouldEQ(_zeroBig, bigint(0))

// int64 > bigint
shouldEQ(bigint(mi64), mbi)

// uint64 > bigint
shouldEQ(bigint(mui64), mbi)

// bigint > int64
shouldEQ(int64(mbi), mi64)

// bigint(too big) > int64
// int64(max_int64_plus_1) // cannot convert BigintKind to Int64Kin

// bigint > uint64
shouldEQ(uint64(mbi), mui64)

// bigint(too big) > uint64
// uint64(max_uint64_plus_1) // cannot convert BigintKind to Int64Kind

// OpInc, OpDec
var _oneBig bigint = 1
_oneBig++
shouldEQ(_oneBig, bigint(2))

_oneBig--
shouldEQ(_oneBig, bigint(1))
}

func shouldEQ(a, b interface{}) bool {
if a == b {
println("true")
} else {
println("false")
}
return a == b
}

// Output:
// true
// true
// true
// true
// true
// true
// true
Loading