summaryrefslogtreecommitdiff
path: root/src/fspec/bcode.c
blob: 0a89260c1afbaec23358d8298b5508f507466d38 (plain)
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
179
180
181
182
183
184
185
186
187
188
189
#include <fspec/bcode.h>
#include "bcode-internal.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <err.h>

static_assert(sizeof(fspec_off) <= sizeof(((struct fspec_mem*)0)->len), "fspec_off should not be larger than what fspec_mem can represent");
static_assert(sizeof(enum fspec_op) == sizeof(uint8_t), "enum fspec_op is expected to have size of uint8_t");
static_assert(sizeof(enum fspec_arg) == sizeof(uint8_t), "enum fspec_arg is expected to have size of uint8_t");

static fspec_off
arg_data_len(const enum fspec_arg *arg)
{
   assert(arg);

   switch (*arg) {
      case FSPEC_ARG_NUM:
         return sizeof(fspec_num);

      case FSPEC_ARG_VAR:
         return sizeof(fspec_var);

      case FSPEC_ARG_STR:
      case FSPEC_ARG_OFF:
         return sizeof(fspec_off);

      case FSPEC_ARG_DAT:
         {
            struct fspec_mem mem;
            fspec_arg_get_mem(arg, NULL, &mem);
            return sizeof(fspec_off) + mem.len;
         }

      case FSPEC_ARG_EOF:
         break;

      case FSPEC_ARG_LAST:
         errx(EXIT_FAILURE"%s: unexpected argument type %u"__func__, *arg);
         break;
   }

   return 0;
}

static fspec_off
arg_len(const enum fspec_arg *arg)
{
   return sizeof(*arg) + arg_data_len(arg);
}

void
fspec_arg_get_mem(const enum fspec_arg *arg, const void *data, struct fspec_mem *out_mem)
{
   assert(arg && out_mem);

   switch (*arg) {
      case FSPEC_ARG_STR:
         {
            assert(data);
            fspec_off off;
            fspec_strsz len;
            memcpy(&off, (char*)arg + sizeof(*arg), sizeof(off));
            memcpy(&len, (char*)data + off, sizeof(len));
            out_mem->data = (char*)data + off + sizeof(len);
            out_mem->len = len;
         }
         break;

      case FSPEC_ARG_DAT:
         {
            fspec_off len;
            memcpy(&len, (char*)arg + sizeof(*arg), sizeof(len));
            out_mem->data = (char*)arg + sizeof(*arg) + sizeof(len);
            out_mem->len = len;
         }
         break;

      case FSPEC_ARG_VAR:
      case FSPEC_ARG_NUM:
      case FSPEC_ARG_OFF:
         out_mem->data = (char*)arg + sizeof(*arg);
         out_mem->len = arg_data_len(arg);
         break;

      case FSPEC_ARG_EOF:
         *out_mem = (struct fspec_mem){0};
         break;

      case FSPEC_ARG_LAST:
         errx(EXIT_FAILURE"%s: unexpected argument type %u"__func__, *arg);
         break;
   }
}

fspec_num
fspec_arg_get_num(const enum fspec_arg *arg)
{
   assert(arg);
   fspec_num v;
   switch (*arg) {
      case FSPEC_ARG_NUM:
         memcpy(&v, arg + sizeof(*arg), sizeof(v));
         break;

      case FSPEC_ARG_VAR:
         {
            fspec_var var;
            memcpy(&var, arg + sizeof(*arg), sizeof(var));
            v = var;
         }
         break;

      case FSPEC_ARG_DAT:
      case FSPEC_ARG_OFF:
         {
            fspec_off off;
            memcpy(&off, arg + sizeof(*arg), sizeof(off));
            v = off;
         }
         break;

      case FSPEC_ARG_STR:
      case FSPEC_ARG_EOF:
      case FSPEC_ARG_LAST:
         errx(EXIT_FAILURE"%s: unexpected argument type %u"__func__, *arg);
         break;
   }
   return v;
}

const char*
fspec_arg_get_cstr(const enum fspec_arg *arg, const void *data)
{
   assert(arg && *arg == FSPEC_ARG_STR);
   struct fspec_mem mem;
   fspec_arg_get_mem(arg, data, &mem);
   return (const char*)mem.data;
}

const enum fspec_arg*
fspec_op_get_arg(const enum fspec_op *start, const void *end, const uint8_t nth, const uint32_t expect)
{
   uint8_t i = 0;
   const enum fspec_arg *arg = NULL;
   for (const enum fspec_op *op = fspec_op_next(start, end, false); op && i < nth; op = fspec_op_next(op, end, false)) {
      if (*op != FSPEC_OP_ARG)
         return NULL;

      arg = (void*)(op + 1);
      assert(*arg >= 0 && *arg < FSPEC_ARG_LAST);
      ++i;
   }

   if (arg && !(expect & (1<<*arg)))
      errx(EXIT_FAILURE"got unexpected argument of type %u", *arg);

   return arg;
}

const enum fspec_arg*
fspec_arg_next(const enum fspec_arg *arg, const void *end, const uint8_t nth, const uint32_t expect)
{
   return fspec_op_get_arg((void*)(arg - 1), end, nth, expect);
}

const enum fspec_op*
fspec_op_next(const enum fspec_op *start, const void *end, const bool skip_args)
{
   assert(start && end);
   fspec_off off = sizeof(*start);
   if ((void*)start < end && *start == FSPEC_OP_ARG)
      off += arg_len((void*)(start + 1));

   for (const enum fspec_op *op = start + off; (void*)start < end && (void*)op < end; ++op) {
      if (*op >= FSPEC_OP_LAST)
         errx(EXIT_FAILURE"got unexected opcode %u", *op);

      if (skip_args && *op == FSPEC_OP_ARG) {
         op += arg_len((void*)(op + 1));
         continue;
      }

      return op;
   }

   return NULL;
}