GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/heap.c Lines: 687 937 73.3 %
Date: 2025-04-14 11:29:35 Branches: 246 403 61.0 %

Line Branch Exec Source
1
/*
2
    Copyright 2018, 2020, 2022 - 2025 Joel Svensson  svenssonjoel@yahoo.se
3
                          2022        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 <stdio.h>
20
#include <stdlib.h>
21
#include <stdint.h>
22
#include <stdarg.h>
23
#include <inttypes.h>
24
#include <lbm_memory.h>
25
#include <lbm_custom_type.h>
26
#include <lbm_defrag_mem.h>
27
#include <lbm_image.h>
28
29
30
#include "heap.h"
31
#include "symrepr.h"
32
#include "stack.h"
33
#include "lbm_channel.h"
34
#include "platform_mutex.h"
35
#include "eval_cps.h"
36
#ifdef VISUALIZE_HEAP
37
#include "heap_vis.h"
38
#endif
39
40
72136448
static inline lbm_value lbm_set_gc_mark(lbm_value x) {
41
72136448
  return x | LBM_GC_MARKED;
42
}
43
71939032
static inline lbm_value lbm_clr_gc_mark(lbm_value x) {
44
71939032
  return x & ~LBM_GC_MASK;
45
}
46
47
1484464822
static inline bool lbm_get_gc_mark(lbm_value x) {
48
1484464822
  return x & LBM_GC_MASK;
49
}
50
51
static inline void gc_mark(lbm_value c) {
52
  //c must be a cons cell.
53
  lbm_cons_t *cell = lbm_ref_cell(c);
54
  cell->cdr = lbm_set_gc_mark(cell->cdr);
55
}
56
57
static inline bool gc_marked(lbm_value c) {
58
  lbm_cons_t *cell = lbm_ref_cell(c);
59
  return lbm_get_gc_mark(cell->cdr);
60
}
61
62
static inline void gc_clear_mark(lbm_value c) {
63
  //c must be a cons cell.
64
  lbm_cons_t *cell = lbm_ref_cell(c);
65
  cell->cdr = lbm_clr_gc_mark(cell->cdr);
66
}
67
68
// flag is the same bit as mark, but in car
69
static inline bool lbm_get_gc_flag(lbm_value x) {
70
  return x & LBM_GC_MARKED;
71
}
72
73
static inline lbm_value lbm_set_gc_flag(lbm_value x) {
74
  return x | LBM_GC_MARKED;
75
}
76
77
static inline lbm_value lbm_clr_gc_flag(lbm_value x) {
78
  return x & ~LBM_GC_MASK;
79
}
80
81
82
lbm_heap_state_t lbm_heap_state;
83
84
lbm_const_heap_t *lbm_const_heap_state;
85
86
lbm_cons_t *lbm_heaps[2] = {NULL, NULL};
87
88
static mutex_t lbm_const_heap_mutex;
89
static bool    lbm_const_heap_mutex_initialized = false;
90
91
static mutex_t lbm_mark_mutex;
92
static bool    lbm_mark_mutex_initialized = false;
93
94
#ifdef USE_GC_PTR_REV
95
void lbm_gc_lock(void) {
96
  mutex_lock(&lbm_mark_mutex);
97
}
98
void lbm_gc_unlock(void) {
99
  mutex_unlock(&lbm_mark_mutex);
100
}
101
#else
102
void lbm_gc_lock(void) {
103
}
104
void lbm_gc_unlock(void) {
105
}
106
#endif
107
108
/****************************************************/
109
/* ENCODERS DECODERS                                */
110
111
2840478
lbm_value lbm_enc_i32(int32_t x) {
112
#ifndef LBM64
113
2840478
  lbm_value i = lbm_cons((lbm_uint)x, ENC_SYM_RAW_I_TYPE);
114
2840478
  if (lbm_type_of(i) == LBM_TYPE_SYMBOL) return i;
115
2839084
  return lbm_set_ptr_type(i, LBM_TYPE_I32);
116
#else
117
  return (((lbm_uint)x) << LBM_VAL_SHIFT) | LBM_TYPE_I32;
118
#endif
119
}
120
121
3679914
lbm_value lbm_enc_u32(uint32_t x) {
122
#ifndef LBM64
123
3679914
  lbm_value u = lbm_cons(x, ENC_SYM_RAW_U_TYPE);
124
3679914
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
125
3679042
  return lbm_set_ptr_type(u, LBM_TYPE_U32);
126
#else
127
  return (((lbm_uint)x) << LBM_VAL_SHIFT) | LBM_TYPE_U32;
128
#endif
129
}
130
131
229868948
lbm_value lbm_enc_float(float x) {
132
#ifndef LBM64
133
  lbm_uint t;
134
229868948
  memcpy(&t, &x, sizeof(lbm_float));
135
229868948
  lbm_value f = lbm_cons(t, ENC_SYM_RAW_F_TYPE);
136
229868948
  if (lbm_type_of(f) == LBM_TYPE_SYMBOL) return f;
137
229725312
  return lbm_set_ptr_type(f, LBM_TYPE_FLOAT);
138
#else
139
  lbm_uint t = 0;
140
  memcpy(&t, &x, sizeof(float));
141
  return (((lbm_uint)t) << LBM_VAL_SHIFT) | LBM_TYPE_FLOAT;
142
#endif
143
}
144
145
#ifndef LBM64
146
8426306
static lbm_value enc_64_on_32(uint8_t *source, lbm_uint type_qual, lbm_uint type) {
147
8426306
  lbm_value res = lbm_cons(ENC_SYM_NIL,ENC_SYM_NIL);
148
8426306
  if (lbm_type_of(res) != LBM_TYPE_SYMBOL) {
149
8423984
    uint8_t* storage = lbm_malloc(sizeof(uint64_t));
150
8423984
    if (storage) {
151
8421356
      memcpy(storage,source, sizeof(uint64_t));
152
8421356
      lbm_set_car_and_cdr(res, (lbm_uint)storage,  type_qual);
153
8421356
      res = lbm_set_ptr_type(res, type);
154
    } else {
155
2628
      res = ENC_SYM_MERROR;
156
    }
157
  }
158
8426306
  return res;
159
}
160
#endif
161
162
4491380
lbm_value lbm_enc_i64(int64_t x) {
163
#ifndef LBM64
164
4491380
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_I_TYPE, LBM_TYPE_I64);
165
#else
166
  lbm_value u = lbm_cons((uint64_t)x, ENC_SYM_RAW_I_TYPE);
167
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
168
  return lbm_set_ptr_type(u, LBM_TYPE_I64);
169
#endif
170
}
171
172
3369218
lbm_value lbm_enc_u64(uint64_t x) {
173
#ifndef LBM64
174
3369218
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_U_TYPE, LBM_TYPE_U64);
175
#else
176
  lbm_value u = lbm_cons(x, ENC_SYM_RAW_U_TYPE);
177
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
178
  return lbm_set_ptr_type(u, LBM_TYPE_U64);
179
#endif
180
}
181
182
565708
lbm_value lbm_enc_double(double x) {
183
#ifndef LBM64
184
565708
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_F_TYPE, LBM_TYPE_DOUBLE);
185
#else
186
  lbm_uint t;
187
  memcpy(&t, &x, sizeof(double));
188
  lbm_value f = lbm_cons(t, ENC_SYM_RAW_F_TYPE);
189
  if (lbm_type_of(f) == LBM_TYPE_SYMBOL) return f;
190
  return lbm_set_ptr_type(f, LBM_TYPE_DOUBLE);
