GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/heap.c Lines: 687 824 83.4 %
Date: 2024-12-05 14:36:58 Branches: 247 351 70.4 %

Line Branch Exec Source
1
/*
2
    Copyright 2018, 2020, 2022 - 2024 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
28
#include "heap.h"
29
#include "symrepr.h"
30
#include "stack.h"
31
#include "lbm_channel.h"
32
#include "platform_mutex.h"
33
#include "eval_cps.h"
34
#ifdef VISUALIZE_HEAP
35
#include "heap_vis.h"
36
#endif
37
38
39
33812031
static inline lbm_value lbm_set_gc_mark(lbm_value x) {
40
33812031
  return x | LBM_GC_MARKED;
41
}
42
43
33645801
static inline lbm_value lbm_clr_gc_mark(lbm_value x) {
44
33645801
  return x & ~LBM_GC_MASK;
45
}
46
47
804727716
static inline bool lbm_get_gc_mark(lbm_value x) {
48
804727716
  return x & LBM_GC_MASK;
49
}
50
51
// flag is the same bit as mark, but in car
52
static inline bool lbm_get_gc_flag(lbm_value x) {
53
  return x & LBM_GC_MARKED;
54
}
55
56
static inline lbm_value lbm_set_gc_flag(lbm_value x) {
57
  return x | LBM_GC_MARKED;
58
}
59
60
static inline lbm_value lbm_clr_gc_flag(lbm_value x) {
61
  return x & ~LBM_GC_MASK;
62
}
63
64
65
lbm_heap_state_t lbm_heap_state;
66
67
lbm_const_heap_t *lbm_const_heap_state;
68
69
lbm_cons_t *lbm_heaps[2] = {NULL, NULL};
70
71
static mutex_t lbm_const_heap_mutex;
72
static bool    lbm_const_heap_mutex_initialized = false;
73
74
static mutex_t lbm_mark_mutex;
75
static bool    lbm_mark_mutex_initialized = false;
76
77
#ifdef USE_GC_PTR_REV
78
void lbm_gc_lock(void) {
79
  mutex_lock(&lbm_mark_mutex);
80
}
81
void lbm_gc_unlock(void) {
82
  mutex_unlock(&lbm_mark_mutex);
83
}
84
#else
85
void lbm_gc_lock(void) {
86
}
87
void lbm_gc_unlock(void) {
88
}
89
#endif
90
91
/****************************************************/
92
/* ENCODERS DECODERS                                */
93
94
2849162
lbm_value lbm_enc_i32(int32_t x) {
95
#ifndef LBM64
96
2849162
  lbm_value i = lbm_cons((lbm_uint)x, ENC_SYM_RAW_I_TYPE);
97
2849162
  if (lbm_type_of(i) == LBM_TYPE_SYMBOL) return i;
98
2839488
  return lbm_set_ptr_type(i, LBM_TYPE_I32);
99
#else
100
  return (((lbm_uint)x) << LBM_VAL_SHIFT) | LBM_TYPE_I32;
101
#endif
102
}
103
104
3682448
lbm_value lbm_enc_u32(uint32_t x) {
105
#ifndef LBM64
106
3682448
  lbm_value u = lbm_cons(x, ENC_SYM_RAW_U_TYPE);
107
3682448
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
108
3679620
  return lbm_set_ptr_type(u, LBM_TYPE_U32);
109
#else
110
  return (((lbm_uint)x) << LBM_VAL_SHIFT) | LBM_TYPE_U32;
111
#endif
112
}
113
114
22782
lbm_value lbm_enc_float(float x) {
115
#ifndef LBM64
116
  lbm_uint t;
117
22782
  memcpy(&t, &x, sizeof(lbm_float));
118
22782
  lbm_value f = lbm_cons(t, ENC_SYM_RAW_F_TYPE);
119
22782
  if (lbm_type_of(f) == LBM_TYPE_SYMBOL) return f;
120
22782
  return lbm_set_ptr_type(f, LBM_TYPE_FLOAT);
121
#else
122
  lbm_uint t = 0;
123
  memcpy(&t, &x, sizeof(float));
124
  return (((lbm_uint)t) << LBM_VAL_SHIFT) | LBM_TYPE_FLOAT;
125
#endif
126
}
127
128
#ifndef LBM64
129
8434134
static lbm_value enc_64_on_32(uint8_t *source, lbm_uint type_qual, lbm_uint type) {
130
8434134
  lbm_value res = lbm_cons(ENC_SYM_NIL,ENC_SYM_NIL);
131
8434134
  if (lbm_type_of(res) != LBM_TYPE_SYMBOL) {
132
8431812
    uint8_t* storage = lbm_malloc(sizeof(uint64_t));
133
8431812
    if (storage) {
134
8429104
      memcpy(storage,source, sizeof(uint64_t));
135
8429104
      lbm_set_car_and_cdr(res, (lbm_uint)storage,  type_qual);
136
8429104
      res = lbm_set_ptr_type(res, type);
137
    } else {
138
2708
      res = ENC_SYM_MERROR;
139
    }
140
  }
141
8434134
  return res;
142
}
143
#endif
144
145
4495172
lbm_value lbm_enc_i64(int64_t x) {
146
#ifndef LBM64
147
4495172
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_I_TYPE, LBM_TYPE_I64);
148
#else
149
  lbm_value u = lbm_cons((uint64_t)x, ENC_SYM_RAW_I_TYPE);
150
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
151
  return lbm_set_ptr_type(u, LBM_TYPE_I64);
152
#endif
153
}
154
155
3372994
lbm_value lbm_enc_u64(uint64_t x) {
156
#ifndef LBM64
157
3372994
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_U_TYPE, LBM_TYPE_U64);
158
#else
159
  lbm_value u = lbm_cons(x, ENC_SYM_RAW_U_TYPE);
160
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
161
  return lbm_set_ptr_type(u, LBM_TYPE_U64);
162
#endif
163
}
164
165
565968
lbm_value lbm_enc_double(double x) {
166
#ifndef LBM64
167
565968
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_F_TYPE, LBM_TYPE_DOUBLE);
168
#else
169
  lbm_uint t;
170
  memcpy(&t, &x, sizeof(double));
171
  lbm_value f = lbm_cons(t, ENC_SYM_RAW_F_TYPE);
172
  if (lbm_type_of(f) == LBM_TYPE_SYMBOL) return f;
173
  return lbm_set_ptr_type(f, LBM_TYPE_DOUBLE);
