script_num.c (4070B)
1 2 #include "script_num.h" 3 #include "alloc.h" 4 #include "val.h" 5 #include <limits.h> 6 #include <inttypes.h> 7 8 /** 9 * Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte 10 * integers. The semantics are subtle, though: operands must be in the range 11 * [-2^31 +1...2^31 -1], but results may overflow (and are valid as long as they 12 * are not used in a subsequent numeric operation). struct num enforces those 13 * semantics by storing results as an int64 and allowing out-of-range values to 14 * be returned, but throwing an exception if arithmetic is done or the result is 15 * interpreted as an integer. 16 */ 17 18 19 void 20 sn_from_int(s64 n, struct num *sn) { 21 sn->ind = -1; 22 sn->val = n; 23 } 24 25 26 /* enum sn_result */ 27 /* sn_to_int(struct num *sn, int *res) { */ 28 /* if (sn_overflowed(sn)) { */ 29 /* return SN_ERR_OVERFLOWED_INT; */ 30 /* } */ 31 /* } */ 32 33 34 void sn_serialize(struct num *sn, u8 *buf, int bufsize, u16 *len) { 35 u8 *p = buf; 36 37 if(sn->val == 0) { 38 *len = 0; 39 return; 40 } 41 42 const int neg = sn->val < 0; 43 u64 absvalue = neg ? -(sn->val) : sn->val; 44 45 while(absvalue) { 46 *(p++) = absvalue & 0xff; 47 assert((p - buf) <= bufsize); 48 absvalue >>= 8; 49 } 50 51 // - If the most significant byte is >= 0x80 and the value is positive, push a 52 // new zero-byte to make the significant byte < 0x80 again. 53 54 // - If the most significant byte is >= 0x80 and the value is negative, push a 55 // new 0x80 byte that will be popped off when converting to an integral. 56 57 // - If the most significant byte is < 0x80 and the value is negative, add 58 // 0x80 to it, since it will be subtracted and interpreted as a negative when 59 // converting to an integral. 60 61 if (*(p - 1) & 0x80) 62 *(p++) = neg ? 0x80 : 0; 63 else if (neg) 64 *(p - 1) |= 0x80; 65 66 *len = p - buf; 67 } 68 69 static inline int 70 int_overflowed(s64 val) { 71 return val < INT_MIN || val > INT_MAX; 72 } 73 74 75 int 76 sn_overflowed(struct num *num) { 77 return int_overflowed(num->val); 78 } 79 80 81 s64 82 int_from_data(u8 *data, u16 size) { 83 s64 result = 0; 84 85 if (size == 0) 86 return 0; 87 88 for (size_t i = 0; i != size; ++i) 89 result |= (s64)((data[i]) << 8*i); 90 91 // If the input vector's most significant byte is 0x80, remove it from 92 // the result's msb and return a negative. 93 if (data[size-1] & 0x80) { 94 return -((s64)(result & ~(0x80ULL << (8 * (size - 1))))); 95 } 96 97 return result; 98 } 99 100 101 enum sn_result 102 sn_from_data(u8 *data, u16 size, struct num **num) { 103 u16 ind; 104 s64 i; 105 if (size > 4) 106 return SN_ERR_OVERFLOWED_INT; 107 i = int_from_data(data, size); 108 /* printf("%" PRId64" okkkk\n", i); */ 109 *num = num_pool_new(&ind); 110 (*num)->val = i; 111 (*num)->ind = ind; 112 return SN_SUCCESS; 113 } 114 115 116 // Return a script num only if it's still a 4-byte integer 117 enum sn_result sn_from_val(struct val val, struct num ** sn) { 118 119 switch (val.type) { 120 case VT_SMALLINT: 121 case VT_SCRIPTNUM: 122 *sn = num_pool_get(val.ind); 123 assert((*sn)->ind == val.ind); 124 assert(*sn); 125 126 // we're trying to return a scriptnum that is larger than 4 bytes. This is not 127 // a valid scriptnum at this point. return an error. 128 if (sn_overflowed(*sn)) { 129 *sn = 0; 130 return SN_ERR_OVERFLOWED_INT; 131 } 132 133 if (val.type == VT_SMALLINT) 134 (*sn)->val = val.ind; 135 136 return SN_SUCCESS; 137 case VT_RAW: 138 case VT_DATA: { 139 u8 *data; 140 u32 size; 141 enum sn_result res; 142 assert(val.ind != -1); 143 data = byte_pool_get(val.ind, &size); 144 return sn_from_data(data, size, sn); 145 } 146 } 147 148 printf("type: %d\n", val.type); 149 assert(!"sn_from_val: unhandled"); 150 151 return SN_SUCCESS; 152 } 153 154 struct val 155 sn_to_val(struct num *sn) { 156 struct val val; 157 struct num *snref; 158 u16 ind; 159 160 if (sn_overflowed(sn)) { 161 u16 ind, len; 162 static u8 tmp[8]; 163 sn_serialize(sn, tmp, 8, &len); 164 u8 *data = byte_pool_new(len, &ind); 165 memcpy(data, tmp, len); 166 val.type = VT_DATA; 167 val.ind = ind; 168 } 169 else 170 { 171 val.type = VT_SCRIPTNUM; 172 if (sn->ind > -1) 173 val.ind = sn->ind; 174 else { 175 snref = num_pool_new(&ind); 176 snref->val = sn->val; 177 val.ind = ind; 178 } 179 180 } 181 return val; 182 }