191
#endif
192
}
193
194
// Type specific (as opposed to the dec_as_X) functions
195
// should only be run on values KNOWN to represent a value of the type
196
// that the decoder decodes.
197
198
333511290
float lbm_dec_float(lbm_value x) {
199
#ifndef LBM64
200
  float f_tmp;
201
333511290
  lbm_uint tmp = lbm_car(x);
202
333511290
  memcpy(&f_tmp, &tmp, sizeof(float));
203
333511290
  return f_tmp;
204
#else
205
  uint32_t tmp = (uint32_t)(x >> LBM_VAL_SHIFT);
206
  float f_tmp;
207
  memcpy(&f_tmp, &tmp, sizeof(float));
208
  return f_tmp;
209
#endif
210
}
211
212
564784
double lbm_dec_double(lbm_value x) {
213
#ifndef LBM64
214
564784
  double d = 0.0;
215
564784
  if (lbm_is_ptr(x)) {
216
564784
    uint32_t *data = (uint32_t*)lbm_ref_cell(x)->car;
217
564784
    memcpy(&d, data, sizeof(double));
218
  }
219
564784
  return d;
220
#else
221
  double f_tmp;
222
  lbm_uint tmp = lbm_car(x);
223
  memcpy(&f_tmp, &tmp, sizeof(double));
224
  return f_tmp;
225
#endif
226
}
227
228
7010126
uint64_t lbm_dec_u64(lbm_value x) {
229
#ifndef LBM64
230
7010126
  uint64_t u = 0;
231
7010126
  if (lbm_is_ptr(x)) {
232
7010126
    uint32_t *data = (uint32_t*)lbm_ref_cell(x)->car;
233
7010126
    memcpy(&u, data, 8);
234
  }
235
7010126
  return u;
236
#else
237
  return (uint64_t)lbm_car(x);
238
#endif
239
}
240
241
9251868
int64_t lbm_dec_i64(lbm_value x) {
242
#ifndef LBM64
243
9251868
  int64_t i = 0;
244
9251868
  if (lbm_is_ptr(x)) {
245
9251868
    uint32_t *data = (uint32_t*)lbm_ref_cell(x)->car;
246
9251868
    memcpy(&i, data, 8);
247
  }
248
9251868
  return i;
249
#else
250
  return (int64_t)lbm_car(x);
251
#endif
252
}
253
254
791056
char *lbm_dec_str(lbm_value val) {
255
791056
  char *res = 0;
256
791056
  if (lbm_is_array_r(val)) {
257
790860
    lbm_array_header_t *array = (lbm_array_header_t *)lbm_car(val);
258
790860
    if (array) {
259
790860
      res = (char *)array->data;
260
    }
261
  }
262
791056
  return res;
263
}
264
265
599354
lbm_array_header_t *lbm_dec_array_r(lbm_value val) {
266
599354
  lbm_array_header_t *array = NULL;
267
599354
  if (lbm_is_array_r(val)) {
268
598010
    array = (lbm_array_header_t *)lbm_car(val);
269
  }
270
599354
  return array;
271
}
272
273
59528
lbm_array_header_t *lbm_dec_array_rw(lbm_value val) {
274
59528
  lbm_array_header_t *array = NULL;
275
59528
  if (lbm_is_array_rw(val)) {
276
59444
    array = (lbm_array_header_t *)lbm_car(val);
277
  }
278
59528
  return array;
279
}
280
281
lbm_array_header_t *lbm_dec_lisp_array_r(lbm_value val) {
282
  lbm_array_header_t *array = NULL;
283
  if (lbm_is_lisp_array_r(val)) {
284
    array = (lbm_array_header_t *)lbm_car(val);
285
  }
286
  return array;
287
}
288
289
lbm_array_header_t *lbm_dec_lisp_array_rw(lbm_value val) {
290
  lbm_array_header_t *array = NULL;
291
  if (lbm_is_lisp_array_rw(val)) {
292
    array = (lbm_array_header_t *)lbm_car(val);
293
  }
294
  return array;
295
}
296
297
11225307
lbm_char_channel_t *lbm_dec_channel(lbm_value val) {
298
11225307
  lbm_char_channel_t *res = NULL;
299
300
11225307
  if (lbm_type_of(val) == LBM_TYPE_CHANNEL) {
301
11225307
    res = (lbm_char_channel_t *)lbm_car(val);
302
  }
303
11225307
  return res;
304
}
305
306
lbm_uint lbm_dec_custom(lbm_value val) {
307
  lbm_uint res = 0;
308
  if (lbm_type_of(val) == LBM_TYPE_CUSTOM) {
309
    res = (lbm_uint)lbm_car(val);
310
  }
311
  return res;
312
}
313
314
60900
uint8_t lbm_dec_as_char(lbm_value a) {
315
60900
  uint8_t r = 0;
316


60900
  switch (lbm_type_of_functional(a)) {
317
60676
  case LBM_TYPE_CHAR:
318
60676
    r = (uint8_t)lbm_dec_char(a); break;
319
28
  case LBM_TYPE_I:
320
28
    r = (uint8_t)lbm_dec_i(a); break;
321
28
  case LBM_TYPE_U:
322
28
    r = (uint8_t)lbm_dec_u(a); break;
323
28
  case LBM_TYPE_I32:
324
28
    r = (uint8_t)lbm_dec_i32(a); break;
325
28
  case LBM_TYPE_U32:
326
28
    r = (uint8_t)lbm_dec_u32(a); break;
327
28
  case LBM_TYPE_FLOAT:
328
28
    r = (uint8_t)lbm_dec_float(a); break;
329
28
  case LBM_TYPE_I64:
330
28
    r = (uint8_t)lbm_dec_i64(a); break;
331
28
  case LBM_TYPE_U64:
332
28
    r = (uint8_t)lbm_dec_u64(a); break;
333
28
  case LBM_TYPE_DOUBLE:
334
28
    r = (uint8_t) lbm_dec_double(a); break;
335
  }
336
60900
  return r;
337
}
338
339
8808544
uint32_t lbm_dec_as_u32(lbm_value a) {
340
8808544
  uint32_t r = 0;
341


8808544
  switch (lbm_type_of_functional(a)) {
342
561938
  case LBM_TYPE_CHAR:
343
561938
    r = (uint32_t)lbm_dec_char(a); break;
344
1639279
  case LBM_TYPE_I:
345
1639279
    r = (uint32_t)lbm_dec_i(a); break;
346
1812055
  case LBM_TYPE_U:
347
1812055
    r = (uint32_t)lbm_dec_u(a); break;
348
4795104
  case LBM_TYPE_I32: /* fall through */
349
  case LBM_TYPE_U32:
350
4795104
    r = (uint32_t)lbm_dec_u32(a); break;
351
28
  case LBM_TYPE_FLOAT:
352
28
    r = (uint32_t)lbm_dec_float(a); break;
353
28
  case LBM_TYPE_I64:
354
28
    r = (uint32_t)lbm_dec_i64(a); break;
355
84
  case LBM_TYPE_U64:
356
84
    r = (uint32_t)lbm_dec_u64(a); break;
357
28
  case LBM_TYPE_DOUBLE:
358
28
    r = (uint32_t)lbm_dec_double(a); break;
359
  }
360
8808544
  return r;
361
}
362
363
242350062
int32_t lbm_dec_as_i32(lbm_value a) {
364
242350062
  int32_t r = 0;
365


242350062
  switch (lbm_type_of_functional(a)) {
366
6130040
  case LBM_TYPE_CHAR:
367
6130040
    r = (int32_t)lbm_dec_char(a); break;
368
232537490
  case LBM_TYPE_I:
369
232537490
    r = (int32_t)lbm_dec_i(a); break;
370
8196
  case LBM_TYPE_U:
371
8196
    r = (int32_t)lbm_dec_u(a); break;
372
3674140
  case LBM_TYPE_I32:
373
3674140
    r = (int32_t)lbm_dec_i32(a); break;
374
28
  case LBM_TYPE_U32:
375
28
    r = (int32_t)lbm_dec_u32(a); break;
376
28
  case LBM_TYPE_FLOAT:
377
28
    r = (int32_t)lbm_dec_float(a); break;
378
56
  case LBM_TYPE_I64:
379
56
    r = (int32_t)lbm_dec_i64(a); break;
380
56
  case LBM_TYPE_U64:
381
56
    r = (int32_t)lbm_dec_u64(a); break;
382
28
  case LBM_TYPE_DOUBLE:
383
28
    r = (int32_t) lbm_dec_double(a); break;
384
  }
385
242350062
  return r;
386
}
387
388
6728328
int64_t lbm_dec_as_i64(lbm_value a) {
389
6728328
  int64_t r = 0;
390


6728328
  switch (lbm_type_of_functional(a)) {
391
562230
  case LBM_TYPE_CHAR:
392
562230
    r = (int64_t)lbm_dec_char(a); break;
393
1402474
  case LBM_TYPE_I:
394
1402474
    r = (int64_t)lbm_dec_i(a); break;
395
168
  case LBM_TYPE_U:
396
168
    r = (int64_t)lbm_dec_u(a); break;
397
168
  case LBM_TYPE_I32:
398
168
    r = (int64_t)lbm_dec_i32(a); break;
399
168
  case LBM_TYPE_U32:
400
168
    r = (int64_t)lbm_dec_u32(a); break;
401
56
  case LBM_TYPE_FLOAT:
402
56
    r = (int64_t)lbm_dec_float(a); break;
403
4762896
  case LBM_TYPE_I64:
404
4762896
    r = (int64_t)lbm_dec_i64(a); break;
405
112
  case LBM_TYPE_U64:
406
112
    r = (int64_t)lbm_dec_u64(a); break;
407
56
  case LBM_TYPE_DOUBLE:
408
56
    r = (int64_t) lbm_dec_double(a); break;
409
  }
410
6728328
  return r;
411
}
412
413
4486502
uint64_t lbm_dec_as_u64(lbm_value a) {
414
4486502
  uint64_t r = 0;
415


4486502
  switch (lbm_type_of_functional(a)) {
416
562202
  case LBM_TYPE_CHAR:
417
562202
    r = (uint64_t)lbm_dec_char(a); break;
418
280592
  case LBM_TYPE_I:
419
280592
    r = (uint64_t)lbm_dec_i(a); break;
420
168
  case LBM_TYPE_U:
421
168
    r = (uint64_t)lbm_dec_u(a); break;
422
168
  case LBM_TYPE_I32:
423
168
    r = (uint64_t)lbm_dec_i32(a); break;
424
168
  case LBM_TYPE_U32:
425
168
    r = (uint64_t)lbm_dec_u32(a); break;
426
56
  case LBM_TYPE_FLOAT:
427
56
    r = (uint64_t)lbm_dec_float(a); break;
428
168
  case LBM_TYPE_I64:
429
168
    r = (uint64_t)lbm_dec_i64(a); break;
430
3642924
  case LBM_TYPE_U64:
431
3642924
    r = (uint64_t)lbm_dec_u64(a); break;
432
56
  case LBM_TYPE_DOUBLE:
433
56
    r = (uint64_t)lbm_dec_double(a); break;
434
  }
435
4486502
  return r;
436
}
437
438
58352
lbm_uint lbm_dec_as_uint(lbm_value a) {
439
58352
  lbm_uint r = 0;
440


58352
  switch (lbm_type_of_functional(a)) {
441
  case LBM_TYPE_CHAR:
442
    r = (lbm_uint)lbm_dec_char(a); break;
443
58352
  case LBM_TYPE_I:
444
58352
    r = (lbm_uint)lbm_dec_i(a); break;
445
  case LBM_TYPE_U:
446
    r = (lbm_uint)lbm_dec_u(a); break;
447
  case LBM_TYPE_I32:
448
    r = (lbm_uint)lbm_dec_i32(a); break;
449
  case LBM_TYPE_U32:
450
    r = (lbm_uint)lbm_dec_u32(a); break;
451
  case LBM_TYPE_FLOAT:
452
    r = (lbm_uint)lbm_dec_float(a); break;
453
  case LBM_TYPE_I64:
454
    r = (lbm_uint)lbm_dec_i64(a); break;
455
  case LBM_TYPE_U64:
456
    r = (lbm_uint) lbm_dec_u64(a); break;
457
  case LBM_TYPE_DOUBLE:
458
    r = (lbm_uint)lbm_dec_double(a); break;
459
  }
460
58352
  return r;
461
}
462
463
644
lbm_int lbm_dec_as_int(lbm_value a) {
464
644
  lbm_int r = 0;
465


644
  switch (lbm_type_of_functional(a)) {
466
  case LBM_TYPE_CHAR:
467
    r = (lbm_int)lbm_dec_char(a); break;
468
644
  case LBM_TYPE_I:
469
644
    r = (lbm_int)lbm_dec_i(a); break;
470
  case LBM_TYPE_U:
471
    r = (lbm_int)lbm_dec_u(a); break;
472
  case LBM_TYPE_I32:
473
    r = (lbm_int)lbm_dec_i32(a); break;
474
  case LBM_TYPE_U32:
475
    r = (lbm_int)lbm_dec_u32(a); break;
476
  case LBM_TYPE_FLOAT:
477
    r = (lbm_int)lbm_dec_float(a); break;
478
  case LBM_TYPE_I64:
479
    r = (lbm_int)lbm_dec_i64(a); break;
480
  case LBM_TYPE_U64:
481
    r = (lbm_int)lbm_dec_u64(a); break;
482
  case LBM_TYPE_DOUBLE:
483
    r = (lbm_int)lbm_dec_double(a); break;
484
  }
485
644
  return r;
486
}
487
488
375465700
float lbm_dec_as_float(lbm_value a) {
489
375465700
  float r = 0;
490


375465700
  switch (lbm_type_of_functional(a)) {
491
103744728
  case LBM_TYPE_CHAR:
492
103744728
    r = (float)lbm_dec_char(a); break;
493
168074742
  case LBM_TYPE_I:
494
168074742
    r = (float)lbm_dec_i(a); break;
495
140
  case LBM_TYPE_U:
496
140
    r = (float)lbm_dec_u(a); break;
497
140
  case LBM_TYPE_I32:
498
140
    r = (float)lbm_dec_i32(a); break;
499
196
  case LBM_TYPE_U32:
500
196
    r = (float)lbm_dec_u32(a); break;
501
103645446
  case LBM_TYPE_FLOAT:
502
103645446
    r = (float)lbm_dec_float(a); break;
503
140
  case LBM_TYPE_I64:
504
140
    r = (float)lbm_dec_i64(a); break;
505
140
  case LBM_TYPE_U64:
506
140
    r = (float)lbm_dec_u64(a); break;
507
28
  case LBM_TYPE_DOUBLE:
508
28
    r = (float)lbm_dec_double(a); break;
509
  }
510
375465700
  return r;
511
}
512
513
563944
double lbm_dec_as_double(lbm_value a) {
514
563944
  double r = 0;
515


563944
  switch (lbm_type_of_functional(a)) {
516
281168
  case LBM_TYPE_CHAR:
517
281168
    r = (double)lbm_dec_char(a); break;
518
280620
  case LBM_TYPE_I:
519
280620
    r = (double)lbm_dec_i(a); break;
520
140
  case LBM_TYPE_U:
521
140
    r = (double)lbm_dec_u(a); break;
522
140
  case LBM_TYPE_I32:
523
140
    r = (double)lbm_dec_i32(a); break;
524
140
  case LBM_TYPE_U32:
525
140
    r = (double)lbm_dec_u32(a); break;
526
364
  case LBM_TYPE_FLOAT:
527
364
    r = (double)lbm_dec_float(a); break;
528
140
  case LBM_TYPE_I64:
529
140
    r = (double)lbm_dec_i64(a); break;
530
140
  case LBM_TYPE_U64:
531
140
    r = (double)lbm_dec_u64(a); break;
532
1092
  case LBM_TYPE_DOUBLE:
533
1092
    r = (double)lbm_dec_double(a); break;
534
  }
535
563944
  return r;
536
}
537
538
/****************************************************/
539
/* HEAP MANAGEMENT                                  */
540
541
21924
static int generate_freelist(size_t num_cells) {
542
21924
  size_t i = 0;
543
544
21924
  if (!lbm_heap_state.heap) return 0;
545
546
21924
  lbm_heap_state.freelist = lbm_enc_cons_ptr(0);
547
548
  lbm_cons_t *t;
549
550
  // Add all cells to free list
551
203655168
  for (i = 1; i < num_cells; i ++) {
552
203633244
    t = lbm_ref_cell(lbm_enc_cons_ptr(i-1));
553
203633244
    t->car = ENC_SYM_RECOVERED;    // all cars in free list are "RECOVERED"
554
203633244
    t->cdr = lbm_enc_cons_ptr(i);
555
  }
556
557
  // Replace the incorrect pointer at the last cell.
558
21924
  t = lbm_ref_cell(lbm_enc_cons_ptr(num_cells-1));
559
21924
  t->cdr = ENC_SYM_NIL;
560
561
21924
  return 1;
562
}
563
564
614552
void lbm_nil_freelist(void) {
565
614552
  lbm_heap_state.freelist = ENC_SYM_NIL;
566
614552
  lbm_heap_state.num_alloc = lbm_heap_state.heap_size;
567
614552
}
568
569
21924
static void heap_init_state(lbm_cons_t *addr, lbm_uint num_cells,
570
                            lbm_uint* gc_stack_storage, lbm_uint gc_stack_size) {
571
21924
  lbm_heap_state.heap         = addr;
572
21924
  lbm_heap_state.heap_bytes   = (unsigned int)(num_cells * sizeof(lbm_cons_t));
573
21924
  lbm_heap_state.heap_size    = num_cells;
574
575
21924
  lbm_stack_create(&lbm_heap_state.gc_stack, gc_stack_storage, gc_stack_size);
576
577
21924
  lbm_heap_state.num_alloc           = 0;
578
21924
  lbm_heap_state.num_alloc_arrays    = 0;
579
21924
  lbm_heap_state.gc_num              = 0;
580
21924
  lbm_heap_state.gc_marked           = 0;
581
21924
  lbm_heap_state.gc_recovered        = 0;
582
21924
  lbm_heap_state.gc_recovered_arrays = 0;
583
21924
  lbm_heap_state.gc_least_free       = num_cells;
584
21924
  lbm_heap_state.gc_last_free        = num_cells;
585
21924
}
586
587
614552
void lbm_heap_new_freelist_length(void) {
588
614552
  lbm_uint l = lbm_heap_state.heap_size - lbm_heap_state.num_alloc;
589
614552
  lbm_heap_state.gc_last_free = l;
590
614552
  if (l < lbm_heap_state.gc_least_free)
591
4018
    lbm_heap_state.gc_least_free = l;
592
614552
}
593
594
21924
int lbm_heap_init(lbm_cons_t *addr, lbm_uint num_cells,
595
                  lbm_uint gc_stack_size) {
596
597
21924
  if (((uintptr_t)addr % 8) != 0) return 0;
598
599
21924
  memset(addr,0, sizeof(lbm_cons_t) * num_cells);
600
601
21924
  lbm_uint *gc_stack_storage = (lbm_uint*)lbm_malloc(gc_stack_size * sizeof(lbm_uint));
602
21924
  if (gc_stack_storage == NULL) return 0;
603
604
21924
  heap_init_state(addr, num_cells,
605
                  gc_stack_storage, gc_stack_size);
606
607
21924
  lbm_heaps[0] = addr;
608
609
21924
  return generate_freelist(num_cells);
610
}
611
612
613
555405679
lbm_value lbm_heap_allocate_cell(lbm_type ptr_type, lbm_value car, lbm_value cdr) {
614
  lbm_value r;
615
555405679
  lbm_value cell = lbm_heap_state.freelist;
616
555405679
  if (cell) {
617
555254229
    lbm_uint heap_ix = lbm_dec_ptr(cell);
618
555254229
    lbm_heap_state.freelist = lbm_heap_state.heap[heap_ix].cdr;
619
555254229
    lbm_heap_state.num_alloc++;
620
555254229
    lbm_heap_state.heap[heap_ix].car = car;
621
555254229
    lbm_heap_state.heap[heap_ix].cdr = cdr;
622
555254229
    r = lbm_set_ptr_type(cell, ptr_type);
623
  } else {
624
151450
    r = ENC_SYM_MERROR;
625
  }
626
555405679
  return r;
627
}
628
629
1255352
lbm_value lbm_heap_allocate_list(lbm_uint n) {
630
1255352
  if (n == 0) return ENC_SYM_NIL;
631
1252048
  if (lbm_heap_num_free() < n) return ENC_SYM_MERROR;
632
633
1250624
  lbm_value curr = lbm_heap_state.freelist;
634
1250624
  lbm_value res  = curr;
635
1250624
  if (lbm_type_of(curr) == LBM_TYPE_CONS) {
636
637
1250624
    lbm_cons_t *c_cell = NULL;
638
1250624
    lbm_uint count = 0;
639
    do {
640
6466096
      c_cell = lbm_ref_cell(curr);
641
6466096
      c_cell->car = ENC_SYM_NIL;
642
6466096
      curr = c_cell->cdr;
643
6466096
      count ++;
644
6466096
    } while (count < n);
645
1250624
    lbm_heap_state.freelist = curr;
646
1250624
    c_cell->cdr = ENC_SYM_NIL;
647
1250624
    lbm_heap_state.num_alloc+=count;
648
1250624
    return res;
649
  }
650
  return ENC_SYM_FATAL_ERROR;
651
}
652
653
21963620
lbm_value lbm_heap_allocate_list_init_va(unsigned int n, va_list valist) {
654
21963620
  if (n == 0) return ENC_SYM_NIL;
655
21963620
  if (lbm_heap_num_free() < n) return ENC_SYM_MERROR;
656
657
21930426
  lbm_value curr = lbm_heap_state.freelist;
658
21930426
  lbm_value res  = curr;
659
21930426
  if (lbm_type_of(curr) == LBM_TYPE_CONS) {
660
661
21930426
    lbm_cons_t *c_cell = NULL;
662
21930426
    unsigned int count = 0;
663
    do {
664
44144572
      c_cell = lbm_ref_cell(curr);
665
44144572
      c_cell->car = va_arg(valist, lbm_value);
666
44144572
      curr = c_cell->cdr;
667
44144572
      count ++;
668
44144572
    } while (count < n);
669
21930426
    lbm_heap_state.freelist = curr;
670
21930426
    c_cell->cdr = ENC_SYM_NIL;
671
21930426
    lbm_heap_state.num_alloc+=count;
672
21930426
    return res;
673
  }
674
  return ENC_SYM_FATAL_ERROR;
675
}
676
677
21963620
lbm_value lbm_heap_allocate_list_init(unsigned int n, ...) {
678
    va_list valist;
679
21963620
    va_start(valist, n);
680
21963620
    lbm_value r = lbm_heap_allocate_list_init_va(n, valist);
681
21963620
    va_end(valist);
682
21963620
    return r;
683
}
684
685
lbm_uint lbm_heap_num_allocated(void) {
686
  return lbm_heap_state.num_alloc;
687
}
688
lbm_uint lbm_heap_size(void) {
689
  return lbm_heap_state.heap_size;
690
}
691
692
lbm_uint lbm_heap_size_bytes(void) {
693
  return lbm_heap_state.heap_bytes;
694
}
695
696
252
void lbm_get_heap_state(lbm_heap_state_t *res) {
697
252
  *res = lbm_heap_state;
698
252
}
699
700
lbm_uint lbm_get_gc_stack_max(void) {
701
  return lbm_get_max_stack(&lbm_heap_state.gc_stack);
702
}
703
704
lbm_uint lbm_get_gc_stack_size(void) {
705
  return lbm_heap_state.gc_stack.size;
706
}
707
708
static inline void value_assign(lbm_value *a, lbm_value b) {
709
  lbm_value a_old = *a & LBM_GC_MASK;
710
  *a = a_old | (b & ~LBM_GC_MASK);
711
}
712
713
#ifdef LBM_USE_GC_PTR_REV
714
/* ************************************************************
715
   Deutch-Schorr-Waite (DSW) pointer reversal GC for 2-ptr cells
716
   with a hack-solution for the lisp-array case (n-ptr cells).
717
718
   DSW visits each branch node 3 times compared to 2 times for
719
   the stack based recursive mark.
720
   Where the stack based recursive mark performs a stack push/pop,
721
   DSW rearranges the, current, prev, next and a ptr field on
722
   the heap.
723
724
   DSW changes the structure of the heap and it introduces an
725
   invalid pointer (LBM_PTR_NULL) temporarily during marking.
726
   Since the heap will be "messed up" while marking, a mutex
727
   is introuded to keep other processes out of the heap while
728
   marking.
729
730
   TODO: See if the extra index field in arrays can be used
731
   to mark arrays without resorting to recursive mark calls.
732
*/
733
734
735
void lbm_gc_mark_phase_nm(lbm_value root) {
736
  bool work_to_do = true;
737
  if (!lbm_is_ptr(root)) return;
738
739
  lbm_value curr = root;
740
  lbm_value prev = lbm_enc_cons_ptr(LBM_PTR_NULL);
741
742
  while (work_to_do) {
743
    // follow leftwards pointers
744
    while (lbm_is_ptr(curr) &&
745
           (lbm_dec_ptr(curr) != LBM_PTR_NULL) &&
746
           ((curr & LBM_PTR_TO_CONSTANT_BIT) == 0) &&
747
           !lbm_get_gc_mark(lbm_cdr(curr))) {
748
      // Mark the cell if not a constant cell
749
      lbm_cons_t *cell = lbm_ref_cell(curr);
750
      cell->cdr = lbm_set_gc_mark(cell->cdr);
751
      if (lbm_is_cons_rw(curr)) {
752
        lbm_value next = 0;
753
        value_assign(&next, cell->car);
754
        value_assign(&cell->car, prev);
755
        value_assign(&prev,curr);
756
        value_assign(&curr, next);
757
      } else if (lbm_type_of(curr) == LBM_TYPE_LISPARRAY) {
758
        lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
759
        lbm_value *arr_data = (lbm_value *)arr->data;
760
        size_t  arr_size = (size_t)arr->size / sizeof(lbm_value);
761
        // C stack recursion as deep as there are nested arrays.
762
        // TODO: Try to do this without recursion on the C side.
763
        for (size_t i = 0; i < arr_size; i ++) {
764
          lbm_gc_mark_phase_nm(arr_data[i]);
765
        }
766
      }
767
      // Will jump out next iteration as gc mark is set in curr.
768
    }
769
    while (lbm_is_ptr(prev) &&
770
           (lbm_dec_ptr(prev) != LBM_PTR_NULL) &&
771
           lbm_get_gc_flag(lbm_car(prev)) ) {
772
      // clear the flag
773
      lbm_cons_t *cell = lbm_ref_cell(prev);
774
      cell->car = lbm_clr_gc_flag(cell->car);
775
      lbm_value next = 0;
776
      value_assign(&next, cell->cdr);
777
      value_assign(&cell->cdr, curr);
778
      value_assign(&curr, prev);
779
      value_assign(&prev, next);
780
    }
781
    if (lbm_is_ptr(prev) &&
782
        lbm_dec_ptr(prev) == LBM_PTR_NULL) {
783
      work_to_do = false;
784
    } else if (lbm_is_ptr(prev)) {
785
      // set the flag
786
      lbm_cons_t *cell = lbm_ref_cell(prev);
787
      cell->car = lbm_set_gc_flag(cell->car);
788
      lbm_value next = 0;
789
      value_assign(&next, cell->car);
790
      value_assign(&cell->car, curr);
791
      value_assign(&curr, cell->cdr);
792
      value_assign(&cell->cdr, next);
793
    }
794
  }
795
}
796
797
void lbm_gc_mark_phase(lbm_value root) {
798
    mutex_lock(&lbm_const_heap_mutex);
799
    lbm_gc_mark_phase_nm(root);
800
    mutex_unlock(&lbm_const_heap_mutex);
801
}
802
803
#else
804
/* ************************************************************
805
   Explicit stack "recursive" mark phase
806
807
   Trees are marked in a left subtree before rigth subtree, car first then cdr,
808
   way to favor lisp lists. This means that stack will grow slowly when
809
   marking right-leaning (cdr-recursive) data-structures while left-leaning
810
   (car-recursive) structures uses a lot of stack.
811
812
   Lisp arrays contain an extra book-keeping field to keep track
813
   of how far into the array the marking process has gone.
814
815
   TODO: DSW should be used as a last-resort if the GC stack is exhausted.
816
         If we use DSW as last-resort can we get away with a way smaller
817
         GC stack and unchanged performance (on sensible programs)?
818
*/
819
820
extern eval_context_t *ctx_running;
821
10447418
void lbm_gc_mark_phase(lbm_value root) {
822
  lbm_value t_ptr;
823
10447418
  lbm_stack_t *s = &lbm_heap_state.gc_stack;
824
10447418
  s->data[s->sp++] = root;
825
826
61125022
  while (!lbm_stack_is_empty(s)) {
827
    lbm_value curr;
828
50677604
    lbm_pop(s, &curr);
829
830
113695386
  mark_shortcut:
831
832
113695386
    if (!lbm_is_ptr(curr) ||
833
69978662
        (curr & LBM_PTR_TO_CONSTANT_BIT)) {
834
48504006
      continue;
835
    }
836
837
69978662
    lbm_cons_t *cell = &lbm_heap_state.heap[lbm_dec_ptr(curr)];
838
839
69978662
    if (lbm_get_gc_mark(cell->cdr)) {
840
4766646
      continue;
841
    }
842
843
65212016
    t_ptr = lbm_type_of(curr);
844
845
    // An array is marked in O(N) time using an additional 32bit
846
    // value per array that keeps track of how far into the array GC
847
    // has progressed.
848
65212016
    if (t_ptr == LBM_TYPE_LISPARRAY) {
849
21462
      lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
850
21462
      lbm_value *arrdata = (lbm_value *)arr->data;
851
21462
      uint32_t index = arr->index;
852
21462
      if (arr->size > 0) {
853
21462
        lbm_push(s, curr); // put array back as bookkeeping.
854
        // Potential optimization.
855
        // 1. CONS pointers are set to curr and recurse.
856
        // 2. Any other ptr is marked immediately and index is increased.
857

21462
        if (lbm_is_ptr(arrdata[index]) && ((arrdata[index] & LBM_PTR_TO_CONSTANT_BIT) == 0) &&
858
1834
            !((arrdata[index] & LBM_CONTINUATION_INTERNAL) == LBM_CONTINUATION_INTERNAL)) {
859
1680
          lbm_cons_t *elt = &lbm_heap_state.heap[lbm_dec_ptr(arrdata[index])];
860
1680
          if (!lbm_get_gc_mark(elt->cdr)) {
861
826
            curr = arrdata[index];
862
826
            goto mark_shortcut;
863
          }
864
        }
865
20636
        if (index < ((arr->size/(sizeof(lbm_value))) - 1)) {
866
18414
          arr->index++;
867
18414
          continue;
868
        }
869
2222
        arr->index = 0;
870
2222
        lbm_pop(s, &curr); // Remove array from GC stack as we are done marking it.
871
      }
872
2222
      cell->cdr = lbm_set_gc_mark(cell->cdr);
873
2222
      lbm_heap_state.gc_marked ++;
874
2222
      continue;
875
65190554
    } else if (t_ptr == LBM_TYPE_CHANNEL) {
876
308330
      cell->cdr = lbm_set_gc_mark(cell->cdr);
877
308330
      lbm_heap_state.gc_marked ++;
878
      // TODO: Can channels be explicitly freed ?
879
308330
      if (cell->car != ENC_SYM_NIL) {
880
308330
        lbm_char_channel_t *chan = (lbm_char_channel_t *)cell->car;
881
308330
        curr = chan->dependency;
882
308330
        goto mark_shortcut;
883
      }
884
      continue;
885
    }
886
887
64882224
    cell->cdr = lbm_set_gc_mark(cell->cdr);
888
64882224
    lbm_heap_state.gc_marked ++;
889
890
64882224
    if (t_ptr == LBM_TYPE_CONS) {
891
62708626
      if (lbm_is_ptr(cell->cdr)) {
892
40210946
        if (!lbm_push(s, cell->cdr)) {
893
          lbm_critical_error();
894
          break;
895
        }
896
      }
897
62708626
      curr = cell->car;
898
62708626
      goto mark_shortcut; // Skip a push/pop
899
    }
900
  }
901
10447418
}
902
#endif
903
904
//Environments are proper lists with a 2 element list stored in each car.
905
20293348
void lbm_gc_mark_env(lbm_value env) {
906
20293348
  lbm_value curr = env;
907
  lbm_cons_t *c;
908
909
23765184
  while (lbm_is_ptr(curr)) {
910
3471836
    c = lbm_ref_cell(curr);
911
3471836
    c->cdr = lbm_set_gc_mark(c->cdr); // mark the environent list structure.
912
3471836
    lbm_cons_t *b = lbm_ref_cell(c->car);
913
3471836
    b->cdr = lbm_set_gc_mark(b->cdr); // mark the binding list head cell.
914
3471836
    lbm_gc_mark_phase(b->cdr);        // mark the bound object.
915
3471836
    lbm_heap_state.gc_marked +=2;
916
3471836
    curr = c->cdr;
917
  }
918
20293348
}
919
920
921
627684
void lbm_gc_mark_aux(lbm_uint *aux_data, lbm_uint aux_size) {
922
15036098
  for (lbm_uint i = 0; i < aux_size; i ++) {
923
14408414
    if (lbm_is_ptr(aux_data[i])) {
924
8638398
      lbm_type pt_t = lbm_type_of(aux_data[i]);
925
8638398
      lbm_uint pt_v = lbm_dec_ptr(aux_data[i]);
926

8638398
      if( pt_t >= LBM_POINTER_TYPE_FIRST &&
927
4543304
          pt_t <= LBM_POINTER_TYPE_LAST &&
928
4543304
          pt_v < lbm_heap_state.heap_size) {
929
4543304
        lbm_gc_mark_phase(aux_data[i]);
930
      }
931
    }
932
  }
933
627684
}
934
935
1256568
void lbm_gc_mark_roots(lbm_uint *roots, lbm_uint num_roots) {
936
3144694
  for (lbm_uint i = 0; i < num_roots; i ++) {
937
1888126
    lbm_gc_mark_phase(roots[i]);
938
  }
939
1256568
}
940
941
// Sweep moves non-marked heap objects to the free list.
942
614552
int lbm_gc_sweep_phase(void) {
943
614552
  unsigned int i = 0;
944
614552
  lbm_cons_t *heap = (lbm_cons_t *)lbm_heap_state.heap;
945
946
1415099032
  for (i = 0; i < lbm_heap_state.heap_size; i ++) {
947
1414484480
    if ( lbm_get_gc_mark(heap[i].cdr)) {
948
71939032
      heap[i].cdr = lbm_clr_gc_mark(heap[i].cdr);
949
    } else {
950
      // Check if this cell is a pointer to an array
951
      // and free it.
952
1342545448
      if (lbm_type_of(heap[i].cdr) == LBM_TYPE_SYMBOL) {
953

298972063
        switch(heap[i].cdr) {
954
955
8397310
        case ENC_SYM_IND_I_TYPE: /* fall through */
956
        case ENC_SYM_IND_U_TYPE:
957
        case ENC_SYM_IND_F_TYPE:
958
8397310
          lbm_memory_free((lbm_uint*)heap[i].car);
959
8397310
          break;
960
29008
        case ENC_SYM_DEFRAG_LISPARRAY_TYPE: /* fall through */
961
        case ENC_SYM_DEFRAG_ARRAY_TYPE:
962
29008
          lbm_defrag_mem_free((lbm_uint*)heap[i].car);
963
29008
          break;
964
572580
        case ENC_SYM_LISPARRAY_TYPE: /* fall through */
965
        case ENC_SYM_ARRAY_TYPE:{
966
572580
          lbm_array_header_t *arr = (lbm_array_header_t*)heap[i].car;
967
572580
          lbm_memory_free((lbm_uint *)arr->data);
968
572580
          lbm_heap_state.gc_recovered_arrays++;
969
572580
          lbm_memory_free((lbm_uint *)arr);
970
572580
        } break;
971
304529
        case ENC_SYM_CHANNEL_TYPE:{
972
304529
          lbm_char_channel_t *chan = (lbm_char_channel_t*)heap[i].car;
973
304529
          lbm_memory_free((lbm_uint*)chan->state);
974
304529
          lbm_memory_free((lbm_uint*)chan);
975
304529
        } break;
976
        case ENC_SYM_CUSTOM_TYPE: {
977
          lbm_uint *t = (lbm_uint*)heap[i].car;
978
          lbm_custom_type_destroy(t);
979
          lbm_memory_free(t);
980
          } break;
981
28
        case ENC_SYM_DEFRAG_MEM_TYPE: {
982
28
          lbm_uint *ptr = (lbm_uint *)heap[i].car;
983
28
          lbm_defrag_mem_destroy(ptr);
984
28
          } break;
985
289668608
        default:
986
289668608
          break;
987
        }
988
1043573385
      }
989
      // create pointer to use as new freelist
990
1342545448
      lbm_uint addr = lbm_enc_cons_ptr(i);
991
992
      // Clear the "freed" cell.
993
1342545448
      heap[i].car = ENC_SYM_RECOVERED;
994
1342545448
      heap[i].cdr = lbm_heap_state.freelist;
995
1342545448
      lbm_heap_state.freelist = addr;
996
1342545448
      lbm_heap_state.num_alloc --;
997
1342545448
      lbm_heap_state.gc_recovered ++;
998
    }
999
  }
1000
614552
  return 1;
1001
}
1002
1003
614552
void lbm_gc_state_inc(void) {
1004
614552
  lbm_heap_state.gc_num ++;
1005
614552
  lbm_heap_state.gc_recovered = 0;
1006
614552
  lbm_heap_state.gc_marked = 0;
1007
614552
}
1008
1009
// construct, alter and break apart
1010
554383123
lbm_value lbm_cons(lbm_value car, lbm_value cdr) {
1011
554383123
  return lbm_heap_allocate_cell(LBM_TYPE_CONS, car, cdr);
1012
}
1013
1014
572533149
lbm_value lbm_car(lbm_value c){
1015
572533149
  if (lbm_is_ptr(c) ){
1016
572532981
    lbm_cons_t *cell = lbm_ref_cell(c);
1017
572532981
    return cell->car;
1018
  }
1019
168
  return c ? ENC_SYM_TERROR : c; //nil if c == nil
1020
}
1021
1022
// TODO: Many comparisons "is this the nil symbol" can be
1023
// streamlined a bit. NIL is 0 and cannot be confused with any other
1024
// lbm_value.
1025
1026
46416
lbm_value lbm_caar(lbm_value c) {
1027
46416
  lbm_value tmp = ENC_SYM_NIL;
1028
46416
  if (lbm_is_ptr(c)) {
1029
46416
    tmp = lbm_ref_cell(c)->car;
1030
46416
    if (lbm_is_ptr(tmp)) {
1031
46416
      return lbm_ref_cell(tmp)->car;
1032
    }
1033
  }
1034
  return c || tmp ? ENC_SYM_TERROR : c; //nil if not something else
1035
}
1036
1037
1038
12348
lbm_value lbm_cadr(lbm_value c) {
1039
12348
  lbm_value tmp = ENC_SYM_NIL;
1040
12348
  if (lbm_is_ptr(c)) {
1041
12348
    tmp = lbm_ref_cell(c)->cdr;
1042
12348
    if (lbm_is_ptr(tmp)) {
1043
12348
      return lbm_ref_cell(tmp)->car;
1044
    }
1045
  }
1046
  return c || tmp ? ENC_SYM_TERROR : c;
1047
}
1048
1049
136404586
lbm_value lbm_cdr(lbm_value c){
1050
136404586
  if (lbm_is_ptr(c)) {
1051
135837978
    lbm_cons_t *cell = lbm_ref_cell(c);
1052
135837978
    return cell->cdr;
1053
  }
1054
566608
  return c ? ENC_SYM_TERROR: c;
1055
}
1056
1057
lbm_value lbm_cddr(lbm_value c) {
1058
  if (lbm_is_ptr(c)) {
1059
    lbm_value tmp = lbm_ref_cell(c)->cdr;
1060
    if (lbm_is_ptr(tmp)) {
1061
      return lbm_ref_cell(tmp)->cdr;
1062
    }
1063
  }
1064
  return c ? ENC_SYM_TERROR : c;
1065
}
1066
1067
6859530
int lbm_set_car(lbm_value c, lbm_value v) {
1068
6859530
  int r = 0;
1069
1070
6859530
  if (lbm_type_of(c) == LBM_TYPE_CONS) {
1071
6859502
    lbm_cons_t *cell = lbm_ref_cell(c);
1072
6859502
    cell->car = v;
1073
6859502
    r = 1;
1074
  }
1075
6859530
  return r;
1076
}
1077
1078
124310586
int lbm_set_cdr(lbm_value c, lbm_value v) {
1079
124310586
  int r = 0;
1080
124310586
  if (lbm_is_cons_rw(c)){
1081
123744062
    lbm_cons_t *cell = lbm_ref_cell(c);
1082
123744062
    cell->cdr = v;
1083
123744062
    r = 1;
1084
  }
1085
124310586
  return r;
1086
}
1087
1088
29788326
int lbm_set_car_and_cdr(lbm_value c, lbm_value car_val, lbm_value cdr_val) {
1089
29788326
  int r = 0;
1090
29788326
  if (lbm_is_cons_rw(c)) {
1091
29788326
    lbm_cons_t *cell = lbm_ref_cell(c);
1092
29788326
    cell->car = car_val;
1093
29788326
    cell->cdr = cdr_val;
1094
29788326
    r = 1;
1095
  }
1096
29788326
  return r;
1097
}
1098
1099
/* calculate length of a proper list */
1100
1248920
lbm_uint lbm_list_length(lbm_value c) {
1101
1248920
  lbm_uint len = 0;
1102
1103
7213614
  while (lbm_is_cons(c)){
1104
5964694
    len ++;
1105
5964694
    c = lbm_cdr(c);
1106
  }
1107
1248920
  return len;
1108
}
1109
1110
/* calculate the length of a list and check that each element
1111
   fullfills the predicate pred */