174
#endif
175
}
176
177
// Type specific (as opposed to the dec_as_X) functions
178
// should only be run on values KNOWN to represent a value of the type
179
// that the decoder decodes.
180
181
33707
float lbm_dec_float(lbm_value x) {
182
#ifndef LBM64
183
  float f_tmp;
184
33707
  lbm_uint tmp = lbm_car(x);
185
33707
  memcpy(&f_tmp, &tmp, sizeof(float));
186
33707
  return f_tmp;
187
#else
188
  uint32_t tmp = (uint32_t)(x >> LBM_VAL_SHIFT);
189
  float f_tmp;
190
  memcpy(&f_tmp, &tmp, sizeof(float));
191
  return f_tmp;
192
#endif
193
}
194
195
564764
double lbm_dec_double(lbm_value x) {
196
#ifndef LBM64
197
  double d;
198
564764
  uint32_t *data = (uint32_t*)lbm_car(x);
199
564764
  memcpy(&d, data, sizeof(double));
200
564764
  return d;
201
#else
202
  double f_tmp;
203
  lbm_uint tmp = lbm_car(x);
204
  memcpy(&f_tmp, &tmp, sizeof(double));
205
  return f_tmp;
206
#endif
207
}
208
209
7017326
uint64_t lbm_dec_u64(lbm_value x) {
210
#ifndef LBM64
211
  uint64_t u;
212
7017326
  uint32_t *data = (uint32_t*)lbm_car(x);
213
7017326
  memcpy(&u, data, 8);
214
7017326
  return u;
215
#else
216
  return (uint64_t)lbm_car(x);
217
#endif
218
}
219
220
9259084
int64_t lbm_dec_i64(lbm_value x) {
221
#ifndef LBM64
222
  int64_t i;
223
9259084
  uint32_t *data = (uint32_t*)lbm_car(x);
224
9259084
  memcpy(&i, data, 8);
225
9259084
  return i;
226
#else
227
  return (int64_t)lbm_car(x);
228
#endif
229
}
230
231
790870
char *lbm_dec_str(lbm_value val) {
232
790870
  char *res = 0;
233
  // If val is an array, car of val will be non-null.
234
790870
  if (lbm_is_array_r(val)) {
235
790674
    lbm_array_header_t *array = (lbm_array_header_t *)lbm_car(val);
236
790674
    res = (char *)array->data;
237
  }
238
790870
  return res;
239
}
240
241
11037165
lbm_char_channel_t *lbm_dec_channel(lbm_value val) {
242
11037165
  lbm_char_channel_t *res = NULL;
243
244
11037165
  if (lbm_type_of(val) == LBM_TYPE_CHANNEL) {
245
11037165
    res = (lbm_char_channel_t *)lbm_car(val);
246
  }
247
11037165
  return res;
248
}
249
250
980
lbm_uint lbm_dec_custom(lbm_value val) {
251
980
  lbm_uint res = 0;
252
980
  if (lbm_type_of(val) == LBM_TYPE_CUSTOM) {
253
980
    res = (lbm_uint)lbm_car(val);
254
  }
255
980
  return res;
256
}
257
258
60872
uint8_t lbm_dec_as_char(lbm_value a) {
259
60872
  uint8_t r = 0;
260


60872
  switch (lbm_type_of_functional(a)) {
261
60648
  case LBM_TYPE_CHAR:
262
60648
    r = (uint8_t)lbm_dec_char(a); break;
263
28
  case LBM_TYPE_I:
264
28
    r = (uint8_t)lbm_dec_i(a); break;
265
28
  case LBM_TYPE_U:
266
28
    r = (uint8_t)lbm_dec_u(a); break;
267
28
  case LBM_TYPE_I32:
268
28
    r = (uint8_t)lbm_dec_i32(a); break;
269
28
  case LBM_TYPE_U32:
270
28
    r = (uint8_t)lbm_dec_u32(a); break;
271
28
  case LBM_TYPE_FLOAT:
272
28
    r = (uint8_t)lbm_dec_float(a); break;
273
28
  case LBM_TYPE_I64:
274
28
    r = (uint8_t)lbm_dec_i64(a); break;
275
28
  case LBM_TYPE_U64:
276
28
    r = (uint8_t)lbm_dec_u64(a); break;
277
28
  case LBM_TYPE_DOUBLE:
278
28
    r = (uint8_t) lbm_dec_double(a); break;
279
  }
280
60872
  return r;
281
}
282
283
8421779
uint32_t lbm_dec_as_u32(lbm_value a) {
284
8421779
  uint32_t r = 0;
285


8421779
  switch (lbm_type_of_functional(a)) {
286
561938
  case LBM_TYPE_CHAR:
287
561938
    r = (uint32_t)lbm_dec_char(a); break;
288
1275690
  case LBM_TYPE_I:
289
1275690
    r = (uint32_t)lbm_dec_i(a); break;
290
1786917
  case LBM_TYPE_U:
291
1786917
    r = (uint32_t)lbm_dec_u(a); break;
292
4795104
  case LBM_TYPE_I32: /* fall through */
293
  case LBM_TYPE_U32:
294
4795104
    r = (uint32_t)lbm_dec_u32(a); break;
295
28
  case LBM_TYPE_FLOAT:
296
28
    r = (uint32_t)lbm_dec_float(a); break;
297
28
  case LBM_TYPE_I64:
298
28
    r = (uint32_t)lbm_dec_i64(a); break;
299
84
  case LBM_TYPE_U64:
300
84
    r = (uint32_t)lbm_dec_u64(a); break;
301
28
  case LBM_TYPE_DOUBLE:
302
28
    r = (uint32_t)lbm_dec_double(a); break;
303
  }
304
8421779
  return r;
305
}
306
307
206222862
int32_t lbm_dec_as_i32(lbm_value a) {
308
206222862
  int32_t r = 0;
309


206222862
  switch (lbm_type_of_functional(a)) {
310
5809792
  case LBM_TYPE_CHAR:
311
5809792
    r = (int32_t)lbm_dec_char(a); break;
312
196730286
  case LBM_TYPE_I:
313
196730286
    r = (int32_t)lbm_dec_i(a); break;
314
196
  case LBM_TYPE_U:
315
196
    r = (int32_t)lbm_dec_u(a); break;
316
3674140
  case LBM_TYPE_I32:
317
3674140
    r = (int32_t)lbm_dec_i32(a); break;
318
28
  case LBM_TYPE_U32:
319
28
    r = (int32_t)lbm_dec_u32(a); break;
320
28
  case LBM_TYPE_FLOAT:
321
28
    r = (int32_t)lbm_dec_float(a); break;
322
56
  case LBM_TYPE_I64:
323
56
    r = (int32_t)lbm_dec_i64(a); break;
324
56
  case LBM_TYPE_U64:
325
56
    r = (int32_t)lbm_dec_u64(a); break;
326
28
  case LBM_TYPE_DOUBLE:
327
28
    r = (int32_t) lbm_dec_double(a); break;
328
  }
329
206222862
  return r;
330
}
331
332
6732120
int64_t lbm_dec_as_i64(lbm_value a) {
333
6732120
  int64_t r = 0;
334


6732120
  switch (lbm_type_of_functional(a)) {
335
562266
  case LBM_TYPE_CHAR:
336
562266
    r = (int64_t)lbm_dec_char(a); break;
337
1402750
  case LBM_TYPE_I:
338
1402750
    r = (int64_t)lbm_dec_i(a); break;
339
168
  case LBM_TYPE_U:
340
168
    r = (int64_t)lbm_dec_u(a); break;
341
168
  case LBM_TYPE_I32:
342
168
    r = (int64_t)lbm_dec_i32(a); break;
343
168
  case LBM_TYPE_U32:
344
168
    r = (int64_t)lbm_dec_u32(a); break;
345
56
  case LBM_TYPE_FLOAT:
346
56
    r = (int64_t)lbm_dec_float(a); break;
347
4766376
  case LBM_TYPE_I64:
348
4766376
    r = (int64_t)lbm_dec_i64(a); break;
349
112
  case LBM_TYPE_U64:
350
112
    r = (int64_t)lbm_dec_u64(a); break;
351
56
  case LBM_TYPE_DOUBLE:
352
56
    r = (int64_t) lbm_dec_double(a); break;
353
  }
354
6732120
  return r;
355
}
356
357
4490278
uint64_t lbm_dec_as_u64(lbm_value a) {
358
4490278
  uint64_t r = 0;
359


4490278
  switch (lbm_type_of_functional(a)) {
360
562238
  case LBM_TYPE_CHAR:
361
562238
    r = (uint64_t)lbm_dec_char(a); break;
362
280852
  case LBM_TYPE_I:
363
280852
    r = (uint64_t)lbm_dec_i(a); break;
364
168
  case LBM_TYPE_U:
365
168
    r = (uint64_t)lbm_dec_u(a); break;
366
168
  case LBM_TYPE_I32:
367
168
    r = (uint64_t)lbm_dec_i32(a); break;
368
168
  case LBM_TYPE_U32:
369
168
    r = (uint64_t)lbm_dec_u32(a); break;
370
56
  case LBM_TYPE_FLOAT:
371
56
    r = (uint64_t)lbm_dec_float(a); break;
372
168
  case LBM_TYPE_I64:
373
168
    r = (uint64_t)lbm_dec_i64(a); break;
374
3646404
  case LBM_TYPE_U64:
375
3646404
    r = (uint64_t)lbm_dec_u64(a); break;
376
56
  case LBM_TYPE_DOUBLE:
377
56
    r = (uint64_t)lbm_dec_double(a); break;
378
  }
379
4490278
  return r;
380
}
381
382
2324
lbm_uint lbm_dec_as_uint(lbm_value a) {
383
2324
  lbm_uint r = 0;
384


2324
  switch (lbm_type_of_functional(a)) {
385
  case LBM_TYPE_CHAR:
386
    r = (lbm_uint)lbm_dec_char(a); break;
387
2324
  case LBM_TYPE_I:
388
2324
    r = (lbm_uint)lbm_dec_i(a); break;
389
  case LBM_TYPE_U:
390
    r = (lbm_uint)lbm_dec_u(a); break;
391
  case LBM_TYPE_I32:
392
    r = (lbm_uint)lbm_dec_i32(a); break;
393
  case LBM_TYPE_U32:
394
    r = (lbm_uint)lbm_dec_u32(a); break;
395
  case LBM_TYPE_FLOAT:
396
    r = (lbm_uint)lbm_dec_float(a); break;
397
  case LBM_TYPE_I64:
398
    r = (lbm_uint)lbm_dec_i64(a); break;
399
  case LBM_TYPE_U64:
400
    r = (lbm_uint) lbm_dec_u64(a); break;
401
  case LBM_TYPE_DOUBLE:
402
    r = (lbm_uint)lbm_dec_double(a); break;
403
  }
404
2324
  return r;
405
}
406
407
644
lbm_int lbm_dec_as_int(lbm_value a) {
408
644
  lbm_int r = 0;
409


644
  switch (lbm_type_of_functional(a)) {
410
  case LBM_TYPE_CHAR:
411
    r = (lbm_int)lbm_dec_char(a); break;
412
644
  case LBM_TYPE_I:
413
644
    r = (lbm_int)lbm_dec_i(a); break;
414
  case LBM_TYPE_U:
415
    r = (lbm_int)lbm_dec_u(a); break;
416
  case LBM_TYPE_I32:
417
    r = (lbm_int)lbm_dec_i32(a); break;
418
  case LBM_TYPE_U32:
419
    r = (lbm_int)lbm_dec_u32(a); break;
420
  case LBM_TYPE_FLOAT:
421
    r = (lbm_int)lbm_dec_float(a); break;
422
  case LBM_TYPE_I64:
423
    r = (lbm_int)lbm_dec_i64(a); break;
424
  case LBM_TYPE_U64:
425
    r = (lbm_int)lbm_dec_u64(a); break;
426
  case LBM_TYPE_DOUBLE:
427
    r = (lbm_int)lbm_dec_double(a); break;
428
  }
429
644
  return r;
430
}
431
432
19903
float lbm_dec_as_float(lbm_value a) {
433
19903
  float r = 0;
434


19903
  switch (lbm_type_of_functional(a)) {
435
1176
  case LBM_TYPE_CHAR:
436
1176
    r = (float)lbm_dec_char(a); break;
437
2128
  case LBM_TYPE_I:
438
2128
    r = (float)lbm_dec_i(a); break;
439
140
  case LBM_TYPE_U:
440
140
    r = (float)lbm_dec_u(a); break;
441
140
  case LBM_TYPE_I32:
442
140
    r = (float)lbm_dec_i32(a); break;
443
196
  case LBM_TYPE_U32:
444
196
    r = (float)lbm_dec_u32(a); break;
445
15815
  case LBM_TYPE_FLOAT:
446
15815
    r = (float)lbm_dec_float(a); break;
447
140
  case LBM_TYPE_I64:
448
140
    r = (float)lbm_dec_i64(a); break;
449
140
  case LBM_TYPE_U64:
450
140
    r = (float)lbm_dec_u64(a); break;
451
28
  case LBM_TYPE_DOUBLE:
452
28
    r = (float)lbm_dec_double(a); break;
453
  }
454
19903
  return r;
455
}
456
457
564204
double lbm_dec_as_double(lbm_value a) {
458
564204
  double r = 0;
459


564204
  switch (lbm_type_of_functional(a)) {
460
281168
  case LBM_TYPE_CHAR:
461
281168
    r = (double)lbm_dec_char(a); break;
462
280880
  case LBM_TYPE_I:
463
280880
    r = (double)lbm_dec_i(a); break;
464
140
  case LBM_TYPE_U:
465
140
    r = (double)lbm_dec_u(a); break;
466
140
  case LBM_TYPE_I32:
467
140
    r = (double)lbm_dec_i32(a); break;
468
140
  case LBM_TYPE_U32:
469
140
    r = (double)lbm_dec_u32(a); break;
470
364
  case LBM_TYPE_FLOAT:
471
364
    r = (double)lbm_dec_float(a); break;
472
140
  case LBM_TYPE_I64:
473
140
    r = (double)lbm_dec_i64(a); break;
474
140
  case LBM_TYPE_U64:
475
140
    r = (double)lbm_dec_u64(a); break;
476
1092
  case LBM_TYPE_DOUBLE:
477
1092
    r = (double)lbm_dec_double(a); break;
478
  }
479
564204
  return r;
480
}
481
482
/****************************************************/
483
/* HEAP MANAGEMENT                                  */
484
485
21672
static int generate_freelist(size_t num_cells) {
486
21672
  size_t i = 0;
487
488
21672
  if (!lbm_heap_state.heap) return 0;
489
490
21672
  lbm_heap_state.freelist = lbm_enc_cons_ptr(0);
491
492
  lbm_cons_t *t;
493
494
  // Add all cells to free list
495
201314304
  for (i = 1; i < num_cells; i ++) {
496
201292632
    t = lbm_ref_cell(lbm_enc_cons_ptr(i-1));
497
201292632
    t->car = ENC_SYM_RECOVERED;    // all cars in free list are "RECOVERED"
498
201292632
    t->cdr = lbm_enc_cons_ptr(i);
499
  }
500
501
  // Replace the incorrect pointer at the last cell.
502
21672
  t = lbm_ref_cell(lbm_enc_cons_ptr(num_cells-1));
503
21672
  t->cdr = ENC_SYM_NIL;
504
505
21672
  return 1;
506
}
507
508
347919
void lbm_nil_freelist(void) {
509
347919
  lbm_heap_state.freelist = ENC_SYM_NIL;
510
347919
  lbm_heap_state.num_alloc = lbm_heap_state.heap_size;
511
347919
}
512
513
21672
static void heap_init_state(lbm_cons_t *addr, lbm_uint num_cells,
514
                            lbm_uint* gc_stack_storage, lbm_uint gc_stack_size) {
515
21672
  lbm_heap_state.heap         = addr;
516
21672
  lbm_heap_state.heap_bytes   = (unsigned int)(num_cells * sizeof(lbm_cons_t));
517
21672
  lbm_heap_state.heap_size    = num_cells;
518
519
21672
  lbm_stack_create(&lbm_heap_state.gc_stack, gc_stack_storage, gc_stack_size);
520
521
21672
  lbm_heap_state.num_alloc           = 0;
522
21672
  lbm_heap_state.num_alloc_arrays    = 0;
523
21672
  lbm_heap_state.gc_num              = 0;
524
21672
  lbm_heap_state.gc_marked           = 0;
525
21672
  lbm_heap_state.gc_recovered        = 0;
526
21672
  lbm_heap_state.gc_recovered_arrays = 0;
527
21672
  lbm_heap_state.gc_least_free       = num_cells;
528
21672
  lbm_heap_state.gc_last_free        = num_cells;
529
21672
}
530
531
347919
void lbm_heap_new_freelist_length(void) {
532
347919
  lbm_uint l = lbm_heap_state.heap_size - lbm_heap_state.num_alloc;
533
347919
  lbm_heap_state.gc_last_free = l;
534
347919
  if (l < lbm_heap_state.gc_least_free)
535
3838
    lbm_heap_state.gc_least_free = l;
536
347919
}
537
538
21672
int lbm_heap_init(lbm_cons_t *addr, lbm_uint num_cells,
539
                  lbm_uint gc_stack_size) {
540
541
21672
  if (((uintptr_t)addr % 8) != 0) return 0;
542
543
21672
  memset(addr,0, sizeof(lbm_cons_t) * num_cells);
544
545
21672
  lbm_uint *gc_stack_storage = (lbm_uint*)lbm_malloc(gc_stack_size * sizeof(lbm_uint));
546
21672
  if (gc_stack_storage == NULL) return 0;
547
548
21672
  heap_init_state(addr, num_cells,
549
                  gc_stack_storage, gc_stack_size);
550
551
21672
  lbm_heaps[0] = addr;
552
553
21672
  return generate_freelist(num_cells);
554
}
555
556
557
365372002
lbm_value lbm_heap_allocate_cell(lbm_type ptr_type, lbm_value car, lbm_value cdr) {
558
  lbm_value r;
559
365372002
  lbm_value cell = lbm_heap_state.freelist;
560
365372002
  if (cell) {
561
365321510
    lbm_uint heap_ix = lbm_dec_ptr(cell);
562
365321510
    lbm_heap_state.freelist = lbm_heap_state.heap[heap_ix].cdr;
563
365321510
    lbm_heap_state.num_alloc++;
564
365321510
    lbm_heap_state.heap[heap_ix].car = car;
565
365321510
    lbm_heap_state.heap[heap_ix].cdr = cdr;
566
365321510
    r = lbm_set_ptr_type(cell, ptr_type);
567
  } else {
568
50492
    r = ENC_SYM_MERROR;
569
  }
570
365372002
  return r;
571
}
572
573
1254984
lbm_value lbm_heap_allocate_list(lbm_uint n) {
574
1254984
  if (n == 0) return ENC_SYM_NIL;
575
1251680
  if (lbm_heap_num_free() < n) return ENC_SYM_MERROR;
576
577
1250416
  lbm_value curr = lbm_heap_state.freelist;
578
1250416
  lbm_value res  = curr;
579
1250416
  if (lbm_type_of(curr) == LBM_TYPE_CONS) {
580
581
1250416
    lbm_cons_t *c_cell = NULL;
582
1250416
    lbm_uint count = 0;
583
    do {
584
6465912
      c_cell = lbm_ref_cell(curr);
585
6465912
      c_cell->car = ENC_SYM_NIL;
586
6465912
      curr = c_cell->cdr;
587
6465912
      count ++;
588
6465912
    } while (count < n);
589
1250416
    lbm_heap_state.freelist = curr;
590
1250416
    c_cell->cdr = ENC_SYM_NIL;
591
1250416
    lbm_heap_state.num_alloc+=count;
592
1250416
    return res;
593
  }
594
  return ENC_SYM_FATAL_ERROR;
595
}
596
597
624102
lbm_value lbm_heap_allocate_list_init_va(unsigned int n, va_list valist) {
598
624102
  if (n == 0) return ENC_SYM_NIL;
599
624102
  if (lbm_heap_num_free() < n) return ENC_SYM_MERROR;
600
601
619892
  lbm_value curr = lbm_heap_state.freelist;
602
619892
  lbm_value res  = curr;
603
619892
  if (lbm_type_of(curr) == LBM_TYPE_CONS) {
604
605
619892
    lbm_cons_t *c_cell = NULL;
606
619892
    unsigned int count = 0;
607
    do {
608
1524040
      c_cell = lbm_ref_cell(curr);
609
1524040
      c_cell->car = va_arg(valist, lbm_value);
610
1524040
      curr = c_cell->cdr;
611
1524040
      count ++;
612
1524040
    } while (count < n);
613
619892
    lbm_heap_state.freelist = curr;
614
619892
    c_cell->cdr = ENC_SYM_NIL;
615
619892
    lbm_heap_state.num_alloc+=count;
616
619892
    return res;
617
  }
618
  return ENC_SYM_FATAL_ERROR;
619
}
620
621
622310
lbm_value lbm_heap_allocate_list_init(unsigned int n, ...) {
622
    va_list valist;
623
622310
    va_start(valist, n);
624
622310
    lbm_value r = lbm_heap_allocate_list_init_va(n, valist);
625
622310
    va_end(valist);
626
622310
    return r;
627
}
628
629
lbm_uint lbm_heap_num_allocated(void) {
630
  return lbm_heap_state.num_alloc;
631
}
632
lbm_uint lbm_heap_size(void) {
633
  return lbm_heap_state.heap_size;
634
}
635
636
lbm_uint lbm_heap_size_bytes(void) {
637
  return lbm_heap_state.heap_bytes;
638
}
639
640
252
void lbm_get_heap_state(lbm_heap_state_t *res) {
641
252
  *res = lbm_heap_state;
642
252
}
643
644
lbm_uint lbm_get_gc_stack_max(void) {
645
  return lbm_get_max_stack(&lbm_heap_state.gc_stack);
646
}
647
648
lbm_uint lbm_get_gc_stack_size(void) {
649
  return lbm_heap_state.gc_stack.size;
650
}
651
652
#ifdef USE_GC_PTR_REV
653
/* ************************************************************
654
   Deutch-Schorr-Waite (DSW) pointer reversal GC for 2-ptr cells
655
   with a hack-solution for the lisp-array case (n-ptr cells).
656
657
   DSW visits each branch node 3 times compared to 2 times for
658
   the stack based recursive mark.
659
   Where the stack based recursive mark performs a stack push/pop,
660
   DSW rearranges the, current, prev, next and a ptr field on
661
   the heap.
662
663
   DSW changes the structure of the heap and it introduces an
664
   invalid pointer (LBM_PTR_NULL) temporarily during marking.
665
   Since the heap will be "messed up" while marking, a mutex
666
   is introuded to keep other processes out of the heap while
667
   marking.
668
669
   TODO: See if the extra index field in arrays can be used
670
   to mark arrays without resorting to recursive mark calls.
671
*/
672
673
static inline void value_assign(lbm_value *a, lbm_value b) {
674
  lbm_value a_old = *a & LBM_GC_MASK;
675
  *a = a_old | (b & ~LBM_GC_MASK);
676
}
677
678
void lbm_gc_mark_phase_nm(lbm_value root) {
679
  bool work_to_do = true;
680
  if (!lbm_is_ptr(root)) return;
681
682
  lbm_value curr = root;
683
  lbm_value prev = lbm_enc_cons_ptr(LBM_PTR_NULL);
684
685
  while (work_to_do) {
686
    // follow leftwards pointers
687
    while (lbm_is_ptr(curr) &&
688
           (lbm_dec_ptr(curr) != LBM_PTR_NULL) &&
689
           ((curr & LBM_PTR_TO_CONSTANT_BIT) == 0) &&
690
           !lbm_get_gc_mark(lbm_cdr(curr))) {
691
      // Mark the cell if not a constant cell
692
      lbm_cons_t *cell = lbm_ref_cell(curr);
693
      cell->cdr = lbm_set_gc_mark(cell->cdr);
694
      if (lbm_is_cons_rw(curr)) {
695
        lbm_value next = 0;
696
        value_assign(&next, cell->car);
697
        value_assign(&cell->car, prev);
698
        value_assign(&prev,curr);
699
        value_assign(&curr, next);
700
      } else if (lbm_type_of(curr) == LBM_TYPE_LISPARRAY) {
701
        lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
702
        lbm_value *arr_data = (lbm_value *)arr->data;
703
        size_t  arr_size = (size_t)arr->size / sizeof(lbm_value);
704
        // C stack recursion as deep as there are nested arrays.
705
        // TODO: Try to do this without recursion on the C side.
706
        for (size_t i = 0; i < arr_size; i ++) {
707
          lbm_gc_mark_phase_nm(arr_data[i]);
708
        }
709
      }
710
      // Will jump out next iteration as gc mark is set in curr.
711
    }
712
    while (lbm_is_ptr(prev) &&
713
           (lbm_dec_ptr(prev) != LBM_PTR_NULL) &&
714
           lbm_get_gc_flag(lbm_car(prev)) ) {
715
      // clear the flag
716
      lbm_cons_t *cell = lbm_ref_cell(prev);
717
      cell->car = lbm_clr_gc_flag(cell->car);
718
      lbm_value next = 0;
719
      value_assign(&next, cell->cdr);
720
      value_assign(&cell->cdr, curr);
721
      value_assign(&curr, prev);
722
      value_assign(&prev, next);
723
    }
724
    if (lbm_is_ptr(prev) &&
725
        lbm_dec_ptr(prev) == LBM_PTR_NULL) {
726
      work_to_do = false;
727
    } else if (lbm_is_ptr(prev)) {
728
      // set the flag
729
      lbm_cons_t *cell = lbm_ref_cell(prev);
730
      cell->car = lbm_set_gc_flag(cell->car);
731
      lbm_value next = 0;
732
      value_assign(&next, cell->car);
733
      value_assign(&cell->car, curr);
734
      value_assign(&curr, cell->cdr);
735
      value_assign(&cell->cdr, next);
736
    }
737
  }
738
}
739
740
void lbm_gc_mark_phase(lbm_value root) {
741
    mutex_lock(&lbm_const_heap_mutex);
742
    lbm_gc_mark_phase_nm(root);
743
    mutex_unlock(&lbm_const_heap_mutex);
744
}
745
746
#else
747
/* ************************************************************
748
   Explicit stack "recursive" mark phase
749
750
   Trees are marked in a left subtree before rigth subtree, car first then cdr,
751
   way to favor lisp lists. This means that stack will grow slowly when
752
   marking right-leaning (cdr-recursive) data-structures while left-leaning
753
   (car-recursive) structures uses a lot of stack.
754
755
   Lisp arrays contain an extra book-keeping field to keep track
756
   of how far into the array the marking process has gone.
757
758
   TODO: DSW should be used as a last-resort if the GC stack is exhausted.
759
         If we use DSW as last-resort can we get away with a way smaller
760
         GC stack and unchanged performance (on sensible programs)?
761
*/
762
763
extern eval_context_t *ctx_running;
764
4813291
void lbm_gc_mark_phase(lbm_value root) {
765
  lbm_value t_ptr;
766
4813291
  lbm_stack_t *s = &lbm_heap_state.gc_stack;
767
4813291
  s->data[s->sp++] = root;
768
769
28334176
  while (!lbm_stack_is_empty(s)) {
770
    lbm_value curr;
771
23520885
    lbm_pop(s, &curr);
772
773
53624146
  mark_shortcut:
774
775
53624146
    if (!lbm_is_ptr(curr) ||
776
32520712
        (curr & LBM_PTR_TO_CONSTANT_BIT)) {
777
23061271
      continue;
778
    }
779
780
32520712
    lbm_cons_t *cell = &lbm_heap_state.heap[lbm_dec_ptr(curr)];
781
782
32520712
    if (lbm_get_gc_mark(cell->cdr)) {
783
1941205
      continue;
784
    }
785
786
30579507
    t_ptr = lbm_type_of(curr);
787
788
    // An array is marked in O(N) time using an additional 32bit
789
    // value per array that keeps track of how far into the array GC
790
    // has progressed.
791
30579507
    if (t_ptr == LBM_TYPE_LISPARRAY) {
792
18172
      lbm_push(s, curr); // put array back as bookkeeping.
793
18172
      lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
794
18172
      lbm_value *arrdata = (lbm_value *)arr->data;
795
18172
      uint32_t index = arr->index;
796
797
      // Potential optimization.
798
      // 1. CONS pointers are set to curr and recurse.
799
      // 2. Any other ptr is marked immediately and index is increased.
800

18172
      if (lbm_is_ptr(arrdata[index]) && ((arrdata[index] & LBM_PTR_TO_CONSTANT_BIT) == 0) &&
801
9212
          !((arrdata[index] & LBM_CONTINUATION_INTERNAL) == LBM_CONTINUATION_INTERNAL)) {
802
4508
        lbm_cons_t *elt = &lbm_heap_state.heap[lbm_dec_ptr(arrdata[index])];
803
4508
        if (!lbm_get_gc_mark(elt->cdr)) {
804
1540
          curr = arrdata[index];
805
1540
          goto mark_shortcut;
806
        }
807
      }
808
16632
      if (index < ((arr->size/(sizeof(lbm_value))) - 1)) {
809
15764
        arr->index++;
810
15764
        continue;
811
      }
812
813
868
      arr->index = 0;
814
868
      cell->cdr = lbm_set_gc_mark(cell->cdr);
815
868
      lbm_heap_state.gc_marked ++;
816
868
      lbm_pop(s, &curr); // Remove array from GC stack as we are done marking it.
817
868
      continue;
818
30561335
    } else if (t_ptr == LBM_TYPE_CHANNEL) {
819
175028
      cell->cdr = lbm_set_gc_mark(cell->cdr);
820
175028
      lbm_heap_state.gc_marked ++;
821
      // TODO: Can channels be explicitly freed ?
822
175028
      if (cell->car != ENC_SYM_NIL) {
823
175028
        lbm_char_channel_t *chan = (lbm_char_channel_t *)cell->car;
824
175028
        curr = chan->dependency;
825
175028
        goto mark_shortcut;
826
      }
827
      continue;
828
    }
829
830
30386307
    cell->cdr = lbm_set_gc_mark(cell->cdr);
831
30386307
    lbm_heap_state.gc_marked ++;
832
833
30386307
    if (t_ptr == LBM_TYPE_CONS) {
834
29926693
      if (lbm_is_ptr(cell->cdr)) {
835
18690290
        if (!lbm_push(s, cell->cdr)) {
836
          lbm_critical_error();
837
          break;
838
        }
839
      }
840
29926693
      curr = cell->car;
841
29926693
      goto mark_shortcut; // Skip a push/pop
842
    }
843
  }
844
4813291
}
845
#endif
846
847
//Environments are proper lists with a 2 element list stored in each car.
848
11494485
void lbm_gc_mark_env(lbm_value env) {
849
11494485
  lbm_value curr = env;
850
  lbm_cons_t *c;
851
852
13119399
  while (lbm_is_ptr(curr)) {
853
1624914
    c = lbm_ref_cell(curr);
854
1624914
    c->cdr = lbm_set_gc_mark(c->cdr); // mark the environent list structure.
855
1624914
    lbm_cons_t *b = lbm_ref_cell(c->car);
856
1624914
    b->cdr = lbm_set_gc_mark(b->cdr); // mark the binding list head cell.
857
1624914
    lbm_gc_mark_phase(b->cdr);        // mark the bound object.
858
1624914
    lbm_heap_state.gc_marked +=2;
859
1624914
    curr = c->cdr;
860
  }
861
11494485
}
862
863
864
361077
void lbm_gc_mark_aux(lbm_uint *aux_data, lbm_uint aux_size) {
865
6483685
  for (lbm_uint i = 0; i < aux_size; i ++) {
866
6122608
    if (lbm_is_ptr(aux_data[i])) {
867
3703092
      lbm_type pt_t = lbm_type_of(aux_data[i]);
868
3703092
      lbm_uint pt_v = lbm_dec_ptr(aux_data[i]);
869

3703092
      if( pt_t >= LBM_POINTER_TYPE_FIRST &&
870
1824382
          pt_t <= LBM_POINTER_TYPE_LAST &&
871
1824382
          pt_v < lbm_heap_state.heap_size) {
872
1824382
        lbm_gc_mark_phase(aux_data[i]);
873
      }
874
    }
875
  }
876
361077
}
877
878
723318
void lbm_gc_mark_roots(lbm_uint *roots, lbm_uint num_roots) {
879
1811513
  for (lbm_uint i = 0; i < num_roots; i ++) {
880
1088195
    lbm_gc_mark_phase(roots[i]);
881
  }
882
723318
}
883
884
// Sweep moves non-marked heap objects to the free list.
885
347919
int lbm_gc_sweep_phase(void) {
886
347919
  unsigned int i = 0;
887
347919
  lbm_cons_t *heap = (lbm_cons_t *)lbm_heap_state.heap;
888
889
772550415
  for (i = 0; i < lbm_heap_state.heap_size; i ++) {
890
772202496
    if ( lbm_get_gc_mark(heap[i].cdr)) {
891
33645801
      heap[i].cdr = lbm_clr_gc_mark(heap[i].cdr);
892
    } else {
893
      // Check if this cell is a pointer to an array
894
      // and free it.
895
738556695
      if (lbm_type_of(heap[i].cdr) == LBM_TYPE_SYMBOL) {
896

52183520
        switch(heap[i].cdr) {
897
898
8369376
        case ENC_SYM_IND_I_TYPE: /* fall through */
899
        case ENC_SYM_IND_U_TYPE:
900
        case ENC_SYM_IND_F_TYPE:
901
8369376
          lbm_memory_free((lbm_uint*)heap[i].car);
902
8369376
          break;
903
1036
        case ENC_SYM_DEFRAG_ARRAY_TYPE:
904
1036
          lbm_defrag_mem_free((lbm_uint*)heap[i].car);
905
1036
          break;
906
294678
        case ENC_SYM_LISPARRAY_TYPE: /* fall through */
907
        case ENC_SYM_ARRAY_TYPE:{
908
294678
          lbm_array_header_t *arr = (lbm_array_header_t*)heap[i].car;
909
294678
          lbm_memory_free((lbm_uint *)arr->data);
910
294678
          lbm_heap_state.gc_recovered_arrays++;
911
294678
          lbm_memory_free((lbm_uint *)arr);
912
294678
        } break;
913
303696
        case ENC_SYM_CHANNEL_TYPE:{
914
303696
          lbm_char_channel_t *chan = (lbm_char_channel_t*)heap[i].car;
915
303696
          lbm_memory_free((lbm_uint*)chan->state);
916
303696
          lbm_memory_free((lbm_uint*)chan);
917
303696
        } break;
918
        case ENC_SYM_CUSTOM_TYPE: {
919
          lbm_uint *t = (lbm_uint*)heap[i].car;
920
          lbm_custom_type_destroy(t);
921
          lbm_memory_free(t);
922
          } break;
923
28
        case ENC_SYM_DEFRAG_MEM_TYPE: {
924
28
          lbm_uint *ptr = (lbm_uint *)heap[i].car;
925
28
          lbm_defrag_mem_destroy(ptr);
926
28
          } break;
927
43214706
        default:
928
43214706
          break;
929
        }
930
686373175
      }
931
      // create pointer to use as new freelist
932
738556695
      lbm_uint addr = lbm_enc_cons_ptr(i);
933
934
      // Clear the "freed" cell.
935
738556695
      heap[i].car = ENC_SYM_RECOVERED;
936
738556695
      heap[i].cdr = lbm_heap_state.freelist;
937
738556695
      lbm_heap_state.freelist = addr;
938
738556695
      lbm_heap_state.num_alloc --;
939
738556695
      lbm_heap_state.gc_recovered ++;
940
    }
941
  }
942
347919
  return 1;
943
}
944
945
347919
void lbm_gc_state_inc(void) {
946
347919
  lbm_heap_state.gc_num ++;
947
347919
  lbm_heap_state.gc_recovered = 0;
948
347919
  lbm_heap_state.gc_marked = 0;
949
347919
}
950
951
// construct, alter and break apart
952
364713222
lbm_value lbm_cons(lbm_value car, lbm_value cdr) {
953
364713222
  return lbm_heap_allocate_cell(LBM_TYPE_CONS, car, cdr);
954
}
955
956
245608648
lbm_value lbm_car(lbm_value c){
957
958
245608648
  if (lbm_is_ptr(c) ){
959
245608480
    lbm_cons_t *cell = lbm_ref_cell(c);
960
245608480
    return cell->car;
961
  }
962
963
168
  if (lbm_is_symbol_nil(c)) {
964
168
    return c; // if nil, return nil.
965
  }
966
967
  return ENC_SYM_TERROR;
968
}
969
970
// TODO: Many comparisons "is this the nil symbol" can be
971
// streamlined a bit. NIL is 0 and cannot be confused with any other
972
// lbm_value.
973
974
68
lbm_value lbm_caar(lbm_value c) {
975
68
  if (lbm_is_ptr(c)) {
976
68
    lbm_value tmp = lbm_ref_cell(c)->car;
977
978
68
    if (lbm_is_ptr(tmp)) {
979
68
      return lbm_ref_cell(tmp)->car;
980
    } else if (lbm_is_symbol_nil(tmp)) {
981
      return tmp;
982
    }
983
  } else if (lbm_is_symbol_nil(c)){
984
    return c;
985
  }
986
  return ENC_SYM_TERROR;
987
}
988
989
990
11620
lbm_value lbm_cadr(lbm_value c) {
991
11620
  if (lbm_is_ptr(c)) {
992
11620
    lbm_value tmp = lbm_ref_cell(c)->cdr;
993
994
11620
    if (lbm_is_ptr(tmp)) {
995
11620
      return lbm_ref_cell(tmp)->car;
996
    } else if (lbm_is_symbol_nil(tmp)) {
997
      return tmp;
998
    }
999
  } else if (lbm_is_symbol_nil(c)) {
1000
    return c;
1001
  }
1002
  return ENC_SYM_TERROR;
1003
}
1004
1005
112306452
lbm_value lbm_cdr(lbm_value c){
1006
112306452
  if (lbm_is_ptr(c)) {
1007
111739844
    lbm_cons_t *cell = lbm_ref_cell(c);
1008
111739844
    return cell->cdr;
1009
  }
1010
566608
  if (lbm_is_symbol_nil(c)) {
1011
566608
    return ENC_SYM_NIL; // if nil, return nil.
1012
  }
1013
  return ENC_SYM_TERROR;
1014
}
1015
1016
lbm_value lbm_cddr(lbm_value c) {
1017
  if (lbm_is_ptr(c)) {
1018
    lbm_value tmp = lbm_ref_cell(c)->cdr;
1019
    if (lbm_is_ptr(tmp)) {
1020
      return lbm_ref_cell(tmp)->cdr;
1021
    }
1022
  }
1023
  if (lbm_is_symbol_nil(c)) {
1024
    return ENC_SYM_NIL;
1025
  }
1026
  return ENC_SYM_TERROR;
1027
}
1028
1029
6513330
int lbm_set_car(lbm_value c, lbm_value v) {
1030
6513330
  int r = 0;
1031
1032
6513330
  if (lbm_type_of(c) == LBM_TYPE_CONS) {
1033
6513302
    lbm_cons_t *cell = lbm_ref_cell(c);
1034
6513302
    cell->car = v;
1035
6513302
    r = 1;
1036
  }
1037
6513330
  return r;
1038
}
1039
1040
99150604
int lbm_set_cdr(lbm_value c, lbm_value v) {
1041
99150604
  int r = 0;
1042
99150604
  if (lbm_is_cons_rw(c)){
1043
98584080
    lbm_cons_t *cell = lbm_ref_cell(c);
1044
98584080
    cell->cdr = v;
1045
98584080
    r = 1;
1046
  }
1047
99150604
  return r;
1048
}
1049
1050
8431652
int lbm_set_car_and_cdr(lbm_value c, lbm_value car_val, lbm_value cdr_val) {
1051
8431652
  int r = 0;
1052
8431652
  if (lbm_is_cons_rw(c)) {
1053
8431652
    lbm_cons_t *cell = lbm_ref_cell(c);
1054
8431652
    cell->car = car_val;
1055
8431652
    cell->cdr = cdr_val;
1056
8431652
    r = 1;
1057
  }
1058
8431652
  return r;
1059
}
1060
1061
/* calculate length of a proper list */
1062
1248496
lbm_uint lbm_list_length(lbm_value c) {
1063
1248496
  lbm_uint len = 0;
1064
1065
7210806
  while (lbm_is_cons(c)){
1066
5962310
    len ++;
1067
5962310
    c = lbm_cdr(c);
1068
  }
1069
1248496
  return len;
1070
}
1071
1072
/* calculate the length of a list and check that each element
1073
   fullfills the predicate pred */
