Skip to content

Commit

Permalink
Merge pull request #11 from micln/bigint
Browse files Browse the repository at this point in the history
Feature/add gxbig.Integer
  • Loading branch information
wongoo authored Dec 3, 2019
2 parents 7368eb9 + 5ac4ecf commit fa878fb
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 3 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,3 @@ classes
# vim
*.swp

# go mod
go.mod
go.sum
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ language: go
go:
- "1.11"
- "1.12"
- "1.13.x"

script:
- go fmt ./... && [[ -z `git status -s` ]]
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/dubbogo/gost

require (
github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.4.0
)

go 1.11
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
113 changes: 113 additions & 0 deletions math/big/integer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package gxbig

import (
"encoding/binary"
"fmt"
"math/big"
)

// Integer represents a integer value.
type Integer struct {
value big.Int

// used in hessian encoding/decoding
// You Should not use it in go
Signum int32
Mag []int

// Deprecated: compatible with java8 serialize
FirstNonzeroIntNum int
LowestSetBit int
BitLength int
BitCount int
}

func (Integer) JavaClassName() string {
return "java.math.BigInteger"
}

// FromString set data from a 10-bases number
func (i *Integer) FromString(s string) (err error) {
_, ok := i.value.SetString(s, 10)
if !ok {
err = fmt.Errorf("'%s' is not a 10-based number", s)
}
return
}

// FromSignAndMag set data from a array of big-endian unsigned uint32, it's used in hessian decoding
// @see https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html#BigInteger-int-byte:A-
func (i *Integer) FromSignAndMag(signum int32, mag []int) {
if signum == 0 && len(mag) == 0 {
return
}

bytes := make([]byte, 4*len(mag))
for j := 0; j < len(mag); j++ {
binary.BigEndian.PutUint32(bytes[j*4:(j+1)*4], uint32(mag[j]))
}
i.value.SetBytes(bytes)

if signum == -1 {
i.value.Neg(&i.value)
}
}

// GetSignAndMag is used in hessian encoding
func (i *Integer) GetSignAndMag() (signum int32, mag []int) {
signum = int32(i.value.Sign())

bytes := i.value.Bytes()
outOf4 := len(bytes) % 4
if outOf4 > 0 {
bytes = append(make([]byte, 4-outOf4), bytes...)
}

size := len(bytes) / 4

mag = make([]int, size)

for i := 0; i < size; i++ {
mag[i] = int(binary.BigEndian.Uint32(bytes[i*4 : (i+1)*4]))
}

return
}

func (i *Integer) Value() *big.Int {
return &i.value
}

func (i *Integer) SetValue(value *big.Int) {
i.value = *value
}

func (i *Integer) String() string { return i.value.String() }

func (i *Integer) Format(s fmt.State, ch rune) { i.value.Format(s, ch) }

func (i *Integer) GobEncode() ([]byte, error) { return i.value.GobEncode() }
func (i *Integer) GobDecode(buf []byte) error { return i.value.GobDecode(buf) }

func (i *Integer) MarshalText() (text []byte, err error) { return i.value.MarshalText() }
func (i *Integer) UnmarshalText(text []byte) error { return i.value.UnmarshalText(text) }

func (i *Integer) MarshalJSON() ([]byte, error) { return i.value.MarshalJSON() }
func (i *Integer) UnmarshalJSON(text []byte) error { return i.value.UnmarshalJSON(text) }
126 changes: 126 additions & 0 deletions math/big/integer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package gxbig

import (
"encoding/json"
"reflect"
"testing"
)

func TestInteger(t *testing.T) {
tests := []struct {
name string
src string
wantString string
wantErr bool
}{
{`ten`, `10`, `10`, false},
{`-ten`, `-10`, `-10`, false},
{`30digits`, `123456789012345678901234567890`, `123456789012345678901234567890`, false},
{`-37digits`, `-3987683987354747618711421180841033728`, `-3987683987354747618711421180841033728`, false},
{`1x2x3x4`, `79228162551157825753847955460`, `79228162551157825753847955460`, false},
{`invalid-x010`, `x010`, ``, true},
{`invalid-a010`, `a010`, ``, true},
{`invalid-10x`, `10x`, ``, true},
{`invalid-010x`, `010x`, ``, true},
{`special-010`, `010`, `10`, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &Integer{}
err := i.FromString(tt.src)
if (err != nil) != tt.wantErr {
t.Errorf("FromString() error = %v, wantErr %v", err, tt.wantErr)
return
}

if err == nil && i.String() != tt.wantString {
t.Errorf("String() got %v, want %v", i.String(), tt.wantString)
}
})
}
}

func TestInteger_FromSignAndMag(t *testing.T) {
type args struct {
signum int32
mag []int
}
tests := []struct {
name string
digit string
args args
}{
{`0`, `0`, args{0, []int{}}},
{`1`, `1`, args{1, []int{1}}},
{`2147483647`, `2147483647`, args{1, []int{2147483647}}},
{`4294967295`, `4294967295`, args{1, []int{4294967295}}},
{`4294967296`, `4294967296`, args{1, []int{1, 0}}},
{`4294967298`, `4294967298`, args{1, []int{1, 2}}},
// these case is build by python
{`1x2x3`, `18446744082299486211`, args{1, []int{1, 2, 3}}},
{`1x2x3x4`, `79228162551157825753847955460`, args{1, []int{1, 2, 3, 4}}},
{`(((1<<32)-1)<<32)|(1<<10)`, `18446744069414585344`, args{1, []int{1<<32 - 1, 1 << 10}}},
{`(((1<<24)-1)<<32)|(1<<10)`, `72057589742961664`, args{1, []int{1<<24 - 1, 1 << 10}}},
{`(((1<<16)-1)<<32)|(1<<10)`, `281470681744384`, args{1, []int{1<<16 - 1, 0x400}}},
{`(((1<<8)-1)<<32)|(1<<10)`, `1095216661504`, args{1, []int{0xFF, 0x400}}},
{`maxuint64=(1<<64)-1`, `18446744073709551615`, args{1, []int{1<<32 - 1, 0xFFFFFFFF}}},
{`(1<<64)`, `18446744073709551616`, args{1, []int{1, 0, 0}}},
{`0x123456781234567812345678`, `5634002657842756053938493048`, args{1, []int{0x12345678, 0x12345678, 0x12345678}}},
{`-1`, `-1`, args{-1, []int{1}}},
{`-4294967296`, `-4294967296`, args{-1, []int{1, 0}}},
{`-4294967298`, `-4294967298`, args{-1, []int{1, 2}}},
{`-1x2x3`, `-18446744082299486211`, args{-1, []int{1, 2, 3}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &Integer{}
i.FromSignAndMag(tt.args.signum, tt.args.mag)
if i.String() != tt.digit {
t.Errorf("digit %s got = %s", tt.digit, i.String())
}

s := &Integer{}
err := s.FromString(tt.digit)
if err != nil {
t.Error("FromString error = ", err)
}

sign, mag := s.GetSignAndMag()
if !(sign == tt.args.signum && reflect.DeepEqual(mag, tt.args.mag)) {
t.Error("want ", tt.args.signum, tt.args.mag,
"got", sign, mag)
}
})
}
}

func TestInteger_Json(t *testing.T) {
i := new(Integer)
i.FromString(`1234567`)
bytes, err := json.Marshal(i.String())
if err != nil || string(bytes) != `"1234567"` {
t.Error(string(bytes), err)
}

err = json.Unmarshal([]byte(`345`), i)
if err != nil || i.String() != `345` {
t.Error(i, err)
}
}

0 comments on commit fa878fb

Please sign in to comment.