GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/symrepr.c
Date: 2025-10-28 15:15:18
Exec Total Coverage
Lines: 140 140 100.0%
Functions: 21 21 100.0%
Branches: 53 59 89.8%

Line Branch Exec Source
1 /*
2 Copyright 2018, 2021, 2022, 2024, 2025 Joel Svensson svenssonjoel@yahoo.se
3 2025 Rasmus Söderhielm rasmus.soderhielm@gmail.com
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 <stdint.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <inttypes.h>
24
25
26 #include <lbm_memory.h>
27 #include <heap.h>
28 #include "symrepr.h"
29 #include "extensions.h"
30 #include "lbm_utils.h"
31 #include "lbm_image.h"
32
33 #define NUM_SPECIAL_SYMBOLS (sizeof(special_symbols) / sizeof(special_sym))
34 #define NAME 0
35 #define ID 1
36 #define NEXT 2
37
38 typedef struct {
39 const char *name;
40 const lbm_uint id;
41 } special_sym;
42
43 special_sym const special_symbols[] = {
44 {"nil" , SYM_NIL},
45 {"quote" , SYM_QUOTE},
46 {"t" , SYM_TRUE},
47 {"if" , SYM_IF},
48 {"cond" , SYM_COND},
49 {"lambda" , SYM_LAMBDA},
50 {"closure" , SYM_CLOSURE},
51 {"let" , SYM_LET},
52 {"define" , SYM_DEFINE},
53 {"progn" , SYM_PROGN},
54 {"read" , SYM_READ},
55 {"read-program" , SYM_READ_PROGRAM},
56 {"read-eval-program", SYM_READ_AND_EVAL_PROGRAM},
57 {"match" , SYM_MATCH},
58 {"_" , SYM_DONTCARE},
59 {"send" , SYM_SEND},
60 {"recv" , SYM_RECEIVE},
61 {"recv-to" , SYM_RECEIVE_TIMEOUT},
62 {"macro" , SYM_MACRO},
63 {"call-cc" , SYM_CALLCC},
64 {"continuation" , SYM_CONT},
65 {"var" , SYM_PROGN_VAR},
66 {"timeout" , SYM_TIMEOUT},
67
68 {"set" , SYM_SETVAR},
69 {"setq" , SYM_SETQ},
70 {"move-to-flash", SYM_MOVE_TO_FLASH},
71 {"exit-ok" , SYM_EXIT_OK},
72 {"exit-error" , SYM_EXIT_ERROR},
73 {"map" , SYM_MAP},
74 {"reverse" , SYM_REVERSE},
75 {"flatten" , SYM_FLATTEN},
76 {"unflatten" , SYM_UNFLATTEN},
77 {"kill" , SYM_KILL},
78 {"sleep" , SYM_SLEEP},
79 {"merge" , SYM_MERGE},
80 {"sort" , SYM_SORT},
81 {"gc" , SYM_PERFORM_GC},
82 {"loop" , SYM_LOOP},
83 {"trap" , SYM_TRAP},
84 {"rest-args" , SYM_REST_ARGS},
85 {"rotate" , SYM_ROTATE},
86 {"call-cc-unsafe", SYM_CALL_CC_UNSAFE},
87 {"apply" , SYM_APPLY},
88
89 // pattern matching
90 {"?" , SYM_MATCH_ANY},
91
92 // Error symbols with parsable names
93 {"no_match" , SYM_NO_MATCH},
94 {"read_error" , SYM_RERROR},
95 {"type_error" , SYM_TERROR},
96 {"eval_error" , SYM_EERROR},
97 {"out_of_memory" , SYM_MERROR},
98 {"fatal_error" , SYM_FATAL_ERROR},
99 {"out_of_stack" , SYM_STACK_ERROR},
100 {"division_by_zero" , SYM_DIVZERO},
101 {"variable_not_bound" , SYM_NOT_FOUND},
102 {"flash_full" , SYM_ERROR_FLASH_HEAP_FULL},
103
104 // Special symbols with unparsable names
105 {"$barray" , SYM_ARRAY_TYPE},
106 {"$raw_i" , SYM_RAW_I_TYPE},
107 {"$raw_u" , SYM_RAW_U_TYPE},
108 {"$raw_f" , SYM_RAW_F_TYPE},
109 {"$ind_i" , SYM_IND_I_TYPE},
110 {"$ind_u" , SYM_IND_U_TYPE},
111 {"$ind_f" , SYM_IND_F_TYPE},
112 {"$channel" , SYM_CHANNEL_TYPE},
113 {"$recovered" , SYM_RECOVERED},
114 {"$placeholder" , SYM_PLACEHOLDER},
115 {"$custom" , SYM_CUSTOM_TYPE},
116 {"$array" , SYM_LISPARRAY_TYPE},
117 {"$nonsense" , SYM_NONSENSE},
118 {"$dm-array" , SYM_DEFRAG_ARRAY_TYPE},
119 {"$dm" , SYM_DEFRAG_MEM_TYPE},
120
121 // tokenizer symbols with unparsable names
122 {"[openpar]" , SYM_OPENPAR},
123 {"[closepar]" , SYM_CLOSEPAR},
124 {"[backquote]" , SYM_BACKQUOTE},
125 {"[comma]" , SYM_COMMA},
126 {"[commaat]" , SYM_COMMAAT},
127 {"[dot]" , SYM_DOT},
128 {"[done]" , SYM_TOKENIZER_DONE},
129 {"[quote_it]" , SYM_QUOTE_IT},
130 {"[colon]" , SYM_COLON},
131 {"[wait]" , SYM_TOKENIZER_WAIT},
132 {"[openbrack]" , SYM_OPENBRACK},
133 {"[closebrack]" , SYM_CLOSEBRACK},
134 {"[rerror]" , SYM_TOKENIZER_RERROR},
135 {"[appcont]" , SYM_APP_CONT},
136 {"[openarr]" , SYM_OPENARRAY},
137 {"[closearr]" , SYM_CLOSEARRAY},
138
139 // special symbols with parseable names
140 {"type-list" , SYM_TYPE_LIST},
141 {"type-i" , SYM_TYPE_I},
142 {"type-u" , SYM_TYPE_U},
143 {"type-float" , SYM_TYPE_FLOAT},
144 {"type-i32" , SYM_TYPE_I32},
145 {"type-u32" , SYM_TYPE_U32},
146 {"type-double" , SYM_TYPE_DOUBLE},
147 {"type-i64" , SYM_TYPE_I64},
148 {"type-u64" , SYM_TYPE_U64},
149 {"type-array" , SYM_TYPE_ARRAY},
150 {"type-symbol" , SYM_TYPE_SYMBOL},
151 {"type-char" , SYM_TYPE_CHAR},
152 {"type-byte" , SYM_TYPE_BYTE},
153 {"type-channel" , SYM_TYPE_CHANNEL},
154 {"type-lisparray" , SYM_TYPE_LISPARRAY},
155 {"type-dm" , SYM_TYPE_DEFRAG_MEM},
156 {"type-custom" , SYM_TYPE_CUSTOM},
157
158 // Fundamental operations
159 {"+" , SYM_ADD},
160 {"-" , SYM_SUB},
161 {"*" , SYM_MUL},
162 {"/" , SYM_DIV},
163 {"//" , SYM_INT_DIV},
164 {"mod" , SYM_MOD},
165 {"=" , SYM_NUMEQ},
166 {"!=" , SYM_NUM_NOT_EQ},
167 {"<" , SYM_LT},
168 {">" , SYM_GT},
169 {"<=" , SYM_LEQ},
170 {">=" , SYM_GEQ},
171 {"eval" , SYM_EVAL},
172 {"eval-program" , SYM_EVAL_PROGRAM},
173 {"and" , SYM_AND},
174 {"or" , SYM_OR},
175 {"not" , SYM_NOT},
176 {"yield" , SYM_YIELD},
177 {"wait" , SYM_WAIT},
178 {"spawn" , SYM_SPAWN},
179 {"atomic" , SYM_ATOMIC},
180 {"self" , SYM_SELF},
181 {"spawn-trap" , SYM_SPAWN_TRAP},
182 {"set-mailbox-size" , SYM_SET_MAILBOX_SIZE},
183 {"eq" , SYM_EQ},
184 {"not-eq" , SYM_NOT_EQ},
185 {"car" , SYM_CAR},
186 {"cdr" , SYM_CDR},
187 {"cons" , SYM_CONS},
188 {"list" , SYM_LIST},
189 {"append" , SYM_APPEND},
190 {"undefine" , SYM_UNDEFINE},
191 {"bufcreate" , SYM_BYTEARRAY_CREATE},
192 {"type-of" , SYM_TYPE_OF},
193 {"sym2str" , SYM_SYMBOL_TO_STRING},
194 {"str2sym" , SYM_STRING_TO_SYMBOL},
195 {"sym2u" , SYM_SYMBOL_TO_UINT},
196 {"u2sym" , SYM_UINT_TO_SYMBOL},
197 {"setcar" , SYM_SET_CAR},
198 {"setcdr" , SYM_SET_CDR},
199 {"setix" , SYM_SET_IX},
200 {"length" , SYM_LIST_LENGTH},
201 {"range" , SYM_RANGE},
202 {"member" , SYM_MEMBER},
203
204 {"assoc" , SYM_ASSOC}, // lookup an association
205 {"cossa" , SYM_COSSA}, // lookup an association "backwards"
206 {"acons" , SYM_ACONS}, // Add to alist
207 {"setassoc" , SYM_SET_ASSOC}, // Change association
208
209 {"shl" , SYM_SHL},
210 {"shr" , SYM_SHR},
211 {"bitwise-and" , SYM_BITWISE_AND},
212 {"bitwise-or" , SYM_BITWISE_OR},
213 {"bitwise-xor" , SYM_BITWISE_XOR},
214 {"bitwise-not" , SYM_BITWISE_NOT},
215
216 {"custom-destruct", SYM_CUSTOM_DESTRUCT},
217
218 {"to-i" , SYM_TO_I},
219 {"to-i32" , SYM_TO_I32},
220 {"to-u" , SYM_TO_U},
221 {"to-u32" , SYM_TO_U32},
222 {"to-float" , SYM_TO_FLOAT},
223 {"to-i64" , SYM_TO_I64},
224 {"to-u64" , SYM_TO_U64},
225 {"to-double" , SYM_TO_DOUBLE},
226 {"to-byte" , SYM_TO_BYTE},
227
228 {"event-register-handler", SYM_REG_EVENT_HANDLER},
229 {"take" , SYM_TAKE},
230 {"drop" , SYM_DROP},
231 {"mkarray" , SYM_MKARRAY},
232
233 {"dm-create" , SYM_DM_CREATE},
234 {"dm-alloc" , SYM_DM_ALLOC},
235
236 {"list?" , SYM_IS_LIST},
237 {"number?" , SYM_IS_NUMBER},
238 {"string?" , SYM_IS_STRING},
239 {"constant?" , SYM_IS_CONSTANT},
240
241 // fast access in list
242 {"ix" , SYM_IX},
243
244 {"identity" , SYM_IDENTITY},
245 {"array" , SYM_ARRAY},
246
247 // aliases
248 {"first" , SYM_CAR},
249 {"rest" , SYM_CDR},
250 {"fn" , SYM_LAMBDA},
251 {"def" , SYM_DEFINE},
252 {"true" , SYM_TRUE},
253 {"false" , SYM_NIL},
254 {"setvar" , SYM_SETVAR},
255 {"type-f32" , SYM_TYPE_FLOAT},
256 {"type-f64" , SYM_TYPE_DOUBLE},
257 {"array-create" , SYM_BYTEARRAY_CREATE},
258
259 };
260
261 static lbm_uint *symlist = NULL;
262 static lbm_uint next_symbol_id = RUNTIME_SYMBOLS_START;
263 static lbm_uint symbol_table_size_list = 0;
264 static lbm_uint symbol_table_size_list_flash = 0;
265 static lbm_uint symbol_table_size_strings = 0;
266 static lbm_uint symbol_table_size_strings_flash = 0;
267
268 // When rebooting an image...
269 5009 void lbm_symrepr_set_symlist(lbm_uint *ls) {
270 5009 symlist = ls;
271 5009 }
272
273
274 5012 lbm_uint lbm_symrepr_get_next_id(void) {
275 5012 return next_symbol_id;
276 }
277
278 5010 void lbm_symrepr_set_next_id(lbm_uint id) {
279 5010 next_symbol_id = id;
280 5010 }
281
282 66568 bool lbm_symrepr_init(void) {
283 66568 symlist = NULL;
284 66568 next_symbol_id = RUNTIME_SYMBOLS_START;
285 66568 symbol_table_size_list = 0;
286 66568 symbol_table_size_list_flash = 0;
287 66568 symbol_table_size_strings = 0;
288 66568 symbol_table_size_strings_flash = 0;
289 66568 return true;
290 }
291
292 1 void lbm_symrepr_name_iterator(symrepr_name_iterator_fun f) {
293
294 1 lbm_uint *curr = symlist;
295
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 while (curr) {
296 10 f((const char *)curr[NAME]);
297 10 curr = (lbm_uint *)curr[NEXT];
298 }
299 1 }
300
301 414617 const char *lookup_symrepr_name_memory(lbm_uint id) {
302 414617 const char *res = NULL;
303 414617 lbm_uint *curr = symlist;
304
2/2
✓ Branch 0 taken 16529921 times.
✓ Branch 1 taken 414617 times.
16944538 while (curr) {
305
2/2
✓ Branch 0 taken 414616 times.
✓ Branch 1 taken 16115305 times.
16529921 if (id == curr[ID]) {
306 414616 res = (const char *)curr[NAME];
307 }
308 16529921 curr = (lbm_uint*)curr[NEXT];
309 }
310 414617 return res;
311 }
312
313 // Lookup symbol name given a symbol id
314 1207361 const char *lbm_get_name_by_symbol(lbm_uint id) {
315 1207361 lbm_uint sym_kind = SYMBOL_KIND(id);
316 1207361 const char *res = NULL;
317
3/3
✓ Branch 0 taken 769330 times.
✓ Branch 1 taken 23414 times.
✓ Branch 2 taken 414617 times.
1207361 switch (sym_kind) {
318 769330 case SYMBOL_KIND_SPECIAL: /* fall through */
319 case SYMBOL_KIND_FUNDAMENTAL:
320 case SYMBOL_KIND_APPFUN:
321
2/2
✓ Branch 0 taken 15251481 times.
✓ Branch 1 taken 28 times.
15251509 for (unsigned int i = 0; i < NUM_SPECIAL_SYMBOLS; i ++) {
322
2/2
✓ Branch 0 taken 769302 times.
✓ Branch 1 taken 14482179 times.
15251481 if (id == special_symbols[i].id) {
323 769302 res = (special_symbols[i].name);
324 // With aliases there can be more than one hit here.
325 // exit after first hit.
326 769302 break;
327 }
328 }
329 769330 break;
330 23414 case SYMBOL_KIND_EXTENSION: {
331 23414 lbm_uint ext_id = id - EXTENSION_SYMBOLS_START;
332
1/2
✓ Branch 0 taken 23414 times.
✗ Branch 1 not taken.
23414 if (ext_id < lbm_get_max_extensions()) {
333 23414 res = extension_table[ext_id].name;
334 }
335 23414 } break;
336 414617 default:
337 414617 res = lookup_symrepr_name_memory(id);
338 }
339 1207361 return res;
340 }
341
342 5 lbm_uint *lbm_get_symbol_list_entry_by_name(char *name) {
343 5 lbm_uint *curr = symlist;
344
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 3 times.
32 while (curr) {
345 29 char *str = (char*)curr[NAME];
346
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 27 times.
29 if (str_eq(name, str)) {
347 2 break;
348 }
349 27 curr = (lbm_uint*)curr[NEXT];
350 }
351 5 return curr;
352 }
353
354 // Lookup symbol id given symbol name
355 11883071 int lbm_get_symbol_by_name(char *name, lbm_uint* id) {
356 11883071 int res = 0;
357 // loop through special symbols
358
2/2
✓ Branch 0 taken 2102752033 times.
✓ Branch 1 taken 10734756 times.
2113486789 for (unsigned int i = 0; i < NUM_SPECIAL_SYMBOLS; i ++) {
359
2/2
✓ Branch 0 taken 1148315 times.
✓ Branch 1 taken 2101603718 times.
2102752033 if (str_eq(name, (char *)special_symbols[i].name)) {
360 1148315 *id = special_symbols[i].id;
361 1148315 res = 1; goto get_symbol_by_name_done;
362 }
363 }
364
365 // loop through extensions
366
2/2
✓ Branch 0 taken 3092652462 times.
✓ Branch 1 taken 10590953 times.
3103243415 for (unsigned int i = 0; i < lbm_get_max_extensions(); i ++) {
367
4/4
✓ Branch 0 taken 686541260 times.
✓ Branch 1 taken 2406111202 times.
✓ Branch 2 taken 143803 times.
✓ Branch 3 taken 686397457 times.
3092652462 if (extension_table[i].name && str_eq(name, extension_table[i].name)) {
368 143803 *id = EXTENSION_SYMBOLS_START + i;
369 143803 res = 1; goto get_symbol_by_name_done;
370 }
371 }
372
373 10590953 lbm_uint *curr = symlist;
374
2/2
✓ Branch 0 taken 129883592 times.
✓ Branch 1 taken 8845718 times.
138729310 while (curr) {
375 129883592 char *str = (char*)curr[NAME];
376
2/2
✓ Branch 0 taken 1745235 times.
✓ Branch 1 taken 128138357 times.
129883592 if (str_eq(name, str)) {
377 1745235 *id = curr[ID];
378 1745235 res = 1; goto get_symbol_by_name_done;
379 }
380 128138357 curr = (lbm_uint*)curr[NEXT];
381 }
382 8845718 get_symbol_by_name_done:
383 11883071 return res;
384 }
385
386 extern lbm_flash_status lbm_write_const_array_padded(uint8_t *data, lbm_uint n, lbm_uint *res);
387
388 455177 bool store_symbol_name_flash(char *name, lbm_uint *res) {
389 455177 bool ret = false;
390 455177 size_t n = strlen(name) + 1;
391
2/2
✓ Branch 0 taken 455176 times.
✓ Branch 1 taken 1 times.
455177 if (n > 1) {
392
393 lbm_uint alloc_size;
394
2/2
✓ Branch 0 taken 56994 times.
✓ Branch 1 taken 398182 times.
455176 if (n % sizeof(lbm_uint) == 0) {
395 56994 alloc_size = n/(sizeof(lbm_uint));
396 } else {
397 398182 alloc_size = (n/(sizeof(lbm_uint))) + 1;
398 }
399
400 455176 lbm_uint symbol_addr = 0;
401 455176 lbm_flash_status s = lbm_write_const_array_padded((uint8_t*)name, n, &symbol_addr);
402
2/4
✓ Branch 0 taken 455176 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 455176 times.
✗ Branch 3 not taken.
455176 if (s == LBM_FLASH_WRITE_OK && symbol_addr) {
403 455176 symbol_table_size_strings_flash += alloc_size;
404 455176 *res = symbol_addr;
405 455176 ret = true;
406 }
407 }
408 455177 return ret;
409 }
410
411 // Symbol table
412 // non-const name copied into symbol-table-entry:
413 // Entry
414 // |
415 // [name-ptr | symbol-id | next-ptr | name n-bytes]
416 // | /
417 // ------------points here -----
418 //
419 // const name referenced by symbol-table-entry:
420 // Entry
421 // |
422 // [name-ptr | symbol-id | next-ptr]
423 // |
424 // [name n-bytes]
425 //
426
427 455177 int lbm_add_symbol_base(char *name, lbm_uint *id) {
428 455177 int res = 0;
429 lbm_uint symbol_name_storage;
430
2/2
✓ Branch 0 taken 455176 times.
✓ Branch 1 taken 1 times.
455177 if (store_symbol_name_flash(name, &symbol_name_storage)) {
431 455176 lbm_uint *new_symlist = lbm_image_add_symbol((char*)symbol_name_storage, next_symbol_id, (lbm_uint)symlist);
432
1/2
✓ Branch 0 taken 455176 times.
✗ Branch 1 not taken.
455176 if (new_symlist) {
433 455176 symlist = new_symlist;
434 455176 *id = next_symbol_id ++;
435 455176 res = 1;
436 }
437 }
438 455177 return res;
439 }
440
441 325932 int lbm_add_symbol(char *name, lbm_uint* id) {
442 325932 int res = 0;
443
2/2
✓ Branch 0 taken 190546 times.
✓ Branch 1 taken 135386 times.
325932 if (lbm_get_symbol_by_name(name, id)) {
444 190546 res = 1;
445 } else {
446 135386 res = lbm_add_symbol_base(name, id);
447 }
448 325932 return res;
449 }
450
451 // on Linux, win, etc a const string may not be at
452 // the same address between runs.
453 1336501 int lbm_add_symbol_const_base(char *name, lbm_uint* id, bool link) {
454 1336501 lbm_uint symbol_name_storage = (lbm_uint)name;
455 lbm_uint *new_symlist;
456 1336501 int res = 0;
457
2/2
✓ Branch 0 taken 1336485 times.
✓ Branch 1 taken 16 times.
1336501 if (link) {
458 1336485 new_symlist = lbm_image_add_and_link_symbol((char*)symbol_name_storage, next_symbol_id, (lbm_uint)symlist, id);
459 } else {
460 16 new_symlist = lbm_image_add_symbol((char*)symbol_name_storage, next_symbol_id, (lbm_uint)symlist);
461 }
462
1/2
✓ Branch 0 taken 1336501 times.
✗ Branch 1 not taken.
1336501 if (new_symlist) {
463 1336501 symlist = new_symlist;
464 1336501 *id = next_symbol_id ++;
465 1336501 res = 1;
466 }
467 1336501 return res;
468 }
469
470 1413675 int lbm_add_symbol_const(char *name, lbm_uint* id) {
471 1413675 int res = 0;
472
2/2
✓ Branch 0 taken 77191 times.
✓ Branch 1 taken 1336484 times.
1413675 if (lbm_get_symbol_by_name(name, id)) {
473 77191 res = 1;
474 } else {
475 1336484 res = lbm_add_symbol_const_base(name, id, true);
476 }
477 1413675 return res;
478 }
479
480 189 int lbm_str_to_symbol(char *name, lbm_uint *sym_id) {
481 189 int res = lbm_get_symbol_by_name(name, sym_id);
482
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 88 times.
189 if (!res)
483 101 res = lbm_add_symbol(name, sym_id);
484 189 return res;
485 }
486
487 86 lbm_uint lbm_get_symbol_table_size(void) {
488 86 return (symbol_table_size_list +symbol_table_size_strings);
489 }
490
491 86 lbm_uint lbm_get_symbol_table_size_flash(void) {
492 86 return (symbol_table_size_list_flash +
493 86 symbol_table_size_strings_flash) * sizeof(lbm_uint);
494 }
495
496 86 lbm_uint lbm_get_symbol_table_size_names(void) {
497 86 return symbol_table_size_strings; // Bytes already
498 }
499
500 86 lbm_uint lbm_get_symbol_table_size_names_flash(void) {
501 86 return symbol_table_size_strings_flash * sizeof(lbm_uint);
502 }
503
504 1 bool lbm_symbol_in_flash(char *str) {
505 1 return !lbm_memory_ptr_inside((lbm_uint*)str);
506 }
507
508 2 bool lbm_symbol_list_entry_in_flash(char *str) {
509 2 lbm_uint *entry = lbm_get_symbol_list_entry_by_name(str);
510
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 return (entry == NULL || !lbm_memory_ptr_inside(entry));
511 }
512