GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/lbm_flat_value.c Lines: 450 534 84.3 %
Date: 2025-04-09 11:39:30 Branches: 202 341 59.2 %

Line Branch Exec Source
1
/*
2
    Copyright 2023, 2024, 2025 Joel Svensson    svenssonjoel@yahoo.se
3
              2023       Benjamin Vedder
4
5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
15
    You should have received a copy of the GNU General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include <lbm_flat_value.h>
20
#include <eval_cps.h>
21
#include <stack.h>
22
23
#include <setjmp.h>
24
25
// ------------------------------------------------------------
26
// Access to GC from eval_cps
27
int lbm_perform_gc(void);
28
29
30
// ------------------------------------------------------------
31
// Flatteners
32
40839
bool lbm_start_flatten(lbm_flat_value_t *v, size_t buffer_size) {
33
40839
  bool res = false;
34
40839
  uint8_t *data = lbm_malloc_reserve(buffer_size);
35
40839
  if (data) {
36
40839
    v->buf = data;
37
40839
    v->buf_size = buffer_size;
38
40839
    v->buf_pos = 0;
39
40839
    res = true;
40
  }
41
40839
  return res;
42
}
43
44
6273
bool lbm_finish_flatten(lbm_flat_value_t *v) {
45
  lbm_uint size_words;
46
6273
  if (v->buf_pos % sizeof(lbm_uint) == 0) {
47
    size_words = v->buf_pos / sizeof(lbm_uint);
48
  } else {
49
6273
    size_words = (v->buf_pos / sizeof(lbm_uint)) + 1;
50
  }
51
6273
  if (v->buf_size  <= size_words * sizeof(lbm_uint)) return true;
52
6245
  v->buf_size = size_words * sizeof(lbm_uint);
53
6245
  return (lbm_memory_shrink((lbm_uint*)v->buf, size_words) >= 0);
54
}
55
56
421555
static bool write_byte(lbm_flat_value_t *v, uint8_t b) {
57
421555
  bool res = false;
58
421555
  if (v->buf_size >= v->buf_pos + 1) {
59
421555
    v->buf[v->buf_pos++] = b;
60
421555
    res = true;
61
  }
62
421555
  return res;
63
}
64
65
181860
static bool write_bytes(lbm_flat_value_t *v, uint8_t *data,lbm_uint num_bytes) {
66
181860
  bool res = false;
67
181860
  if (v->buf_size >= v->buf_pos + num_bytes) {
68
181860
    memcpy(v->buf + v->buf_pos, data, num_bytes);
69
181860
    v->buf_pos += num_bytes;
70
181860
    res = true;
71
  }
72
181860
  return res;
73
}
74
75
237063
static bool write_word(lbm_flat_value_t *v, uint32_t w) {
76
237063
  bool res = false;
77
237063
  if (v->buf_size >= v->buf_pos + 4) {
78
237063
    v->buf[v->buf_pos++] = (uint8_t)(w >> 24);
79
237063
    v->buf[v->buf_pos++] = (uint8_t)(w >> 16);
80
237063
    v->buf[v->buf_pos++] = (uint8_t)(w >> 8);
81
237063
    v->buf[v->buf_pos++] = (uint8_t)w;
82
237063
    res = true;
83
  }
84
237063
  return res;
85
}
86
87
84
static bool write_dword(lbm_flat_value_t *v, uint64_t w) {
88
84
  bool res = false;
89
84
  if (v->buf_size >= v->buf_pos + 8) {
90
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 56);
91
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 48);
92
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 40);
93
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 32);
94
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 24);
95
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 16);
96
84
    v->buf[v->buf_pos++] = (uint8_t)(w >> 8);
97
84
    v->buf[v->buf_pos++] = (uint8_t)w;
98
84
    res = true;
99
  }
100
84
  return res;
101
}
102
103
319704
bool f_cons(lbm_flat_value_t *v) {
104
319704
  bool res = false;
105
319704
  if (v->buf_size >= v->buf_pos + 1) {
106
319704
    v->buf[v->buf_pos++] = S_CONS;
107
319704
    res = true;
108
  }
109
319704
  return res;
110
}
111
112
112
bool f_lisp_array(lbm_flat_value_t *v, uint32_t size) {
113
  // arrays are smaller than 2^32 elements long
114
112
  bool res = true;
115

112
  res = res && write_byte(v, S_LBM_LISP_ARRAY);
116

112
  res = res && write_word(v, size); // number of elements.
117
112
  return res;
118
}
119
120
4987
bool f_sym(lbm_flat_value_t *v, lbm_uint sym_id) {
121
4987
  bool res = true;
122

4987
  res = res && write_byte(v,S_SYM_VALUE);
123
  #ifndef LBM64
124

4987
  res = res && write_word(v,sym_id);
125
  #else
126
  res = res && write_dword(v,sym_id);
127
  #endif
128
4987
  return res;
129
}
130
131
63112
bool f_sym_string(lbm_flat_value_t *v, char *str) {
132
63112
  bool res = false;
133
63112
  if (str) {
134
63112
    lbm_uint sym_bytes = strlen(str) + 1;
135

126224
    if (write_byte(v, S_SYM_STRING) &&
136
63112
        write_bytes(v, (uint8_t*)str, sym_bytes)) {
137
63112
      res = true;
138
    }
139
  }
140
63112
  return res;
141
}
142
143
// Potentially a difference between 32/64 bit version.
144
// strlen returns size_t which is different on 32/64 bit platforms.
145
63072
int f_sym_string_bytes(lbm_value sym) {
146
63072
  int res = FLATTEN_VALUE_ERROR_FATAL;
147
63072
  if (lbm_is_symbol(sym)) {
148
63072
    lbm_uint s = lbm_dec_sym(sym);
149
63072
    char *sym_str = (char*)lbm_get_name_by_symbol(s);
150
63072
    if (sym_str) {
151
63072
      lbm_uint sym_bytes = strlen(sym_str) + 1;
152
63072
      res = (int)sym_bytes;
153
    }
154
  }
155
63072
  return res;
156
}
157
158
45626
bool f_i(lbm_flat_value_t *v, lbm_int i) {
159
45626
  bool res = true;
160
#ifndef LBM64
161

45626
  res = res && write_byte(v,S_I28_VALUE);
162

45626
  res = res && write_word(v,(uint32_t)i);
163
#else
164
  res = res && write_byte(v,S_I56_VALUE);
165
  res = res && write_dword(v, (uint64_t)i);
166
#endif
167
45626
  return res;
168
}
169
170
28
bool f_u(lbm_flat_value_t *v, lbm_uint u) {
171
28
  bool res = true;
172
#ifndef LBM64
173

28
  res = res && write_byte(v,S_U28_VALUE);
174

28
  res = res && write_word(v,(uint32_t)u);
175
#else
176
  res = res && write_byte(v,S_U56_VALUE);
177
  res = res && write_dword(v,(uint64_t)u);
178
#endif
179
28
  return res;
180
}
181
182
60648
bool f_b(lbm_flat_value_t *v, uint8_t b) {
183
60648
  bool res = true;
184

60648
  res = res && write_byte(v,S_BYTE_VALUE);
185

60648
  res = res && write_byte(v,b);
186
60648
  return res;
187
}
188
189
29274
bool f_i32(lbm_flat_value_t *v, int32_t w) {
190
29274
  bool res = true;
191

29274
  res = res && write_byte(v, S_I32_VALUE);
192

29274
  res = res && write_word(v, (uint32_t)w);
193
29274
  return res;
194
}
195
196
31122
bool f_u32(lbm_flat_value_t *v, uint32_t w) {
197
31122
  bool res = true;
198

31122
  res = res && write_byte(v, S_U32_VALUE);
199

31122
  res = res && write_word(v, w);
200
31122
  return res;
201
}
202
203
7166
bool f_float(lbm_flat_value_t *v, float f) {
204
7166
  bool res = true;
205

7166
  res = res && write_byte(v, S_FLOAT_VALUE);
206
  uint32_t u;
207
7166
  memcpy(&u, &f, sizeof(uint32_t));
208

7166
  res = res && write_word(v, (uint32_t)u);
209
7166
  return res;
210
}
211
212
28
bool f_double(lbm_flat_value_t *v, double d) {
213
28
  bool res = true;
214

28
  res = res && write_byte(v, S_DOUBLE_VALUE);
215
  uint64_t u;
216
28
  memcpy(&u, &d, sizeof(uint64_t));
217

28
  res = res && write_dword(v, u);
218
28
  return res;
219
}
220
221
28
bool f_i64(lbm_flat_value_t *v, int64_t w) {
222
28
  bool res = true;
223

28
  res = res && write_byte(v, S_I64_VALUE);
224

28
  res = res && write_dword(v, (uint64_t)w);
225
28
  return res;
226
}
227
228
28
bool f_u64(lbm_flat_value_t *v, uint64_t w) {
229
28
  bool res = true;
230

28
  res = res && write_byte(v, S_U64_VALUE);
231

28
  res = res && write_dword(v, w);
232
28
  return res;
233
}
234
235
// num_bytes is specifically an uint32_t
236
118748
bool f_lbm_array(lbm_flat_value_t *v, uint32_t num_bytes, uint8_t *data) {
237
118748
  bool res = write_byte(v, S_LBM_ARRAY);
238

118748
  res = res && write_word(v, num_bytes);
239

118748
  res = res && write_bytes(v, data, num_bytes);
240
118748
  return res;
241
}
242
243
static int flatten_maximum_depth = FLATTEN_VALUE_MAXIMUM_DEPTH;
244
245
28
void lbm_set_max_flatten_depth(int depth) {
246
28
  flatten_maximum_depth = depth;
247
28
}
248
249
int lbm_get_max_flatten_depth(void) {
250
  return flatten_maximum_depth;
251
}
252
253
28
void flatten_error(jmp_buf jb, int val) {
254
28
  longjmp(jb, val);
255
}
256
257
662746
int flatten_value_size_internal(jmp_buf jb, lbm_value v, int depth, bool image) {
258
662746
  if (depth > flatten_maximum_depth) {
259
28
    flatten_error(jb, FLATTEN_VALUE_ERROR_MAXIMUM_DEPTH);
260
  }
261
262
662718
  lbm_uint t = lbm_type_of(v);
263

662718
  if (t >= LBM_POINTER_TYPE_FIRST && t < LBM_POINTER_TYPE_LAST) {
264
    //  Clear constant bit, it is irrelevant to flattening
265
493320
    t = t & ~(LBM_PTR_TO_CONSTANT_BIT);
266
  }
267
268

662718
  if (image && lbm_is_ptr(v) && (v & LBM_PTR_TO_CONSTANT_BIT)) {
269
    // If flattening to image, constants can be stored by reference.
270
    return (sizeof(lbm_uint) + 1); // one byte tag, one word ptr
271
  }
272
273


662718
  switch (t) {
274
313932
  case LBM_TYPE_CONS: {
275
313932
    int res = 0;
276
313932
    int s1 = flatten_value_size_internal(jb,lbm_car(v), depth + 1, image);
277
313848
    if (s1 > 0) {
278
313848
      int s2 = flatten_value_size_internal(jb,lbm_cdr(v), depth + 1, image);
279
313848
      if (s2 > 0) {
280
313848
        res = (1 + s1 + s2);
281
      }
282
    }
283
313848
    return res;
284
  }
285
112
  case LBM_TYPE_LISPARRAY: {
286
112
    int sum = 4 + 1; // sizeof(uint32_t) + 1;
287
112
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(v);
288
112
    if (header) {
289
112
      lbm_value *arrdata = (lbm_value*)header->data;
290
112
      lbm_uint size = header->size / sizeof(lbm_value);
291
476
      for (lbm_uint i = 0; i < size; i ++ ) {
292
364
        sum += flatten_value_size_internal(jb, arrdata[i], depth + 1, image);
293
      }
294
    } else {
295
      flatten_error(jb, FLATTEN_VALUE_ERROR_ARRAY);
296
    }
297
112
    return sum;
298
  }
299
60664
  case LBM_TYPE_BYTE:
300
60664
    return 1 + 1;
301
45662
  case LBM_TYPE_U: /* fall through */