1112
unsigned int lbm_list_length_pred(lbm_value c, bool *pres, bool (*pred)(lbm_value)) {
1113
  bool res = true;
1114
  unsigned int len = 0;
1115
1116
  while (lbm_is_cons(c)){
1117
    len ++;
1118
    res = res && pred(lbm_car(c));
1119
    c = lbm_cdr(c);
1120
  }
1121
  *pres = res;
1122
  return len;
1123
}
1124
1125
/* reverse a proper list */
1126
lbm_value lbm_list_reverse(lbm_value list) {
1127
  if (lbm_type_of(list) == LBM_TYPE_SYMBOL) {
1128
    return list;
1129
  }
1130
1131
  lbm_value curr = list;
1132
1133
  lbm_value new_list = ENC_SYM_NIL;
1134
  while (lbm_is_cons(curr)) {
1135
1136
    new_list = lbm_cons(lbm_car(curr), new_list);
1137
    if (lbm_type_of(new_list) == LBM_TYPE_SYMBOL) {
1138
      return ENC_SYM_MERROR;
1139
    }
1140
    curr = lbm_cdr(curr);
1141
  }
1142
  return new_list;
1143
}
1144
1145
10484
lbm_value lbm_list_destructive_reverse(lbm_value list) {
1146
10484
  if (lbm_type_of(list) == LBM_TYPE_SYMBOL) {
1147
    return list;
1148
  }
1149
10484
  lbm_value curr = list;
1150
10484
  lbm_value last_cell = ENC_SYM_NIL;
1151
1152
37256
  while (lbm_is_cons_rw(curr)) {
1153
26772
    lbm_value next = lbm_cdr(curr);
1154
26772
    lbm_set_cdr(curr, last_cell);
1155
26772
    last_cell = curr;
1156
26772
    curr = next;
1157
  }
1158
10484
  return last_cell;
1159
}
1160
1161
1162
330382
lbm_value lbm_list_copy(int *m, lbm_value list) {
1163
330382
  lbm_value curr = list;
1164
330382
  lbm_uint n = lbm_list_length(list);
1165
330382
  lbm_uint copy_n = n;
1166

330382
  if (*m >= 0 && (lbm_uint)*m < n) {
1167
5414
    copy_n = (lbm_uint)*m;
1168
324968
  } else if (*m == -1) {
1169
295666
    *m = (int)n; // TODO: smaller range in target variable.
1170
  }
1171
330382
  if (copy_n == 0) return ENC_SYM_NIL;
1172
330158
  lbm_uint new_list = lbm_heap_allocate_list(copy_n);
1173
330158
  if (lbm_is_symbol(new_list)) return new_list;
1174
329410
  lbm_value curr_targ = new_list;
1175
1176

4090636
  while (lbm_is_cons(curr) && copy_n > 0) {
1177
3761226
    lbm_value v = lbm_car(curr);
1178
3761226
    lbm_set_car(curr_targ, v);
1179
3761226
    curr_targ = lbm_cdr(curr_targ);
1180
3761226
    curr = lbm_cdr(curr);
1181
3761226
    copy_n --;
1182
  }
1183
1184
329410
  return new_list;
1185
}
1186
1187
// Append for proper lists only
1188
// Destructive update of list1.
1189
23992
lbm_value lbm_list_append(lbm_value list1, lbm_value list2) {
1190
1191

47984
  if(lbm_is_list_rw(list1) &&
1192
23992
     lbm_is_list(list2)) {
1193
1194
23992
    lbm_value curr = list1;
1195
55902
    while(lbm_type_of(lbm_cdr(curr)) == LBM_TYPE_CONS) {
1196
31910
      curr = lbm_cdr(curr);
1197
    }
1198
23992
    if (lbm_is_symbol_nil(curr)) return list2;
1199
23964
    lbm_set_cdr(curr, list2);
1200
23964
    return list1;
1201
  }
1202
  return ENC_SYM_EERROR;
1203
}
1204
1205
84
lbm_value lbm_list_drop(unsigned int n, lbm_value ls) {
1206
84
  lbm_value curr = ls;
1207

784
  while (lbm_type_of_functional(curr) == LBM_TYPE_CONS &&
1208
         n > 0) {
1209
700
    curr = lbm_cdr(curr);
1210
700
    n --;
1211
  }
1212
84
  return curr;
1213
}
1214
1215
153072
lbm_value lbm_index_list(lbm_value l, int32_t n) {
1216
153072
  lbm_value curr = l;
1217
1218
153072
  if (n < 0) {
1219
112
    int32_t len = (int32_t)lbm_list_length(l);
1220
112
    n = len + n;
1221
112
    if (n < 0) return ENC_SYM_NIL;
1222
  }
1223
1224

229510
  while (lbm_is_cons(curr) &&
1225
          n > 0) {
1226
76438
    curr = lbm_cdr(curr);
1227
76438
    n --;
1228
  }
1229
153072
  if (lbm_is_cons(curr)) {
1230
153044
    return lbm_car(curr);
1231
  } else {
1232
28
    return ENC_SYM_NIL;
1233
  }
1234
}
1235
1236
// High-level arrays are just bytearrays but with a different tag and pointer type.
1237
// These arrays will be inspected by GC and the elements of the array will be marked.
1238
1239
// Arrays are part of the heap module because their lifespan is managed
1240
// by the garbage collector. The data in the array is not stored
1241
// in the "heap of cons cells".
1242
575752
int lbm_heap_allocate_array_base(lbm_value *res, bool byte_array, lbm_uint size){
1243
1244
575752
  lbm_uint tag = ENC_SYM_ARRAY_TYPE;
1245
575752
  lbm_uint type = LBM_TYPE_ARRAY;
1246
575752
  lbm_array_header_t *array = NULL;
1247
575752
  lbm_array_header_extended_t *ext_array = NULL;
1248
1249
575752
  if (byte_array) {
1250
293208
    array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_t));
