-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpjson_node.h
More file actions
1426 lines (1241 loc) · 63.3 KB
/
pjson_node.h
File metadata and controls
1426 lines (1241 loc) · 63.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#pragma once
#include "pam_pmm_config.h"
#include "pmap_pmm.h"
#include "fptr_pmm.h"
using namespace pjson;
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
// pjson_node.h — Расширенная модель узлов JSON.
//
// Цель: переработать pjson на node_id-адресацию. Добавить типы ref и binary.
//
// Ключевые архитектурные принципы:
// - Все узлы в ПАП — POD-структуры, доступ через смещения (node_id).
// - node_id = uintptr_t (смещение узла в ПАП; 0 = null/invalid).
// - Строки JSON-значений: pstring (readwrite, изменяемые на лету).
// - Ключи объектов и пути $ref: pstringview (readonly, интернированные).
// - node_view — безопасный accessor для чтения узлов из ПАП.
//
// Все комментарии — на русском языке.
// ---------------------------------------------------------------------------
// Константы
// ---------------------------------------------------------------------------
/// Максимальная глубина разыменования цепочки $ref-узлов.
/// Ограничивает рекурсию в deref() для защиты от циклических ссылок
/// и чрезмерно длинных цепочек.
constexpr uintptr_t PJSON_MAX_REF_DEPTH = 32;
// ---------------------------------------------------------------------------
// node_tag — дискриминант типа узла
// ---------------------------------------------------------------------------
/// Расширенный набор типов узлов JSON.
enum class node_tag : uint32_t
{
null = 0, ///< null
boolean = 1, ///< true / false
integer = 2, ///< int64_t
uinteger = 3, ///< uint64_t
real = 4, ///< double
string = 5, ///< pstring (readwrite, изменяемое строковое значение JSON)
binary = 6, ///< parray<uint8_t> в ПАП ($base64 при сериализации)
array = 7, ///< parray<node_id>
object = 8, ///< pmap<pstringview, node_id> — ключи readonly (pstringview)
ref = 9, ///< pstringview path (readonly) + node_id target ($ref при сериализации)
_free = 0xFFFFFFFFu, ///< Служебный тег: слот освобождён (для free-list пула)
};
// ---------------------------------------------------------------------------
// node_error — код ошибки в node_view
// ---------------------------------------------------------------------------
/// Код ошибки, возвращаемый в node_view при неудачных операциях.
///
/// node_view с ненулевым кодом ошибки создаётся фабричной функцией node_view_error().
/// Позволяет отличить «узел не найден» от «JSON null-значение».
enum class node_error : uintptr_t
{
none = 0, ///< Нет ошибки
not_found = 1, ///< Узел не найден по пути
wrong_type = 2, ///< Неверный тип узла при навигации (не объект / не массив)
index_out_of_range = 3, ///< Индекс массива вне диапазона
readonly = 4, ///< Попытка записи в readonly-пространство (/$metrics)
ref_cycle = 5, ///< Обнаружен цикл или превышена глубина при разыменовании $ref
parse_error = 6, ///< Ошибка парсинга JSON
};
/// Базовое значение sentinel-идентификаторов для ошибочных node_view.
/// Область ~uintptr_t(255)..~uintptr_t(0) недостижима реальным ПАП.
/// Ошибочный node_view имеет id = NODE_ERROR_BASE + static_cast<uintptr_t>(node_error).
static constexpr uintptr_t NODE_ERROR_BASE = ~uintptr_t( 255 );
/// Получить человекочитаемое сообщение для кода ошибки.
/// Возвращает статическую строку (не требует освобождения).
/// Для неизвестных кодов возвращает "unknown error".
inline const char* node_error_message( node_error err )
{
switch ( err )
{
case node_error::none:
return "no error";
case node_error::not_found:
return "node not found";
case node_error::wrong_type:
return "wrong node type for navigation";
case node_error::index_out_of_range:
return "array index out of range";
case node_error::readonly:
return "cannot modify read-only path";
case node_error::ref_cycle:
return "cyclic $ref detected or max depth exceeded";
case node_error::parse_error:
return "JSON parse error";
default:
return "unknown error";
}
}
// ---------------------------------------------------------------------------
// node_id — идентификатор узла (смещение в ПАП)
// ---------------------------------------------------------------------------
/// Смещение узла в ПАП; 0 = null/invalid.
using node_id = uintptr_t;
// ---------------------------------------------------------------------------
// object_entry — запись в объектной карте (ключ + значение)
// ---------------------------------------------------------------------------
//
// Раскладка идентична pmap_entry<pstringview, node_id>:
// key_length: uintptr_t — длина ключа (pstringview::length)
// key_chars_offset: uintptr_t — смещение символов ключа в ПАП (pstringview::chars_offset)
// value: node_id — смещение узла-значения в ПАП
//
// Используется вместо pmap_entry<pstringview, node_id> для избежания
// конфликта с приватным конструктором pstringview.
// Физическая раскладка совместима с pmap_entry<pstringview, node_id>,
// позволяет хранить сортированный массив пар ключ-значение в ПАП.
struct object_entry
{
uintptr_t key_length; ///< Длина строки ключа (аналог pstringview::length)
uintptr_t key_chars_offset; ///< Смещение массива char ключа в ПАП (аналог pstringview::chars_offset)
node_id value; ///< node_id узла-значения; 0 = invalid
};
static_assert( std::is_trivially_copyable<object_entry>::value, "object_entry должен быть тривиально копируемым" );
// Проверяем, что раскладка совместима с pmap_entry<pstringview, node_id>:
// pstringview занимает 2 * sizeof(void*), node_id занимает sizeof(void*).
static_assert(
sizeof( object_entry ) == 3 * sizeof( void* ),
"object_entry должен занимать 3 * sizeof(void*) байт (совместимость с pmap_entry<pstringview, node_id>)" );
// ---------------------------------------------------------------------------
// node — структура узла JSON в ПАП
// ---------------------------------------------------------------------------
//
// Раскладка:
// tag: 4 байта — дискриминант (node_tag : uint32_t)
// _pad: 4 байта — выравнивание для 8-байтового union (на 64-битных платформах)
// union: payload
//
// Все поля — POD (тривиально копируемые), живут только в ПАП.
// Доступ — только через смещение (node_id) и pmm_resolve<node>.
//
// Типы строк:
// string_val: pstring (readwrite) — JSON string-value узлы, изменяемые на лету.
// ref_val.path_*: pstringview-совместимые поля (readonly, интернированные).
struct node
{
node_tag tag; ///< 4 байта — дискриминант
uint32_t _pad; ///< Выравнивание для корректного доступа к 8-байтным полям union
union
{
uint32_t boolean_val; ///< boolean: 0 = false, ненулевое = true
int64_t int_val; ///< integer: int64_t
uint64_t uint_val; ///< uinteger: uint64_t
double real_val; ///< real: double
/// string: PamManager::pstring (readwrite, из PMM).
/// Строковые значения JSON — изменяемые на лету (необходимо для jsonRVM).
PamManager::pstring string_val;
/// binary: массив uint8_t в ПАП ($base64 при сериализации).
/// PamManager::parray<uint8_t>: { uint32_t _size; uint32_t _capacity; index_type _data_idx; }.
PamManager::parray<uint8_t> binary_val;
/// array: массив node_id в ПАП.
/// PamManager::parray<node_id>: { uint32_t _size; uint32_t _capacity; index_type _data_idx; }.
PamManager::parray<node_id> array_val;
/// object: сортированный массив object_entry в ПАП.
/// Ключи — readonly pstringview (интернированные), значения — node_id.
/// PamManager::parray<object_entry>: { uint32_t _size; uint32_t _capacity; index_type _data_idx; }.
PamManager::parray<object_entry> object_val;
/// ref: путь ($ref) + целевой node_id.
/// path_* — pstringview-совместимые поля (readonly, интернированные).
/// target — node_id целевого узла (0 = не разрешён).
struct
{
uintptr_t path_length; ///< Длина строки пути
uintptr_t path_chars_offset; ///< Смещение символов пути в словаре pstringview; 0 = пустой путь
uintptr_t target; ///< node_id целевого узла; 0 = не разрешён
} ref_val;
/// _free_next: следующий свободный слот в free-list пула (tag == _free).
uintptr_t _free_next; ///< node_id следующего свободного узла; 0 = конец списка
};
};
// Проверяем размеры раскладки.
static_assert( std::is_trivially_copyable<node>::value, "node должен быть тривиально копируемым" );
static_assert( std::is_trivially_copyable<PamManager::pstring>::value,
"PamManager::pstring (node::string_val) должен быть тривиально копируемым" );
static_assert( std::is_trivially_copyable<PamManager::parray<node_id>>::value,
"PamManager::parray<node_id> (node::array_val) должен быть тривиально копируемым" );
static_assert( sizeof( node::ref_val ) == 3 * sizeof( void* ), "node::ref_val должен занимать 3 * sizeof(void*) байт" );
// ---------------------------------------------------------------------------
// Предварительные объявления итераторов
// ---------------------------------------------------------------------------
// Необходимы до объявления node_view, чтобы методы begin()/end()/items()
// могли использовать эти типы как возвращаемые.
template <typename Derived, typename Value> struct pjson_iterator_base; ///< CRTP-база итераторов
template <typename Iterator> struct pjson_range; ///< Шаблонный диапазон для range-based for
struct node_view_iterator; ///< Итератор элементов array-узла
struct object_item; ///< Пара ключ-значение для object-итерации
struct object_iterator; ///< Итератор пар ключ-значение object-узла
using object_items_range = pjson_range<object_iterator>; ///< Диапазон для items()
using array_range = pjson_range<node_view_iterator>; ///< Диапазон для range-based for по массиву
// ---------------------------------------------------------------------------
// Предварительные объявления helpers для бинарного поиска
// ---------------------------------------------------------------------------
// Необходимы до объявления node_view, чтобы node_view::at(const char*)
// мог вызывать node_object_find_key.
/// Результат бинарного поиска по объектному ключу.
struct object_find_result
{
uintptr_t index; ///< Индекс найденного элемента (== size при отсутствии ключа)
bool found; ///< true если ключ найден
};
/// Бинарный поиск ключа в отсортированном массиве object_entry.
inline object_find_result node_object_find_key( const object_entry* entries, uintptr_t sz, const char* key );
// ---------------------------------------------------------------------------
// node_view — безопасный accessor для чтения узлов
// ---------------------------------------------------------------------------
//
// node_view хранит node_id (смещение узла в ПАП) и предоставляет типобезопасный
// read-only интерфейс для работы с узлами через PMM.
//
// Использование:
// node_view v{ some_node_id };
// if (v.is_string()) printf("%s\n", v.as_string().data());
// node_view child = v.at("key"); // для object
// node_view elem = v.at(0); // для array
struct node_view
{
node_id id; ///< Смещение узла в ПАП; 0 = null/invalid
// ----------------------------------------------------------------
// Конструкторы
/// Создать пустой (null) node_view.
node_view() : id( 0 ) {}
/// Создать node_view для узла с заданным node_id.
explicit node_view( node_id nid ) : id( nid ) {}
// ----------------------------------------------------------------
// Запросы типа
/// Проверить, является ли node_view ошибочным.
/// Ошибочный node_view создаётся через node_view_error(); имеет id >= NODE_ERROR_BASE.
bool is_error() const { return id >= NODE_ERROR_BASE && id != ~uintptr_t( 0 ); }
/// Получить код ошибки.
/// Возвращает node_error::none для обычных и null node_view.
node_error error() const
{
if ( !is_error() )
return node_error::none;
return static_cast<node_error>( id - NODE_ERROR_BASE );
}
/// Получить человекочитаемое сообщение об ошибке.
/// Для обычных и null node_view возвращает "no error".
const char* error_message() const { return node_error_message( error() ); }
/// Проверить, является ли node_view действительным (не null/invalid/error).
bool valid() const { return id != 0 && !is_error(); }
/// Получить тег типа узла.
node_tag tag() const
{
if ( id == 0 )
return node_tag::null;
// Ошибочный node_view не является узлом — возвращаем _free как sentinel.
if ( is_error() )
return node_tag::_free;
const node* n = _resolve();
if ( n == nullptr )
return node_tag::null;
return n->tag;
}
/// Проверить, является ли узел JSON null-значением.
/// Возвращает true только для id==0 или узлов с tag==null (но не для ошибочных view).
bool is_null() const { return tag() == node_tag::null; }
bool is_boolean() const { return tag() == node_tag::boolean; }
bool is_integer() const { return tag() == node_tag::integer; }
bool is_uinteger() const { return tag() == node_tag::uinteger; }
bool is_real() const { return tag() == node_tag::real; }
/// Проверить, является ли узел числовым (integer, uinteger или real).
/// Выполняет единственный pmm_resolve вместо трёх отдельных вызовов tag().
bool is_number() const
{
node_tag t = tag();
return t == node_tag::integer || t == node_tag::uinteger || t == node_tag::real;
}
bool is_string() const { return tag() == node_tag::string; }
bool is_binary() const { return tag() == node_tag::binary; }
bool is_array() const { return tag() == node_tag::array; }
bool is_object() const { return tag() == node_tag::object; }
bool is_ref() const { return tag() == node_tag::ref; }
// ----------------------------------------------------------------
// Получение значений
/// Получить boolean-значение узла.
bool as_bool() const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::boolean )
return false;
return n->boolean_val != 0u;
}
/// Получить integer-значение узла.
int64_t as_int() const
{
const node* n = _resolve();
if ( n == nullptr )
return 0;
if ( n->tag == node_tag::integer )
return n->int_val;
if ( n->tag == node_tag::uinteger )
return static_cast<int64_t>( n->uint_val );
if ( n->tag == node_tag::real )
return static_cast<int64_t>( n->real_val );
return 0;
}
/// Получить uinteger-значение узла.
uint64_t as_uint() const
{
const node* n = _resolve();
if ( n == nullptr )
return 0u;
if ( n->tag == node_tag::uinteger )
return n->uint_val;
if ( n->tag == node_tag::integer )
return static_cast<uint64_t>( n->int_val );
if ( n->tag == node_tag::real )
return static_cast<uint64_t>( n->real_val );
return 0u;
}
/// Получить real-значение узла.
double as_double() const
{
const node* n = _resolve();
if ( n == nullptr )
return 0.0;
if ( n->tag == node_tag::real )
return n->real_val;
if ( n->tag == node_tag::integer )
return static_cast<double>( n->int_val );
if ( n->tag == node_tag::uinteger )
return static_cast<double>( n->uint_val );
return 0.0;
}
/// Получить string-значение узла (вид на pstring — readwrite JSON string).
/// Возвращает std::string_view на символьные данные pstring в ПАП.
/// Действителен, пока ПАМ жив и узел не изменён.
std::string_view as_string() const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::string )
return std::string_view{};
if ( n->string_val.empty() )
return std::string_view{};
const char* s = n->string_val.c_str();
if ( s == nullptr || s[0] == '\0' )
return std::string_view{};
return std::string_view{ s, n->string_val.size() };
}
/// Получить путь ref-узла (pstringview-совместимые поля — readonly, интернированные).
/// Возвращает std::string_view на символьные данные пути в ПАП.
std::string_view ref_path() const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::ref )
return std::string_view{};
if ( n->ref_val.path_chars_offset == 0 )
return std::string_view{};
const char* s = pmm_resolve<char>( n->ref_val.path_chars_offset );
if ( s == nullptr )
return std::string_view{};
return std::string_view{ s, static_cast<std::size_t>( n->ref_val.path_length ) };
}
/// Получить target node_id ref-узла (0 = не разрешён).
node_id ref_target() const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::ref )
return 0;
return n->ref_val.target;
}
// ----------------------------------------------------------------
// Навигация
/// Получить размер array или object узла.
uintptr_t size() const
{
const node* n = _resolve();
if ( n == nullptr )
return 0;
if ( n->tag == node_tag::array )
return n->array_val.size();
if ( n->tag == node_tag::object )
return n->object_val.size();
if ( n->tag == node_tag::binary )
return n->binary_val.size();
if ( n->tag == node_tag::string )
return static_cast<uintptr_t>( n->string_val.size() );
return 0;
}
bool empty() const { return size() == 0; }
/// Получить элемент массива по индексу idx.
/// Возвращает node_view(0) при ошибке индекса или неверном типе узла.
node_view at( uintptr_t idx ) const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::array )
return node_view{};
if ( idx >= n->array_val.size() )
return node_view{};
const node_id* arr = n->array_val.data();
if ( arr == nullptr )
return node_view{};
return node_view{ arr[idx] };
}
/// Получить значение объекта по ключу key (C-строка).
/// Ищет ключ в отсортированном массиве object_entry через бинарный поиск.
/// Возвращает node_view(0) при отсутствии ключа или неверном типе.
node_view at( const char* key ) const
{
if ( key == nullptr )
return node_view{};
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::object )
return node_view{};
if ( n->object_val.empty() )
return node_view{};
// Делегируем бинарный поиск в node_object_find_key.
const object_entry* entries = n->object_val.data();
if ( entries == nullptr )
return node_view{};
auto result = node_object_find_key( entries, n->object_val.size(), key );
if ( !result.found )
return node_view{};
return node_view{ entries[result.index].value };
}
/// Получить ключ i-го поля объекта (для итерации).
/// Возвращает std::string_view; "" при ошибке.
std::string_view key_at( uintptr_t idx ) const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::object )
return std::string_view{};
if ( idx >= n->object_val.size() )
return std::string_view{};
const object_entry* entries = n->object_val.data();
if ( entries == nullptr )
return std::string_view{};
const object_entry& e = entries[idx];
if ( e.key_chars_offset == 0 )
return std::string_view{};
const char* s = pmm_resolve<char>( e.key_chars_offset );
if ( s == nullptr )
return std::string_view{};
return std::string_view{ s, static_cast<std::size_t>( e.key_length ) };
}
/// Получить значение i-го поля объекта (для итерации).
node_view value_at( uintptr_t idx ) const
{
const node* n = _resolve();
if ( n == nullptr || n->tag != node_tag::object )
return node_view{};
if ( idx >= n->object_val.size() )
return node_view{};
const object_entry* entries = n->object_val.data();
if ( entries == nullptr )
return node_view{};
return node_view{ entries[idx].value };
}
// ----------------------------------------------------------------
// Разыменование ref
/// Разыменовать ref-узел.
/// Если текущий узел — ref, возвращает node_view на целевой узел.
/// Если recursive == true, разыменовывает цепочку ref-узлов.
/// Защита от зависания: ограничение глубиной max_depth.
/// Если текущий узел не ref — возвращает себя.
/// Оптимизировано: единственный pmm_resolve за итерацию (вместо отдельных is_ref + ref_target).
node_view deref( bool recursive = true, uintptr_t max_depth = PJSON_MAX_REF_DEPTH ) const
{
node_view cur = *this;
for ( uintptr_t depth = 0; depth < max_depth; depth++ )
{
const node* n = cur._resolve();
if ( n == nullptr || n->tag != node_tag::ref )
return cur;
node_id target = n->ref_val.target;
if ( target == 0 )
return node_view{}; // не разрешён
node_view next{ target };
if ( !recursive )
return next;
// Проверка на цикл: если target == текущий id, останавливаемся.
if ( target == cur.id )
return node_view{}; // цикл обнаружен
cur = next;
}
// Превышена максимальная глубина.
return node_view{};
}
// ----------------------------------------------------------------
// Удобные операторы
/// Доступ к элементу массива через operator[].
node_view operator[]( uintptr_t idx ) const { return at( idx ); }
/// Доступ к полю объекта через operator[].
node_view operator[]( const char* key ) const { return at( key ); }
bool operator==( const node_view& other ) const { return id == other.id; }
bool operator!=( const node_view& other ) const { return id != other.id; }
// ----------------------------------------------------------------
// Поддержка range-based for
/// Итератор начала для array-узла (для range-based for).
/// Использует node_view_iterator; для не-массивов возвращает end().
node_view_iterator begin() const;
/// Итератор конца для array-узла (для range-based for).
node_view_iterator end() const;
/// Диапазон для итерации по парам ключ-значение object-узла.
/// Использование: for (auto [key, val] : obj.items()) { ... }
object_items_range items() const;
private:
/// Разрешить указатель на узел в ПАП.
const node* _resolve() const
{
if ( id == 0 )
return nullptr;
return pmm_resolve<node>( id );
}
};
// ---------------------------------------------------------------------------
// Фабричная функция для создания ошибочного node_view
// ---------------------------------------------------------------------------
/// Создать node_view с кодом ошибки err.
/// Использование: return node_view_error(node_error::not_found);
/// is_error() == true, valid() == false, is_null() == false.
inline node_view node_view_error( node_error err )
{
return node_view{ NODE_ERROR_BASE + static_cast<uintptr_t>( err ) };
}
// ---------------------------------------------------------------------------
// Вспомогательные функции для работы с узлами в ПАП
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Шаблонные helpers для устранения дублирования boilerplate
// ---------------------------------------------------------------------------
/// Разрешить узел по смещению, установить тег и обнулить _pad.
/// Возвращает указатель на узел (nullptr при невалидном смещении).
/// Используется внутренне для устранения дублирования в node_set_* функциях.
inline node* node_resolve_and_set_tag( uintptr_t node_off, node_tag tag )
{
if ( node_off == 0 )
return nullptr;
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return nullptr;
n->tag = tag;
n->_pad = 0;
return n;
}
/// Инициализировать контейнерный узел (array/object/binary) — пустой.
/// Устанавливает тег, обнуляет всю область данных union через array_val
/// (все контейнерные поля имеют одинаковую раскладку: _size/_capacity/_data_idx).
inline void node_set_container_empty( uintptr_t node_off, node_tag tag )
{
node* n = node_resolve_and_set_tag( node_off, tag );
if ( n == nullptr )
return;
// array_val, object_val, binary_val имеют одинаковую раскладку {_size, _capacity, _data_idx}.
// Обнуляем через array_val (все три union-члена перекрывают одну и ту же область памяти).
n->array_val = PamManager::parray<node_id>{};
}
// ---------------------------------------------------------------------------
// Вспомогательные функторы для сортированного массива object_entry
// ---------------------------------------------------------------------------
/// Извлечение ключа (chars_offset) из object_entry.
struct object_entry_key_of
{
uintptr_t operator()( const object_entry& e ) const { return e.key_chars_offset; }
};
/// Лексикографическое сравнение ключей по chars_offset.
struct object_entry_less
{
bool operator()( uintptr_t a_offset, uintptr_t b_offset ) const
{
if ( a_offset == 0 && b_offset == 0 )
return false;
if ( a_offset == 0 )
return true; // "" < any non-empty
if ( b_offset == 0 )
return false;
const char* a = pmm_resolve<char>( a_offset );
const char* b = pmm_resolve<char>( b_offset );
if ( a == nullptr || b == nullptr )
return false;
return std::strcmp( a, b ) < 0;
}
};
/// Безопасный push_back в parray поля node: стековая копия → push_back → переразрешение → запись обратно.
/// Шаблон устраняет дублирование для array_val, binary_val и других parray-полей node.
/// @tparam T Тип элемента parray.
/// @tparam Field Тип указатель-на-поле node (PamManager::parray<T> node::*).
/// @param node_off Смещение node в ПАП (для переразрешения после push_back).
/// @param field Указатель на parray-поле node (например, &node::array_val).
/// @param elem Элемент для добавления.
template <typename T, typename Field>
inline void parray_push_back_safe( uintptr_t node_off, Field field, const T& elem )
{
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return;
auto arr_copy = n->*field;
arr_copy.push_back( elem );
n = pmm_resolve<node>( node_off );
n->*field = arr_copy;
}
/// Вставить object_entry в отсортированный parray (вставка или обновление).
/// Выполняет бинарный поиск, вставляет со сдвигом или обновляет существующий.
/// Вставить object_entry в отсортированный parray. Безопасна при росте PMM-пула:
/// push_back выполняется на стековой копии parray, результат записывается обратно
/// в node после повторной резолвации node_off.
/// @param node_off Смещение object-узла в ПАП (для переразрешения после push_back).
/// @param value Вставляемый object_entry.
inline void parray_insert_sorted_object_entry( uintptr_t node_off, const object_entry& value )
{
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return;
PamManager::parray<object_entry> arr = n->object_val;
uintptr_t sz = arr.size();
uintptr_t lo = 0, hi = sz;
// Бинарный поиск (lower_bound)
{
const object_entry* raw = arr.data();
if ( raw != nullptr )
{
while ( lo < hi )
{
uintptr_t mid = ( lo + hi ) / 2;
if ( object_entry_less{}( object_entry_key_of{}( raw[mid] ), object_entry_key_of{}( value ) ) )
lo = mid + 1;
else
hi = mid;
}
}
}
uintptr_t idx = lo;
// Проверяем, существует ли уже такой ключ
{
const object_entry* raw = arr.data();
if ( raw != nullptr && idx < sz &&
!object_entry_less{}( object_entry_key_of{}( value ), object_entry_key_of{}( raw[idx] ) ) &&
!object_entry_less{}( object_entry_key_of{}( raw[idx] ), object_entry_key_of{}( value ) ) )
{
// Ключ найден — обновляем элемент
arr.set( idx, value );
// Записываем обратно в node (set не вызывает аллокацию, но для единообразия).
n = pmm_resolve<node>( node_off );
n->object_val = arr;
return;
}
}
// Нужно вставить новый элемент в позицию idx.
// insert может вызвать рост PMM-пула, инвалидируя все указатели.
// Работаем на стековой копии arr (безопасно).
arr.insert( idx, value );
// Записываем обновлённый parray обратно в node (после возможного роста пула).
n = pmm_resolve<node>( node_off );
n->object_val = arr;
}
// ---------------------------------------------------------------------------
// Бинарный поиск по ключу в отсортированном массиве object_entry
// ---------------------------------------------------------------------------
/// Реализация бинарного поиска ключа key в отсортированном массиве object_entry.
/// entries — указатель на массив, sz — число элементов.
/// Возвращает object_find_result { index, found }.
/// Делегирует три ранее дублировавшихся реализации бинарного поиска.
inline object_find_result node_object_find_key( const object_entry* entries, uintptr_t sz, const char* key )
{
if ( entries == nullptr )
return { sz, false };
uintptr_t lo = 0, hi = sz;
while ( lo < hi )
{
uintptr_t mid = ( lo + hi ) / 2;
const object_entry& e = entries[mid];
int cmp;
if ( e.key_chars_offset == 0 )
cmp = ( key[0] == '\0' ) ? 0 : 1;
else
{
const char* ks = pmm_resolve<char>( e.key_chars_offset );
cmp = ( ks != nullptr ) ? std::strcmp( ks, key ) : -1;
}
if ( cmp < 0 )
lo = mid + 1;
else if ( cmp > 0 )
hi = mid;
else
return { mid, true };
}
return { sz, false };
}
// ---------------------------------------------------------------------------
// Шаблонный helper для безопасного разрешения узла с проверкой тега
// ---------------------------------------------------------------------------
/// Разрешить узел по смещению id и проверить, что тег == expected_tag.
/// Возвращает указатель на узел (nullptr при невалидном смещении или несовпадении тега).
/// Устраняет дублирование паттерна «resolve + tag check» в методах pjson_db_pmm.
inline node* node_resolve_checked( node_id id, node_tag expected_tag )
{
if ( id == 0 )
return nullptr;
node* n = pmm_resolve<node>( id );
if ( n == nullptr || n->tag != expected_tag )
return nullptr;
return n;
}
// ---------------------------------------------------------------------------
/// Инициализировать узел нулями (null-узел) по смещению node_off в ПАП.
inline void node_init_null( uintptr_t node_off )
{
node* n = node_resolve_and_set_tag( node_off, node_tag::null );
if ( n == nullptr )
return;
n->ref_val.path_length = 0;
n->ref_val.path_chars_offset = 0;
n->ref_val.target = 0;
}
/// Установить boolean-значение узла.
inline void node_set_bool( uintptr_t node_off, bool v )
{
node* n = node_resolve_and_set_tag( node_off, node_tag::boolean );
if ( n != nullptr )
n->boolean_val = v ? 1u : 0u;
}
/// Установить integer-значение узла.
inline void node_set_int( uintptr_t node_off, int64_t v )
{
node* n = node_resolve_and_set_tag( node_off, node_tag::integer );
if ( n != nullptr )
n->int_val = v;
}
/// Установить uinteger-значение узла.
inline void node_set_uint( uintptr_t node_off, uint64_t v )
{
node* n = node_resolve_and_set_tag( node_off, node_tag::uinteger );
if ( n != nullptr )
n->uint_val = v;
}
/// Установить real-значение узла.
inline void node_set_real( uintptr_t node_off, double v )
{
node* n = node_resolve_and_set_tag( node_off, node_tag::real );
if ( n != nullptr )
n->real_val = v;
}
/// Установить строковое значение узла (PamManager::pstring, readwrite).
/// Использует PMM pstring::assign() для управления памятью.
/// Безопасна при realloc: переразрешает node после операций с памятью.
inline void node_set_string( uintptr_t node_off, const char* s )
{
if ( node_off == 0 )
return;
// Освобождаем предыдущие данные (если были).
{
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return;
if ( n->tag == node_tag::string )
{
n->string_val.free_data();
// free_data() может вызвать deallocate, но не allocate — re-resolve не нужен.
}
n->tag = node_tag::string;
n->_pad = 0;
n->string_val = PamManager::pstring{};
}
if ( s == nullptr || s[0] == '\0' )
return;
// Выполняем assign на стековой копии, чтобы обойти проблему с realloc.
// pstring::assign() может вызвать allocate(), который может вызвать realloc хранилища,
// инвалидируя указатель на node. Поэтому работаем со стековой копией.
PamManager::pstring tmp{};
tmp.assign( s );
// Переразрешаем node после возможного realloc.
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return;
n->string_val = tmp;
}
/// Переназначить строковое значение узла (pstring assign — readwrite).
/// Освобождает старые символьные данные и выделяет новые.
inline void node_assign_string( uintptr_t node_off, const char* s )
{
node_set_string( node_off, s );
}
/// Инициализировать array-узел (пустой массив).
inline void node_set_array( uintptr_t node_off )
{
node_set_container_empty( node_off, node_tag::array );
}
/// Инициализировать object-узел (пустой объект).
inline void node_set_object( uintptr_t node_off )
{
node_set_container_empty( node_off, node_tag::object );
}
/// Инициализировать binary-узел (пустой массив байт).
inline void node_set_binary( uintptr_t node_off )
{
node_set_container_empty( node_off, node_tag::binary );
}
/// Установить ref-узел (путь + не разрешённый target).
/// Путь интернируется через pstringview_manager.
inline void node_set_ref( uintptr_t node_off, const char* path )
{
if ( node_off == 0 )
return;
{
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return;
n->tag = node_tag::ref;
n->_pad = 0;
n->ref_val.path_length = 0;
n->ref_val.path_chars_offset = 0;
n->ref_val.target = 0;
}
if ( path == nullptr || path[0] == '\0' )
return;
// Интернируем путь через pstringview_table (readonly).
auto result = pam_intern_string( path );
// Переразрешаем node после возможного realloc внутри intern().
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr )
return;
n->ref_val.path_length = result.length;
n->ref_val.path_chars_offset = result.chars_offset;
}
/// Установить target для ref-узла (после разрешения ссылки).
inline void node_set_ref_target( uintptr_t node_off, node_id target )
{
if ( node_off == 0 )
return;
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr || n->tag != node_tag::ref )
return;
n->ref_val.target = target;
}
/// Добавить элемент в array-узел (push_back).
/// Возвращает node_id нового слота (инициализирован как null).
/// Безопасна при realloc.
inline node_id node_array_push_back( uintptr_t node_off )
{
if ( node_off == 0 )
return 0;
// Аллоцируем новый node-слот (null).
fptr<node> new_slot;
new_slot.New(); // Может вызвать realloc!
uintptr_t slot_off = new_slot.addr();
// Инициализируем слот нулями.
node_init_null( slot_off );
// Переразрешаем array-узел после возможного realloc.
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr || n->tag != node_tag::array )
return slot_off;
// Добавляем slot_off в массив через parray::push_back.
// push_back может вызвать рост PMM-пула, инвалидируя n.
parray_push_back_safe<node_id>( node_off, &node::array_val, slot_off );
return slot_off;
}
/// Добавить существующий node_id в array-узел (push_back) без создания временного слота.
/// В отличие от node_array_push_back(), не аллоцирует новый node —
/// напрямую вставляет existing_id в массив (Issue #190, Этап 9.2).
/// Безопасна при realloc.
inline void node_array_push_back_id( uintptr_t node_off, node_id existing_id )
{
if ( node_off == 0 || existing_id == 0 )
return;
node* n = pmm_resolve<node>( node_off );
if ( n == nullptr || n->tag != node_tag::array )
return;
// Добавляем existing_id в массив через parray::push_back.
// push_back может вызвать рост PMM-пула, инвалидируя n.
parray_push_back_safe<node_id>( node_off, &node::array_val, existing_id );
}
/// Вставить или обновить ключ key в object-узле.
/// Возвращает node_id нового/существующего слота (инициализирован как null при новом).
/// Ключ интернируется как pstringview (readonly).
/// Ключи хранятся в отсортированном массиве object_entry (совместимо с pmap_entry<pstringview, node_id>).
inline node_id node_object_insert( uintptr_t node_off, const char* key )
{
if ( node_off == 0 || key == nullptr )
return 0;