elementdrawing 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/elementdrawing.min.js +3 -0
  3. package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
  4. package/dist/elementdrawing.min.js.map +1 -0
  5. package/dist/index.html +1 -0
  6. package/package.json +127 -0
  7. package/src/core/bridge.h +855 -0
  8. package/src/core/diff.c +900 -0
  9. package/src/core/element.c +1078 -0
  10. package/src/core/event.c +813 -0
  11. package/src/core/fiber.c +1027 -0
  12. package/src/core/hooks.c +919 -0
  13. package/src/core/renderer.c +963 -0
  14. package/src/core/scheduler.c +702 -0
  15. package/src/core/state.c +803 -0
  16. package/src/css/animations.css +779 -0
  17. package/src/css/base.css +615 -0
  18. package/src/css/components.css +1311 -0
  19. package/src/css/tailwind.css +370 -0
  20. package/src/css/themes.css +517 -0
  21. package/src/css/utilities.css +475 -0
  22. package/src/index.js +746 -0
  23. package/src/js/animation.js +655 -0
  24. package/src/js/dom.js +665 -0
  25. package/src/js/events.js +585 -0
  26. package/src/js/http.js +446 -0
  27. package/src/js/index.js +26 -0
  28. package/src/js/router.js +483 -0
  29. package/src/js/store.js +539 -0
  30. package/src/js/utils.js +593 -0
  31. package/src/js/validator.js +529 -0
  32. package/src/jsx/components/Accordion.jsx +210 -0
  33. package/src/jsx/components/Alert.jsx +169 -0
  34. package/src/jsx/components/Avatar.jsx +214 -0
  35. package/src/jsx/components/Badge.jsx +136 -0
  36. package/src/jsx/components/Breadcrumb.jsx +200 -0
  37. package/src/jsx/components/Button.jsx +188 -0
  38. package/src/jsx/components/Card.jsx +192 -0
  39. package/src/jsx/components/Carousel.jsx +278 -0
  40. package/src/jsx/components/Checkbox.jsx +215 -0
  41. package/src/jsx/components/Dialog.jsx +242 -0
  42. package/src/jsx/components/Drawer.jsx +190 -0
  43. package/src/jsx/components/Dropdown.jsx +268 -0
  44. package/src/jsx/components/Form.jsx +274 -0
  45. package/src/jsx/components/Input.jsx +285 -0
  46. package/src/jsx/components/Menu.jsx +276 -0
  47. package/src/jsx/components/Modal.jsx +274 -0
  48. package/src/jsx/components/Navbar.jsx +292 -0
  49. package/src/jsx/components/Pagination.jsx +268 -0
  50. package/src/jsx/components/Progress.jsx +252 -0
  51. package/src/jsx/components/Radio.jsx +208 -0
  52. package/src/jsx/components/Select.jsx +397 -0
  53. package/src/jsx/components/Sidebar.jsx +250 -0
  54. package/src/jsx/components/Slider.jsx +310 -0
  55. package/src/jsx/components/Spinner.jsx +198 -0
  56. package/src/jsx/components/Switch.jsx +201 -0
  57. package/src/jsx/components/Table.jsx +332 -0
  58. package/src/jsx/components/Tabs.jsx +227 -0
  59. package/src/jsx/components/Textarea.jsx +212 -0
  60. package/src/jsx/components/Toast.jsx +270 -0
  61. package/src/jsx/components/Tooltip.jsx +178 -0
  62. package/src/jsx/components/Typography.jsx +299 -0
  63. package/src/jsx/components/index.jsx +70 -0
  64. package/src/jsx/core/element.js +3 -0
  65. package/src/jsx/hooks/index.js +356 -0
  66. package/src/jsx/hooks/useCallback.js +472 -0
  67. package/src/jsx/hooks/useContext.js +586 -0
  68. package/src/jsx/hooks/useEffect.js +704 -0
  69. package/src/jsx/hooks/useLayoutEffect.js +508 -0
  70. package/src/jsx/hooks/useMemo.js +689 -0
  71. package/src/jsx/hooks/useReducer.js +729 -0
  72. package/src/jsx/hooks/useRef.js +542 -0
  73. package/src/jsx/hooks/useState.js +854 -0
  74. package/src/jsx/runtime/commit.js +903 -0
  75. package/src/jsx/runtime/createElement.js +860 -0
  76. package/src/jsx/runtime/index.js +356 -0
  77. package/src/jsx/runtime/reconcile.js +687 -0
  78. package/src/jsx/runtime/render.js +914 -0