302
  case LBM_TYPE_I:
303
#ifndef LBM64
304
45662
    return 1 + 4;
305
#else
306
    return 1 + 8;
307
#endif
308
60468
  case LBM_TYPE_U32: /* fall through */
309
  case LBM_TYPE_I32:
310
  case LBM_TYPE_FLOAT:
311
60468
    return 1 + 4;
312
84
  case LBM_TYPE_U64: /* fall through */
313
  case LBM_TYPE_I64:
314
  case LBM_TYPE_DOUBLE:
315
84
    return 1 + 8;
316
63072
  case LBM_TYPE_SYMBOL: {
317
63072
    if (!image) {
318
63072
      int s = f_sym_string_bytes(v);
319
63072
      if (s > 0) return 1 + s;
320
      flatten_error(jb, (int)s);
321
    } else {
322
      return 1 + sizeof(lbm_uint);
323
    }
324
  } return 0; // already terminated with error
325
118724
  case LBM_TYPE_ARRAY: {
326
    // Platform dependent size.
327
    // TODO: Something needs to be done to these inconsistencies.
328
118724
    lbm_int s = lbm_heap_array_get_size(v);
329
118724
    if (s > 0)
330
118724
      return 1 + 4 + (int)s;
331
    flatten_error(jb, (int)s);
332
  } return 0; // already terminated with error