1074
168
unsigned int lbm_list_length_pred(lbm_value c, bool *pres, bool (*pred)(lbm_value)) {
1075
168
  bool res = true;
1076
168
  unsigned int len = 0;
1077
1078
924
  while (lbm_is_cons(c)){
1079
756
    len ++;
1080

756
    res = res && pred(lbm_car(c));
1081
756
    c = lbm_cdr(c);
1082
  }
1083
168
  *pres = res;
1084
168
  return len;
1085
}
1086
1087
/* reverse a proper list */
1088
lbm_value lbm_list_reverse(lbm_value list) {
1089
  if (lbm_type_of(list) == LBM_TYPE_SYMBOL) {
1090
    return list;
1091
  }
1092
1093
  lbm_value curr = list;
1094
1095
  lbm_value new_list = ENC_SYM_NIL;
1096
  while (lbm_is_cons(curr)) {
1097
1098
    new_list = lbm_cons(lbm_car(curr), new_list);
1099
    if (lbm_type_of(new_list) == LBM_TYPE_SYMBOL) {
1100
      return ENC_SYM_MERROR;
1101
    }
1102
    curr = lbm_cdr(curr);
1103
  }
1104
  return new_list;
1105
}
1106
1107
168
lbm_value lbm_list_destructive_reverse(lbm_value list) {
1108
168
  if (lbm_type_of(list) == LBM_TYPE_SYMBOL) {
1109
    return list;
1110
  }
1111
168
  lbm_value curr = list;
1112
168
  lbm_value last_cell = ENC_SYM_NIL;
1113
1114
952
  while (lbm_is_cons_rw(curr)) {
1115
784
    lbm_value next = lbm_cdr(curr);
1116
784
    lbm_set_cdr(curr, last_cell);
1117
784
    last_cell = curr;
1118
784
    curr = next;
1119
  }
1120
168
  return last_cell;
1121
}
1122
1123
1124
330098
lbm_value lbm_list_copy(int *m, lbm_value list) {
1125
330098
  lbm_value curr = list;
1126
330098
  lbm_uint n = lbm_list_length(list);
1127
330098
  lbm_uint copy_n = n;
1128

330098
  if (*m >= 0 && (lbm_uint)*m < n) {
1129
5414
    copy_n = (lbm_uint)*m;
1130
324684
  } else if (*m == -1) {
1131
295540
    *m = (int)n; // TODO: smaller range in target variable.
1132
  }
1133
330098
  if (copy_n == 0) return ENC_SYM_NIL;
1134
329874
  lbm_uint new_list = lbm_heap_allocate_list(copy_n);
1135
329874
  if (lbm_is_symbol(new_list)) return new_list;
1136
329286
  lbm_value curr_targ = new_list;
1137
1138

4089908
  while (lbm_is_cons(curr) && copy_n > 0) {
1139
3760622
    lbm_value v = lbm_car(curr);
1140
3760622
    lbm_set_car(curr_targ, v);
1141
3760622
    curr_targ = lbm_cdr(curr_targ);
1142
3760622
    curr = lbm_cdr(curr);
1143
3760622
    copy_n --;
1144
  }
1145
1146
329286
  return new_list;
1147
}
1148
1149
// Append for proper lists only
1150
// Destructive update of list1.
1151
23744
lbm_value lbm_list_append(lbm_value list1, lbm_value list2) {
1152
1153

47488
  if(lbm_is_list_rw(list1) &&
1154
23744
     lbm_is_list(list2)) {
1155
1156
23744
    lbm_value curr = list1;
1157
55174
    while(lbm_type_of(lbm_cdr(curr)) == LBM_TYPE_CONS) {
1158
31430
      curr = lbm_cdr(curr);
1159
    }
1160
23744
    if (lbm_is_symbol_nil(curr)) return list2;
1161
23716
    lbm_set_cdr(curr, list2);
1162
23716
    return list1;
1163
  }
1164
  return ENC_SYM_EERROR;
1165
}
1166
1167
84
lbm_value lbm_list_drop(unsigned int n, lbm_value ls) {
1168
84
  lbm_value curr = ls;
1169

784
  while (lbm_type_of_functional(curr) == LBM_TYPE_CONS &&
1170
         n > 0) {
1171
700
    curr = lbm_cdr(curr);
1172
700
    n --;
1173
  }
1174
84
  return curr;
1175
}
1176
1177
151068
lbm_value lbm_index_list(lbm_value l, int32_t n) {
1178
151068
  lbm_value curr = l;
1179
1180
151068
  if (n < 0) {
1181
112
    int32_t len = (int32_t)lbm_list_length(l);
1182
112
    n = len + n;
1183
112
    if (n < 0) return ENC_SYM_NIL;
1184
  }
1185
1186

227470
  while (lbm_is_cons(curr) &&
1187
          n > 0) {
1188
76402
    curr = lbm_cdr(curr);
1189
76402
    n --;
1190
  }
1191
151068
  if (lbm_is_cons(curr)) {
1192
151040
    return lbm_car(curr);
1193
  } else {
1194
28
    return ENC_SYM_NIL;
1195
  }
1196
}
1197
1198
// High-level arrays are just bytearrays but with a different tag and pointer type.
1199
// These arrays will be inspected by GC and the elements of the array will be marked.
1200
1201
// Arrays are part of the heap module because their lifespan is managed
1202
// by the garbage collector. The data in the array is not stored
1203
// in the "heap of cons cells".
1204
296388
int lbm_heap_allocate_array_base(lbm_value *res, bool byte_array, lbm_uint size){
1205
1206
296388
  lbm_uint tag = ENC_SYM_ARRAY_TYPE;
1207
296388
  lbm_uint type = LBM_TYPE_ARRAY;
1208
296388
  if (!byte_array) {
1209
952
      tag = ENC_SYM_LISPARRAY_TYPE;
1210
952
      type = LBM_TYPE_LISPARRAY;
1211
952
      size = sizeof(lbm_value) * size;
1212
  }
1213
296388
  lbm_array_header_t *array = NULL;
1214
296388
  if (byte_array) {
1215
295436
    array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_t));
