-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathufser.cpp
2007 lines (1979 loc) · 92.5 KB
/
ufser.cpp
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
/** This file is part of the uFser project which is released under the MIT license.
* See file COPYING for full license details.
* Copyright 2024 Ericsson AB
*/
#include "ufser.h"
uint32_t uf::impl::default_value(const char *&type, const char *const tend, char **to) {
if (type > tend) throw internal_typestring_error{type};
if (type == tend) return 0;
switch (*type++) {
default: throw internal_typestring_error{type};
case 'd':
case 'I':
case 'a': return fill_bytes(8, to);
case 'm':
if (auto [len, problem] = parse_type(type, tend, false); !problem) type += len;
else throw internal_typestring_error{type + len};
[[fallthrough]];
case 'l':
if (auto [len, problem] = parse_type(type, tend, false); !problem) type += len;
else throw internal_typestring_error{type + len};
[[fallthrough]];
case 'i':
case 's': return fill_bytes(4, to);
case 'o':
if (auto [len, problem] = parse_type(type, tend, false); !problem) type += len;
else throw internal_typestring_error{type + len};
[[fallthrough]];
case 'c':
case 'b': return fill_bytes(1, to);
case 'X': return fill_bytes(1, to, 1); //default for expected is to have a value
case 'e': return fill_bytes(20, to); //3 stirngs of 4 bytes and one any of 8 bytes. All zeros
case 'x':
if (type == tend) throw internal_typestring_error{type};
fill_bytes(1, to, 1);
return 1 + default_value(type, tend, to);
case 't':
uint32_t size = 0, ret = 0;
const char *const orig = type;
while (type < tend && '0' <= *type && *type <= '9')
size = size * 10 + *type++ - '0';
if (size < 2)
throw internal_typestring_error{orig};
while (size--)
ret += default_value(type, tend, to);
return ret;
}
}
uf::any_view &uf::any_view::assign(from_raw_t, std::string_view v, bool check) {
const char *p = v.data(), *const end = p + v.length();
std::string_view ty, va;
if (impl::deserialize_from<true>(p, end, ty) || impl::deserialize_from<true>(p, end, va))
throw uf::value_mismatch_error("Raw string does not contain a valid serialized uf::any.", "a", 0);
if (p!=end)
throw uf::value_mismatch_error("Raw string does contains extra characters after a serialized uf::any.", "a", 0);
if (check) {
auto [err, tlen, vlen] = impl::serialize_scan_by_type(ty, va, false, true);
if (err)
err->append_msg(" (in any_view(from_raw,...))").throw_me();
if (tlen<ty.length())
throw uf::typestring_error(impl::ser_error_str(impl::ser::tlong), ty, tlen);
if (vlen<va.length())
throw uf::value_mismatch_error(impl::ser_error_str(impl::ser::vlong), ty, 0);
}
_type = ty;
_value = va;
return *this;
}
uf::any &uf::any::assign(from_raw_t, std::string_view v, bool check) {
const char *p = v.data(), *const end = p + v.length();
any tmp;
if (impl::deserialize_from<false>(p, end, tmp))
throw uf::value_mismatch_error("Raw string does not contain a valid serialized uf::any.", "a", 0);
if (p!=end)
throw uf::value_mismatch_error("Raw string does contains extra characters after a serialized uf::any.", "a", 0);
if (check) {
auto [err, tlen, vlen] = impl::serialize_scan_by_type(tmp.type(), tmp.value(), false, true);
if (err)
err->append_msg(" (in any_view(from_raw,...))").throw_me();
if (tlen<tmp.type().length())
throw uf::typestring_error(impl::ser_error_str(impl::ser::tlong), tmp.type(), tlen);
if (vlen<tmp.value().length())
throw uf::value_mismatch_error(impl::ser_error_str(impl::ser::vlong), tmp.type(), 0);
}
return *this = std::move(tmp);
}
namespace uf::impl {
/** Parse the type from 'type' till 'target_tend'. Do not accept void.
* In the error we return we assume this is the target type.*/
inline std::pair<std::unique_ptr<value_error>, size_t> parse_target_type_or_error(const char *type, const deserialize_convert_params &p) {
assert(p.target_tstart<=type && type<=p.target_tend); //type is inside the source type
if (type >= p.target_tend)
return {create_des_typestring_target(p, ser_error_str(ser::end)), 0};
else if (auto [tlen, problem] = parse_type(type, p.target_tend, false); !problem)
return {std::unique_ptr<value_error>{}, tlen};
else {
deserialize_convert_params local_p(p);
local_p.target_type = type+tlen;
return {create_des_typestring_target(local_p, ser_error_str(problem)), 0};
}
}
} //ns uf::impl
template <bool has_source, bool has_target>
std::unique_ptr<uf::value_error> uf::impl::cant_convert(deserialize_convert_params& p, StringViewAccumulator* target)
{
if constexpr (!has_source) static_assert(!has_target, "cant_convert<false, true>() is invalid");
assert(has_target == bool(target));
//fast path
const auto [source_tlen, source_tproblen] = parse_type(p.type, p.tend, true);
if (source_tproblen!=uf::impl::ser::ok) {
p.type += source_tlen;
return create_des_typestring_source(p, ser_error_str(source_tproblen));
}
//do not handle incoming void here
if (source_tlen && p.target_type+ source_tlen <= p.target_tend && 0==memcmp(p.target_type, p.type, source_tlen)) {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
p.target_type += source_tlen;
p.type += source_tlen;
return {};
}
//OK, we are not completely identical, try conversion
if (p.type == p.tend) {//void incoming
if (p.target_type == p.target_tend)
return {}; //void->void
if (*p.target_type == 'a') {
//void->a
if (!(p.convpolicy & uf::allow_converting_any))
return create_des_type_error(p, allow_converting_any);
p.target_type++;
if constexpr (has_target)
*target << std::string(8, char(0)); //any containing void is two four byte zero lengths
return {}; //
}
if (p.target_type + 1 < p.target_tend && p.target_type[0] == 'X') {
//void->X
if (!(p.convpolicy & uf::allow_converting_expected))
return create_des_type_error(p, allow_converting_expected);
p.target_type++;
if constexpr (has_target)
*target << char(1); //X containing void is just a one-byte 'has value' indicator
return {};
}
if (p.target_type[0] == 'o') {
//void->oT
auto [err, target_tlen] = parse_target_type_or_error(p.target_type, p);
if (err) return std::move(err);
if (!(p.convpolicy & uf::allow_converting_aux))
return create_des_type_error(p, allow_converting_aux);
p.target_type += target_tlen;
if constexpr (has_target)
*target << char(0); //oT not containing any value is just a one-byte 'has no value' indicator
return {};
}
//No type other than (void, 'a', 'X', 'oT') can accept void
return create_des_type_error(p);
}
const char c = p.target_type < p.target_tend ? *p.target_type : 0;
if (c == 'a') {
//An any can take anything
if (*p.type != 'a' && !(p.convpolicy & allow_converting_any))
return create_des_type_error(p, allow_converting_any);
if constexpr (has_source) {
if (has_target && *p.type!='a') {
//We actually convert and there is a need for conversion as the target is not an any
//Parse upcoming type/value
std::string_view t(p.type, p.tend - p.type);
const char* old_p = p.p;
if (auto err = serialize_scan_by_type_from(t, p.p, p.end, false)) return err;
//Encapsulate what comes here in an any
//serialize len of type
char buff[4]; char* pp = buff;
serialize_to(uint32_t(t.data() - p.type), pp);
//append len and type to target
*target << std::string(buff, 4) << std::string_view{ p.type, size_t(t.data() - p.type) };
//serialize len of value
pp = buff;
serialize_to(uint32_t(p.p-old_p), pp);
*target << std::string(buff, 4) << std::string_view{ old_p, size_t(p.p-old_p) };
p.type = t.data();
} else if (auto err = advance_source(p, target)) //just checking types: skip over, a->a: just copy
return err;
} else {
auto [err, len] = parse_type_or_error(p.type, p);
if (err) return std::move(err);
p.type += len;
}
p.target_type++;
return {};
}
if (c == 'x' || c == 'X') {
if (*p.type == 'e') {
//e->xT or e->X
if constexpr (has_source) {
if constexpr (has_target) *target << char(0); //prefix error with an expected indicator of no value
if (auto err = advance_source(p, target)) return err; //step through e's content and type
} else {
p.type++; //step through e's type
}
//step through the xT type
auto [err, len] = parse_target_type_or_error(p.target_type, p);
if (err) return std::move(err);
p.target_type += len;
return {};
}
if (*p.type == 'x' || *p.type == 'X') {//one expected can only be deserialized into a compatible expected
if (*p.type == 'X' && c == 'X') {
//X->X
p.type++;
p.target_type++;
if constexpr (has_source) if (auto err = advance_source(p, target, p.type-1)) return err;
return {};
} else if (*p.type == 'x' && c == 'X') {
//xT->X, check if T can disappear
p.type++;
if (p.type>=p.tend)
return create_des_typestring_source(p, ser_error_str(ser::end));
if constexpr (has_source) {
if (!*p.p) {
//No value, copy error
p.type--;
if (auto err = advance_source(p, target)) return err; //source type pointer advances xT*, target remains *X
} else {
//Check that value of 'xT' can be converted to void
deserialize_convert_params local_p(p, p.tend, p.target_type); //target type will be empty
if (auto err = cant_convert<has_source, false>(local_p, target)) return err;
p.type = local_p.type;
}
} else {
//Check that value of 'xT' can be converted to void
deserialize_convert_params local_p(p, p.tend, p.target_type); //target type will be empty
if (auto err = cant_convert<has_source, false>(local_p, target)) return err;
p.type = local_p.type;
}
p.target_type++; //step over 'X' in target type
return {};
} else if (*p.type == 'X' && c == 'x') {
//X->xT, check if T can initialize from void
p.target_type++;
if (p.target_type >= p.target_tend)
return create_des_typestring_target(p, ser_error_str(ser::end));
if constexpr (has_source) {
if (!*p.p) {
//No value, copy error
if (auto err = advance_source(p, target, p.type - 1)) return err; //type pointers remain at *X and x*T
if (auto [err, len] = parse_target_type_or_error(p.target_type, p); err)
return std::move(err);
else
p.target_type += len;
} else {
//Check that value of 'xT' can be converted to void
deserialize_convert_params local_p(p, p.type, p.target_tend); //source type will be empty
if (auto err = cant_convert<has_source, has_target>(local_p, target)) return err;
p.target_type = local_p.target_type;
}
} else {
deserialize_convert_params local_p(p, p.type, p.target_tend); //source type will be empty
if (auto err = cant_convert<has_source, has_target>(local_p, target)) return err;
p.target_type = local_p.target_type;
}
p.type++; //step over 'X' in source type
return {};
}
//xU->xT
p.type++;
p.target_type++; //step over 'x' in target type
if constexpr (has_source) {
if (!*p.p) {
//No value. Just copy the error over and check their type for compatibility
if (auto err = advance_source(p, target, p.type-1)) return err; //type pointers remain at x*U and x*T
return cant_convert<false, false>(p, nullptr); //compare just the types - the value (error) will be possible to convert
}
//source xT has value: copy the indicator to target
if constexpr (has_target) *target << std::string_view{ p.p, 1 };
p.p++;
}
//fallthrough to U->T
} else {
//This is the U->xT or U->X case, where U is neither 'e', 'X', nor 'x'
if (!(p.convpolicy & allow_converting_expected)) //xU->non-expected (non-error)
return create_des_type_error(p, allow_converting_expected);
//Push a "has value" indicator
if constexpr (has_source) if constexpr (has_target) *target << char(1);
p.target_type++; //step over 'x' in target type
}
//Try U->T
return cant_convert<has_source, has_target>(p, target);
}
if (c == 'o' && *p.type != 'o') {
//T->oU: test T->U
//Push a "has value" indicator
if constexpr (has_source) if constexpr (has_target) *target << char(1);
p.target_type++; //step over 'o' in target type
return cant_convert<has_source, has_target>(p, target);
}
switch (*p.type) {
default:
return create_des_typestring_source(p, ser_error_str(ser::chr));
case 'b':
if (c == 'b') {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
goto step1_1_return_true;
}
if (c == 'c' || c == 'i' || c == 'I') {
if constexpr (has_source) {
const char* old_p = p.p;
if (auto err = advance_source(p, nullptr, p.type)) return err;
if constexpr (has_target) {
char buff[8]; char* pp = buff;
switch (c) {
case 'c': *target << char(*old_p ? 1 : 0); break;
case 'i': serialize_to(uint32_t(*old_p ? 1 : 0), pp); *target << std::string(buff, 4); break;
case 'I': serialize_to(uint64_t(*old_p ? 1 : 0), pp); *target << std::string(buff, 8); break;
default: assert(0);
}
}
(void)old_p;
}
if (p.convpolicy & allow_converting_bool) goto step1_1_return_true;
else return create_des_type_error(p, allow_converting_bool);
}
return create_des_type_error(p);
case 'c':
if (c == 'c') {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
goto step1_1_return_true;
}
if (c == 'b' || c == 'i' || c == 'I') {
if constexpr (has_source) {
const char* old_p = p.p;
if (auto err = advance_source(p, nullptr, p.type)) return err;
if constexpr (has_target) {
char buff[8]; char* pp = buff;
switch (c) {
case 'b': *target << char(*old_p ? 1 : 0); break;
case 'i': serialize_to(uint32_t(*old_p), pp); *target << std::string(buff, 4); break;
case 'I': serialize_to(uint64_t(*old_p), pp); *target << std::string(buff, 8); break;
default: assert(0);
}
}
(void)old_p;
}
const serpolicy policy = c == 'b' ? allow_converting_bool : allow_converting_ints;
if (p.convpolicy & policy) goto step1_1_return_true;
else return create_des_type_error(p, policy);
}
return create_des_type_error(p);
case 'i':
if (c == 'i') {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
goto step1_1_return_true;
}
if (c == 'b' || c == 'c' || c == 'I' || c == 'd') {
if constexpr (has_source) {
const char* old_p = p.p;
if (auto err = advance_source(p, nullptr, p.type)) return err;
if constexpr (has_target) {
int32_t val;
if (deserialize_from<false>(old_p, old_p + 4, val))
return create_des_value_error(p);
char buff[8]; char* pp = buff;
switch (c) {
case 'b': *target << char(val ? 1 : 0); break;
case 'c': *target << char(val); break;
case 'I': serialize_to(int64_t(val), pp); *target << std::string(buff, 8); break;
case 'd': serialize_to(double(val), pp); *target << std::string(buff, 8); break;
default: assert(0);
}
}
(void)old_p;
}
const serpolicy policy = c == 'b' ? allow_converting_bool : c=='c' ? allow_converting_ints_narrowing : c=='I' ? allow_converting_ints : allow_converting_double;
if ((p.convpolicy & policy)==policy) goto step1_1_return_true;
else return create_des_type_error(p, policy);
}
return create_des_type_error(p);
case 'I':
if (c == 'I') {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
goto step1_1_return_true;
}
if (c == 'b' || c == 'c' || c == 'i' || c == 'd') {
if constexpr (has_source) {
const char* old_p = p.p;
if (auto err = advance_source(p, nullptr, p.type)) return err;
if constexpr (has_target) {
int64_t val;
if (deserialize_from<false>(old_p, old_p + 8, val))
return create_des_value_error(p);
char buff[8]; char* pp = buff;
switch (c) {
case 'b': *target << char(val ? 1 : 0); break;
case 'c': *target << char(val); break;
case 'i': serialize_to(int32_t(val), pp); *target << std::string(buff, 4); break;
case 'd': serialize_to(double(val), pp); *target << std::string(buff, 8); break;
default: assert(0);
}
}
(void)old_p;
}
const serpolicy policy = c == 'b' ? allow_converting_bool : c == 'd' ? allow_converting_double : allow_converting_ints_narrowing;
if ((p.convpolicy & policy)==policy) goto step1_1_return_true;
else return create_des_type_error(p, policy);
}
return create_des_type_error(p);
case 'd':
if (c == 'd') {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
goto step1_1_return_true;
}
if (c == 'i' || c == 'I') {
if constexpr (has_source) {
const char* old_p = p.p;
if (auto err = advance_source(p, nullptr, p.type)) return err;
if constexpr (has_target) {
double val;
if (deserialize_from<false>(old_p, old_p + 8, val))
return create_des_value_error(p);
char buff[8]; char* pp = buff;
switch (c) {
case 'i': serialize_to(int32_t(val), pp); *target << std::string(buff, 4); break;
case 'I': serialize_to(int64_t(val), pp); *target << std::string(buff, 8); break;
default: assert(0);
}
}
(void)old_p;
}
const serpolicy policy = allow_converting_double;
if (p.convpolicy & policy) goto step1_1_return_true;
else return create_des_type_error(p, policy);
}
return create_des_type_error(p);
case 's':
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
if (c == 's') goto step1_1_return_true;
if (c == 'l' && p.target_type + 1 < p.target_tend && p.target_type[1] == 'c') {
if (!(p.convpolicy & allow_converting_aux))
return create_des_type_error(p, allow_converting_aux);
p.target_type++;
goto step1_1_return_true;
}
return create_des_type_error(p);
case 'e':
if (c == 'e') {
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err;
goto step1_1_return_true;
}
//Note e->xT is handled above
return create_des_type_error(p);
case 'a':
//This is a->T
if (!(p.convpolicy & allow_converting_any))
return create_des_type_error(p, allow_converting_any);
if constexpr (has_source) {
any_view a;
if (deserialize_from<true>(p.p, p.end, a))
return create_des_value_error(p);
deserialize_convert_params local_p(a.value().data(), a.value().data() + a.value().length(),
a.type().data(), a.type().data(), a.type().data() + a.type().length(),
p.target_tstart, p.target_type, p.target_tend,
p.convpolicy, &p, p.errors, p.error_pos);
if (auto err = cant_convert<has_source, has_target>(local_p, target)) return err;
else if (local_p.type < local_p.tend) return create_des_typestring_source(local_p, impl::ser_error_str(impl::ser::tlong));
p.type++;
p.target_type = local_p.target_type;
//p.p has already been advaced by deserialize_from
return {};
} else {
p.type++;
if (p.target_type == p.target_tend) return {}; //We accept 'a'->void if no source is present
if (auto [err, len] = parse_target_type_or_error(p.target_type, p); err) //this call returns error on void
return std::move(err);
else
p.target_type += len;
return {};
}
case 'x':
case 'X':
//This is xT->U, where U is not an expected, we have already checked that
if (p.target_type<p.target_tend && *p.target_type=='e') {
//xT->e or X->e
if (!(p.convpolicy & allow_converting_expected))
return create_des_type_error(p, allow_converting_expected);
//if we have source, check if we have an error
if constexpr (has_source) {
bool has_value;
if (deserialize_from<false>(p.p, p.end, has_value))
return create_des_value_error(p);
if (has_value)
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("Cannot convert a ready expected to an error <%1> to <%2>", std::string_view{}, std::string_view{}), &p);
if (auto err = advance_source(p, target, "e")) return err; //step through the error in 'xT'
} else {
auto [err, len] = parse_type_or_error(p.type, p);
if (err) return std::move(err);
p.type += len;
}
p.target_type++; //step through the 'e'
return {};
}
//xU->T or X->T (T may be void, but not 'e', 'a' or 'x')
if (!(p.convpolicy & allow_converting_expected))
return create_des_type_error(p, allow_converting_expected);
if constexpr (has_source) {
bool has;
if (deserialize_from<false>(p.p, p.end, has))
return create_des_value_error(p);
if (!has) {
//We are an error. check if we types alone are compatible (for better errors)
const size_t orig_pos = p.type - p.tstart;
const size_t orig_tpos = p.target_type - p.target_tstart;
p.type++;
//if we are an X, we can disappear (X->void, dont consume any char from the target, which is not X here)
//else compare U->T
if (*(p.type-1) == 'x')
if (auto err = cant_convert<false, false>(p, nullptr))
return err;
//OK, this expected could have been converted to the target, but it is an error instead
if (!p.errors)
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("uf::expected contains error <%1> when converting to <%2>", std::string_view{}, std::string_view{}), &p);
//eat and collect the serialized error
if (deserialize_from<false>(p.p, p.end, p.errors->emplace_back()))
return create_des_value_error(p);
if (p.error_pos)
p.error_pos->emplace_back(orig_pos, orig_tpos);
return {}; //xT(e)->T conversion is OK, but we record the error in p.errors
}
//if xU contains a value, continue with trying to match the value of expected to the target type
}
p.type++;
//if we are an X, we can disappear (X->void, dont consume any char from the target, which is not X here)
if (*(p.type-1) == 'X')
return {};
//check U->T (correct even with has_source)
return cant_convert<has_source, has_target>(p, target);
case 'o':
//oT->U or oT->oU
if constexpr (has_source) {
//if we have source, check if we have a value
bool has_value;
if (deserialize_from<false>(p.p, p.end, has_value))
return create_des_value_error(p);
if (!has_value) {
//empty optional can only convert to an other optional
if (c == 'o') {
p.type++;
p.target_type++; //step over o:s
return cant_convert<false, false>(p, nullptr); //compare just the types
}
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("Empty optional <%1> can only convert to an optional and not <%2>", std::string_view{}, std::string_view{}), &p);
}
//else fallthrough
}
p.type++;
//T->U or T->oU
return cant_convert<has_source, has_target>(p, target);
case 'l':
if (p.type+1<p.tend && p.type[1] == 'c' && c == 's' &&
(p.convpolicy & allow_converting_aux)) {
//lc->s special case
if constexpr (has_source) if (auto err = advance_source(p, target, p.type)) return err; //leave p.type unchanged, but copy value as is if there is a target
p.type++;
goto step1_1_return_true;
} else if (c == 't') {
if (!(p.convpolicy & allow_converting_tuple_list))
return create_des_type_error(p, allow_converting_tuple_list);
//lX->tXYZ special case
p.type++;
p.target_type++;
uint32_t size = 0;
while (p.target_type<p.target_tend && '0' <= *p.target_type && *p.target_type <= '9') {
size = size * 10 + *p.target_type - '0';
p.target_type++;
}
if (size<2) return create_des_typestring_target(p, ser_error_str(ser::num));
if constexpr (has_source) {
uint32_t size2;
if (deserialize_from<false>(p.p, p.end, size2))
return create_des_value_error(p);
if (size!=size2)
return create_error_for_des(std::make_unique<uf::value_mismatch_error>(uf::concat("Size mismatch when converting <%1> to <%2> (", size2, "!=", size, ").")), &p);
}
const char* const save_type = p.type; //the location of the list type : "l*<type>"
while (size--) {
p.type = save_type;
if (auto err = cant_convert<has_source, has_target>(p, target))
return err;
}
return {};
} else { //use else to create new scope to be able to declare local variables.
//Create a limited local_p: target set to void if it is not a list
deserialize_convert_params local_p(p, p.tend, (c != 'l') ? p.target_type : p.target_tend);
local_p.type++;
if (local_p.target_type < local_p.target_tend)
local_p.target_type++;
if constexpr (has_source) {
//check every element. Here we know that the two list
//are not of exactly the same type (checked at the very beginning)
//Also target list may be void
if constexpr (has_target) *target << std::string_view(local_p.p, 4);
uint32_t size;
if (deserialize_from<false>(local_p.p, local_p.end, size))
return create_des_value_error(local_p);
if (size == 0) {
auto err = cant_convert<false, false>(local_p, nullptr); //compare just the types (target may be void)
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
return err;
}
const char *original_type = local_p.type;
const char *original_target_type = local_p.target_type;
while (size--) {
local_p.type = original_type;
local_p.target_type = original_target_type;
if (auto err = cant_convert<has_source, has_target>(local_p, target)) {
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
return err;
}
}
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
return {};
} else {
auto err = cant_convert<false, false>(local_p, nullptr); //compare just the types (target may be void)
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
return err;
}
}
case 'm':
if (c == 'm') {
//We know that maps cannot disappear (cannot be all-X types) as
//key must be non-X. But they can degenerate to a list if mapped type is all-X.
if constexpr (has_source) {
//go through the map and try one-by-one
//check every element. Here we know that the two maps
//are not of exactly the same type (checked at the very beginning)
if constexpr (has_target) *target << std::string_view(p.p, 4);
uint32_t size;
if (deserialize_from<false>(p.p, p.end, size))
return create_des_value_error(p);
if (size == 0)
return cant_convert<false, false>(p, nullptr); //compare just the type of the two maps
deserialize_convert_params local_p(&p);
while (size--) {
local_p.type = p.type+1;
local_p.target_type = p.target_type + 1;
if (auto err = cant_convert<has_source, has_target>(local_p, target))
return err;
if (local_p.target_type == p.target_type + 1)
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("Strange target type <%1> -> <%2>", std::string_view{}, std::string_view{}), &local_p);
const char *beginning_of_mapped_type = local_p.target_type;
if (auto err = cant_convert<has_source, has_target>(local_p, target))
return err;
if (local_p.target_type == beginning_of_mapped_type)
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("Strange target type <%1> -> <%2>", std::string_view{}, std::string_view{}), &local_p);
}
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
return {};
} else {
deserialize_convert_params local_p(&p);
local_p.type++;
local_p.target_type++;
if (auto err = cant_convert<false, false>(local_p, nullptr))
return err;
if (local_p.target_type == p.target_type + 1)
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("Strange target type <%1> -> <%2>", std::string_view{}, std::string_view{}), &local_p);
const char *beginning_of_mapped_type = local_p.target_type;
if (auto err = cant_convert<false, false>(local_p, nullptr))
return err;
if (local_p.target_type == beginning_of_mapped_type)
return create_error_for_des(std::make_unique<uf::type_mismatch_error>("Strange target type <%1> -> <%2>", std::string_view{}, std::string_view{}), &local_p);
p.type = local_p.type;
p.target_type = local_p.target_type;
return {};
}
}
if (c == 'l') {
//We can only serialize into a list if one of our key/mapped type is
//all-X and the other deserializes into the type of the list.
//This is tested by trimming target type to the type of the list and
//attempting to deserialize the two types to the list
auto [err, ttlen] = parse_target_type_or_error(p.target_type, p);
if (err) return std::move(err);
deserialize_convert_params local_p(p, p.tend, p.target_type+ttlen); //trim target type after list
if constexpr (has_source) {
//go through the list and try one-by-one
//check every element. Here we know that the two list
//are not of exactly the same type (checked at the very beginning)
if constexpr (has_target) *target << std::string_view(p.p, 4);
uint32_t size;
if (deserialize_from<false>(p.p, p.end, size))
return create_des_value_error(p);
if (size == 0)
return cant_convert<false, false>(p, nullptr); //compare just the type of the two maps
local_p.p = p.p;
while (size--) {
local_p.type = p.type + 1;
local_p.target_type = p.target_type + 1;
if (auto err = cant_convert<has_source, has_target>(local_p, target))
return err;
if (auto err = cant_convert<has_source, has_target>(local_p, target)) //will fail if cannot be deserialized into void
return err;
}
//if OK fallthrough
} else {
local_p.type++;
local_p.target_type++;
if (auto err = cant_convert<has_source, has_target>(local_p, target))
return err;
if (auto err = cant_convert<has_source, has_target>(local_p, target)) //will fail if cannot be deserialized into void
return err;
//if OK fallthrough
}
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
return {};
}
//We cannot deserialize into any other type
return create_des_type_error(p);
case 't':
//OK, try to match our fields to that of the target
//This includes the case when we have a single non-X element that
//deserializes into a non-tuple type and the all-X case that serializes
//into void or into an X or to some tuple requesting X
//Start by trimming the target type to that of the upcoming type. (accept void)
auto [ttlen, tproblem] = parse_type(p.target_type, p.target_tend, true);
if (tproblem!=ser::ok) {
p.target_type += ttlen;
return create_des_typestring_target(p, ser_error_str(tproblem));
}
deserialize_convert_params local_p(p, p.tend, p.target_type+ttlen);
StringViewAccumulator local_target;
//First jump across our element number
local_p.type++;
int size1 = 0;
while (local_p.type<local_p.tend && '0' <= *local_p.type && *local_p.type <= '9') {
size1 = size1 * 10 + *local_p.type - '0';
local_p.type++;
}
if (size1<2) return create_des_typestring_source(p, ser_error_str(ser::num));
if (c == 't') {
//If the other type is a tuple, jump over the element num
local_p.target_type++;
int size2 = 0;
while (local_p.target_type < local_p.target_tend &&
'0' <= *local_p.target_type && *local_p.target_type <= '9') {
size2 = size2 * 10 + *local_p.type - '0';
local_p.target_type++;
}
if (size2<2) return create_des_typestring_target(p, ser_error_str(ser::num));
}
std::unique_ptr<value_error> ret;
if (c == 'l' && (p.convpolicy & allow_converting_tuple_list)) {
const char* const save_target_type = p.target_type+1; //jump over the 'l' in the target type
p.type = local_p.type; //jump over the tuple number in the source type
if constexpr (has_target) {
char buff[4]; char* pp = buff;
serialize_to(uint32_t(size1), pp);
*target << std::string(buff, 4);
}
while (size1--) {
p.target_type = save_target_type;
ret = cant_convert<has_source, has_target>(p, target);
if (ret) break;
}
if (!ret) return {};
//else we try converting void-like members away - but in case of failure of that we emit the error generated here
//as the user is likely to expect this error.
} //else if the target is a list, but allow_converting_tuple_list is not set, we try 'converting away' some void-like members below.
//Try deserializing our elements into t2.
//Here we may need to do a backtracking thingy.
//Consider, for example t2ai being converted to i. Conversion is possible if 'a' holds a void.
//However, it will be consumed to 'i' and we will fail to convert 'i' to void.
//So we need to backtrack and try what if 'a' would disappear.
//For this we identify backtracking points, where
//A type could disappear, but did not and come back to this event on failure.
//We store the backtracking points in a stack (vector).
struct backtrack_point
{
const char *p;
const char *type;
const char *target_type;
size_t error_list_size;
int remaining_size;
uf::impl::StringViewAccumulator target;
};
std::vector<backtrack_point> stack;
bool updated_error = false;
//We need to do backtracking even if we have serialized source available,
//as ambiguity remains with 'xa' types that carry an error. Specifically,
//they can disappear if they carried void, thus t2xai -> i should succeed
//(with the collected error from xa), since it could have succeeeded if
//'a' carried void and if the user is counting on that we want to
//give the right kind of error (unplaceable errors as opposed to type mismatch)
while (true) {
bool success = true;
while (success && size1--) {
const char * const original_target_type = local_p.target_type;
const bool can_dis = can_disappear(std::string_view(local_p.type, local_p.tend - local_p.type)).has_value();
if (auto err = cant_convert<has_source, has_target>(local_p, has_target ? &local_target : nullptr)) {
if (!ret)
ret = std::move(err); //store the first error we see - the most natural
success = false; //will break
} else//It is not a backtracking point only if the type could disappear but it didnt.
if (can_dis && original_target_type != local_p.target_type)
//Save stack state, size1 is already decremented
stack.push_back(backtrack_point{ local_p.p, local_p.type, original_target_type,
local_p.errors ? local_p.errors->size() : 0, size1, local_target });
}
//now test that we have exhausted all of t2's elements
if (success && local_p.target_type == local_p.target_tend)
break; //success
//failure
if (stack.size() && ret && !updated_error) {
//For errors inside tuples, when we did backtracking,
//Indicate that we would fail anyway.
//(below we place the error at the front of the tuple, no msg change needed)
ret->append_msg(" (With any incoming value.)");
updated_error = true;
}
if (!ret) //generate error if not yet
ret = create_des_type_error(p); //in the error mark the tuples as the culprit - use original p.
//try to backtrack if we have no source.
if (stack.size() == 0)
return ret;
//restore to the branching point (local_p.p can be ignored, we have no source here)
local_p.p = stack.back().p;
local_p.type = stack.back().type;
local_p.target_type = stack.back().target_type;
if (local_p.errors)
local_p.errors->resize(stack.back().error_list_size);
if (local_p.error_pos)
local_p.error_pos->resize(stack.back().error_list_size);
size1 = stack.back().remaining_size;
local_target = std::move(stack.back().target);
stack.pop_back();
}
//success
p.p = local_p.p;
p.type = local_p.type;
p.target_type = local_p.target_type;
if constexpr (has_target) *target << std::move(local_target);
return {};
}
step1_1_return_true:
p.type++;
p.target_type++;
return {};
}
template std::unique_ptr<uf::value_error>
uf::impl::cant_convert<false, false>(deserialize_convert_params &p, StringViewAccumulator *target);
template std::unique_ptr<uf::value_error>
uf::impl::cant_convert<true, false>(deserialize_convert_params &p, StringViewAccumulator *target);
template std::unique_ptr<uf::value_error>
uf::impl::cant_convert<true, true>(deserialize_convert_params &p, StringViewAccumulator *target);
//cant_convert<false,true> is invalid
namespace uf::impl
{
/** Parses string and checks if it is a valid type description. Type can be fragmented in multiple chunks.
* @param [in] type The typestring to check. We actually remove all scanned bytes from it.
* @param [in] accept_void If true, we return a valid 0 on an empty 'type'
* @param [in] more A function to add more bytes to the typestring. Called when 'type' has
* been parsed to its end. Shall be f(std::string_view&)->void, when
* called should set the view to the new typestring chunk. Set to empty type
* to signal that no more bytes. The last (partial) chunk is returned in 'type'.
* @returns On success the parsed type and false, whereas on
* error we return the partially parsed type until the error and true.*/
std::pair<std::string, ser> parse_type_chunks(std::string_view &type, bool accept_void,
const std::function<void(std::string_view &)> &more) {
if (type.empty() && more) more(type);
if (type.empty()) return {{}, accept_void ? ser::ok : ser::end};
const char c = type.front();
if (c=='s' || c=='c' || c=='b' || c=='i' || c=='I' || c=='X' ||c=='d' || c=='a' || c=='e') {
type.remove_prefix(1);
return {{c}, ser::ok};
}
if (c == 'l' || c=='x' || c=='o') {
type.remove_prefix(1);
auto ret = parse_type_chunks(type, false, more);
ret.first.insert(ret.first.begin(), c);
return ret;
}
if (c=='m') {
type.remove_prefix(1);
auto ret = parse_type_chunks(type, false, more);
ret.first.insert(ret.first.begin(), c);
if (ret.second!=ser::ok) return ret;
auto ret2 = parse_type_chunks(type, false, more);
ret2.first.insert(0, ret.first);
return ret2;
}
if (c=='t') {
type.remove_prefix(1);
if (type.empty() && more) more(type);
std::string ret = "t";
size_t size = 0;
while (type.size() && '0'<=type.front() && type.front() <='9') {
size = size*10 + type.front() - '0';
ret.push_back(type.front());
type.remove_prefix(1);
if (type.empty() && more) more(type);
}
if (size < 2) return {{}, ser::num};
while (size--)
if (auto [ty, problem] = parse_type_chunks(type, false, more); problem==ser::ok)
ret.append(std::move(ty));
else
return {std::move(ret), problem};
return {std::move(ret), ser::ok};
}
return {{}, ser::chr};
}
} //ns
std::unique_ptr<uf::value_error>
uf::impl::serialize_scan_by_type_from(std::string_view &type, const char*&p, const char *&end,
const std::function<void(std::string_view &)> &more_type,
const std::function<void(const char *&, const char *&)> &more_val,
bool check_recursively) {
if (type.empty() && more_type) more_type(type);
if (type.empty()) return {};
if (p == end && more_val) more_val(p, end);
switch (type.front()) {
case 'c':
case 'b':
++p;
if (p>end) goto value_mismatch;
type.remove_prefix(1);
break;
case 'i':
p += 4;
if (p>end) goto value_mismatch;
type.remove_prefix(1);
break;
case 'I':
case 'd':
p += 8;
if (p>end) goto value_mismatch;
type.remove_prefix(1);
break;
case 's':
{
uint32_t size;
if (deserialize_from<false>(p, end, size)) goto value_mismatch;
while (size) {
if (p == end && more_val) more_val(p, end);
if (p == end) goto value_mismatch;
const uint32_t len = std::min<uint32_t>(size, end - p);
p += len;
size -= len;
}
type.remove_prefix(1);
break;
}
case 'a':
if (check_recursively) {
if (p == end && more_val) more_val(p, end);
if (p == end) goto value_mismatch;
uint32_t tsize;
if (deserialize_from<false>(p, end, tsize)) goto value_mismatch;
std::string inner_type;
inner_type.reserve(tsize);
while (tsize) {
if (p == end && more_val) more_val(p, end);
if (p == end) goto value_mismatch;
const uint32_t len = std::min<uint32_t>(tsize, end - p);
inner_type.append(p, len);
p += len;
tsize -= len;
}
if (p == end && more_val) more_val(p, end);
if (p == end) goto value_mismatch;
uint32_t vsize;
if (deserialize_from<false>(p, end, vsize)) goto value_mismatch;
if (p == end && more_val) more_val(p, end);
std::string_view inner_ty = inner_type;
const char *const old_p = p;
const bool all_in_this_chunk = end-p>=vsize;
type.remove_prefix(1);
if (all_in_this_chunk) {
const char *inner_end = p+vsize;
if (auto err = serialize_scan_by_type_from(inner_ty, p, inner_end, {}, {}, true)) {
err->encaps(inner_type, inner_ty, type);
return err;