1251
  } else {
1252
282544
    tag = ENC_SYM_LISPARRAY_TYPE;
1253
282544
    type = LBM_TYPE_LISPARRAY;
1254
282544
    size = sizeof(lbm_value) * size;
1255
282544
    array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_extended_t));
1256
282544
    ext_array = (lbm_array_header_extended_t*)array;
1257
  }
1258
575752
  if (array) {
1259
574834
    if (!byte_array) ext_array->index = 0;
1260
1261
574834
    array->data = NULL;
1262
574834
    array->size = size;
1263
574834
    if ( size > 0) {
1264
574694
      array->data = (lbm_uint*)lbm_malloc(size);
1265
574694
      if (array->data == NULL) {
1266
5758
        lbm_memory_free((lbm_uint*)array);
1267
5758
        goto allocate_array_merror;
1268
      }
1269
      // It is more important to zero out high-level arrays.
1270
      // 0 is symbol NIL which is perfectly safe for the GC to inspect.
1271
568936
      memset(array->data, 0, size);
1272
    }
1273
    // allocating a cell for array's heap-presence
1274
569076
    lbm_value cell = lbm_heap_allocate_cell(type, (lbm_uint) array, tag);
1275
569076
    if (cell == ENC_SYM_MERROR) {
1276
302
      lbm_memory_free((lbm_uint*)array->data);
1277
302
      lbm_memory_free((lbm_uint*)array);
1278
302
      goto allocate_array_merror;
1279
    }
1280
568774
    *res = cell;
1281
568774
    lbm_heap_state.num_alloc_arrays ++;
1282
568774
    return 1;
1283
  }