1216
  } else {
1217
952
    array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_extended_t));
1218
  }
1219
1220
296388
  if (array == NULL) {
1221
390
    *res = ENC_SYM_MERROR;
1222
390
    return 0;
1223
  }
1224
295998
  array->data = NULL;
1225
295998
  if ( size > 0) {
1226
295914
    if (!byte_array) {
1227
952
      lbm_array_header_extended_t *ext_array = (lbm_array_header_extended_t*)array;
1228
952
      ext_array->index = 0;
1229
    }
1230
1231
295914
    array->data = (lbm_uint*)lbm_malloc(size);
1232
1233
295914
    if (array->data == NULL) {
1234
5630
      lbm_memory_free((lbm_uint*)array);
1235
5630
      *res = ENC_SYM_MERROR;
1236
5630
      return 0;
1237
    }
1238
    // It is more important to zero out high-level arrays.
1239
    // 0 is symbol NIL which is perfectly safe for the GC to inspect.
1240
290284
    memset(array->data, 0, size);
1241
  }
1242
290368
  array->size = size;
1243
1244
  // allocating a cell for array's heap-presence
1245
290368
  lbm_value cell = lbm_heap_allocate_cell(type, (lbm_uint) array, tag);
1246
290368
  if (cell == ENC_SYM_MERROR) {
1247
88
    lbm_memory_free((lbm_uint*)array->data);
1248
88
    lbm_memory_free((lbm_uint*)array);
1249
88
    *res = ENC_SYM_MERROR;
1250
88
    return 0;
1251
  }