@@ -0,0 +1,1078 @@
1
+ /*
2
+ * element.c - ElementDrawing Core: Virtual DOM, Memory Pool, String Interning
3
+ *
4
+ * Implements virtual node creation/manipulation, memory pool allocation,
5
+ * string interning table, and general utility functions for the
6
+ * ElementDrawing C-core engine.
7
+ */
8
+
9
+ #include "bridge.h"
10
+
11
+ /* ================================================================
12
+ * Internal Static State
13
+ * ================================================================ */
14
+
15
+ static EDMemoryPool* g_default_pool = NULL;
16
+ static EDStringEntry* g_string_table[ED_STRING_TABLE_SIZE];
17
+ static uint32_t g_string_table_entries = 0;
18
+ static bool g_string_table_initialized = false;
19
+ static EDSymbolEntry* g_symbol_table[ED_SYMBOL_TABLE_SIZE];
20
+ static uint32_t g_symbol_id_counter = 1;
21
+ static EDWeakRef* g_weak_ref_table[ED_WEAK_REF_TABLE_SIZE];
22
+ static uint32_t g_weak_ref_counter = 0;
23
+ static EDConfiguration g_config = {0};
24
+ static bool g_renderer_initialized = false;
25
+
26
+ /* ================================================================
27
+ * Hash Utilities
28
+ * ================================================================ */
29
+
30
+ uint32_t ed_hash_djb2(const char* str)
31
+ {
32
+ if (!str) return 0;
33
+ uint32_t hash = 5381;
34
+ int c;
35
+ while ((c = (unsigned char)*str++)) {
36
+ hash = ((hash << 5) + hash) + c;
37
+ }
38
+ return hash;
39
+ }
40
+
41
+ uint32_t ed_hash_fnv1a(const char* str)
42
+ {
43
+ if (!str) return 0;
44
+ uint32_t hash = 2166136261u;
45
+ while (*str) {
46
+ hash ^= (unsigned char)*str++;
47
+ hash *= 16777619u;
48
+ }
49
+ return hash;
50
+ }
51
+
52
+ static uint32_t ed_hash_combine(uint32_t h1, uint32_t h2)
53
+ {
54
+ return h1 ^ (h2 * 2654435761u);
55
+ }
56
+
57
+ /* ================================================================
58
+ * String Utilities
59
+ * ================================================================ */
60
+
61
+ bool ed_str_equals(const char* a, const char* b)
62
+ {
63
+ if (a == b) return true;
64
+ if (!a || !b) return false;
65
+ return strcmp(a, b) == 0;
66
+ }
67
+
68
+ char* ed_str_duplicate(const char* str)
69
+ {
70
+ if (!str) return NULL;
71
+ size_t len = strlen(str);
72
+ char* dup = (char*)malloc(len + 1);
73
+ if (!dup) return NULL;
74
+ memcpy(dup, str, len + 1);
75
+ return dup;
76
+ }
77
+
78
+ uint32_t ed_next_power_of_2(uint32_t n)
79
+ {
80
+ if (n == 0) return 1;
81
+ n--;
82
+ n |= n >> 1;
83
+ n |= n >> 2;
84
+ n |= n >> 4;
85
+ n |= n >> 8;
86
+ n |= n >> 16;
87
+ return n + 1;
88
+ }
89
+
90
+ /* ================================================================
91
+ * Logging
92
+ * ================================================================ */
93
+
94
+ static const char* ed_log_level_name(int level)
95
+ {
96
+ switch (level) {
97
+ case 0: return "INFO";
98
+ case 1: return "WARN";
99
+ case 2: return "ERROR";
100
+ case 3: return "DEBUG";
101
+ default: return "UNKNOWN";
102
+ }
103
+ }
104
+
105
+ static void ed_log_vprintf(int level, const char* format, va_list args)
106
+ {
107
+ const char* level_name = ed_log_level_name(level);
108
+ fprintf(stderr, "[ElementDrawing][%s] ", level_name);
109
+ vfprintf(stderr, format, args);
110
+ fprintf(stderr, "\n");
111
+ }
112
+
113
+ void ed_log_info(const char* format, ...)
114
+ {
115
+ va_list args;
116
+ va_start(args, format);
117
+ ed_log_vprintf(0, format, args);
118
+ va_end(args);
119
+ }
120
+
121
+ void ed_log_warn(const char* format, ...)
122
+ {
123
+ va_list args;
124
+ va_start(args, format);
125
+ ed_log_vprintf(1, format, args);
126
+ va_end(args);
127
+ }
128
+
129
+ void ed_log_error(const char* format, ...)
130
+ {
131
+ va_list args;
132
+ va_start(args, format);
133
+ ed_log_vprintf(2, format, args);
134
+ va_end(args);
135
+ }
136
+
137
+ void ed_log_debug(const char* format, ...)
138
+ {
139
+ va_list args;
140
+ va_start(args, format);
141
+ ed_log_vprintf(3, format, args);
142
+ va_end(args);
143
+ }
144
+
145
+ /* ================================================================
146
+ * Timestamp
147
+ * ================================================================ */
148
+
149
+ uint64_t ed_get_timestamp_ms(void)
150
+ {
151
+ if (g_config.get_timestamp) {
152
+ return g_config.get_timestamp();
153
+ }
154
+ struct timespec ts;
155
+ timespec_get(&ts, TIME_UTC);
156
+ return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
157
+ }
158
+
159
+ /* ================================================================
160
+ * Version and Name Utilities
161
+ * ================================================================ */
162
+
163
+ const char* ed_version(void)
164
+ {
165
+ return ED_VERSION_STRING;
166
+ }
167
+
168
+ const char* ed_node_type_name(EDNodeType type)
169
+ {
170
+ static const char* names[] = {
171
+ "Element", "Text", "Comment", "Fragment", "Portal",
172
+ "Suspense", "Lazy", "Memo", "ForwardRef", "ContextProvider",
173
+ "ContextConsumer", "Profiler", "StrictMode", "ConcurrentMode"
174
+ };
175
+ if (type >= 0 && type <= 13) return names[type];
176
+ return "Unknown";
177
+ }
178
+
179
+ const char* ed_fiber_tag_name(EDFiberTag tag)
180
+ {
181
+ static const char* names[] = {
182
+ "HostRoot", "HostComponent", "HostText", "ClassComponent",
183
+ "FunctionComponent", "IndirectComponent", "ContextProvider",
184
+ "ContextConsumer", "ForwardRef", "Fragment", "Mode",
185
+ "Profiler", "SuspenseComponent", "SuspenseList",
186
+ "MemoComponent", "LazyComponent", "Scope", "Offscreen",
187
+ "LegacyHidden", "Cache", "TracingMarker"
188
+ };
189
+ if (tag >= 0 && tag <= 20) return names[tag];
190
+ return "Unknown";
191
+ }
192
+
193
+ const char* ed_error_message(int error_code)
194
+ {
195
+ switch (error_code) {
196
+ case ED_OK: return "No error";
197
+ case ED_ERR_MEMORY: return "Memory allocation failed";
198
+ case ED_ERR_NULL_PTR: return "Null pointer encountered";
199
+ case ED_ERR_INVALID_ARG: return "Invalid argument provided";
200
+ case ED_ERR_OUT_OF_BOUNDS: return "Index out of bounds";
201
+ case ED_ERR_TYPE_MISMATCH: return "Type mismatch";
202
+ case ED_ERR_MAX_DEPTH: return "Maximum tree depth exceeded";
203
+ case ED_ERR_MAX_CHILDREN: return "Maximum children count exceeded";
204
+ case ED_ERR_POOL_EXHAUSTED:return "Memory pool exhausted";
205
+ case ED_ERR_GC_FAILED: return "Garbage collection failed";
206
+ case ED_ERR_INVALID_STATE: return "Invalid state";
207
+ case ED_ERR_SCHEDULE_FAILED: return "Scheduling failed";
208
+ case ED_ERR_DIFF_FAILED: return "Diff computation failed";
209
+ case ED_ERR_PATCH_FAILED: return "Patch application failed";
210
+ case ED_ERR_RENDER_FAILED: return "Rendering failed";
211
+ case ED_ERR_EVENT_FAILED: return "Event processing failed";
212
+ case ED_ERR_HOOK_VIOLATION:return "Hook rules violation";
213
+ default: return "Unknown error";
214
+ }
215
+ }
216
+
217
+ /* ================================================================
218
+ * Memory Pool Implementation
219
+ * ================================================================ */
220
+
221
+ static EDPoolBlock* ed_pool_block_create(uint32_t size)
222
+ {
223
+ EDPoolBlock* block = (EDPoolBlock*)malloc(sizeof(EDPoolBlock));
224
+ if (!block) return NULL;
225
+ block->data = (uint8_t*)malloc(size);
226
+ if (!block->data) {
227
+ free(block);
228
+ return NULL;
229
+ }
230
+ block->size = size;
231
+ block->used = 0;
232
+ block->next = NULL;
233
+ return block;
234
+ }
235
+
236
+ static void ed_pool_block_destroy(EDPoolBlock* block)
237
+ {
238
+ if (!block) return;
239
+ if (block->data) free(block->data);
240
+ free(block);
241
+ }
242
+
243
+ EDMemoryPool* ed_pool_create(uint32_t initial_size)
244
+ {
245
+ if (initial_size == 0) initial_size = ED_INITIAL_POOL_SIZE;
246
+ if (initial_size > ED_MAX_POOL_SIZE) initial_size = ED_MAX_POOL_SIZE;
247
+
248
+ EDMemoryPool* pool = (EDMemoryPool*)malloc(sizeof(EDMemoryPool));
249
+ if (!pool) return NULL;
250
+ memset(pool, 0, sizeof(EDMemoryPool));
251
+
252
+ EDPoolBlock* first = ed_pool_block_create(initial_size);
253
+ if (!first) {
254
+ free(pool);
255
+ return NULL;
256
+ }
257
+
258
+ pool->blocks = first;
259
+ pool->total_size = initial_size;
260
+ pool->used_size = 0;
261
+ pool->peak_usage = 0;
262
+ pool->block_count = 1;
263
+ pool->alloc_count = 0;
264
+ pool->free_count = 0;
265
+ pool->is_initialized = true;
266
+
267
+ return pool;
268
+ }
269
+
270
+ void ed_pool_destroy(EDMemoryPool* pool)
271
+ {
272
+ if (!pool) return;
273
+ EDPoolBlock* block = pool->blocks;
274
+ while (block) {
275
+ EDPoolBlock* next = block->next;
276
+ ed_pool_block_destroy(block);
277
+ block = next;
278
+ }
279
+ pool->blocks = NULL;
280
+ pool->is_initialized = false;
281
+ free(pool);
282
+ }
283
+
284
+ void* ed_pool_alloc(EDMemoryPool* pool, uint32_t size)
285
+ {
286
+ if (!pool || !pool->is_initialized || size == 0) return NULL;
287
+
288
+ /* Align size to 8 bytes */
289
+ size = (size + 7) & ~(uint32_t)7;
290
+
291
+ /* Try to fit in existing blocks */
292
+ EDPoolBlock* block = pool->blocks;
293
+ while (block) {
294
+ if (block->size - block->used >= size) {
295
+ void* ptr = block->data + block->used;
296
+ block->used += size;
297
+ pool->used_size += size;
298
+ pool->alloc_count++;
299
+ if (pool->used_size > pool->peak_usage) {
300
+ pool->peak_usage = pool->used_size;
301
+ }
302
+ return ptr;
303
+ }
304
+ block = block->next;
305
+ }
306
+
307
+ /* Need a new block */
308
+ uint32_t block_size = ED_POOL_BLOCK_SIZE;
309
+ if (size > block_size) block_size = size;
310
+ if (pool->total_size + block_size > ED_MAX_POOL_SIZE) {
311
+ ed_log_error("Pool exhausted: cannot allocate %u bytes", size);
312
+ return NULL;
313
+ }
314
+
315
+ EDPoolBlock* new_block = ed_pool_block_create(block_size);
316
+ if (!new_block) return NULL;
317
+
318
+ new_block->next = pool->blocks;
319
+ pool->blocks = new_block;
320
+ pool->total_size += block_size;
321
+ pool->block_count++;
322
+
323
+ void* ptr = new_block->data;
324
+ new_block->used = size;
325
+ pool->used_size += size;
326
+ pool->alloc_count++;
327
+ if (pool->used_size > pool->peak_usage) {
328
+ pool->peak_usage = pool->used_size;
329
+ }
330
+ return ptr;
331
+ }
332
+
333
+ void ed_pool_free(EDMemoryPool* pool, void* ptr)
334
+ {
335
+ if (!pool || !ptr) return;
336
+ /* Pool allocator: free is a no-op; memory reclaimed on pool reset/destroy */
337
+ pool->free_count++;
338
+ }
339
+
340
+ void ed_pool_reset(EDMemoryPool* pool)
341
+ {
342
+ if (!pool) return;
343
+ EDPoolBlock* block = pool->blocks;
344
+ while (block) {
345
+ block->used = 0;
346
+ block = block->next;
347
+ }
348
+ pool->used_size = 0;
349
+ }
350
+
351
+ uint32_t ed_pool_available(EDMemoryPool* pool)
352
+ {
353
+ if (!pool) return 0;
354
+ return pool->total_size - pool->used_size;
355
+ }
356
+
357
+ void ed_pool_gc(EDMemoryPool* pool)
358
+ {
359
+ if (!pool || !pool->is_initialized) return;
360
+
361
+ /* Compact blocks by removing completely free trailing blocks */
362
+ EDPoolBlock** prev = &pool->blocks;
363
+ EDPoolBlock* block = pool->blocks;
364
+ uint32_t kept = 0;
365
+
366
+ while (block) {
367
+ EDPoolBlock* next = block->next;
368
+ if (block->used == 0 && kept > 0) {
369
+ /* Free empty block (keep at least one) */
370
+ *prev = next;
371
+ pool->total_size -= block->size;
372
+ pool->block_count--;
373
+ ed_pool_block_destroy(block);
374
+ } else {
375
+ prev = &block->next;
376
+ kept++;
377
+ }
378
+ block = next;
379
+ }
380
+
381
+ /* Check threshold and compact if needed */
382
+ if (pool->total_size > 0) {
383
+ float usage = (float)pool->used_size / (float)pool->total_size;
384
+ if (usage < 0.25f && pool->block_count > 2) {
385
+ ed_log_info("Pool GC: usage %.1f%%, compacting blocks", usage * 100.0f);
386
+ }
387
+ }
388
+ }
389
+
390
+ uint32_t ed_pool_defragment(EDMemoryPool* pool)
391
+ {
392
+ if (!pool || !pool->is_initialized) return 0;
393
+
394
+ /* Count total used across all blocks */
395
+ uint32_t total_used = 0;
396
+ EDPoolBlock* block = pool->blocks;
397
+ while (block) {
398
+ total_used += block->used;
399
+ block = block->used > 0 ? block->next : NULL;
400
+ }
401
+
402
+ /* Simple defragmentation: consolidate used data into fewer blocks */
403
+ /* In a real implementation, this would relocate objects and update pointers */
404
+ ed_pool_gc(pool);
405
+ return pool->block_count;
406
+ }
407
+
408
+ /* ================================================================
409
+ * String Interning Implementation
410
+ * ================================================================ */
411
+
412
+ static void ed_string_table_init(void)
413
+ {
414
+ if (g_string_table_initialized) return;
415
+ memset(g_string_table, 0, sizeof(g_string_table));
416
+ g_string_table_entries = 0;
417
+ g_string_table_initialized = true;
418
+ }
419
+
420
+ uint32_t ed_string_hash(const char* str)
421
+ {
422
+ return ed_hash_fnv1a(str);
423
+ }
424
+
425
+ const char* ed_string_intern(const char* str)
426
+ {
427
+ if (!str) return NULL;
428
+ if (!g_string_table_initialized) ed_string_table_init();
429
+
430
+ uint32_t hash = ed_string_hash(str);
431
+ uint32_t idx = hash % ED_STRING_TABLE_SIZE;
432
+
433
+ /* Search for existing entry */
434
+ EDStringEntry* entry = g_string_table[idx];
435
+ while (entry) {
436
+ if (entry->hash == hash && strcmp(entry->str, str) == 0) {
437
+ entry->ref_count++;
438
+ return entry->str;
439
+ }
440
+ entry = entry->next;
441
+ }
442
+
443
+ /* Create new entry */
444
+ EDStringEntry* new_entry = (EDStringEntry*)malloc(sizeof(EDStringEntry));
445
+ if (!new_entry) return str; /* Fallback: return the original */
446
+
447
+ new_entry->str = ed_str_duplicate(str);
448
+ if (!new_entry->str) {
449
+ free(new_entry);
450
+ return str;
451
+ }
452
+ new_entry->len = (uint32_t)strlen(str);
453
+ new_entry->hash = hash;
454
+ new_entry->ref_count = 1;
455
+ new_entry->next = g_string_table[idx];
456
+ g_string_table[idx] = new_entry;
457
+ g_string_table_entries++;
458
+
459
+ return new_entry->str;
460
+ }
461
+
462
+ void ed_string_release(const char* str)
463
+ {
464
+ if (!str || !g_string_table_initialized) return;
465
+
466
+ uint32_t hash = ed_string_hash(str);
467
+ uint32_t idx = hash % ED_STRING_TABLE_SIZE;
468
+
469
+ EDStringEntry** prev = &g_string_table[idx];
470
+ EDStringEntry* entry = g_string_table[idx];
471
+
472
+ while (entry) {
473
+ if (entry->hash == hash && strcmp(entry->str, str) == 0) {
474
+ entry->ref_count--;
475
+ if (entry->ref_count == 0) {
476
+ *prev = entry->next;
477
+ free(entry->str);
478
+ free(entry);
479
+ g_string_table_entries--;
480
+ return;
481
+ }
482
+ return;
483
+ }
484
+ prev = &entry->next;
485
+ entry = entry->next;
486
+ }
487
+ }
488
+
489
+ bool ed_string_equals_interned(const char* a, const char* b)
490
+ {
491
+ if (a == b) return true;
492
+ if (!a || !b) return false;
493
+ /* If both are interned, pointer comparison suffices */
494
+ uint32_t ha = ed_string_hash(a);
495
+ uint32_t hb = ed_string_hash(b);
496
+ if (ha != hb) return false;
497
+ return strcmp(a, b) == 0;
498
+ }
499
+
500
+ /* ================================================================
501
+ * Virtual Property Implementation
502
+ * ================================================================ */
503
+
504
+ static EDVirtualProp* ed_vprop_create(const char* key, const char* value, uint8_t type)
505
+ {
506
+ EDVirtualProp* prop = (EDVirtualProp*)calloc(1, sizeof(EDVirtualProp));
507
+ if (!prop) return NULL;
508
+
509
+ if (key) {
510
+ strncpy(prop->key, key, ED_MAX_PROP_KEY_LEN - 1);
511
+ prop->key[ED_MAX_PROP_KEY_LEN - 1] = '\0';
512
+ }
513
+ if (value) {
514
+ strncpy(prop->value, value, ED_MAX_PROP_VALUE_LEN - 1);
515
+ prop->value[ED_MAX_PROP_VALUE_LEN - 1] = '\0';
516
+ }
517
+
518
+ prop->hash = ed_hash_fnv1a(key ? key : "");
519
+ prop->type = type;
520
+ prop->is_event = (key && strncmp(key, "on", 2) == 0);
521
+ prop->is_style = (key && strcmp(key, "style") == 0);
522
+ prop->is_class = (key && (strcmp(key, "class") == 0 || strcmp(key, "className") == 0));
523
+ prop->is_ref = (key && strcmp(key, "ref") == 0);
524
+ prop->is_key = (key && strcmp(key, "key") == 0);
525
+ prop->is_children = (key && strcmp(key, "children") == 0);
526
+ prop->is_dangerous_html = (key && strcmp(key, "dangerouslySetInnerHTML") == 0);
527
+ prop->next = NULL;
528
+
529
+ return prop;
530
+ }
531
+
532
+ static void ed_vprop_destroy(EDVirtualProp* prop)
533
+ {
534
+ while (prop) {
535
+ EDVirtualProp* next = prop->next;
536
+ free(prop);
537
+ prop = next;
538
+ }
539
+ }
540
+
541
+ /* ================================================================
542
+ * Virtual Node Implementation
543
+ * ================================================================ */
544
+
545
+ EDVirtualNode* ed_vnode_create(EDNodeType type, const char* tag, const char* key)
546
+ {
547
+ EDVirtualNode* node = (EDVirtualNode*)calloc(1, sizeof(EDVirtualNode));
548
+ if (!node) {
549
+ ed_log_error("Failed to allocate virtual node");
550
+ return NULL;
551
+ }
552
+
553
+ node->type = type;
554
+
555
+ if (tag) {
556
+ strncpy(node->tag, tag, ED_MAX_TAG_NAME_LEN - 1);
557
+ node->tag[ED_MAX_TAG_NAME_LEN - 1] = '\0';
558
+ }
559
+
560
+ if (key) {
561
+ strncpy(node->key, key, ED_MAX_KEY_LEN - 1);
562
+ node->key[ED_MAX_KEY_LEN - 1] = '\0';
563
+ }
564
+
565
+ node->child_capacity = 8;
566
+ node->children = (EDVirtualNode**)calloc(node->child_capacity, sizeof(EDVirtualNode*));
567
+ if (!node->children) {
568
+ free(node);
569
+ return NULL;
570
+ }
571
+
572
+ node->ref_count = 1;
573
+ node->version = 1;
574
+ node->is_dirty = true;
575
+ node->is_committed = false;
576
+ node->is_suspended = false;
577
+ node->is_hidden = false;
578
+
579
+ return node;
580
+ }
581
+
582
+ void ed_vnode_destroy(EDVirtualNode* node)
583
+ {
584
+ if (!node) return;
585
+
586
+ node->ref_count--;
587
+ if (node->ref_count > 0) return;
588
+
589
+ /* Destroy all children recursively */
590
+ for (uint32_t i = 0; i < node->child_count; i++) {
591
+ if (node->children[i]) {
592
+ ed_vnode_destroy(node->children[i]);
593
+ }
594
+ }
595
+ free(node->children);
596
+
597
+ /* Destroy property list */
598
+ ed_vprop_destroy(node->props);
599
+
600
+ /* Destroy the node itself */
601
+ free(node);
602
+ }
603
+
604
+ void ed_vnode_add_child(EDVirtualNode* parent, EDVirtualNode* child)
605
+ {
606
+ if (!parent || !child) return;
607
+ if (parent->child_count >= ED_MAX_CHILDREN) {
608
+ ed_log_error("Max children exceeded for node type=%s tag=%s",
609
+ ed_node_type_name(parent->type), parent->tag);
610
+ return;
611
+ }
612
+
613
+ /* Grow children array if needed */
614
+ if (parent->child_count >= parent->child_capacity) {
615
+ uint32_t new_cap = parent->child_capacity * 2;
616
+ if (new_cap > ED_MAX_CHILDREN) new_cap = ED_MAX_CHILDREN;
617
+ EDVirtualNode** new_children = (EDVirtualNode**)realloc(
618
+ parent->children, new_cap * sizeof(EDVirtualNode*));
619
+ if (!new_children) return;
620
+ memset(new_children + parent->child_capacity, 0,
621
+ (new_cap - parent->child_capacity) * sizeof(EDVirtualNode*));
622
+ parent->children = new_children;
623
+ parent->child_capacity = new_cap;
624
+ }
625
+
626
+ child->parent = parent;
627
+ child->sibling = NULL;
628
+
629
+ if (parent->last_child) {
630
+ parent->last_child->sibling = child;
631
+ } else {
632
+ parent->first_child = child;
633
+ }
634
+ parent->last_child = child;
635
+
636
+ parent->children[parent->child_count++] = child;
637
+ parent->is_dirty = true;
638
+ parent->version++;
639
+ }
640
+
641
+ void ed_vnode_remove_child(EDVirtualNode* parent, EDVirtualNode* child)
642
+ {
643
+ if (!parent || !child) return;
644
+
645
+ for (uint32_t i = 0; i < parent->child_count; i++) {
646
+ if (parent->children[i] == child) {
647
+ /* Shift remaining children */
648
+ for (uint32_t j = i; j < parent->child_count - 1; j++) {
649
+ parent->children[j] = parent->children[j + 1];
650
+ }
651
+ parent->children[parent->child_count - 1] = NULL;
652
+ parent->child_count--;
653
+
654
+ /* Rebuild sibling links */
655
+ parent->first_child = parent->child_count > 0 ? parent->children[0] : NULL;
656
+ parent->last_child = parent->child_count > 0 ? parent->children[parent->child_count - 1] : NULL;
657
+ for (uint32_t j = 0; j + 1 < parent->child_count; j++) {
658
+ parent->children[j]->sibling = parent->children[j + 1];
659
+ }
660
+ if (parent->child_count > 0) {
661
+ parent->children[parent->child_count - 1]->sibling = NULL;
662
+ }
663
+
664
+ child->parent = NULL;
665
+ child->sibling = NULL;
666
+ parent->is_dirty = true;
667
+ parent->version++;
668
+ return;
669
+ }
670
+ }
671
+ }
672
+
673
+ void ed_vnode_insert_child(EDVirtualNode* parent, EDVirtualNode* child, uint32_t index)
674
+ {
675
+ if (!parent || !child) return;
676
+ if (index > parent->child_count) index = parent->child_count;
677
+ if (parent->child_count >= ED_MAX_CHILDREN) return;
678
+
679
+ /* Grow children array if needed */
680
+ if (parent->child_count >= parent->child_capacity) {
681
+ uint32_t new_cap = parent->child_capacity * 2;
682
+ if (new_cap > ED_MAX_CHILDREN) new_cap = ED_MAX_CHILDREN;
683
+ EDVirtualNode** new_children = (EDVirtualNode**)realloc(
684
+ parent->children, new_cap * sizeof(EDVirtualNode*));
685
+ if (!new_children) return;
686
+ parent->children = new_children;
687
+ parent->child_capacity = new_cap;
688
+ }
689
+
690
+ /* Shift children to make room */
691
+ for (uint32_t i = parent->child_count; i > index; i--) {
692
+ parent->children[i] = parent->children[i - 1];
693
+ }
694
+ parent->children[index] = child;
695
+ parent->child_count++;
696
+
697
+ child->parent = parent;
698
+
699
+ /* Rebuild sibling links */
700
+ for (uint32_t i = 0; i + 1 < parent->child_count; i++) {
701
+ parent->children[i]->sibling = parent->children[i + 1];
702
+ }
703
+ if (parent->child_count > 0) {
704
+ parent->children[parent->child_count - 1]->sibling = NULL;
705
+ }
706
+ parent->first_child = parent->children[0];
707
+ parent->last_child = parent->children[parent->child_count - 1];
708
+
709
+ parent->is_dirty = true;
710
+ parent->version++;
711
+ }
712
+
713
+ void ed_vnode_replace_child(EDVirtualNode* parent, EDVirtualNode* new_child, EDVirtualNode* old_child)
714
+ {
715
+ if (!parent || !new_child || !old_child) return;
716
+
717
+ for (uint32_t i = 0; i < parent->child_count; i++) {
718
+ if (parent->children[i] == old_child) {
719
+ parent->children[i] = new_child;
720
+ new_child->parent = parent;
721
+ new_child->sibling = old_child->sibling;
722
+ old_child->parent = NULL;
723
+ old_child->sibling = NULL;
724
+
725
+ /* Rebuild sibling links */
726
+ if (i > 0) {
727
+ parent->children[i - 1]->sibling = new_child;
728
+ }
729
+ if (i + 1 < parent->child_count && parent->children[i + 1]) {
730
+ new_child->sibling = parent->children[i + 1];
731
+ }
732
+
733
+ if (parent->first_child == old_child) parent->first_child = new_child;
734
+ if (parent->last_child == old_child) parent->last_child = new_child;
735
+
736
+ parent->is_dirty = true;
737
+ parent->version++;
738
+ return;
739
+ }
740
+ }
741
+ }
742
+
743
+ void ed_vnode_set_prop(EDVirtualNode* node, const char* key, const char* value, uint8_t type)
744
+ {
745
+ if (!node || !key) return;
746
+ if (node->prop_count >= ED_MAX_PROPS) {
747
+ ed_log_warn("Max props exceeded for node tag=%s", node->tag);
748
+ return;
749
+ }
750
+
751
+ /* Check for existing prop */
752
+ EDVirtualProp* existing = ed_vnode_get_prop(node, key);
753
+ if (existing) {
754
+ if (value) {
755
+ strncpy(existing->value, value, ED_MAX_PROP_VALUE_LEN - 1);
756
+ existing->value[ED_MAX_PROP_VALUE_LEN - 1] = '\0';
757
+ } else {
758
+ existing->value[0] = '\0';
759
+ }
760
+ existing->type = type;
761
+ node->version++;
762
+ return;
763
+ }
764
+
765
+ /* Add new prop at the head of the list */
766
+ EDVirtualProp* prop = ed_vprop_create(key, value, type);
767
+ if (!prop) return;
768
+
769
+ prop->next = node->props;
770
+ node->props = prop;
771
+ node->prop_count++;
772
+ node->is_dirty = true;
773
+ node->version++;
774
+ }
775
+
776
+ void ed_vnode_remove_prop(EDVirtualNode* node, const char* key)
777
+ {
778
+ if (!node || !key) return;
779
+
780
+ EDVirtualProp** prev = &node->props;
781
+ EDVirtualProp* prop = node->props;
782
+
783
+ while (prop) {
784
+ if (strcmp(prop->key, key) == 0) {
785
+ *prev = prop->next;
786
+ prop->next = NULL;
787
+ ed_vprop_destroy(prop);
788
+ node->prop_count--;
789
+ node->is_dirty = true;
790
+ node->version++;
791
+ return;
792
+ }
793
+ prev = &prop->next;
794
+ prop = prop->next;
795
+ }
796
+ }
797
+
798
+ EDVirtualProp* ed_vnode_get_prop(EDVirtualNode* node, const char* key)
799
+ {
800
+ if (!node || !key) return NULL;
801
+
802
+ EDVirtualProp* prop = node->props;
803
+ while (prop) {
804
+ if (strcmp(prop->key, key) == 0) return prop;
805
+ prop = prop->next;
806
+ }
807
+ return NULL;
808
+ }
809
+
810
+ void ed_vnode_set_text(EDVirtualNode* node, const char* text)
811
+ {
812
+ if (!node) return;
813
+ if (text) {
814
+ strncpy(node->text, text, ED_MAX_TEXT_LEN - 1);
815
+ node->text[ED_MAX_TEXT_LEN - 1] = '\0';
816
+ } else {
817
+ node->text[0] = '\0';
818
+ }
819
+ node->is_dirty = true;
820
+ node->version++;
821
+ }
822
+
823
+ void ed_vnode_set_key(EDVirtualNode* node, const char* key)
824
+ {
825
+ if (!node) return;
826
+ if (key) {
827
+ strncpy(node->key, key, ED_MAX_KEY_LEN - 1);
828
+ node->key[ED_MAX_KEY_LEN - 1] = '\0';
829
+ } else {
830
+ node->key[0] = '\0';
831
+ }
832
+ node->version++;
833
+ }
834
+
835
+ uint32_t ed_vnode_hash(EDVirtualNode* node)
836
+ {
837
+ if (!node) return 0;
838
+ if (node->hash != 0) return node->hash;
839
+
840
+ uint32_t h = ed_hash_fnv1a(node->tag);
841
+ h = ed_hash_combine(h, (uint32_t)node->type);
842
+ h = ed_hash_combine(h, ed_hash_fnv1a(node->text));
843
+ h = ed_hash_combine(h, ed_hash_fnv1a(node->key));
844
+
845
+ EDVirtualProp* prop = node->props;
846
+ while (prop) {
847
+ h = ed_hash_combine(h, prop->hash);
848
+ h = ed_hash_combine(h, ed_hash_fnv1a(prop->value));
849
+ prop = prop->next;
850
+ }
851
+
852
+ node->hash = h;
853
+ return h;
854
+ }
855
+
856
+ bool ed_vnode_equals(EDVirtualNode* a, EDVirtualNode* b)
857
+ {
858
+ if (a == b) return true;
859
+ if (!a || !b) return false;
860
+ if (a->type != b->type) return false;
861
+ if (strcmp(a->tag, b->tag) != 0) return false;
862
+ if (strcmp(a->key, b->key) != 0) return false;
863
+ if (a->child_count != b->child_count) return false;
864
+ if (a->prop_count != b->prop_count) return false;
865
+
866
+ /* Compare text for text nodes */
867
+ if (a->type == ED_NODE_TEXT && strcmp(a->text, b->text) != 0) return false;
868
+
869
+ /* Compare props */
870
+ EDVirtualProp* pa = a->props;
871
+ while (pa) {
872
+ EDVirtualProp* pb = ed_vnode_get_prop(b, pa->key);
873
+ if (!pb || strcmp(pa->value, pb->value) != 0) return false;
874
+ pa = pa->next;
875
+ }
876
+
877
+ /* Compare children recursively */
878
+ for (uint32_t i = 0; i < a->child_count; i++) {
879
+ if (!ed_vnode_equals(a->children[i], b->children[i])) return false;
880
+ }
881
+
882
+ return true;
883
+ }
884
+
885
+ EDVirtualNode* ed_vnode_clone(EDVirtualNode* node)
886
+ {
887
+ if (!node) return NULL;
888
+
889
+ EDVirtualNode* clone = ed_vnode_create(node->type, node->tag, node->key);
890
+ if (!clone) return NULL;
891
+
892
+ strncpy(clone->text, node->text, ED_MAX_TEXT_LEN - 1);
893
+ clone->ref_id = node->ref_id;
894
+ clone->component_instance = node->component_instance;
895
+ clone->component_state = node->component_state;
896
+ clone->context_value = node->context_value;
897
+ clone->is_hidden = node->is_hidden;
898
+ clone->is_suspended = node->is_suspended;
899
+
900
+ /* Clone props */
901
+ EDVirtualProp* src = node->props;
902
+ EDVirtualProp** dst = &clone->props;
903
+ while (src) {
904
+ EDVirtualProp* prop = ed_vprop_create(src->key, src->value, src->type);
905
+ if (prop) {
906
+ prop->is_event = src->is_event;
907
+ prop->is_style = src->is_style;
908
+ prop->is_class = src->is_class;
909
+ prop->is_ref = src->is_ref;
910
+ prop->is_key = src->is_key;
911
+ prop->is_children = src->is_children;
912
+ prop->is_dangerous_html = src->is_dangerous_html;
913
+ *dst = prop;
914
+ dst = &prop->next;
915
+ clone->prop_count++;
916
+ }
917
+ src = src->next;
918
+ }
919
+
920
+ /* Clone children recursively */
921
+ for (uint32_t i = 0; i < node->child_count; i++) {
922
+ EDVirtualNode* child_clone = ed_vnode_clone(node->children[i]);
923
+ if (child_clone) {
924
+ ed_vnode_add_child(clone, child_clone);
925
+ }
926
+ }
927
+
928
+ return clone;
929
+ }
930
+
931
+ void ed_vnode_mark_dirty(EDVirtualNode* node)
932
+ {
933
+ if (!node) return;
934
+ node->is_dirty = true;
935
+ EDVirtualNode* p = node->parent;
936
+ while (p) {
937
+ p->is_dirty = true;
938
+ p = p->parent;
939
+ }
940
+ }
941
+
942
+ void ed_vnode_clear_dirty(EDVirtualNode* node)
943
+ {
944
+ if (!node) return;
945
+ node->is_dirty = false;
946
+ }
947
+
948
+ uint32_t ed_vnode_depth(EDVirtualNode* node)
949
+ {
950
+ if (!node) return 0;
951
+ uint32_t depth = 0;
952
+ EDVirtualNode* p = node->parent;
953
+ while (p) {
954
+ depth++;
955
+ p = p->parent;
956
+ if (depth > ED_MAX_DEPTH) break;
957
+ }
958
+ return depth;
959
+ }
960
+
961
+ uint32_t ed_vnode_count(EDVirtualNode* node)
962
+ {
963
+ if (!node) return 0;
964
+ uint32_t count = 1;
965
+ for (uint32_t i = 0; i < node->child_count; i++) {
966
+ count += ed_vnode_count(node->children[i]);
967
+ }
968
+ return count;
969
+ }
970
+
971
+ void ed_vnode_traverse(EDVirtualNode* node, void (*visitor)(EDVirtualNode*, void*), void* data)
972
+ {
973
+ if (!node || !visitor) return;
974
+ visitor(node, data);
975
+ for (uint32_t i = 0; i < node->child_count; i++) {
976
+ ed_vnode_traverse(node->children[i], visitor, data);
977
+ }
978
+ }
979
+
980
+ /* BFS traversal using a simple queue */
981
+ #define ED_BFS_QUEUE_SIZE 8192
982
+
983
+ void ed_vnode_traverse_bfs(EDVirtualNode* node, void (*visitor)(EDVirtualNode*, void*), void* data)
984
+ {
985
+ if (!node || !visitor) return;
986
+
987
+ EDVirtualNode* queue[ED_BFS_QUEUE_SIZE];
988
+ uint32_t head = 0, tail = 0;
989
+
990
+ queue[tail++] = node;
991
+
992
+ while (head != tail) {
993
+ EDVirtualNode* current = queue[head++];
994
+ head %= ED_BFS_QUEUE_SIZE;
995
+
996
+ visitor(current, data);
997
+
998
+ for (uint32_t i = 0; i < current->child_count; i++) {
999
+ if (current->children[i]) {
1000
+ queue[tail++] = current->children[i];
1001
+ tail %= ED_BFS_QUEUE_SIZE;
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+
1007
+ /* ================================================================
1008
+ * Weak Reference Table
1009
+ * ================================================================ */
1010
+
1011
+ static EDWeakRef* ed_weak_ref_create(void* target)
1012
+ {
1013
+ EDWeakRef* ref = (EDWeakRef*)calloc(1, sizeof(EDWeakRef));
1014
+ if (!ref) return NULL;
1015
+ ref->target = target;
1016
+ ref->id = ++g_weak_ref_counter;
1017
+ ref->is_alive = true;
1018
+ ref->next = NULL;
1019
+ return ref;
1020
+ }
1021
+
1022
+ /* ================================================================
1023
+ * Symbol Table
1024
+ * ================================================================ */
1025
+
1026
+ static void ed_symbol_table_init(void)
1027
+ {
1028
+ memset(g_symbol_table, 0, sizeof(g_symbol_table));
1029
+ g_symbol_id_counter = 1;
1030
+ }
1031
+
1032
+ static EDSymbolEntry* ed_symbol_lookup(const char* name)
1033
+ {
1034
+ if (!name) return NULL;
1035
+ uint32_t hash = ed_hash_fnv1a(name) % ED_SYMBOL_TABLE_SIZE;
1036
+ EDSymbolEntry* entry = g_symbol_table[hash];
1037
+ while (entry) {
1038
+ if (strcmp(entry->name, name) == 0) return entry;
1039
+ entry = entry->next;
1040
+ }
1041
+ return NULL;
1042
+ }
1043
+
1044
+ static EDSymbolEntry* ed_symbol_intern(const char* name, void* value)
1045
+ {
1046
+ if (!name) return NULL;
1047
+ EDSymbolEntry* existing = ed_symbol_lookup(name);
1048
+ if (existing) {
1049
+ existing->value = value;
1050
+ return existing;
1051
+ }
1052
+
1053
+ EDSymbolEntry* entry = (EDSymbolEntry*)calloc(1, sizeof(EDSymbolEntry));
1054
+ if (!entry) return NULL;
1055
+ strncpy(entry->name, name, 127);
1056
+ entry->name[127] = '\0';
1057
+ entry->id = g_symbol_id_counter++;
1058
+ entry->value = value;
1059
+
1060
+ uint32_t hash = ed_hash_fnv1a(name) % ED_SYMBOL_TABLE_SIZE;
1061
+ entry->next = g_symbol_table[hash];
1062
+ g_symbol_table[hash] = entry;
1063
+ return entry;
1064
+ }
1065
+
1066
+ /* ================================================================
1067
+ * Configuration Access
1068
+ * ================================================================ */
1069
+
1070
+ EDConfiguration* ed_internal_get_config(void)
1071
+ {
1072
+ return &g_config;
1073
+ }
1074
+
1075
+ bool ed_internal_is_initialized(void)
1076
+ {
1077
+ return g_renderer_initialized;
1078
+ }