commit 270f7597b189064a7b298a14210e35884da8a266
parent c50cf5a2b2be76ec5c4a0f4098eef0dc87904684
Author: William Casarin <jb55@jb55.com>
Date: Sun, 5 Nov 2017 14:28:39 -0800
WIP: huge refactor
Diffstat:
M | alloc.c | | | 3 | +-- |
M | alloc.h | | | 2 | +- |
M | main.c | | | 11 | +++++++---- |
M | misc.h | | | 22 | ++++++++++++++++++++++ |
M | op.c | | | 5 | ++++- |
M | op.h | | | 2 | +- |
M | script.c | | | 110 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
M | script.h | | | 16 | +++++++++++----- |
M | script_num.c | | | 40 | +++++++++++++++++++++++++++++++++++----- |
M | script_num.h | | | 6 | +++--- |
M | test.c | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
M | val.c | | | 60 | +++++++++++++++++++++++++++++++++++++++++------------------- |
M | val.h | | | 13 | +++++++++++++ |
13 files changed, 292 insertions(+), 65 deletions(-)
diff --git a/alloc.c b/alloc.c
@@ -41,13 +41,12 @@ num_pool_new(int *ind) {
}
char *
-byte_pool_new(const char *bytes, const u16 len) {
+byte_pool_new(const u16 len) {
assert(g_arenas.bytes_top - g_arenas.bytes + len <= g_arenas.nbytes);
char *start = g_arenas.bytes_top;
u16 *c = (u16*)g_arenas.bytes_top;
*c++ = len;
char *p = (char*)c;
- memcpy(p, bytes, len);
p += len;
g_arenas.bytes_top = p;
assert(*p == 0);
diff --git a/alloc.h b/alloc.h
@@ -12,7 +12,7 @@
struct num * num_pool_new(int *ind);
struct num * num_pool_get(const int ind);
-char * byte_pool_new(const char *bytes, const u16 len);
+char * byte_pool_new(const u16 len);
char * byte_pool_get(const int ind, u16 *len);
char * byte_pool_pop();
diff --git a/main.c b/main.c
@@ -8,25 +8,28 @@
extern int yyparse();
extern FILE* yyin;
-struct stack reader_stack;
+char * g_reader_buf;
+char * g_reader_buf_top;
+u32 g_reader_buf_cap;
void yyerror(const char* s);
int main() {
yyin = stdin;
+ size_t size;
struct stack tmp_stack;
alloc_arenas(0, MAX_STACK_SIZE, MAX_STACK_SIZE * MAX_STACK_SIZE);
- stack_init(&reader_stack);
stack_init(&tmp_stack);
do {
yyparse();
} while(!feof(yyin));
- script_eval(&reader_stack, &tmp_stack);
+ size = g_reader_buf_top - g_reader_buf;
+ script_eval(g_reader_buf, size, &tmp_stack);
printf("script: ");
- script_print_ops(&reader_stack);
+ script_print_ops(g_reader_buf, g_reader_buf_top -);
printf("stack: ");
script_print_vals(&tmp_stack);
diff --git a/misc.h b/misc.h
@@ -4,6 +4,8 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+/* #include <endian.h> */
+#include <string.h>
#include <stdint.h>
#define DEBUG 0
@@ -21,7 +23,27 @@ typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef int64_t s64;
+typedef uint64_t u64;
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
+// TODO: host endianness
+#define le16toh(x) (x)
+#define le32toh(x) (x)
+
+u16 static inline
+readle16(const u8* ptr) {
+ u16 x;
+ memcpy((char*)&x, ptr, 2);
+ return le16toh(x);
+}
+
+u32 static inline
+readle32(const u8* ptr) {
+ u32 x;
+ memcpy((char*)&x, ptr, 4);
+ return le32toh(x);
+}
+
+
#endif /* BCS_MISC_H */
diff --git a/op.c b/op.c
@@ -359,7 +359,10 @@ val_print(struct val val) {
case VT_SCRIPTNUM:
assert(val.ind != -1);
n = num_pool_get(val.ind);
- printf("%lu", n->val);
+ printf("sn:%lu", n->val);
+ break;
+ case VT_SMALLINT:
+ printf("si:%d", val.ind);
break;
default:
assert(!"val_print data");
diff --git a/op.h b/op.h
@@ -312,7 +312,7 @@ const char * val_name(struct val);
static inline void
stack_push_val(struct stack *stack, struct val val) {
-#if 0
+#if 1
printf("pushing val ");
val_print(val);
printf("\n");
diff --git a/script.c b/script.c
@@ -8,15 +8,20 @@
#define SCRIPTERR(serr) script_add_error(c, opcode, serr)
+int g_silence_script_err = 0;
+int g_silence_script_warn = 0;
+
int script_add_error(int c, enum opcode cur_op, const char *serror) {
// TODO: set_error
- fprintf(stderr, "error: %s @ op %d (%s)\n", serror, c, op_name(cur_op));
+ if (!g_silence_script_err)
+ fprintf(stderr, "error: %s @ op %d (%s)\n", serror, c, op_name(cur_op));
return 0;
}
void script_add_warning(const char *warning) {
// TODO: set_error
- fprintf(stderr, "warning: %s\n", warning);
+ if (!g_silence_script_warn)
+ fprintf(stderr, "warning: %s\n", warning);
}
@@ -38,9 +43,51 @@ cast_to_bool(struct val val) {
}
int
-script_eval(struct stack *script, struct stack *stack) {
+script_getop(u8 **p, u8 *end, enum opcode *opcode, u8 *buf, int bufsize, u32 *outlen) {
+ u32 nsize = 0;
+
+ *opcode = *(*p++);
+
+ if (*opcode > OP_PUSHDATA4)
+ return 1;
+
+ if (*opcode < OP_PUSHDATA1)
+ nsize = *opcode;
+ else if (*opcode == OP_PUSHDATA1) {
+ if (end - *p < 1)
+ return 0;
+ nsize = *(*p++);
+ }
+ else if (*opcode == OP_PUSHDATA2) {
+ if (end - *p < 2)
+ return 0;
+ nsize = readle16(*p);
+ *p += 2;
+ }
+ else if (*opcode == OP_PUSHDATA4) {
+ if (end - *p < 4)
+ return 0;
+ nsize = readle32(*p);
+ *p += 4;
+ }
+
+ if (buf) {
+ *outlen = nsize;
+ memcpy(buf, *p, nsize);
+ *p += nsize;
+ }
+
+ return 1;
+}
+
+
+int
+script_eval(u8 *script, size_t script_size, struct stack *stack) {
int op_count = 0;
- void **p = script->bottom;
+ u32 tmplen;
+ u8 *p = script;
+ u8 *top = script + script_size;
+ static char tmpbuf[32];
static const struct val val_true = {.type = VT_SMALLINT, .ind = 1};
static const struct val val_false = {.type = VT_SMALLINT, .ind = 0};
static const struct num bn_one = {.val = 1, .ind = -1};
@@ -55,14 +102,13 @@ script_eval(struct stack *script, struct stack *stack) {
// TODO: require minimal?
int require_minimal =
!(flags & ~(CO_WARNINGS_ARE_ERRORS | CO_WARN_MINIMAL));
-
- char tmpbuf[32];
stack_init(altstack);
stack_init(ifstack);
- while (p < script->top) {
+ while (p < top) {
c++;
- enum opcode opcode = *(enum opcode*)p;
+ enum opcode opcode;
+ script_getop(&p, top, &opcode, (u8*)tmpbuf, ARRAY_SIZE(tmpbuf), &tmplen);
int if_exec = !stack_size(ifstack);
p++;
// TODO: pushdata ops
@@ -550,11 +596,19 @@ script_eval(struct stack *script, struct stack *stack) {
return 1;
}
-void script_print_ops(struct stack *stack) {
- void **p = stack->bottom;
- while (p < stack->top) {
- enum opcode opcode = (enum opcode)*p++;
+void script_print(u8 *script, size_t script_size) {
+ u32 len;
+ static u8 tmpbuf[4096];
+ u8 *t = tmpbuf;
+ u8 *p = script;
+ u8 *top = script + script_size;
+ while (p < top) {
+ enum opcode opcode;
+ script_getop(&p, top, &opcode, tmpbuf, ARRAY_SIZE(tmpbuf), &len);
+ u8 *ttop = tmpbuf + len;
printf("%s ", op_name(opcode));
+ if (t < ttop)
+ printf("%02x", *t++);
}
printf("\n");
}
@@ -589,3 +643,35 @@ void script_free(struct script *script) {
int script_new(struct script *script) {
return 0;
}
+
+void
+script_push_int(struct stack *script, s64 intval) {
+ /* u16 len; */
+ /* u16 i; */
+ /* static u8 buf[8]; */
+ struct val val = val_from_int(intval);
+ stack_push_val(script, val);
+ /* val_serialize(val, &len, buf, ARRAY_SIZE(buf)); */
+ /* for (i = 0; i < len; ++i) */
+ /* stack_push_small(u8, script, &buf[i]); */
+}
+
+
+void
+script_serialize(struct stack *stack, u8 *buf, int buflen, int* len) {
+ struct val val;
+ u8 *huh = (u8*)&val;
+ u8 *p = buf;
+ u16 valsize;
+ *len = 0;
+
+ while (stack_size(stack) != 0) {
+ val = stack_pop_val(stack);
+ printf("%02x %02x %02x %02x | ", huh[0], huh[1], huh[2], huh[3]);
+ printf("%d %d\n", val.type, val.ind);
+ val_serialize(val, &valsize, p, buflen-(p-buf));
+ p += valsize;
+ *len += valsize;
+ assert(p-buf <= buflen);
+ }
+}
diff --git a/script.h b/script.h
@@ -9,11 +9,17 @@ struct script {
struct stack pushdata; // a stack of pushdata stacks
};
-int script_new(struct script *);
-void script_free(struct script *);
-int script_eval(struct stack *, struct stack*);
-void script_print_ops(struct stack *);
-void script_print_vals(struct stack *);
+int script_new(struct script *);
+void script_free(struct script *);
+int script_eval(u8 *script, size_t script_size, struct stack *stack);
+void script_print_ops(char * buf, size_t size);
+void script_print_vals(struct stack *);
+void script_push_int(struct stack *, s64);
+void
+script_serialize(struct stack *stack, u8 *buf, int buflen, int* len);
+
+extern int g_silence_script_err;
+extern int g_silence_script_warn;
#endif /* BTCS_SCRIPT_H */
diff --git a/script_num.c b/script_num.c
@@ -15,7 +15,7 @@
void
-sn_from_int(int n, struct num *sn) {
+sn_from_int(s64 n, struct num *sn) {
sn->ind = -1;
sn->val = n;
}
@@ -29,10 +29,40 @@ sn_from_int(int n, struct num *sn) {
/* } */
-const char *
-sn_serialize(struct num *sn, u16 *len) {
- assert(!"implement sn_serialize");
- return "";
+void
+sn_serialize(struct num *sn, u8 *buf, int bufsize, u16 *len) {
+ u8 *p = buf;
+
+ if(sn->val == 0) {
+ *len = 0;
+ return;
+ }
+
+ const int neg = sn->val < 0;
+ u64 absvalue = neg ? -(sn->val) : sn->val;
+
+ while(absvalue) {
+ *p++ = absvalue & 0xff;
+ assert((p - buf) <= bufsize);
+ absvalue >>= 8;
+ }
+
+ // - If the most significant byte is >= 0x80 and the value is positive, push a
+ // new zero-byte to make the significant byte < 0x80 again.
+
+ // - If the most significant byte is >= 0x80 and the value is negative, push a
+ // new 0x80 byte that will be popped off when converting to an integral.
+
+ // - If the most significant byte is < 0x80 and the value is negative, add
+ // 0x80 to it, since it will be subtracted and interpreted as a negative when
+ // converting to an integral.
+
+ if (*p & 0x80)
+ *p++ = neg ? 0x80 : 0;
+ else if (neg)
+ *p++ |= 0x80;
+
+ *len = p - buf;
}
diff --git a/script_num.h b/script_num.h
@@ -16,7 +16,7 @@ struct num {
};
void
-sn_from_int(int n, struct num *);
+sn_from_int(s64 n, struct num *);
int
sn_overflowed(struct num *num);
@@ -30,8 +30,8 @@ sn_to_val(struct num *sn);
int
sn_to_int(struct num *sn, int require_minimal);
-const char *
-sn_serialize(struct num *sn, u16 *len);
+void
+sn_serialize(struct num *sn, u8 *buf, int bufsize, u16 *len);
static void inline
diff --git a/test.c b/test.c
@@ -14,6 +14,27 @@ typedef void (program)(struct stack *script, struct stack *stack,\
struct stack *expected \
)
+
+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)
+ printf("%02x ", a[i] & 0xff);
+
+ printf("\n# > ");
+ for (i = 0; i < blen; ++i)
+ printf("%02x ", b[i] & 0xff);
+
+ printf("\n#\n");
+ fail("%s: data should match", msg);
+ }
+ else
+ pass("%s: data should match", msg);
+}
/* static void */
/* test_nip(struct stack *stack, struct stack *expected) { */
/* stack_push_op(OP_1); */
@@ -43,33 +64,52 @@ ok_stacks_equal(struct stack *s1, struct stack *s2, const char *context) {
}
TEST(test_simple) {
- stack_push_op(script, OP_1);
+ u8 in_script[] = { OP_1 };
- script_eval(script, stack);
+ script_eval(in_script, 1, stack);
stack_push_val(expected, smallintval(1));
ok_stacks_equal(stack, expected, "test_simple");
}
TEST(test_nip) {
- stack_push_op(script, OP_1);
- stack_push_op(script, OP_2);
- stack_push_op(script, OP_NIP);
+ u8 in_script[] = { OP_1, OP_2, OP_NIP };
- script_eval(script, stack);
+ script_eval(in_script, ARRAY_SIZE(in_script), stack);
stack_push_val(expected, smallintval(2));
ok_stacks_equal(stack, expected, "test_nip");
}
TEST(test_2dup_not_enough_input) {
- stack_push_op(script, OP_1);
- stack_push_op(script, OP_2DUP);
+ u8 in_script[] = { OP_1, OP_2DUP };
- int res = script_eval(script, stack);
+ int res = script_eval(in_script, ARRAY_SIZE(in_script), stack);
ok(res == 0, "2dup fail on small stack");
}
+TEST(big_int_serializes_ok) {
+ int len;
+ static u8 buf[12];
+ static u8 expected_in[] = { 0x04, 0xff, 0xff, 0xff, 0x7f,
+ 0x04, 0xff, 0xff, 0xff, 0x7f, OP_ADD };
+ static u8 expected_out[] = { 0x05, 0xfe, 0xff, 0xff, 0xff, 0 };
+
+ script_push_int(script, 2147483647);
+ script_push_int(script, 2147483647);
+ stack_push_op(script, OP_ADD);
+
+ script_serialize(script, buf, ARRAY_SIZE(buf), &len);
+
+ cmp_data(buf, expected_in, len, ARRAY_SIZE(expected_in),
+ "big int input serializes ok");
+
+ script_eval(buf, ARRAY_SIZE(expected_in), stack);
+ script_serialize(stack, buf, ARRAY_SIZE(buf), &len);
+
+ cmp_data(buf, expected_out, len, ARRAY_SIZE(expected_out),
+ "big int output serializes ok");
+}
// TODO test scriptnum overflows
// TODO test scriptnum negative zero boolean logic
@@ -89,8 +129,7 @@ TEST(test_2dup_not_enough_input) {
static inline void
-run_test(struct stack *script, struct stack *stack, struct stack *expected,
- program *prog)
+run_test(struct stack *script, struct stack *stack, struct stack *expected, program *prog)
{
stack_clear(script);
stack_clear(stack);
@@ -106,6 +145,9 @@ main(int argc, char *argv[]) {
struct stack *stack = &_stack;
struct stack *expected = &_expected;
+ g_silence_script_err = 0;
+ g_silence_script_warn = 1;
+
alloc_arenas();
plan(5);
@@ -117,10 +159,11 @@ main(int argc, char *argv[]) {
RUNTEST(test_simple);
RUNTEST(test_nip);
RUNTEST(test_2dup_not_enough_input);
+ RUNTEST(big_int_serializes_ok);
+ stack_free(script);
stack_free(expected);
stack_free(stack);
- stack_free(script);
free_arenas(0);
diff --git a/val.c b/val.c
@@ -14,23 +14,43 @@ static const char intbytes[] = {
OP_16
};
-const char *
-val_serialize(struct val val, u16 *len, int require_minimal) {
+struct val
+val_from_int(s64 intval) {
+ struct val val;
struct num *sn;
- int n, ind;
+ int ind;
+ sn = num_pool_new(&ind);
+ val.type = VT_SCRIPTNUM;
+ val.ind = ind;
+ sn->ind = ind;
+ sn->val = intval;
+ return val;
+}
+
+void
+val_serialize(struct val val, u16 *len, u8 *buf, int bufsize) {
+ struct num *sn;
+ int n;
+ u16 valsize;
switch (val.type) {
case VT_SCRIPTNUM:
sn = num_pool_get(val.ind);
assert(sn);
- return sn_serialize(sn, len);
+ sn_serialize(sn, buf+1, bufsize - 1, &valsize);
+ assert(valsize <= 0xFF);
+ *buf = (u8)valsize;
+ *len = valsize + 1;
+ return;
case VT_DATA:
- return byte_pool_get(val.ind, len);
+ assert("!implement val_serialize VT_DATA");
+ byte_pool_get(val.ind, len);
case VT_SMALLINT:
*len = 1;
n = val.ind;
if (n >= -1 && n <= 16) {
val.type = VT_SMALLINT;
- return &intbytes[n+1];
+ assert(bufsize >= 1);
+ *buf = intbytes[n+1];
}
else {
assert(!"non-small VT_SMALLINT");
@@ -38,28 +58,30 @@ val_serialize(struct val val, u16 *len, int require_minimal) {
}
assert(!"val_serialize missing implementation");
- return 0;
}
int
val_eq(struct val a, struct val b, int require_minimal) {
u16 alen, blen;
int eq = 0;
- const char *abytes, *bbytes;
- abytes = val_serialize(a, &alen, require_minimal);
- bbytes = val_serialize(b, &blen, require_minimal);
- // TODO: what if they're semantically equivalent? or does that matter?
- if (alen != blen) {
- if (a.type == VT_DATA) byte_pool_pop();
- if (b.type == VT_DATA) byte_pool_pop();
- return 0;
- }
+ static const int tmpsize = 4096;
+ static u8 tmpa[tmpsize];
+ static u8 tmpb[tmpsize];
+
+ const u8 *abytes, *bbytes;
+ // TODO: do I need to serialize to compare?
+ /* abytes = val_serialize(a, &alen, require_minimal); */
+ /* bbytes = val_serialize(b, &blen, require_minimal); */
+ val_serialize(a, &alen, tmpa, tmpsize);
+ val_serialize(b, &blen, tmpb, tmpsize);
+ // TODO: what if they're semantically equivalent? or does that matter
+ // (eg. minimal vs not miniminal)?
- eq = memcmp(abytes, bbytes, alen) == 0;
+ if (alen != blen)
+ return 0;
- if (a.type == VT_DATA) byte_pool_pop();
- if (b.type == VT_DATA) byte_pool_pop();
+ eq = memcmp(tmpa, tmpb, alen) == 0;
return eq;
}
diff --git a/val.h b/val.h
@@ -36,6 +36,14 @@ stack_top_val(struct stack *stack, int ind) {
return val;
}
+static inline struct val
+stack_pop_val(struct stack *stack) {
+ struct val val;
+ val = stack_top_val(stack, -1);
+ stack_pop(stack);
+ return val;
+}
+
static inline void
stack_set_val(struct stack *stack, int ind, struct val val) {
struct val *pval = (struct val *)(stack->top + ind);
@@ -45,4 +53,9 @@ stack_set_val(struct stack *stack, int ind, struct val val) {
int
val_eq(struct val a, struct val b, int require_minimal);
+void
+val_serialize(struct val val, u16 *len, u8 *buf, int bufsize);
+
+struct val val_from_int(s64);
+
#endif /* BTCS_VAL_H */