1252
290280
  *res = cell;
1253
1254
290280
  lbm_heap_state.num_alloc_arrays ++;
1255
1256
290280
  return 1;
1257
}
1258
1259
295436
int lbm_heap_allocate_array(lbm_value *res, lbm_uint size){
1260
295436
  return lbm_heap_allocate_array_base(res, true, size);
1261
}
1262
1263
952
int lbm_heap_allocate_lisp_array(lbm_value *res, lbm_uint size) {
1264
952
  return lbm_heap_allocate_array_base(res, false, size);
1265
}
1266
1267
// Convert a C array into an lbm_array.
1268
// if the array is in LBM_MEMORY, the lifetime will be managed by the GC after lifting.
1269
int lbm_lift_array(lbm_value *value, char *data, lbm_uint num_elt) {
1270
1271
  lbm_array_header_t *array = NULL;
1272
  lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CONS, ENC_SYM_NIL, ENC_SYM_ARRAY_TYPE);
1273
1274
  if (cell == ENC_SYM_MERROR) {
1275
    *value = cell;
1276
    return 0;
1277
  }
1278
1279
  array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_t));
1280
1281
  if (array == NULL) {
1282
    lbm_set_car_and_cdr(cell, ENC_SYM_NIL, ENC_SYM_NIL);
1283
    *value = ENC_SYM_MERROR;
1284
    return 0;
1285
  }
