-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvalues.go
178 lines (162 loc) · 4.92 KB
/
values.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package fnplot
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"math/big"
"reflect"
"strconv"
"github.com/pkg/errors"
)
// A Values is a collection of any type of value. A Values can be converted to a
// scalar value (a floating point number).
type Values []reflect.Value
func NewValues(args ...interface{}) Values {
values := make(Values, len(args))
for i := range args {
values[i] = reflect.ValueOf(args[i])
}
return values
}
// smallestInt returns the smallest fixed-size signed or unsigned integer value
// necessary to store the given variable-size signed integer value.
func smallestInt(x int) interface{} {
if x >= 0 {
return smallestUint(uint(x))
}
switch {
case x <= math.MaxInt8 && x >= math.MinInt8:
return int8(x)
case x <= math.MaxInt16 && x >= math.MinInt16:
return int16(x)
case x <= math.MaxInt32 && x >= math.MinInt32:
return int32(x)
}
return int64(x)
}
// smallestUint returns the smallest fixed-size unsigned integer value necessary
// to store the given variable-size unsigned integer value.
func smallestUint(x uint) interface{} {
switch {
case x <= math.MaxUint8:
return uint8(x)
case x <= math.MaxUint16:
return uint16(x)
case x <= math.MaxUint32:
return uint32(x)
}
return uint64(x)
}
func writeBinary(buf *bytes.Buffer, value reflect.Value) error {
if !value.IsValid() {
return nil
}
value = indirect(value)
// Unpack slice, array, and map types.
switch value.Type().Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < value.Len(); i++ {
err := writeBinary(buf, value.Index(i))
if err != nil {
return errors.WithMessage(
err,
"error writing binary for slice or array value at "+strconv.Itoa(i))
}
}
return nil
case reflect.Map:
for _, mapKey := range value.MapKeys() {
err := writeBinary(buf, mapKey)
if err != nil {
return errors.WithMessage(
err,
"error writing binary for map key "+mapKey.String())
}
err = writeBinary(buf, value.MapIndex(mapKey))
if err != nil {
return errors.WithMessage(
err,
"error writing binary for map value at key "+mapKey.String())
}
}
return nil
}
// Handle the rest of the types as interface{} and defer to binary.Write. If
// the value cannot be converted to interface{} here, we don't know how to
// handle it.
if !value.CanInterface() {
return errors.New("Unsupported type: " + value.Type().String())
}
iValue := value.Interface()
if iValue == nil {
return nil
}
// Write any types that a bytes.Buffer natively supports to the buffer. If
// the type is "int" or "uint", convert the value to the smallest possible
// bit width integer that can hold the value because binary.Write can't
// handle "int" and "uint" types because they can be variable bit widths.
switch v := iValue.(type) {
case byte:
err := buf.WriteByte(v)
return errors.WithMessage(err, "error writing byte to writer")
case []byte:
_, err := buf.Write(v)
return errors.WithMessage(err, "error writing []byte to writer")
case rune:
_, err := buf.WriteRune(v)
return errors.WithMessage(err, "error writing rune to writer")
case string:
_, err := buf.WriteString(v)
return errors.WithMessage(err, "error writing string to writer")
case int:
iValue = smallestInt(v)
case uint:
iValue = smallestUint(v)
}
err := binary.Write(buf, binary.BigEndian, iValue)
return errors.WithMessage(
err,
fmt.Sprintf("error converting value to binary: %#v", value))
}
// Scalar converts a Values to an arbitrary precision floating point number. The
// scalar value conversion depends on the type of input value.
//
// Individual values that are already scalar values (floats and ints) are returned
// as their original value.
//
// Collections of values (slices, arrays, and maps) are unpacked into individual
// values. All individual values are converted to their binary representation and
// appended to a byte slice. When all values are appended to the byte buffer, the
// bytes are interpreted as a big-endian integer value.
func (vs Values) Scalar() (*big.Float, error) {
// Return the zero value of a *big.Float if the input is empty.
if len(vs) == 0 {
return big.NewFloat(0), nil
}
// Convert some individual scalar values directly to a *big.Float
if len(vs) == 1 {
if !vs[0].IsValid() {
return big.NewFloat(0), nil
}
value := indirect(vs[0])
if value.Kind() == reflect.Float32 || value.Kind() == reflect.Float64 {
return big.NewFloat(value.Float()), nil
}
}
// Convert everything else into bytes, interpret those bytes as a variable
// precision integer, and return that integer represented as a *big.Float
buf := bytes.NewBuffer(nil)
for _, value := range vs {
if err := writeBinary(buf, value); err != nil {
return nil, errors.WithMessage(err, "error writing values as binary")
}
}
return big.NewFloat(0).SetInt(big.NewInt(0).SetBytes(buf.Bytes())), nil
}
func indirect(v reflect.Value) reflect.Value {
for v.Kind() == reflect.Ptr {
v = reflect.Indirect(v)
}
return v
}