commit f356e6217ece3187666cc957b3b7ee2a94ad27c7
parent db74143362d279e09ed7f5a4cad83bb7590f2dc6
Author: William Casarin <jb55@jb55.com>
Date: Sun, 12 Nov 2017 19:24:48 -0800
integers: more tests pass for negative integers
Diffstat:
10 files changed, 131 insertions(+), 28 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-CFLAGS=-O0 -Ideps -std=c99 -g -Wall -Wno-unused-variable -Wno-unused-function -Wunreachable-code
+CFLAGS=-g -O0 -Ideps -std=c99 -Wall -Wno-unused-variable -Wno-unused-function -Wunreachable-code
DEPS=script.c \
oplookup.c \
diff --git a/alloc.c b/alloc.c
@@ -21,6 +21,7 @@ alloc_arena_sizes(struct arenas *arenas, const int nums, const int bytes) {
arenas->bytes = (u8*)calloc(bytes, 1);
stack_init_size(&arenas->bytes_map, bytes);
arenas->bytes_top = arenas->bytes;
+ arenas->num_count = 0;
arenas->nbytes = bytes;
}
@@ -44,12 +45,13 @@ num_pool_new(int *ind) {
p = &g_arenas.nums[*ind];
assert(p);
assert(p->val == 0 && p->ind == 0);
+ p->ind = *ind;
return p;
}
u8 *
-byte_pool_new(const u16 len, u16 *ind) {
- assert(g_arenas.bytes_top - g_arenas.bytes + len <= g_arenas.nbytes);
+byte_pool_new(u16 len, u16 *ind) {
+ assert((g_arenas.bytes_top - g_arenas.bytes + len) <= g_arenas.nbytes);
u8 *start = g_arenas.bytes_top;
u16 *c = (u16*)g_arenas.bytes_top;
*c++ = len;
@@ -59,7 +61,8 @@ byte_pool_new(const u16 len, u16 *ind) {
stack_push(&g_arenas.bytes_map, (void*)start);
g_arenas.bytes_top = p;
assert(*p == 0);
- return start;
+ assert(((g_arenas.bytes_top - g_arenas.bytes) + len) <= g_arenas.nbytes);
+ return p - len;
}
@@ -74,14 +77,19 @@ byte_pool_pop() {
u8 *
-byte_pool_get(const int ind, u16 *len) {
+byte_pool_get(int ind, u16 *len) {
+ assert(ind <= stack_size(&g_arenas.bytes_map) - 1);
+ void **vp;
u8 *p;
u16 *up;
- p = (u8*)(g_arenas.bytes_map.bottom + ind);
+ vp = g_arenas.bytes_map.bottom + ind;
+ p = (u8*)(*vp);
+ assert((g_arenas.bytes_top - g_arenas.bytes + *len) <= g_arenas.nbytes);
assert(p);
up = (u16*)p;
- *len = *(up++);
+ *len = *up++;
p = (u8*)up;
+ assert((g_arenas.bytes_top - g_arenas.bytes + *len) <= g_arenas.nbytes);
return p;
}
@@ -93,3 +101,4 @@ free_arenas(struct arenas *arenas) {
free(arenas->bytes);
free(arenas->nums);
}
+
diff --git a/misc.h b/misc.h
@@ -7,6 +7,7 @@
/* #include <endian.h> */
#include <string.h>
#include <stdint.h>
+#include <stdio.h>
#define DEBUG 0
@@ -45,5 +46,14 @@ readle32(const u8* ptr) {
return le32toh(x);
}
+void static inline
+print_bytes(u8 *bytes, size_t size) {
+ size_t i;
+ for (i = 0; i < size; i++) {
+ printf("%02x ", bytes[i]);
+ }
+ printf("\n");
+}
+
#endif /* BCS_MISC_H */
diff --git a/op.c b/op.c
@@ -368,6 +368,13 @@ val_print(struct val val) {
case VT_SMALLINT:
printf("si:%d", val.ind);
break;
+ case VT_DATA: {
+ u16 len;
+ u8 *data = byte_pool_get(val.ind, &len);
+ printf("data (%d): ", len);
+ print_bytes(data, len);
+ break;
+ }
default:
assert(!"val_print data");
}
diff --git a/script.c b/script.c
@@ -160,7 +160,6 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) {
/* if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { */
/* return set_error(serror, SCRIPT_ERR_MINIMALDATA); */
/* } */
- printf("pushing data of size %d\n", tmplen);
stack_push_data(stack, (u8*)tmpbuf, tmplen);
} else if (if_exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
switch (opcode) {
@@ -546,14 +545,21 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) {
if (stack_size(stack) < 2)
return SCRIPTERR("SCRIPT_ERR_INVALID_STACK_OPERATION");
struct num *bn1, *bn2, bn;
- sn_from_val(stack_top_val(stack, -2), &bn1, require_minimal);
- sn_from_val(stack_top_val(stack, -1), &bn2, require_minimal);
+ enum sn_result res;
+ bn.ind = -1;
+ res = sn_from_val(stack_top_val(stack, -2), &bn1, require_minimal);
+ if (res == SN_ERR_OVERFLOWED_INT)
+ return SCRIPTERR("SCRIPT_INT_OVERFLOW");
+ res = sn_from_val(stack_top_val(stack, -1), &bn2, require_minimal);
+ if (res == SN_ERR_OVERFLOWED_INT)
+ return SCRIPTERR("SCRIPT_INT_OVERFLOW");
/* struct num bn(0); */
switch (opcode)
{
case OP_ADD:
- bn.val = bn1->val + bn2->val;
- break;
+ printf("%d + %d\n", bn1->val, bn2->val);
+ bn.val = bn1->val + bn2->val;
+ break;
case OP_SUB:
bn.val = bn1->val - bn2->val;
@@ -580,7 +586,8 @@ script_eval(u8 *script, size_t script_size, struct stack *stack) {
}
stack_pop(stack);
stack_pop(stack);
- stack_push_val(stack, sn_to_val(&bn));
+ struct val bnval = sn_to_val(&bn);
+ stack_push_val(stack, bnval);
if (opcode == OP_NUMEQUALVERIFY)
{
@@ -640,11 +647,12 @@ void script_print(u8 *script, size_t script_size) {
void script_print_vals(struct stack *stack) {
void **p = stack->bottom;
+ int c = 0;
while (p < stack->top) {
struct val val;
memcpy(&val, &*p++, sizeof(struct val));
+ printf("s[%d] ", c++);
val_print(val);
- putchar(' ');
}
putchar('\n');
}
@@ -695,8 +703,6 @@ script_serialize(struct stack *stack, u8 *buf, int buflen, int* len) {
/* printf("%02x %02x %02x %02x | ", huh[0], huh[1], huh[2], huh[3]); */
/* printf("%d %d\n", val.type, val.ind); */
valp = (struct val*)sp;
- val_print(*valp);
- printf("\n");
val_serialize(*valp, &valsize, p, buflen-(p-buf));
p += valsize;
*len += valsize;
diff --git a/script_num.c b/script_num.c
@@ -2,6 +2,7 @@
#include "script_num.h"
#include "alloc.h"
#include "val.h"
+#include <limits.h>
/**
* Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte
@@ -57,27 +58,70 @@ sn_serialize(struct num *sn, u8 *buf, int bufsize, u16 *len) {
// 0x80 to it, since it will be subtracted and interpreted as a negative when
// converting to an integral.
- if (*p & 0x80)
+ if (*(p - 1) & 0x80)
*p++ = neg ? 0x80 : 0;
else if (neg)
- *p++ |= 0x80;
+ *(p - 1) |= 0x80;
*len = p - buf;
}
+static inline int
+int_overflowed(s64 val) {
+ return val < INT_MIN || val > INT_MAX;
+}
+
int
sn_overflowed(struct num *num) {
- return (num->val & ~0xFFFFFFFF) != 0;
+ return int_overflowed(num->val);
+}
+
+
+s64
+int_from_data(u8 *data, u16 size) {
+ s64 result = 0;
+
+ if (size == 0)
+ return 0;
+
+ for (size_t i = 0; i != size; ++i)
+ result |= (s64)((data[i]) << 8*i);
+
+ // If the input vector's most significant byte is 0x80, remove it from
+ // the result's msb and return a negative.
+ if (data[size-1] & 0x80) {
+ return -((s64)(result & ~(0x80ULL << (8 * (size - 1)))));
+ }
+
+ return result;
+}
+
+
+enum sn_result
+sn_from_data(u8 *data, u16 size, struct num **num) {
+ int ind;
+ s64 i;
+ i = int_from_data(data, size);
+ if (int_overflowed(i)) {
+ *num = 0;
+ return SN_ERR_OVERFLOWED_INT;
+ }
+ *num = num_pool_new(&ind);
+ (*num)->val = i;
+ (*num)->ind = ind;
+ return SN_SUCCESS;
}
// Return a script num only if it's still a 4-byte integer
enum sn_result
sn_from_val(struct val val, struct num ** sn, int require_minimal) {
+
switch (val.type) {
case VT_SCRIPTNUM:
*sn = num_pool_get(val.ind);
+ assert((*sn)->ind == val.ind);
assert(*sn);
// we're trying to return a scriptnum that is larger than 4 bytes. This is not
@@ -88,9 +132,17 @@ sn_from_val(struct val val, struct num ** sn, int require_minimal) {
}
break;
- case VT_DATA:
- assert(!"TODO implement raw data to script num");
+ case VT_DATA: {
+ u8 *data;
+ u16 size;
+ enum sn_result res;
+ assert(val.ind != -1);
+ data = byte_pool_get(val.ind, &size);
+ return sn_from_data(data, size, sn);
}
+ }
+
+ assert("!sn_from_val: unhandled");
return SN_SUCCESS;
}
@@ -101,7 +153,7 @@ sn_to_val(struct num *sn) {
struct num *snref;
int ind;
- // TODO: implement return overflowed scriptnums
+ //TODO: implement return overflowed scriptnums
assert(!sn_overflowed(sn));
val.type = VT_SCRIPTNUM;
@@ -109,7 +161,7 @@ sn_to_val(struct num *sn) {
val.ind = sn->ind;
else {
snref = num_pool_new(&ind);
- *sn = *snref;
+ snref->val = sn->val;
val.ind = ind;
}
return val;
diff --git a/stack.c b/stack.c
@@ -13,7 +13,7 @@ stack_clear(struct stack *stack) {
int
stack_init_size(struct stack *stack, int capacity) {
if (capacity > DEFAULT_STACK_SIZE) {
- void *bottom = malloc(capacity * sizeof(void*));
+ void *bottom = calloc(capacity, sizeof(void*));
if (!bottom) return 0;
stack->bottom = bottom;
stack->top = bottom;
diff --git a/test.c b/test.c
@@ -19,8 +19,6 @@ typedef void (program)(struct stack *script, struct stack *stack,\
static void
cmp_data(const u8 *a, const u8 *b, int alen, int blen, const char *msg) {
int i = 0;
- cmp_ok(alen, "==", blen);
-
if (memcmp(a, b, blen) != 0) {
printf("#\n# Failed data cmp test\n# > ");
for (i = 0; i < alen; ++i)
@@ -89,6 +87,25 @@ TEST(test_2dup_not_enough_input) {
ok(res == 0, "2dup fail on small stack");
}
+TEST(negative_integer) {
+ static u8 buf[6];
+ int len;
+ u8 in_script[] = { 0x01, 0x82 };
+ int res = script_eval(in_script, ARRAY_SIZE(in_script), stack);
+ script_serialize(stack, buf, ARRAY_SIZE(buf), &len);
+ cmp_data(buf, in_script, len, ARRAY_SIZE(in_script), "negative 2 serializes ok");
+}
+
+TEST(add_negative_two) {
+ static u8 buf[12];
+ int len;
+ static u8 in_script[] = { 0x01, 0x82, 0x01, 0x82, OP_ADD };
+ static u8 expected_out[] = { 0x01, 0x84 };
+ int res = script_eval(in_script, ARRAY_SIZE(in_script), stack);
+ script_serialize(stack, buf, ARRAY_SIZE(buf), &len);
+ cmp_data(buf, expected_out, len, ARRAY_SIZE(expected_out), "add negative two twice");
+}
+
TEST(big_int_serializes_ok) {
int len;
static u8 buf[12];
@@ -160,6 +177,8 @@ main(int argc, char *argv[]) {
RUNTEST(test_simple);
RUNTEST(test_nip);
RUNTEST(test_2dup_not_enough_input);
+ RUNTEST(negative_integer);
+ RUNTEST(add_negative_two);
RUNTEST(big_int_serializes_ok);
stack_free(script);
diff --git a/val.h b/val.h
@@ -4,7 +4,6 @@
#include "misc.h"
#include "stack.h"
-#include "op.h"
enum valtype {
VT_SCRIPTNUM=0,
diff --git a/valstack.h b/valstack.h
@@ -41,7 +41,8 @@ stack_push_data(struct stack *stack, u8 *data, int len) {
struct val val = { .type = VT_DATA };
u8 *p;
u16 ind;
- byte_pool_new(len, &ind);
+ p = byte_pool_new(len, &ind);
+ memcpy(p, data, len);
val.ind = ind;
stack_push_val(stack, val);
}