1286
1287
  array->data = (lbm_uint*)data;
1288
  array->size = num_elt;
1289
1290
  lbm_set_car(cell, (lbm_uint)array);
1291
1292
  cell = lbm_set_ptr_type(cell, LBM_TYPE_ARRAY);
1293
  *value = cell;
1294
  return 1;
1295
}
1296
1297
237384
lbm_int lbm_heap_array_get_size(lbm_value arr) {
1298
1299
237384
  lbm_int r = -1;
1300
237384
  if (lbm_is_array_r(arr)) {
1301
237384
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(arr);
1302
237384
    if (header == NULL) {
1303
      return r;
1304
    }
1305
237384
    r = (lbm_int)header->size;
1306
  }
1307
237384
  return r;
1308
}
1309
1310
118692
const uint8_t *lbm_heap_array_get_data_ro(lbm_value arr) {
1311
118692
  uint8_t *r = NULL;
1312
118692
  if (lbm_is_array_r(arr)) {
1313
118692
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(arr);
1314
118692
    r = (uint8_t*)header->data;
1315
  }
1316
118692
  return r;
1317
}
1318
1319
uint8_t *lbm_heap_array_get_data_rw(lbm_value arr) {
1320
  uint8_t *r = NULL;
1321
  if (lbm_is_array_rw(arr)) {
1322
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(arr);
1323
    r = (uint8_t*)header->data;
1324
  }
1325
  return r;
1326
}
1327
1328
1329
/* Explicitly freeing an array.
1330
1331
   This is a highly unsafe operation and can only be safely
1332
   used if the heap cell that points to the array has not been made
1333
   accessible to the program.
1334
1335
   So This function can be used to free an array in case an array
1336
   is being constructed and some error case appears while doing so
1337
   If the array still have not become available it can safely be
1338
   "explicitly" freed.
1339
1340
   The problem is that if the "array" heap-cell is made available to
1341
   the program, this cell can easily be duplicated and we would have
1342
   to search the entire heap to find all cells pointing to the array
1343
   memory in question and "null"-them out before freeing the memory
1344
*/
1345
1346
112
int lbm_heap_explicit_free_array(lbm_value arr) {
1347
1348
112
  int r = 0;
1349

112
  if (lbm_is_array_rw(arr) && lbm_cdr(arr) == ENC_SYM_ARRAY_TYPE) {
1350
112
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(arr);
1351
112
    if (header == NULL) {
1352
      return 0;
1353
    }
1354
112
    lbm_memory_free((lbm_uint*)header->data);
1355
112
    lbm_memory_free((lbm_uint*)header);
1356
1357
112
    arr = lbm_set_ptr_type(arr, LBM_TYPE_CONS);
1358
112
    lbm_set_car(arr, ENC_SYM_NIL);
1359
112
    lbm_set_cdr(arr, ENC_SYM_NIL);
1360
112
    r = 1;
1361
  }
1362
1363
112
  return r;
1364
}
1365
1366
lbm_uint lbm_size_of(lbm_type t) {
1367
  lbm_uint s = 0;
1368
  switch(t) {
1369
  case LBM_TYPE_BYTE:
1370
    s = 1;
1371
    break;
1372
  case LBM_TYPE_I: /* fall through */
1373
  case LBM_TYPE_U:
1374
  case LBM_TYPE_SYMBOL:
1375
    s = sizeof(lbm_uint);
1376
    break;
1377
  case LBM_TYPE_I32: /* fall through */
1378
  case LBM_TYPE_U32:
1379
  case LBM_TYPE_FLOAT:
1380
    s = 4;
1381
    break;
1382
  case LBM_TYPE_I64: /* fall through */
1383
  case LBM_TYPE_U64:
1384
  case LBM_TYPE_DOUBLE:
1385
    s = 8;
1386
    break;
1387
  }
1388
  return s;
1389
}
1390
1391
static bool dummy_flash_write(lbm_uint ix, lbm_uint val) {
1392
  (void)ix;
1393
  (void)val;
1394
  return false;
1395
}
1396
1397
static const_heap_write_fun const_heap_write = dummy_flash_write;
1398
1399
21672
int lbm_const_heap_init(const_heap_write_fun w_fun,
1400
                        lbm_const_heap_t *heap,
1401
                        lbm_uint *addr,
1402
                        lbm_uint  num_words) {
1403
21672
  if (((uintptr_t)addr % 4) != 0) return 0;
1404
21672
  if ((num_words % 2) != 0) return 0;
1405
1406
21672
  if (!lbm_const_heap_mutex_initialized) {
1407
21672
    mutex_init(&lbm_const_heap_mutex);
1408
21672
    lbm_const_heap_mutex_initialized = true;
1409
  }
1410
1411
21672
  if (!lbm_mark_mutex_initialized) {
1412
21672
    mutex_init(&lbm_mark_mutex);
1413
21672
    lbm_mark_mutex_initialized = true;
1414
  }
1415
1416
21672
  const_heap_write = w_fun;
1417
1418
21672
  heap->heap = addr;
1419
21672
  heap->size = num_words;
1420
21672
  heap->next = 0;
1421
1422
21672
  lbm_const_heap_state = heap;
1423
  // ref_cell views the lbm_uint array as an lbm_cons_t array
1424
21672
  lbm_heaps[1] = (lbm_cons_t*)addr;
1425
21672
  return 1;
1426
}
1427
1428
2408
lbm_flash_status lbm_allocate_const_cell(lbm_value *res) {
1429
2408
  lbm_flash_status r = LBM_FLASH_FULL;
1430
1431
2408
  mutex_lock(&lbm_const_heap_mutex);
1432
  // waste a cell if we have ended up unaligned after writing an array to flash.
1433
2408
  if (lbm_const_heap_state->next % 2 == 1) {
1434
28
    lbm_const_heap_state->next++;
1435
  }
1436
1437
2408
  if (lbm_const_heap_state &&
1438
2408
      (lbm_const_heap_state->next+1) < lbm_const_heap_state->size) {
1439
    // A cons cell uses two words.
1440
2408
    lbm_value cell = lbm_const_heap_state->next;
1441
2408
    lbm_const_heap_state->next += 2;
1442
2408
    *res = (cell << LBM_ADDRESS_SHIFT) | LBM_PTR_BIT | LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT;
1443
2408
    r = LBM_FLASH_WRITE_OK;
1444
  }
1445
2408
  mutex_unlock(&lbm_const_heap_mutex);
1446
2408
  return r;
1447
}
1448
1449
28
lbm_flash_status lbm_allocate_const_raw(lbm_uint nwords, lbm_uint *res) {
1450
28
  lbm_flash_status r = LBM_FLASH_FULL;
1451
1452
28
  if (lbm_const_heap_state &&
1453
28
      (lbm_const_heap_state->next + nwords) < lbm_const_heap_state->size) {
1454
28
    lbm_uint ix = lbm_const_heap_state->next;
1455
28
    *res = (lbm_uint)&lbm_const_heap_state->heap[ix];
1456
28
    lbm_const_heap_state->next += nwords;
1457
28
    r = LBM_FLASH_WRITE_OK;
1458
  }
1459
28
  return r;
1460
}
1461
1462
462
lbm_flash_status lbm_write_const_raw(lbm_uint *data, lbm_uint n, lbm_uint *res) {
1463
1464
462
  lbm_flash_status r = LBM_FLASH_FULL;
1465
1466
462
  if (lbm_const_heap_state &&
1467
462
      (lbm_const_heap_state->next + n) < lbm_const_heap_state->size) {
1468
462
    lbm_uint ix = lbm_const_heap_state->next;
1469
1470
1442
    for (unsigned int i = 0; i < n; i ++) {
1471
980
      if (!const_heap_write(ix + i, ((lbm_uint*)data)[i]))
1472
        return LBM_FLASH_WRITE_ERROR;
1473
    }
1474
462
    lbm_const_heap_state->next += n;
1475
462
    *res = (lbm_uint)&lbm_const_heap_state->heap[ix];
1476
462
    r = LBM_FLASH_WRITE_OK;
1477
  }
1478
462
  return r;
1479
}
1480
1481
84
lbm_flash_status lbm_const_write(lbm_uint *tgt, lbm_uint val) {
1482
1483
84
  if (lbm_const_heap_state) {
1484
84
    lbm_uint flash = (lbm_uint)lbm_const_heap_state->heap;
1485
84
    lbm_uint ix = (((lbm_uint)tgt - flash) / sizeof(lbm_uint)); // byte address to ix
1486
84
    if (const_heap_write(ix, val)) {
1487
84
      return LBM_FLASH_WRITE_OK;
1488
    }
1489
    return LBM_FLASH_WRITE_ERROR;
1490
  }
1491
  return LBM_FLASH_FULL;
1492
}
1493
1494
2408
lbm_flash_status write_const_cdr(lbm_value cell, lbm_value val) {
1495
2408
  lbm_uint addr = lbm_dec_ptr(cell);
1496
2408
  if (const_heap_write(addr+1, val))
1497
2408
    return LBM_FLASH_WRITE_OK;
1498
  return LBM_FLASH_WRITE_ERROR;
1499
}
1500
1501
2408
lbm_flash_status write_const_car(lbm_value cell, lbm_value val) {
1502
2408
  lbm_uint addr = lbm_dec_ptr(cell);
1503
2408
  if (const_heap_write(addr, val))
1504
2408
    return LBM_FLASH_WRITE_OK;
1505
  return LBM_FLASH_WRITE_ERROR;
1506
}
1507
1508
lbm_uint lbm_flash_memory_usage(void) {
1509
  return lbm_const_heap_state->next;
1510
}