1284
918
 allocate_array_merror:
1285
6978
  *res = ENC_SYM_MERROR;
1286
6978
  return 0;
1287
}
1288
1289
293208
int lbm_heap_allocate_array(lbm_value *res, lbm_uint size){
1290
293208
  return lbm_heap_allocate_array_base(res, true, size);
1291
}
1292
1293
282544
int lbm_heap_allocate_lisp_array(lbm_value *res, lbm_uint size) {
1294
282544
  return lbm_heap_allocate_array_base(res, false, size);
1295
}
1296
1297
int lbm_lift_array(lbm_value *value, char *data, lbm_uint num_elt) {
1298
1299
  lbm_array_header_t *array = NULL;
1300
  lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CONS, ENC_SYM_NIL, ENC_SYM_ARRAY_TYPE);
1301
1302
  if (cell == ENC_SYM_MERROR) {
1303
    *value = cell;
1304
    return 0;
1305
  }
1306
1307
  array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_t));
1308
1309
  if (array == NULL) {
1310
    lbm_set_car_and_cdr(cell, ENC_SYM_NIL, ENC_SYM_NIL);
1311
    *value = ENC_SYM_MERROR;
1312
    return 0;
1313
  }
1314
1315
  array->data = (lbm_uint*)data;
1316
  array->size = num_elt;
