/
dataIntBytes.go
173 lines (153 loc) · 5.13 KB
/
dataIntBytes.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
package ieletestingmodel
import (
"math/big"
)
// IntBitRange is modelled on K rule:
// bitRangeInt(I::Int, IDX::Int, LEN::Int) => (I >>Int IDX) modInt (1 <<Int LEN)
func (ms *ModelState) IntBitRange(refI, refOffset, refLen KReference) (KReference, bool) {
if ms.IsZero(refI) {
return IntZero, true // any operation on zero will result in zero
}
if ms.IsZero(refLen) {
return IntZero, true // length = 0 means the result is zero
}
length, lengthOk := ms.GetPositiveInt(refLen)
if !lengthOk {
return NullReference, false
}
bigI, iOk := ms.GetBigInt(refI)
if !iOk {
return NullReference, false
}
offset, offsetOk := ms.GetPositiveInt(refOffset)
if !offsetOk {
if bigI.Sign() > 0 {
// means it doesn't fit in an int32, so a huge number
// huge offset means that certainly no 1 bits will be caught
// scenario occurs in tests/VMTests/vmIOandFlowOperations/byte1/byte1.iele.json
// but only if the number is positive, otherwise the result would be a ridiculously large number of 1's
return IntZero, true
}
return NullReference, false
}
if offset&7 != 0 || length&7 != 0 {
// this is a quick check that they are both divisible by 8
// as long as they are divisible by 8, we can operate on whole bytes
// if they are not, things get more complicated, will only implement when necessary
return NullReference, false
}
offsetBytes := offset >> 3 // divide by 8 to get number of bytes
lengthBytes := length >> 3 // divide by 8 to get number of bytes
resultBytes := BigIntToTwosComplementBytes(bigI, lengthBytes+offsetBytes)
if offsetBytes != 0 {
resultBytes = resultBytes[0:lengthBytes]
}
result := new(big.Int)
result.SetBytes(resultBytes)
return ms.FromBigInt(result), true
}
// IntSignExtendBitRange is modelled on K rule:
// signExtendBitRangeInt(I::Int, IDX::Int, LEN::Int) => (bitRangeInt(I, IDX, LEN) +Int (1 <<Int (LEN -Int 1))) modInt (1 <<Int LEN) -Int (1 <<Int (LEN -Int 1))
func (ms *ModelState) IntSignExtendBitRange(refI, refOffset, refLen KReference) (KReference, bool) {
if ms.IsZero(refI) {
return IntZero, true // any operation on zero will result in zero
}
if ms.IsZero(refLen) {
return IntZero, true // length = 0 means the result is zero
}
length, lengthOk := ms.GetPositiveInt(refLen)
if !lengthOk {
return NullReference, false
}
bigI, iOk := ms.GetBigInt(refI)
if !iOk {
return NullReference, false
}
offset, offsetOk := ms.GetPositiveInt(refOffset)
if !offsetOk {
if bigI.Sign() > 0 {
// means it doesn't fit in an int32, so a huge number
// huge offset means that certainly no 1 bits will be caught
// scenario occurs in tests/VMTests/vmIOandFlowOperations/byte1/byte1.iele.json
// but only if the number is positive, otherwise the result would be a ridiculously large number of 1's
return IntZero, true
}
return NullReference, false
}
if offset&7 != 0 || length&7 != 0 {
// this is a quick check that they are both divisible by 8
// as long as they are divisible by 8, we can operate on whole bytes
// if they are not, things get more complicated, will only implement when necessary
return NullReference, false
}
offsetBytes := offset >> 3 // divide by 8 to get number of bytes
lengthBytes := length >> 3 // divide by 8 to get number of bytes
resultBytes := BigIntToTwosComplementBytes(bigI, lengthBytes+offsetBytes)
if offsetBytes != 0 {
resultBytes = resultBytes[0:lengthBytes]
}
result := TwosComplementBytesToBigInt(resultBytes)
return ms.FromBigInt(result), true
}
// BigIntToTwosComplementBytes returns a byte array representation, 2's complement if number is negative
// big endian
func BigIntToTwosComplementBytes(i *big.Int, bytesLength int) []byte {
var resultBytes []byte
switch i.Sign() {
case -1:
// compute 2's complement
plus1 := big.NewInt(0)
plus1.Add(i, big.NewInt(1)) // add 1
plus1Bytes := plus1.Bytes()
offset := len(plus1Bytes) - bytesLength
resultBytes = make([]byte, bytesLength)
for i := 0; i < bytesLength; i++ {
j := offset + i
if j < 0 {
resultBytes[i] = 255 // pad left with 11111111
} else {
resultBytes[i] = ^plus1Bytes[j] // also negate every bit
}
}
break
case 0:
// just zeroes
resultBytes = make([]byte, bytesLength)
break
case 1:
originalBytes := i.Bytes()
resultBytes = make([]byte, bytesLength)
offset := len(originalBytes) - bytesLength
for i := 0; i < bytesLength; i++ {
j := offset + i
if j < 0 {
resultBytes[i] = 0 // pad left with 00000000
} else {
resultBytes[i] = originalBytes[j]
}
}
break
}
return resultBytes
}
// TwosComplementBytesToBigInt convert a byte array to a number
// interprets input as a 2's complement representation if the first bit (most significant) is 1
// big endian
func TwosComplementBytesToBigInt(twosBytes []byte) *big.Int {
testBit := twosBytes[0] >> 7
result := new(big.Int)
if testBit == 0 {
// positive number, no further processing required
result.SetBytes(twosBytes)
} else {
// convert to negative number
notBytes := make([]byte, len(twosBytes))
for i, b := range twosBytes {
notBytes[i] = ^b // negate every bit
}
result.SetBytes(notBytes)
result.Neg(result)
result.Sub(result, bigOne) // -1
}
return result
}