333
  default:
334
    return FLATTEN_VALUE_ERROR_CANNOT_BE_FLATTENED;
335
  }
336
}
337
338
34602
int flatten_value_size(lbm_value v, bool image) {
339
  jmp_buf jb;
340
34602
  int r = setjmp(jb);
341
34630
  if (r != 0) {
342
28
    return r;
343
  }
344
34602
  return flatten_value_size_internal(jb, v, 0, image);
345
}
346
347
662466
int flatten_value_c(lbm_flat_value_t *fv, lbm_value v) {
348
349
662466
  lbm_uint t = lbm_type_of(v);
350

662466
  if (t >= LBM_POINTER_TYPE_FIRST && t < LBM_POINTER_TYPE_LAST) {
351
    //  Clear constant bit, it is irrelevant to flattening
352
493108
    t = t & ~(LBM_PTR_TO_CONSTANT_BIT);
353
  }
354
355



662466
  switch (t) {
356
313768
  case LBM_TYPE_CONS: {
357
313768
    bool res = true;
358

313768
    res = res && f_cons(fv);
359
313768
    if (res) {
360
313768
      int fv_r = flatten_value_c(fv, lbm_car(v));
361
313768
      if (fv_r == FLATTEN_VALUE_OK) {
362
313768
        fv_r = flatten_value_c(fv, lbm_cdr(v));
363
      }
364
313768
      return fv_r;
365
    }
366
  }break;
367
112
  case LBM_TYPE_LISPARRAY: {
368
112
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(v);
369
112
    if (header) {
370
112
      lbm_value *arrdata = (lbm_value*)header->data;
371
      // always exact multiple of sizeof(lbm_value)
372
112
      uint32_t size = (uint32_t)(header->size / sizeof(lbm_value));
373
112
      if (!f_lisp_array(fv, size)) return FLATTEN_VALUE_ERROR_NOT_ENOUGH_MEMORY;
374
112
      int fv_r = FLATTEN_VALUE_OK;
375
476
      for (lbm_uint i = 0; i < size; i ++ ) {
376
364
        fv_r =  flatten_value_c(fv, arrdata[i]);
377
364
        if (fv_r != FLATTEN_VALUE_OK) {
378
          break;
379
        }
380
      }
381
112
      return fv_r;
382
    } else {
383
      return FLATTEN_VALUE_ERROR_ARRAY;
384
    }
385
  } break;
386
60648
  case LBM_TYPE_BYTE:
387
60648
    if (f_b(fv, (uint8_t)lbm_dec_as_char(v))) {
388
60648
      return FLATTEN_VALUE_OK;
389
    }
390
    break;
391
28
  case LBM_TYPE_U:
392
28
    if (f_u(fv, lbm_dec_u(v))) {
393
28
      return FLATTEN_VALUE_OK;
394
    }
395
    break;
396
45626
  case LBM_TYPE_I:
397
45626
    if (f_i(fv, lbm_dec_i(v))) {
398
45626
      return FLATTEN_VALUE_OK;
399
    }
400
    break;
401
31122
  case LBM_TYPE_U32:
402
31122
    if (f_u32(fv, lbm_dec_as_u32(v))) {
403
31122
      return FLATTEN_VALUE_OK;
404
    }
405
    break;
406
29274
  case LBM_TYPE_I32:
407
29274
    if (f_i32(fv, lbm_dec_as_i32(v))) {
408
29274
      return FLATTEN_VALUE_OK;
409
    }
410
    break;
411
28
  case LBM_TYPE_U64:
412
28
    if (f_u64(fv, lbm_dec_as_u64(v))) {
413
28
      return FLATTEN_VALUE_OK;
414
    }
415
    break;
416
28
  case LBM_TYPE_I64:
417
28
    if (f_i64(fv, lbm_dec_as_i64(v))) {
418
28
      return FLATTEN_VALUE_OK;
419
    }
420
    break;
421
56
  case LBM_TYPE_FLOAT:
422
56
    if (f_float(fv, lbm_dec_as_float(v))) {
423
56
      return FLATTEN_VALUE_OK;
424
    }
425
    break;
426
28
  case LBM_TYPE_DOUBLE:
427
28
    if (f_double(fv, lbm_dec_as_double(v))) {
428
28
      return FLATTEN_VALUE_OK;
429
    }
430
    break;
431
63056
  case LBM_TYPE_SYMBOL: {
432
63056
    char *sym_str = (char*)lbm_get_name_by_symbol(lbm_dec_sym(v));
433
63056
    if (f_sym_string(fv, sym_str)) {
434
63056
      return FLATTEN_VALUE_OK;
435
    }
436
  } break;
437
118692
  case LBM_TYPE_ARRAY: {
438
118692
    lbm_int s = lbm_heap_array_get_size(v);
439
118692
    const uint8_t *d = lbm_heap_array_get_data_ro(v);
440

118692
    if (s > 0 && d != NULL) {
441
118692
      if (f_lbm_array(fv, (uint32_t)s, (uint8_t*)d)) {
442
118692
        return FLATTEN_VALUE_OK;
443
      }
444
    } else {
445
      return FLATTEN_VALUE_ERROR_ARRAY;
446
    }
447
  }break;
448
  default:
449
    return FLATTEN_VALUE_ERROR_CANNOT_BE_FLATTENED;
450
  }
451
  return FLATTEN_VALUE_ERROR_BUFFER_TOO_SMALL;
452
}
453
454
28
lbm_value handle_flatten_error(int err_val) {
455

28
  switch (err_val) {
456
  case FLATTEN_VALUE_ERROR_CANNOT_BE_FLATTENED:
457
    return ENC_SYM_EERROR;
458
  case FLATTEN_VALUE_ERROR_BUFFER_TOO_SMALL: /* fall through */
459
  case FLATTEN_VALUE_ERROR_FATAL:
460
    return ENC_SYM_FATAL_ERROR;
461
28
  case FLATTEN_VALUE_ERROR_CIRCULAR: /* fall through */
462
  case FLATTEN_VALUE_ERROR_MAXIMUM_DEPTH:
463
28
    return ENC_SYM_EERROR;
464
  case FLATTEN_VALUE_ERROR_ARRAY: /* fall through */
465
  case FLATTEN_VALUE_ERROR_NOT_ENOUGH_MEMORY:
466
    return ENC_SYM_MERROR;
467
  }
468
  return ENC_SYM_NIL;
469
}
470
471
34628
lbm_value flatten_value(lbm_value v) {
472
473
34628
  lbm_value array_cell = lbm_heap_allocate_cell(LBM_TYPE_CONS, ENC_SYM_NIL, ENC_SYM_ARRAY_TYPE);
474
475
34628
  if (array_cell == ENC_SYM_MERROR) {
476
26
    return array_cell;
477
  }
478
479
  lbm_flat_value_t fv;
480
481
34602
  lbm_array_header_t *array = NULL;
482
34602
  int required_mem = flatten_value_size(v, false);
483
34602
  if (required_mem > 0) {
484
34574
    array = (lbm_array_header_t *)lbm_malloc(sizeof(lbm_array_header_t));
485
34574
    if (array == NULL) {
486
8
      lbm_set_car_and_cdr(array_cell, ENC_SYM_NIL, ENC_SYM_NIL);
487
8
      return ENC_SYM_MERROR;
488
    }
489
490
34566
    bool r = lbm_start_flatten(&fv, (lbm_uint)required_mem);
491
34566
    if (!r) {
492
      lbm_free(array);
493
      lbm_set_car_and_cdr(array_cell, ENC_SYM_NIL, ENC_SYM_NIL);
494
      return ENC_SYM_MERROR;
495
    }
496
497
34566
    if (flatten_value_c(&fv, v) == FLATTEN_VALUE_OK) {
498
      // it would be wasteful to run finish_flatten here.
499
34566
      r = true;
500
    } else {
501
      r = false;
502
    }
503
504
34566
    if (r)  {
505
      // lift flat_value
506
34566
      array->data = (lbm_uint*)fv.buf;
507
34566
      array->size = fv.buf_size;
508
34566
      lbm_set_car(array_cell, (lbm_uint)array);
509
34566
      array_cell = lbm_set_ptr_type(array_cell, LBM_TYPE_ARRAY);
510
34566
      return array_cell;
511
    }
512
  }
513
28
  lbm_set_car_and_cdr(array_cell, ENC_SYM_NIL, ENC_SYM_NIL);
514
28
  return handle_flatten_error(required_mem);
515
}
516
517
// ------------------------------------------------------------
518
// Unflattening
519
60650
static bool extract_byte(lbm_flat_value_t *v, uint8_t *r) {
520
60650
  if (v->buf_size >= v->buf_pos + 1) {
521
60650
    *r = v->buf[v->buf_pos++];
522
60650
    return true;
523
  }
524
  return false;
525
}
526
527
236489
static bool extract_word(lbm_flat_value_t *v, uint32_t *r) {
528
236489
  bool res = false;
529
236489
  if (v->buf_size >= v->buf_pos + 4) {
530
236489
    uint32_t tmp = 0;
531
236489
    tmp |= (lbm_value)v->buf[v->buf_pos++];
532
236489
    tmp = tmp << 8 | (uint32_t)v->buf[v->buf_pos++];
533
236489
    tmp = tmp << 8 | (uint32_t)v->buf[v->buf_pos++];
534
236489
    tmp = tmp << 8 | (uint32_t)v->buf[v->buf_pos++];
535
236489
    *r = tmp;
536
236489
    res = true;
537
  }
538
236489
  return res;
539
}
540
541
84
static bool extract_dword(lbm_flat_value_t *v, uint64_t *r) {
542
84
  bool res = false;
543
84
  if (v->buf_size >= v->buf_pos + 8) {
544
84
    uint64_t tmp = 0;
545
84
    tmp |= (lbm_value)v->buf[v->buf_pos++];
546
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
547
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
548
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
549
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
550
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
551
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
552
84
    tmp = tmp << 8 | (uint64_t)v->buf[v->buf_pos++];
553
84
    *r = tmp;
554
84
    res = true;;
555
  }
556
84
  return res;
557
}
558
559
359899
static int lbm_unflatten_value_atom(lbm_flat_value_t *v, lbm_value *res) {
560
359899
  if (v->buf_size == v->buf_pos) return UNFLATTEN_MALFORMED;
561
562
359899
  uint8_t curr = v->buf[v->buf_pos++];
563
564




359899
  switch(curr) {
565
  case S_CONS: {
566
    return UNFLATTEN_MALFORMED;
567
  }
568
  case S_CONSTANT_REF: {
569
    lbm_uint tmp;
570
    bool b;
571
#ifndef LBM64
572
    b = extract_word(v, &tmp);
573
#else
574
    b = extract_dword(v, &tmp);
575
#endif
576
    if (b) {
577
      *res = tmp;
578
      return UNFLATTEN_OK;
579
    }
580
    return UNFLATTEN_MALFORMED;
581
  }
582
4983
  case S_SYM_VALUE: {
583
    lbm_uint tmp;
584
    bool b;
585
#ifndef LBM64
586
4983
    b = extract_word(v, &tmp);
587
#else
588
    b = extract_dword(v, &tmp);
589
#endif
590
4983
    if (b) {
591
4983
      *res = lbm_enc_sym(tmp);
592
4983
      return UNFLATTEN_OK;
593
    }
594
    return UNFLATTEN_MALFORMED;
595
  }
596
60650
  case S_BYTE_VALUE: {
597
    uint8_t tmp;
598
60650
    bool b = extract_byte(v, &tmp);
599
60650
    if (b) {
600
60650
      *res = lbm_enc_char((uint8_t)tmp);
601
60650
      return UNFLATTEN_OK;
602
    }
603
    return UNFLATTEN_MALFORMED;
604
  }
605
45486
  case S_I28_VALUE: {
606
    uint32_t tmp;
607
    bool b;
608
45486
    b = extract_word(v, &tmp);
609
45486
    if (b) {
610
45486
      *res = lbm_enc_i((int32_t)tmp);
611
45486
      return UNFLATTEN_OK;
612
    }
613
    return UNFLATTEN_MALFORMED;
614
  }
615
28
  case S_U28_VALUE: {
616
    uint32_t tmp;
617
    bool b;
618
28
    b = extract_word(v, &tmp);
619
28
    if (b) {
620
28
      *res = lbm_enc_u((uint32_t)tmp);
621
28
      return UNFLATTEN_OK;
622
    }
623
    return UNFLATTEN_MALFORMED;
624
  }
625
  case S_I56_VALUE: {
626
    uint64_t tmp;
627
    bool b;
628
    b = extract_dword(v, &tmp);
629
    if (b) {
630
#ifndef LBM64
631
      *res = lbm_enc_i64((int64_t)tmp);
632
#else
633
      *res = lbm_enc_i((int64_t)tmp);
634
#endif
635
      return UNFLATTEN_OK;
636
    }
637
    return UNFLATTEN_MALFORMED;
638
  }
639
  case S_U56_VALUE: {
640
    uint64_t tmp;
641
    bool b;
642
    b = extract_dword(v, &tmp);
643
    if (b) {
644
#ifndef LBM64
645
      *res = lbm_enc_u64(tmp);
646
#else
647
      *res = lbm_enc_u(tmp);
648
#endif
649
      return UNFLATTEN_OK;
650
    }
651
    return UNFLATTEN_MALFORMED;
652
  }
653
7164
  case S_FLOAT_VALUE: {
654
    uint32_t tmp;
655
    bool b;
656
7164
    b = extract_word(v, &tmp);
657
7164
    if (b) {
658
      lbm_float f;
659
7164
      memcpy(&f, &tmp, sizeof(lbm_float));
660
7164
      lbm_value im  = lbm_enc_float(f);
661
7164
      if (lbm_is_symbol_merror(im)) {
662
        return UNFLATTEN_GC_RETRY;
663
      }
664
7164
      *res = im;
665
7164
      return UNFLATTEN_OK;
666
    }
667
    return UNFLATTEN_MALFORMED;
668
  }
669
28
  case S_DOUBLE_VALUE: {
670
    uint64_t tmp;
671
    bool b;
672
28
    b = extract_dword(v, &tmp);
673
28
    if (b) {
674
      double f;
675
28
      memcpy(&f, &tmp, sizeof(uint64_t));
676
28
      lbm_value im  = lbm_enc_double(f);
677
28
      if (lbm_is_symbol_merror(im)) {
678
        return UNFLATTEN_GC_RETRY;
679
      }
680
28
      *res = im;
681
28
      return UNFLATTEN_OK;
682
    }
683
    return UNFLATTEN_MALFORMED;
684
  }
685
29130
  case S_I32_VALUE: {
686
   uint32_t tmp;
687
29130
    if (extract_word(v, &tmp)) {
688
29130
      lbm_value im = lbm_enc_i32((int32_t)tmp);
689
29130
      if (lbm_is_symbol_merror(im)) {
690
8
        return UNFLATTEN_GC_RETRY;
691
      }
692
29122
      *res = im;
693
29122
      return UNFLATTEN_OK;
694
    }
695
    return UNFLATTEN_MALFORMED;
696
  }
697
31194
  case S_U32_VALUE: {
698
    uint32_t tmp;
699
31194
    if (extract_word(v, &tmp)) {
700
31194
      lbm_value im = lbm_enc_u32(tmp);
701
31194
      if (lbm_is_symbol_merror(im)) {
702
20
        return UNFLATTEN_GC_RETRY;
703
      }
704
31174
      *res = im;
705
31174
      return UNFLATTEN_OK;
706
    }
707
    return UNFLATTEN_MALFORMED;
708
  }
709
28
  case S_I64_VALUE: {
710
28
   uint64_t tmp = 0;
711
28
    if (extract_dword(v, &tmp)) {
712
28
      lbm_value im = lbm_enc_i64((int64_t)tmp);
713
28
      if (lbm_is_symbol_merror(im)) {
714
        return UNFLATTEN_GC_RETRY;
715
      }
716
28
      *res = im;
717
28
      return UNFLATTEN_OK;
718
    }
719
    return UNFLATTEN_MALFORMED;
720
  }
721
28
  case S_U64_VALUE: {
722
28
    uint64_t tmp = 0;
723
28
    if (extract_dword(v, &tmp)) {
724
28
      lbm_value im = lbm_enc_u64(tmp);
725
28
      if (lbm_is_symbol_merror(im)) {
726
        return UNFLATTEN_GC_RETRY;
727
      }
728
28
      *res = im;
729
28
      return UNFLATTEN_OK;
730
    }
731
    return UNFLATTEN_MALFORMED;
732
  }
733
118392
  case S_LBM_ARRAY: {
734
    uint32_t num_elt;
735
118392
    if (extract_word(v, &num_elt)) {
736
118392
      if (lbm_heap_allocate_array(res, num_elt)) {
737
118254
        lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(*res);
738
118254
        lbm_uint num_bytes = num_elt;
739
118254
        memcpy(arr->data, v->buf + v->buf_pos, num_bytes);
740
118254
        v->buf_pos += num_bytes;
741
      } else {
742
138
        return UNFLATTEN_GC_RETRY;
743
      }
744
118254
      return UNFLATTEN_OK;
745
    }
746
    return UNFLATTEN_MALFORMED;
747
  }
748
62788
  case S_SYM_STRING: {
749
    lbm_uint sym_id;
750
62788
    if (lbm_add_symbol((char *)(v->buf + v->buf_pos), &sym_id)) {
751
62788
      lbm_uint num_bytes = strlen((char*)(v->buf + v->buf_pos)) + 1;
752
62788
      v->buf_pos += num_bytes;
753
62788
      *res = lbm_enc_sym(sym_id);
754
62788
      return UNFLATTEN_OK;
755
    }
756
    return UNFLATTEN_GC_RETRY;
757
  }
758
  default:
759
    return UNFLATTEN_MALFORMED;
760
  }
761
}
762
763
// ////////////////////////////////////////////////////////////
764
// Pointer-reversal-esque "stackless" deserialization of
765
// flattened (serialized) trees.
766
//
767
// Initially:
768
//   curr = LBM_NULL;    v->buf = { ... }
769
//
770
// FORWARDS PHASE:
771
// Cons case:
772
//   Reading conses from the buffer builds a backpointing list.
773
//   Placeholder element acts as a 1 bit "visited" field.
774
//
775
//   curr = p;   v->buf = {S_CONS, ... }
776
//   =>
777
//   curr = [p, placeholder]; v->buf = { ... }
778
//
779
// Lisp array case:
780
//   An Array tag in the buffer leads to the creation of an array
781
//   with a backptr in the last element position. Placeholder element
782
//   is not needed as LBM-Arrays have a built in index field (used by GC)
783
//   that can keep a count of how far along the array we have progressed.
784
//
785
//   curr = p;  v->buf = {S_LBM_LISP_ARRAY, ... }
786
//   =>
787
//   curr = [| nil ... p |]; v->buf = { ... }
788
//
789
// Atom case:
790
//   Reading an atom triggers a backwards traversal along the backpointer
791
//   structure.
792
//
793
//   curr = X;   v->buf = {any_atom, ... } example integer, string.
794
//   =>
795
//   val = unflatten_atom(v->buf);      v->buf = { ... }
796
//
797
//   BACKWARDS PHASE: Start the backwards traversal:
798
//
799
//   Case on X
800
//     LBM_NULL;
801
//     => Done! result = val
802
//
803
//     [p, placeholder];
804
//     =>
805
//     [p, val]   Base case. Finishes back traversal.
806
//                Go back to FORWARDS PHASE.
807
//
808
//
809
//     [p, val0];
810
//     =>
811
//     tmp = [val0, val];  val = tmp;  curr = p;   continue backwards with value pointing to recently constructed final subresult.
812
//
813
//
814
//     [| a b nil ... p |]
815
//     =>
816
//     [| a b val ... p |]   Base case. Finishes back traversal.
817
//                           Array internal index field keeps track of write position.
818
//                           Go back to FORWARDS PHASE.
819
//
820
//
821
//    [| a0 a1 ... an p |]
822
//    =>
823
//    tmp =  [| a0 a1 ... an val |];  val = tmp; curr = p; continue backwards
824
//
825
40891
static int lbm_unflatten_value_nostack(lbm_flat_value_t *v, lbm_value *res) {
826
40891
  bool done = false;
827
828
40891
  lbm_value curr = lbm_enc_cons_ptr(LBM_PTR_NULL);
829
679105
  while (!done) {
830
679105
    if (v->buf[v->buf_pos] == S_CONS) {
831
319094
      lbm_value tmp = curr;
832
319094
      curr = lbm_cons(tmp, ENC_SYM_PLACEHOLDER);
833
319094
      if (lbm_is_symbol_merror(curr)) return UNFLATTEN_GC_RETRY;
834
319008
      v->buf_pos ++;
835
360011
    } else if (v->buf[v->buf_pos] == S_LBM_LISP_ARRAY) {
836
      uint32_t size;
837
112
      v->buf_pos ++;
838
112
      bool b = extract_word(v, &size);
839
112
      if (b) {
840
        lbm_value array;
841
112
        lbm_heap_allocate_lisp_array(&array, size);
842
112
        lbm_array_header_extended_t *header = (lbm_array_header_extended_t*)lbm_car(array);
843
112
        lbm_value *arrdata = (lbm_value*)header->data;
844
112
        if (lbm_is_symbol_merror(array)) return UNFLATTEN_GC_RETRY;
845
112
        header->index = 0;
846
112
        arrdata[size-1] = curr; // backptr
847
112
        curr = array;
848
      } else {
849
        return UNFLATTEN_MALFORMED;
850
      }
851
359899
    } else if (v->buf[v->buf_pos] == 0) {
852
      return UNFLATTEN_MALFORMED;
853
    } else {
854
      lbm_value unflattened;
855
359899
      if (lbm_unflatten_value_atom(v, &unflattened) != UNFLATTEN_OK) {
856
166
        return UNFLATTEN_MALFORMED;
857
      }
858
359733
      lbm_value val0 = unflattened;
859

1315027
      while (lbm_dec_ptr(curr) != LBM_PTR_NULL &&
860
637194
             lbm_cdr(curr) != ENC_SYM_PLACEHOLDER) { // has done left
861
318352
        if ( lbm_type_of(curr) == LBM_TYPE_LISPARRAY) {
862
364
          lbm_array_header_extended_t *header = (lbm_array_header_extended_t*)lbm_car(curr);
863
364
          lbm_value *arrdata = (lbm_value*)header->data;
864
364
          uint32_t arrlen = header->size / sizeof(lbm_value);
865
364
          if (header->index == arrlen - 1) {
866
112
            lbm_value prev = arrdata[arrlen-1];
867
112
            header->index = 0;
868
112
            arrdata[arrlen-1] = val0;
869
112
            val0 = curr;
870
112
            curr = prev;
871
          } else {
872
252
            arrdata[header->index++] = val0;
873
252
            break;
874
          }
875
        } else {
876
317988
          lbm_value prev = lbm_car(curr);
877
317988
          lbm_value r0   = lbm_cdr(curr);
878
317988
          lbm_set_cdr(curr, val0);
879
317988
          lbm_set_car(curr, r0);
880
317988
          val0 = curr;
881
317988
          curr = prev;
882
        }
883
      }
884
359733
      if (lbm_dec_ptr(curr) == LBM_PTR_NULL) {
885
40639
        *res = val0; // done
886
40639
        break;
887
319094
      } else if (lbm_type_of(curr) == LBM_TYPE_LISPARRAY) {
888
        // Do nothing in this case. It has been arranged..
889
318842
      } else if (lbm_cdr(curr) == ENC_SYM_PLACEHOLDER) {
890
318842
        lbm_set_cdr(curr, val0);
891
      } else {
892
        return UNFLATTEN_MALFORMED;
893
      }
894
    }
895
  }
896
40639
  return UNFLATTEN_OK;
897
}
898
899
40805
bool lbm_unflatten_value(lbm_flat_value_t *v, lbm_value *res) {
900
40805
  bool b = false;
901
#ifdef LBM_ALWAYS_GC
902
  lbm_perform_gc();
903
#endif
904
40805
  int r = lbm_unflatten_value_nostack(v,res);
905
40805
  if (r == UNFLATTEN_GC_RETRY) {
906
86
    lbm_perform_gc();
907
86
    v->buf_pos = 0;
908
86
    r = lbm_unflatten_value_nostack(v,res);
909
  }
910
40805
  if (r == UNFLATTEN_MALFORMED) {
911
166
    *res = ENC_SYM_EERROR;
912
40639
  } else if (r == UNFLATTEN_GC_RETRY) {
913
    *res = ENC_SYM_MERROR;
914
  } else {
915
40639
    b = true;
916
  }
917
  // Do not free the flat value buffer here.
918
  // there are 2 cases:
919
  // 1: unflatten was called from lisp code -> GC removes the buffer.
920
  // 2: unflatten called from event processing -> event processor frees buffer.
921
40805
  return b;
922
}