1317
1318
  lbm_set_car(cell, (lbm_uint)array);
1319
1320
  cell = lbm_set_ptr_type(cell, LBM_TYPE_ARRAY);
1321
  *value = cell;
1322
  return 1;
1323
}
1324
1325
237416
lbm_int lbm_heap_array_get_size(lbm_value arr) {
1326
1327
237416
  lbm_int r = -1;
1328
237416
  lbm_array_header_t *header = lbm_dec_array_r(arr);
1329
237416
  if (header) {
1330
237416
    r = (lbm_int)header->size;
1331
  }
1332
237416
  return r;
1333
}
1334
1335
118692
const uint8_t *lbm_heap_array_get_data_ro(lbm_value arr) {
1336
118692
  uint8_t *r = NULL;
1337
118692
  lbm_array_header_t *header = lbm_dec_array_r(arr);
1338
118692
  if (header) {
1339
118692
    r = (uint8_t*)header->data;
1340
  }
1341
118692
  return r;
1342
}
1343
1344
uint8_t *lbm_heap_array_get_data_rw(lbm_value arr) {
1345
  uint8_t *r = NULL;
1346
  lbm_array_header_t *header = lbm_dec_array_rw(arr);
1347
  if (header) {
1348
    r = (uint8_t*)header->data;
1349
  }
1350
  return r;
1351
}
1352
1353
1354
/* Explicitly freeing an array.
1355
1356
   This is a highly unsafe operation and can only be safely
1357
   used if the heap cell that points to the array has not been made
1358
   accessible to the program.
1359
1360
   So This function can be used to free an array in case an array
1361
   is being constructed and some error case appears while doing so
1362
   If the array still have not become available it can safely be
1363
   "explicitly" freed.
1364
1365
   The problem is that if the "array" heap-cell is made available to
1366
   the program, this cell can easily be duplicated and we would have
1367
   to search the entire heap to find all cells pointing to the array
1368
   memory in question and "null"-them out before freeing the memory
1369
*/
1370
1371
112
int lbm_heap_explicit_free_array(lbm_value arr) {
1372
1373
112
  int r = 0;
1374

112
  if (lbm_is_array_rw(arr) && lbm_cdr(arr) == ENC_SYM_ARRAY_TYPE) {
1375
112
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(arr);
1376
112
    if (header == NULL) {
1377
      return 0;
1378
    }
1379
112
    lbm_memory_free((lbm_uint*)header->data);
1380
112
    lbm_memory_free((lbm_uint*)header);
1381
1382
112
    arr = lbm_set_ptr_type(arr, LBM_TYPE_CONS);
1383
112
    lbm_set_car(arr, ENC_SYM_NIL);
1384
112
    lbm_set_cdr(arr, ENC_SYM_NIL);
1385
112
    r = 1;
1386
  }
1387
1388
112
  return r;
1389
}
1390
1391
lbm_uint lbm_size_of(lbm_type t) {
1392
  lbm_uint s = 0;
1393
  switch(t) {
1394
  case LBM_TYPE_BYTE:
1395
    s = 1;
1396
    break;
1397
  case LBM_TYPE_I: /* fall through */
1398
  case LBM_TYPE_U:
1399
  case LBM_TYPE_SYMBOL:
1400
    s = sizeof(lbm_uint);
1401
    break;
1402
  case LBM_TYPE_I32: /* fall through */
1403
  case LBM_TYPE_U32:
1404
  case LBM_TYPE_FLOAT:
1405
    s = 4;
1406
    break;
1407
  case LBM_TYPE_I64: /* fall through */
1408
  case LBM_TYPE_U64:
1409
  case LBM_TYPE_DOUBLE:
1410
    s = 8;
1411
    break;
1412
  }
1413
  return s;
1414
}
1415
1416
static bool dummy_flash_write(lbm_uint ix, lbm_uint val) {
1417
  (void)ix;
1418
  (void)val;
1419
  return false;
1420
}
1421
1422
static const_heap_write_fun const_heap_write = dummy_flash_write;
1423
1424
21924
int lbm_const_heap_init(const_heap_write_fun w_fun,
1425
                        lbm_const_heap_t *heap,
1426
                        lbm_uint *addr) {
1427
21924
  if (((uintptr_t)addr % 4) != 0) return 0;
1428
1429
21924
  if (!lbm_const_heap_mutex_initialized) {
1430
21924
    mutex_init(&lbm_const_heap_mutex);
1431
21924
    lbm_const_heap_mutex_initialized = true;
1432
  }
1433
1434
21924
  if (!lbm_mark_mutex_initialized) {
1435
21924
    mutex_init(&lbm_mark_mutex);
1436
21924
    lbm_mark_mutex_initialized = true;
1437
  }
1438
1439
21924
  const_heap_write = w_fun;
1440
1441
21924
  heap->heap = addr;
1442
21924
  heap->size = 0;
1443
21924
  heap->next = 0;
1444
1445
21924
  lbm_const_heap_state = heap;
1446
  // ref_cell views the lbm_uint array as an lbm_cons_t array
1447
21924
  lbm_heaps[1] = (lbm_cons_t*)addr;
1448
21924
  return 1;
1449
}
1450
1451
2352
lbm_flash_status lbm_allocate_const_cell(lbm_value *res) {
1452
2352
  lbm_flash_status r = LBM_FLASH_FULL;
1453
1454
2352
  mutex_lock(&lbm_const_heap_mutex);
1455
  // waste a cell if we have ended up unaligned after writing an array to flash.
1456
2352
  if (lbm_const_heap_state->next % 2 == 1) {
1457
126
    lbm_const_heap_state->next++;
1458
  }
1459
1460
2352
  if (lbm_const_heap_state &&
1461
2352
      (lbm_const_heap_state->next+1) < (uint32_t)lbm_image_get_write_index()) {
1462
    // A cons cell uses two words.
1463
2352
    lbm_value cell = lbm_const_heap_state->next;
1464
2352
    lbm_const_heap_state->next += 2;
1465
2352
    *res = (cell << LBM_ADDRESS_SHIFT) | LBM_PTR_BIT | LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT;
1466
2352
    r = LBM_FLASH_WRITE_OK;
1467
  }
1468
2352
  mutex_unlock(&lbm_const_heap_mutex);
1469
2352
  return r;
1470
}
1471
1472
28
lbm_flash_status lbm_allocate_const_raw(lbm_uint nwords, lbm_uint *res) {
1473
28
  lbm_flash_status r = LBM_FLASH_FULL;
1474
1475
28
  if (lbm_const_heap_state &&
1476
28
      (lbm_const_heap_state->next + nwords) < (uint32_t)lbm_image_get_write_index()) {
1477
28
    lbm_uint ix = lbm_const_heap_state->next;
1478
28
    *res = (lbm_uint)&lbm_const_heap_state->heap[ix];
1479
28
    lbm_const_heap_state->next += nwords;
1480
28
    r = LBM_FLASH_WRITE_OK;
1481
  }
1482
28
  return r;
1483
}
1484
1485
186352
lbm_flash_status lbm_write_const_raw(lbm_uint *data, lbm_uint n, lbm_uint *res) {
1486
1487
186352
  lbm_flash_status r = LBM_FLASH_FULL;
1488
1489
186352
  if (lbm_const_heap_state &&
1490
186352
      (lbm_const_heap_state->next + n) < (uint32_t)lbm_image_get_write_index()) {
1491
186352
    lbm_uint ix = lbm_const_heap_state->next;
1492
1493
389728
    for (unsigned int i = 0; i < n; i ++) {
1494
203376
      if (!const_heap_write(((lbm_uint*)data)[i],ix + i))
1495
        return LBM_FLASH_WRITE_ERROR;
1496
    }
1497
186352
    lbm_const_heap_state->next += n;
1498
186352
    *res = (lbm_uint)&lbm_const_heap_state->heap[ix];
1499
186352
    r = LBM_FLASH_WRITE_OK;
1500
  }
1501
186352
  return r;
1502
}
1503
1504
84
lbm_flash_status lbm_const_write(lbm_uint *tgt, lbm_uint val) {
1505
1506
84
  if (lbm_const_heap_state) {
1507
84
    lbm_uint flash = (lbm_uint)lbm_const_heap_state->heap;
1508
84
    lbm_uint ix = (((lbm_uint)tgt - flash) / sizeof(lbm_uint)); // byte address to ix
1509
84
    if (const_heap_write(val, ix)) {
1510
84
      return LBM_FLASH_WRITE_OK;
1511
    }
1512
    return LBM_FLASH_WRITE_ERROR;
1513
  }
1514
  return LBM_FLASH_FULL;
1515
}
1516
1517
2352
lbm_flash_status write_const_cdr(lbm_value cell, lbm_value val) {
1518
2352
  lbm_uint addr = lbm_dec_ptr(cell);
1519
2352
  if (const_heap_write(val, addr+1))
1520
2352
    return LBM_FLASH_WRITE_OK;
1521
  return LBM_FLASH_WRITE_ERROR;
1522
}
1523
1524
2352
lbm_flash_status write_const_car(lbm_value cell, lbm_value val) {
1525
2352
  lbm_uint addr = lbm_dec_ptr(cell);
1526
2352
  if (const_heap_write(val, addr))
1527
2352
    return LBM_FLASH_WRITE_OK;
1528
  return LBM_FLASH_WRITE_ERROR;
1529
}
1530
1531
lbm_uint lbm_flash_memory_usage(void) {
1532
  return lbm_const_heap_state->next;
1533
}
1534
1535
1536
// ////////////////////////////////////////////////////////////
1537
// pointer reversal traversal
1538
//
1539
// Caveats:
1540
//   * Structures on the constant heap cannot be traversed using
1541
//     pointer reversal. If a dynamic structure is pointing into the
1542
//     constant heap, the 'f' will be applied to the constant cons cell on
1543
//     the border and then traversal will retreat.
1544
//   * Traversal is for trees and graphs without cycles.
1545
//     - Note that if used to "flatten" a graph, the resulting flat
1546
//       value will encode a tree where sharing is duplicated.
1547
//     - NOT suitable for flattening in general, but should be
1548
//       a perfect fit for the flattening we do into images.
1549
1550
bool lbm_ptr_rev_trav(void (*f)(lbm_value, void*), lbm_value v, void* arg) {
1551
1552
  bool cyclic = false;
1553
  lbm_value curr = v;
1554
  lbm_value prev = lbm_enc_cons_ptr(LBM_PTR_NULL);
1555
1556
  while (true) {
1557
1558
    // Run leftwards and process conses until
1559
    // hitting a leaf in the left direction.
1560
    while ((lbm_is_cons_rw(curr) &&
1561
            !gc_marked(curr)) ||         // do not step into a loop
1562
           lbm_is_lisp_array_rw(curr)) { // do not step into the constant heap
1563
      lbm_cons_t *cell = lbm_ref_cell(curr);
1564
      if (lbm_is_cons(curr)) {
1565
        gc_mark(curr);
1566
        // In-order traversal
1567
        f(curr, arg);
1568
        lbm_value next = 0;
1569
        value_assign(&next, cell->car);
1570
        value_assign(&cell->car, prev);
1571
        value_assign(&prev, curr);
1572
        value_assign(&curr, next);
1573
      } else { // it is an array
1574
        lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
1575
        lbm_value *arr_data = (lbm_value *)arr->data;
1576
        uint32_t index = arr->index;
1577
        if (arr->size == 0) break;
1578
        if (index == 0) { // index should only be 0 or there is a potential cycle
1579
          f(curr, arg);
1580
          arr->index = 1;
1581
1582
          lbm_value next = 0;
1583
          value_assign(&next, arr_data[0]);
1584
          value_assign(&arr_data[0], prev);
1585
          value_assign(&prev, curr);
1586
          value_assign(&curr, next);
1587
        } else {
1588
          cyclic = true;
1589
          break;
1590
        }
1591
      }
1592
    }
1593
1594
    if (!lbm_is_cons(curr) || // Found a leaf
1595
        (curr & LBM_PTR_TO_CONSTANT_BIT)) {
1596
      f(curr, arg);
1597
    } else if (gc_marked(curr)) {
1598
      cyclic = true;
1599
      gc_clear_mark(curr);
1600
    }
1601
1602
    // Now either prev has the "flag" set or it doesnt.
1603
    // If the flag is set that means that the prev node
1604
    // have had both its car and cdr visited. So that node is done!
1605
    //
1606
    // If the flag is not set, jump down to SWAP
1607
1608
    while ((lbm_is_cons(prev) &&
1609
            (lbm_dec_ptr(prev) != LBM_PTR_NULL) && // is LBM_NULL a cons type?
1610
            lbm_get_gc_flag(lbm_car(prev))) ||
1611
           lbm_is_lisp_array_rw(prev)) {
1612
      lbm_cons_t *cell = lbm_ref_cell(prev);
1613
      if (lbm_is_cons(prev)) {
1614
1615
        // clear the flag
1616
        // This means that we are done with a "CDR" child.
1617
        // prev = [ a , b ][flag = 1]
1618
        // =>
1619
        // prev = [ a , b ][flag = 0]
1620
1621
        gc_clear_mark(prev);
1622
        cell->car = lbm_clr_gc_flag(cell->car);
1623
        // Move on downwards until
1624
        //   finding a cons cell without flag or NULL
1625
1626
        // curr = c
1627
        // prev = [ a , b ][flag = 0]
1628
        // =>
1629
        // prev = [ a , c ][flag = 0]
1630
        // curr = prev
1631
        // prev = b
1632
1633
        lbm_value next = 0;
1634
        value_assign(&next, cell->cdr);
1635
        value_assign(&cell->cdr, curr);
1636
        value_assign(&curr, prev);
1637
        value_assign(&prev, next);
1638
      } else { // is an array
1639
        lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
1640
        lbm_value *arr_data = (lbm_value *)arr->data;
1641
        size_t arr_size = (size_t)arr->size / sizeof(lbm_value);
1642
        lbm_value next = 0;
1643
        if (arr->index == arr_size) {
1644
          value_assign(&next, arr_data[arr->index-1]);
1645
          value_assign(&arr_data[arr->index-1], curr);
1646
          value_assign(&curr, prev);
1647
          value_assign(&prev, next);
1648
          arr->index = 0;
1649
        } else {
1650
          break;
1651
        }
1652
      }
1653
    }
1654
1655
    // SWAP
1656
1657
    // if the prev node is NULL we have traced backwards all the
1658
    // way back to where curr == v. Another alternative is that
1659
    // the input v was an Atom.  We are done!
1660
    if (lbm_is_ptr(prev) &&
1661
        lbm_dec_ptr(prev) == LBM_PTR_NULL) {
1662
      if (lbm_is_cons(curr)) {
1663
        gc_clear_mark(curr);
1664
      }
1665
      //done = true;
1666
      break;
1667
    }
1668
1669
    // if the prev node is not NULL then we should move
1670
    // down to the prev node and start process its remaining child.
1671
    else if (lbm_is_cons(prev)) {
1672
1673
      lbm_cons_t *cell = lbm_ref_cell(prev);
1674
      lbm_value next = 0;
1675
1676
1677
      //  prev = [ p , cdr ][flag = 0]
1678
      //  =>
1679
      //  prev = [ p , cdr ][flag = 1]
1680
1681
      cell->car = lbm_set_gc_flag(cell->car);
1682
1683
      // switch to processing the cdr field and set the flag.
1684
      // curr = c
1685
      // prev = [ a, b ][flag = 1]
1686
      // =>
1687
      // prev = [ c, a ][flag = 1]
1688
      // curr = b
1689
1690
      value_assign(&next, cell->car);
1691
      value_assign(&cell->car, curr);
1692
      value_assign(&curr, cell->cdr);
1693
      value_assign(&cell->cdr, next);
1694
    } else if (lbm_is_lisp_array_rw(prev)) {
1695
      lbm_cons_t *cell = lbm_ref_cell(prev);
1696
      lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
1697
      lbm_value *arr_data = (lbm_value *)arr->data;
1698
      lbm_value next = 0;
1699
1700
      value_assign(&next, arr_data[arr->index-1]);
1701
      value_assign(&arr_data[arr->index-1], curr);
1702
      value_assign(&curr, arr_data[arr->index]);
1703
      value_assign(&arr_data[arr->index], next);
1704
      arr->index = arr->index + 1;
1705
    }
1706
  }
1707
  return !cyclic;
1708
}