Skip to content

Commit 128b731

Browse files
authored
fix: properly support negative numerator for CBOR rational numbers (#793)
1 parent 68d9395 commit 128b731

File tree

2 files changed

+47
-10
lines changed

2 files changed

+47
-10
lines changed

cbor/tags.go

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package cbor
1616

1717
import (
18+
"fmt"
1819
"math/big"
1920
"reflect"
2021

@@ -93,29 +94,57 @@ type Rat struct {
9394
}
9495

9596
func (r *Rat) UnmarshalCBOR(cborData []byte) error {
96-
tmpRat := []uint64{}
97+
tmpRat := []any{}
9798
if _, err := Decode(cborData, &tmpRat); err != nil {
9899
return err
99100
}
100-
// Convert numerator and denominator to big.Int
101-
// It's necessary to do this to support num/denom larger than int64 (up to uint64)
101+
// Convert numerator to big.Int
102102
tmpNum := new(big.Int)
103-
tmpNum.SetUint64(tmpRat[0])
103+
switch v := tmpRat[0].(type) {
104+
case int64:
105+
tmpNum.SetInt64(v)
106+
case uint64:
107+
tmpNum.SetUint64(v)
108+
default:
109+
return fmt.Errorf("unsupported numerator type for cbor.Rat: %T", v)
110+
}
111+
// Convert denominator to big.Int
104112
tmpDenom := new(big.Int)
105-
tmpDenom.SetUint64(tmpRat[1])
113+
switch v := tmpRat[1].(type) {
114+
case int64:
115+
tmpDenom.SetInt64(v)
116+
case uint64:
117+
tmpDenom.SetUint64(v)
118+
default:
119+
return fmt.Errorf("unsupported demoninator type for cbor.Rat: %T", v)
120+
}
106121
// Create new big.Rat with num/denom set to big.Int values above
107122
r.Rat = new(big.Rat)
108123
r.Rat.SetFrac(tmpNum, tmpDenom)
109124
return nil
110125
}
111126

112127
func (r *Rat) MarshalCBOR() ([]byte, error) {
128+
tmpContent := make([]any, 2)
129+
// Numerator
130+
if r.Num().IsUint64() {
131+
tmpContent[0] = r.Num().Uint64()
132+
} else if r.Num().IsInt64() {
133+
tmpContent[0] = r.Num().Int64()
134+
} else {
135+
return nil, fmt.Errorf("numerator cannot be represented at int64/uint64")
136+
}
137+
// Denominator
138+
if r.Denom().IsUint64() {
139+
tmpContent[1] = r.Denom().Uint64()
140+
} else if r.Denom().IsInt64() {
141+
tmpContent[1] = r.Denom().Int64()
142+
} else {
143+
return nil, fmt.Errorf("numerator cannot be represented at int64/uint64")
144+
}
113145
tmpData := _cbor.Tag{
114-
Number: CborTagRational,
115-
Content: []uint64{
116-
r.Num().Uint64(),
117-
r.Denom().Uint64(),
118-
},
146+
Number: CborTagRational,
147+
Content: tmpContent,
119148
}
120149
return Encode(&tmpData)
121150
}

cbor/tags_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ var tagsTestDefs = []struct {
5454
},
5555
),
5656
},
57+
// 30([9223372036854775809, 10000000000000000000])
5758
{
5859
cborHex: "d81e821b80000000000000011b8ac7230489e80000",
5960
object: cbor.Rat{
@@ -63,6 +64,13 @@ var tagsTestDefs = []struct {
6364
),
6465
},
6566
},
67+
// 30([-1, 2])
68+
{
69+
cborHex: "d81e822002",
70+
object: cbor.Rat{
71+
Rat: big.NewRat(-1, 2),
72+
},
73+
},
6674
}
6775

6876
func TestTagsDecode(t *testing.T) {

0 commit comments

Comments
 (0)