-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMsgcore_c.cpp
More file actions
2420 lines (2161 loc) · 81.2 KB
/
Copy pathMsgcore_c.cpp
File metadata and controls
2420 lines (2161 loc) · 81.2 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
// Copyright © 2026 Khrustal & Mann
// MELBOURNE, VICTORIA, AUSTRALIA, 3000
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Msgcore_c.cpp
// Flat C wrapper implementation over the Msgcore C++ API.
// Add this file to the Msgcore VS project (Msgcore_EXPORTS defined in project properties).
//
#include "stdafx.h"
#include "Msgcore_c.h"
#include "P2PmsgMgr.h"
#include "MsgAttr.h"
#include "MsgDesc.h"
#include "MsgCurs.h"
#include "MsgList.h"
#include "MsgVect.h"
#include "MsgStck.h"
#include <string>
#include <cstring>
#include <cwchar>
#include <cstdio>
#include <map>
#include <mutex>
// ---------------------------------------------------------------------------
// Handle registry (SECURITY_REVIEW M1)
//
// Every handle this API hands to a C caller is recorded here on creation and
// forgotten on destroy; every entry point looks its raw void* up before using
// it. The load-bearing property is that the lookup NEVER DEREFERENCES the
// caller's pointer. A magic word in a wrapper struct would have to read through
// the pointer to check it, which is the defect itself rather than a fix for it:
// a hostile or merely stale value crashes (or worse) AT THE CHECK.
//
// This is the backport of the registry that closed M1 for Targetcore's flat C
// ABI in session 24. That surface has since been deleted from the tree; THIS one
// has not -- Msgcore_c is built into Msgcore.dll and still has consumers -- so
// the identical defect was still live here: nine helpers static_cast<>ing
// whatever void* arrived, spread over 217 entry points.
//
// The registry is TYPE-AWARE. All nine handle typedefs in the header are
// `void*`, so before this nothing stopped a caller spending a MsgCursHandle
// where a MsgMgrHandle was expected -- nine unrelated C++ classes with one ABI.
// Storing the kind alongside the key makes that a refusal rather than type
// confusion.
//
// std::map keyed by the address, exactly like the paging registry below
// (s_mapPaging / MsgcorePagingForget, called from msgcore_mgr_destroy): the key
// IS the handle, and the map's node stability means an entry never moves while
// it is registered.
//
// What this does NOT close, and cannot: a caller that destroys a handle on one
// thread while another thread is inside an entry point holding it. That window
// is only closable by refcounting every handle, which would change the ownership
// contract the header publishes. What it DOES close is the three holes reachable
// from a correct caller's mistake or from a hostile one -- a pointer we never
// issued, a pointer we issued and have since forgotten, and a pointer of the
// wrong kind.
//
// Cost: one uncontended mutex acquisition per handle argument, and the guard
// form spends a second lookup where the body uses the pointer again. That is
// tens of nanoseconds against an FFI transition, and it keeps every entry
// point's guard to a single line that cannot be got wrong halfway down a block
// of thirty similar-looking functions.
// ---------------------------------------------------------------------------
enum MsgcoreHandleKind_e
{
MsgcoreHandle_Mgr = 1,
MsgcoreHandle_Field,
MsgcoreHandle_List,
MsgcoreHandle_Vect,
MsgcoreHandle_Attr,
MsgcoreHandle_Desc,
MsgcoreHandle_Curs,
MsgcoreHandle_Stck,
MsgcoreHandle_Recurs
};
static std::map<const void*, MsgcoreHandleKind_e> s_mapHandle;
static std::mutex s_mtxHandle;
// Records a handle just produced and returns it, so a factory stays one line.
template <class T>
static T* MsgcoreHandleAdd(T* p, MsgcoreHandleKind_e eKind)
{
if (!p) return nullptr;
std::lock_guard<std::mutex> lock(s_mtxHandle);
// Assignment rather than insert(): the allocator recycles addresses, and a
// recycled address must take the NEW kind. insert() would silently keep the
// dead entry's kind and turn address reuse into type confusion -- the exact
// failure this registry exists to prevent. (The same hazard
// MsgcorePagingForget is commented against below.)
s_mapHandle[p] = eKind;
return p;
}
// True only when h is a live handle of exactly eKind. Never dereferences it.
static bool MsgcoreHandleIs(const void* h, MsgcoreHandleKind_e eKind)
{
if (!h) return false;
std::lock_guard<std::mutex> lock(s_mtxHandle);
std::map<const void*, MsgcoreHandleKind_e>::const_iterator it = s_mapHandle.find(h);
return it != s_mapHandle.end() && it->second == eKind;
}
// Forgets a handle. Returns true ONLY for the caller that actually removed it,
// which is what makes destroy safe: a double destroy deletes once and refuses
// the second time, and two threads racing one destroy cannot both reach delete.
static bool MsgcoreHandleForget(const void* h, MsgcoreHandleKind_e eKind)
{
if (!h) return false;
std::lock_guard<std::mutex> lock(s_mtxHandle);
std::map<const void*, MsgcoreHandleKind_e>::iterator it = s_mapHandle.find(h);
if (it == s_mapHandle.end() || it->second != eKind) return false;
s_mapHandle.erase(it);
return true;
}
// Cast helpers. Each answers nullptr for anything that is not a LIVE handle of
// its own kind, so an entry point's guard reads `if (!toField(hField)) return 0;`
// and a bogus, dangling or wrong-typed handle takes the path a null one took.
static inline P2PmsgMgr* toMgr (MsgMgrHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Mgr) ? static_cast<P2PmsgMgr*>(h) : nullptr; }
static inline P3PmsgField* toField(MsgFieldHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Field) ? static_cast<P3PmsgField*>(h) : nullptr; }
static inline P3PmsgList* toList (MsgListHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_List) ? static_cast<P3PmsgList*>(h) : nullptr; }
static inline P3PmsgVect* toVect (MsgVectHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Vect) ? static_cast<P3PmsgVect*>(h) : nullptr; }
static inline P3PmsgAttr* toAttr (MsgAttrHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Attr) ? static_cast<P3PmsgAttr*>(h) : nullptr; }
static inline P3PmsgDesc* toDesc (MsgDescHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Desc) ? static_cast<P3PmsgDesc*>(h) : nullptr; }
static inline P3PmsgCurs* toCurs (MsgCursHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Curs) ? static_cast<P3PmsgCurs*>(h) : nullptr; }
static inline MsgStck* toStck (MsgStckHandle h) { return MsgcoreHandleIs(h, MsgcoreHandle_Stck) ? static_cast<MsgStck*>(h) : nullptr; }
static inline P2PmsgRecurs* toRecurs(MsgRecursHandle h){ return MsgcoreHandleIs(h, MsgcoreHandle_Recurs) ? static_cast<P2PmsgRecurs*>(h) : nullptr; }
// ---------------------------------------------------------------------------
// Helpers for the Containers II / MsgStck / Recurs / paging blocks.
// These live OUTSIDE the extern "C" block below on purpose: a C-linkage
// function may not return a C++ class by value, and MsgcoreProtoData does.
// ---------------------------------------------------------------------------
// A zero-valued P3PmsgData of the requested MSGCORE_DATA_* type, used as the
// element prototype a P3PmsgVect constructor requires. An unrecognised type
// falls back to INT32, matching msgcore_field_declare_int_typed.
static P3PmsgData MsgcoreProtoData(unsigned char uDataType)
{
switch (uDataType)
{
case MSGCORE_DATA_INT08: return P3PmsgData((INT08)0);
case MSGCORE_DATA_UINT08: return P3PmsgData((UINT08)0);
case MSGCORE_DATA_INT16: return P3PmsgData((INT16)0);
case MSGCORE_DATA_UINT16: return P3PmsgData((UINT16)0);
case MSGCORE_DATA_UINT32: return P3PmsgData((UINT32)0);
case MSGCORE_DATA_INT64: return P3PmsgData((INT64)0);
case MSGCORE_DATA_UINT64: return P3PmsgData((UINT64)0);
case MSGCORE_DATA_FLOAT: return P3PmsgData((float)0.0f);
case MSGCORE_DATA_DOUBLE: return P3PmsgData((double)0.0);
case MSGCORE_DATA_BOOL: return P3PmsgData((bool)false);
case MSGCORE_DATA_WSTR16:
case MSGCORE_DATA_BSTR16: return P3PmsgData(L"");
case MSGCORE_DATA_INT32:
default: return P3PmsgData((INT32)0);
}
}
// The nIndex'th cell of a list, or nullptr when the index is out of range.
// P3PmsgList is a linked sequence walked by VBLaddr, so this is O(nIndex); see
// the header for why an index rather than the raw position crosses the ABI.
static P3PmsgData* MsgcoreListCellAt(P3PmsgList* pList, int nIndex)
{
if (!pList || nIndex < 0) return nullptr;
VBLaddr aPos = pList->GetHeadPos();
for (int i = 0; i < nIndex && aPos; ++i)
pList->GetNext(aPos);
if (!aPos) return nullptr;
return &pList->GetNext(aPos);
}
// --- paging sink registry --------------------------------------------------
// P2PmsgMgr::PageRegistration takes a PINT_PTR "callback key" that it hands
// back to every invocation -- already a user-data slot, so the flat sinks need
// no core change: the key IS the address of this record. std::map is chosen
// for its reference stability (the record must not move while registered).
struct MsgcorePagingRec
{
msgcore_pagein_fn pfnIn{nullptr};
msgcore_pageout_fn pfnOut{nullptr};
msgcore_populate_fn pfnPopulate{nullptr};
void* pvUserPage{nullptr};
void* pvUserPopulate{nullptr};
};
static std::map<P2PmsgMgr*, MsgcorePagingRec> s_mapPaging;
static std::mutex s_mtxPaging;
static BOOL CALLBACK MsgcorePageinTramp(PINT_PTR nKey, P2Pos posItem)
{
MsgcorePagingRec* p = reinterpret_cast<MsgcorePagingRec*>(nKey);
if (!p || !p->pfnIn) return FALSE;
return p->pfnIn(p->pvUserPage, (unsigned long long)posItem) ? TRUE : FALSE;
}
static BOOL CALLBACK MsgcorePageoutTramp(PINT_PTR nKey, P2Pos posItem, BOOL bFlush)
{
MsgcorePagingRec* p = reinterpret_cast<MsgcorePagingRec*>(nKey);
if (!p || !p->pfnOut) return FALSE;
return p->pfnOut(p->pvUserPage, (unsigned long long)posItem, bFlush ? 1 : 0) ? TRUE : FALSE;
}
static BOOL CALLBACK MsgcorePopulateTramp(PINT_PTR nKey, P2Pos posItem, BOOL /*bSpare*/)
{
MsgcorePagingRec* p = reinterpret_cast<MsgcorePagingRec*>(nKey);
if (!p || !p->pfnPopulate) return FALSE;
return p->pfnPopulate(p->pvUserPopulate, (unsigned long long)posItem) ? TRUE : FALSE;
}
// Forget a manager's paging record. Called from msgcore_mgr_destroy so a
// destroyed manager cannot leave a stale entry behind for a later manager that
// the allocator happens to place at the same address.
static void MsgcorePagingForget(P2PmsgMgr* pMgr)
{
std::lock_guard<std::mutex> lock(s_mtxPaging);
s_mapPaging.erase(pMgr);
}
// ---------------------------------------------------------------------------
// P2PmsgMgr
// ---------------------------------------------------------------------------
extern "C" {
MSGCORE_C_API MsgMgrHandle
msgcore_mgr_create()
{
return MsgcoreHandleAdd(new P2PmsgMgr(), MsgcoreHandle_Mgr);
}
MSGCORE_C_API MsgMgrHandle
msgcore_mgr_create_nn(unsigned char uAddrNN, unsigned int nSizeInitial, unsigned int nSizeMax)
{
return MsgcoreHandleAdd(new P2PmsgMgr(uAddrNN, nSizeInitial, nSizeMax), MsgcoreHandle_Mgr);
}
MSGCORE_C_API MsgMgrHandle
msgcore_mgr_open_file(const wchar_t* lpszFilename)
{
if (!lpszFilename) return nullptr;
P2PmsgMgr* pMgr = new P2PmsgMgr(lpszFilename);
return MsgcoreHandleAdd(pMgr, MsgcoreHandle_Mgr);
}
MSGCORE_C_API void
msgcore_mgr_destroy(MsgMgrHandle hMgr)
{
// Drop any paging registration first: the record is keyed by this pointer,
// and the allocator may hand the same address to the next manager.
P2PmsgMgr* pMgr = toMgr(hMgr);
if (!pMgr) return; // never issued, already destroyed, or wrong kind
MsgcorePagingForget(pMgr);
// Forget BEFORE the delete, and act only on the caller that actually
// removed the entry: that is what makes a double destroy a no-op rather
// than a double free, and stops two threads racing one destroy from both
// reaching delete.
if (!MsgcoreHandleForget(hMgr, MsgcoreHandle_Mgr)) return;
delete pMgr;
}
MSGCORE_C_API int
msgcore_mgr_load(MsgMgrHandle hMgr, const wchar_t* lpszFilename)
{
if (!toMgr(hMgr) || !lpszFilename) return 0;
return toMgr(hMgr)->Load(lpszFilename) ? 1 : 0;
}
MSGCORE_C_API int
msgcore_mgr_save(MsgMgrHandle hMgr, const wchar_t* lpszFilename)
{
if (!toMgr(hMgr)) return 0;
return toMgr(hMgr)->Save(lpszFilename) ? 1 : 0;
}
MSGCORE_C_API void
msgcore_mgr_nullify(MsgMgrHandle hMgr)
{
if (toMgr(hMgr)) toMgr(hMgr)->Nullify();
}
MSGCORE_C_API int
msgcore_mgr_rename(MsgMgrHandle hMgr, const wchar_t* lpszNewname)
{
if (!toMgr(hMgr) || !lpszNewname) return 0;
return toMgr(hMgr)->Rename(lpszNewname) ? 1 : 0;
}
MSGCORE_C_API const wchar_t*
msgcore_mgr_get_filename(MsgMgrHandle hMgr)
{
if (!toMgr(hMgr)) return nullptr;
return toMgr(hMgr)->GetFilename();
}
MSGCORE_C_API const wchar_t*
msgcore_mgr_get_rootname(MsgMgrHandle hMgr)
{
if (!toMgr(hMgr)) return nullptr;
return toMgr(hMgr)->GetRootname();
}
MSGCORE_C_API int
msgcore_mgr_is_dirty(MsgMgrHandle hMgr)
{
if (!toMgr(hMgr)) return 0;
return toMgr(hMgr)->IsDirty() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_mgr_set_dirty(MsgMgrHandle hMgr, int bDirty)
{
if (!toMgr(hMgr)) return 0;
return toMgr(hMgr)->SetDirty(bDirty ? TRUE : FALSE) ? 1 : 0;
}
MSGCORE_C_API unsigned int
msgcore_mgr_sizeof(MsgMgrHandle hMgr)
{
if (!toMgr(hMgr)) return 0;
return (unsigned int)toMgr(hMgr)->Sizeof();
}
MSGCORE_C_API int
msgcore_mgr_is_valid(MsgMgrHandle hMgr)
{
if (!toMgr(hMgr)) return 0;
return toMgr(hMgr)->IsValid() ? 1 : 0;
}
// ---------------------------------------------------------------------------
// P3PmsgField
// ---------------------------------------------------------------------------
MSGCORE_C_API MsgFieldHandle
msgcore_mgr_as_field(MsgMgrHandle hMgr)
{
if (!toMgr(hMgr)) return nullptr;
// P2PmsgMgr IS-A P3PmsgItem (=P3PmsgField) – copy-construct an independent field
P3PmsgField* pField = new P3PmsgField(*toMgr(hMgr));
return MsgcoreHandleAdd(pField, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_create()
{
return MsgcoreHandleAdd(new P3PmsgField(), MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_clone(MsgFieldHandle hField)
{
if (!toField(hField)) return nullptr;
return MsgcoreHandleAdd(new P3PmsgField(*toField(hField)), MsgcoreHandle_Field);
}
MSGCORE_C_API void
msgcore_field_destroy(MsgFieldHandle hField)
{
P3PmsgField* p = toField(hField);
// Forget first, and act only on the caller that removed the entry -
// a double destroy then deletes once and refuses the second time.
if (!p || !MsgcoreHandleForget(hField, MsgcoreHandle_Field)) return;
delete p;
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_select_item(MsgFieldHandle hField, const wchar_t* lpszName)
{
if (!toField(hField) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toField(hField)->SelectItem(lpszName);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API int
msgcore_field_exists(MsgFieldHandle hField, const wchar_t* lpszName)
{
if (!toField(hField) || !lpszName) return 0;
return toField(hField)->Exists(lpszName) ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_delete_item(MsgFieldHandle hField, const wchar_t* lpszName)
{
if (!toField(hField) || !lpszName) return 0;
return toField(hField)->Delete(lpszName) ? 1 : 0;
}
MSGCORE_C_API void
msgcore_field_truncate(MsgFieldHandle hField)
{
if (toField(hField)) toField(hField)->Truncate();
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_declare_int(MsgFieldHandle hField, const wchar_t* lpszName, int value, int bUpdate)
{
if (!toField(hField) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toField(hField)->DeclareItem(lpszName, P3PmsgData(value), bUpdate ? TRUE : FALSE);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_declare_int64(MsgFieldHandle hField, const wchar_t* lpszName, long long value, int bUpdate)
{
if (!toField(hField) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toField(hField)->DeclareItem(lpszName, P3PmsgData((INT64)value), bUpdate ? TRUE : FALSE);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_declare_double(MsgFieldHandle hField, const wchar_t* lpszName, double value, int bUpdate)
{
if (!toField(hField) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toField(hField)->DeclareItem(lpszName, P3PmsgData(value), bUpdate ? TRUE : FALSE);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_declare_bool(MsgFieldHandle hField, const wchar_t* lpszName, int bValue, int bUpdate)
{
if (!toField(hField) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toField(hField)->DeclareItem(lpszName, P3PmsgData((bool)(bValue != 0)), bUpdate ? TRUE : FALSE);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
// UTF-16 unit count of a wide string (an astral code point is two units). This is
// the width a value is stored at (P2PWCHAR, 16-bit): on Windows wchar_t already IS a
// UTF-16 unit; on the Linux port a >U+FFFF wchar_t encodes to a surrogate pair. Used
// to reject an over-capacity value up front (see the guard in declare_wstr).
static size_t msgcore_wstr16_units(const wchar_t* s)
{
size_t units = 0;
for (; s && *s; ++s)
#if WCHAR_MAX > 0xFFFF
units += (static_cast<unsigned long>(*s) > 0xFFFFu) ? 2u : 1u; // UTF-32 wchar_t
#else
units += 1u; // UTF-16 wchar_t
#endif
return units;
}
MSGCORE_C_API MsgFieldHandle
msgcore_field_declare_wstr(MsgFieldHandle hField, const wchar_t* lpszName, const wchar_t* lpszValue, int bUpdate)
{
if (!toField(hField) || !lpszName || !lpszValue) return nullptr;
// A WSTR16 blob caps at 0xFFFF bytes -> 32767 UTF-16 units. Reject an over-cap
// value HERE rather than let it reach the core: the over-cap store throws from deep
// in the VBHeap copy and orphans this P3PmsgData's heap object on the C++ unwind
// (a latent core exception-safety gap, ASan-confirmed). Rejecting up front is
// behaviour-identical (returns null -> the caller sees the write rejected) and
// leak-free. Regression: TreeFsPosixSuite "an over-cap value write is rejected
// without corrupting the node".
if (msgcore_wstr16_units(lpszValue) > 32767) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toField(hField)->DeclareItem(lpszName, P3PmsgData(lpszValue), bUpdate ? TRUE : FALSE);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API const wchar_t*
msgcore_field_get_name(MsgFieldHandle hField)
{
if (!toField(hField)) return nullptr;
return toField(hField)->c_name();
}
MSGCORE_C_API unsigned char
msgcore_field_get_data_type(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->DataType();
}
MSGCORE_C_API int
msgcore_field_is_null(MsgFieldHandle hField)
{
if (!toField(hField)) return 1;
return toField(hField)->IsNull() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_void(MsgFieldHandle hField)
{
if (!toField(hField)) return 1;
return toField(hField)->IsVoid() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_get_int(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->c_int();
}
MSGCORE_C_API void
msgcore_field_set_int(MsgFieldHandle hField, int value)
{
if (toField(hField)) toField(hField)->c_int(value);
}
MSGCORE_C_API long long
msgcore_field_get_int64(MsgFieldHandle hField)
{
if (!toField(hField)) return 0LL;
return (long long)toField(hField)->c_int64();
}
MSGCORE_C_API void
msgcore_field_set_int64(MsgFieldHandle hField, long long value)
{
if (toField(hField)) toField(hField)->c_int64((INT64)value);
}
MSGCORE_C_API double
msgcore_field_get_double(MsgFieldHandle hField)
{
if (!toField(hField)) return 0.0;
return toField(hField)->c_double();
}
MSGCORE_C_API void
msgcore_field_set_double(MsgFieldHandle hField, double value)
{
if (toField(hField)) toField(hField)->c_double(value);
}
MSGCORE_C_API int
msgcore_field_get_bool(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->c_bool() ? 1 : 0;
}
MSGCORE_C_API void
msgcore_field_set_bool(MsgFieldHandle hField, int bValue)
{
if (toField(hField)) toField(hField)->c_bool((bValue != 0));
}
MSGCORE_C_API const wchar_t*
msgcore_field_get_wstr(MsgFieldHandle hField)
{
if (!toField(hField)) return nullptr;
return toField(hField)->c_wstr();
}
MSGCORE_C_API void
msgcore_field_set_wstr(MsgFieldHandle hField, const wchar_t* lpszValue)
{
if (toField(hField) && lpszValue) toField(hField)->c_wcscpy(lpszValue);
}
MSGCORE_C_API int
msgcore_field_is_list(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->r_Object().IsList() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_vect(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->r_Object().IsVect() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_attr(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->r_Object().IsAttr() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_desc(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->r_Object().IsDesc() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_stacked(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->IsStacked() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_attributed(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->IsAttributed() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_field_is_descendant(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->IsDescendant() ? 1 : 0;
}
// P3PmsgField::IsSole, not P3PmsgObject::IsSole: the field's override subtracts
// the sub-objects it made itself, so a field that has merely been asked for its
// descendants still answers TRUE. The object's version cannot tell those from a
// stranger. The contract the header publishes -- TRUE guarantees, FALSE does not
// -- is the field's, and forwarding to r_Object() here would quietly narrow the
// TRUE half that the whole export exists for.
//
// The bad-handle answer is 0 and that is not arbitrary: 0 is the answer that
// promises nothing, so a stale or hostile handle can never be turned into a
// licence to write in place. (msgcore_field_is_null / _is_void answer 1 for the
// same reason -- in those, 1 is the pessimistic half.)
MSGCORE_C_API int
msgcore_field_is_sole(MsgFieldHandle hField)
{
if (!toField(hField)) return 0;
return toField(hField)->IsSole() ? 1 : 0;
}
// ---------------------------------------------------------------------------
// P3PmsgList
// ---------------------------------------------------------------------------
MSGCORE_C_API MsgListHandle
msgcore_list_from_field(MsgFieldHandle hField)
{
if (!toField(hField)) return nullptr;
return MsgcoreHandleAdd(new P3PmsgList(*toField(hField)), MsgcoreHandle_List);
}
MSGCORE_C_API void
msgcore_list_destroy(MsgListHandle hList)
{
P3PmsgList* p = toList(hList);
// Forget first, and act only on the caller that removed the entry -
// a double destroy then deletes once and refuses the second time.
if (!p || !MsgcoreHandleForget(hList, MsgcoreHandle_List)) return;
delete p;
}
MSGCORE_C_API int
msgcore_list_get_count(MsgListHandle hList)
{
if (!toList(hList)) return 0;
return (int)toList(hList)->GetCount();
}
MSGCORE_C_API void
msgcore_list_add_head_int(MsgListHandle hList, int value)
{
if (toList(hList)) toList(hList)->AddListHead(P3PmsgData(value));
}
MSGCORE_C_API void
msgcore_list_add_tail_int(MsgListHandle hList, int value)
{
if (toList(hList)) toList(hList)->AddListTail(P3PmsgData(value));
}
MSGCORE_C_API void
msgcore_list_add_head_double(MsgListHandle hList, double value)
{
if (toList(hList)) toList(hList)->AddListHead(P3PmsgData(value));
}
MSGCORE_C_API void
msgcore_list_add_tail_double(MsgListHandle hList, double value)
{
if (toList(hList)) toList(hList)->AddListTail(P3PmsgData(value));
}
MSGCORE_C_API void
msgcore_list_add_head_wstr(MsgListHandle hList, const wchar_t* lpszValue)
{
if (toList(hList) && lpszValue) toList(hList)->AddListHead(P3PmsgData(lpszValue));
}
MSGCORE_C_API void
msgcore_list_add_tail_wstr(MsgListHandle hList, const wchar_t* lpszValue)
{
if (toList(hList) && lpszValue) toList(hList)->AddListTail(P3PmsgData(lpszValue));
}
MSGCORE_C_API void
msgcore_list_drop_head(MsgListHandle hList)
{
if (toList(hList)) toList(hList)->DropHead();
}
MSGCORE_C_API void
msgcore_list_drop_tail(MsgListHandle hList)
{
if (toList(hList)) toList(hList)->DropTail();
}
MSGCORE_C_API void
msgcore_list_truncate(MsgListHandle hList)
{
if (toList(hList)) toList(hList)->Truncate();
}
// ---------------------------------------------------------------------------
// P3PmsgVect
// ---------------------------------------------------------------------------
MSGCORE_C_API MsgVectHandle
msgcore_vect_from_field(MsgFieldHandle hField)
{
if (!toField(hField)) return nullptr;
return MsgcoreHandleAdd(new P3PmsgVect(toField(hField)->r_Object()), MsgcoreHandle_Vect);
}
MSGCORE_C_API void
msgcore_vect_destroy(MsgVectHandle hVect)
{
P3PmsgVect* p = toVect(hVect);
// Forget first, and act only on the caller that removed the entry -
// a double destroy then deletes once and refuses the second time.
if (!p || !MsgcoreHandleForget(hVect, MsgcoreHandle_Vect)) return;
delete p;
}
MSGCORE_C_API int
msgcore_vect_is_data(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return 0;
return toVect(hVect)->IsData(nElem) ? 1 : 0;
}
MSGCORE_C_API int
msgcore_vect_is_field(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return 0;
return toVect(hVect)->IsField(nElem) ? 1 : 0;
}
MSGCORE_C_API int
msgcore_vect_is_list(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return 0;
return toVect(hVect)->IsList(nElem) ? 1 : 0;
}
MSGCORE_C_API int
msgcore_vect_is_vect(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return 0;
return toVect(hVect)->IsVect(nElem) ? 1 : 0;
}
MSGCORE_C_API int
msgcore_vect_get_int(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return 0;
return toVect(hVect)->r_data(nElem).c_int();
}
MSGCORE_C_API double
msgcore_vect_get_double(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return 0.0;
return toVect(hVect)->r_data(nElem).c_double();
}
MSGCORE_C_API const wchar_t*
msgcore_vect_get_wstr(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return nullptr;
return toVect(hVect)->r_data(nElem).c_wstr();
}
MSGCORE_C_API MsgFieldHandle
msgcore_vect_get_item(MsgVectHandle hVect, int nElem)
{
if (!toVect(hVect)) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toVect(hVect)->r_item(nElem);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API void
msgcore_vect_truncate(MsgVectHandle hVect)
{
if (toVect(hVect)) toVect(hVect)->Truncate();
}
// ---------------------------------------------------------------------------
// P3PmsgAttr
// ---------------------------------------------------------------------------
MSGCORE_C_API MsgAttrHandle
msgcore_attr_from_field(MsgFieldHandle hField, int bCreate)
{
if (!toField(hField)) return nullptr;
P3PmsgAttr* pAttr = new P3PmsgAttr(toField(hField));
if (bCreate && pAttr->IsEmpty())
pAttr->Create();
return MsgcoreHandleAdd(pAttr, MsgcoreHandle_Attr);
}
MSGCORE_C_API void
msgcore_attr_destroy(MsgAttrHandle hAttr)
{
P3PmsgAttr* p = toAttr(hAttr);
// Forget first, and act only on the caller that removed the entry -
// a double destroy then deletes once and refuses the second time.
if (!p || !MsgcoreHandleForget(hAttr, MsgcoreHandle_Attr)) return;
delete p;
}
MSGCORE_C_API int
msgcore_attr_get_count(MsgAttrHandle hAttr)
{
if (!toAttr(hAttr)) return 0;
return (int)toAttr(hAttr)->GetCount();
}
MSGCORE_C_API int
msgcore_attr_is_empty(MsgAttrHandle hAttr)
{
if (!toAttr(hAttr)) return 1;
return toAttr(hAttr)->IsEmpty() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_attr_exists(MsgAttrHandle hAttr, const wchar_t* lpszName)
{
if (!toAttr(hAttr) || !lpszName) return 0;
return toAttr(hAttr)->Exists(lpszName) ? 1 : 0;
}
MSGCORE_C_API MsgFieldHandle
msgcore_attr_select_item(MsgAttrHandle hAttr, const wchar_t* lpszName)
{
if (!toAttr(hAttr) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toAttr(hAttr)->SelectItem(lpszName);
} catch (...) {
delete pResult;
return nullptr;
}
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_attr_declare_int(MsgAttrHandle hAttr, const wchar_t* lpszName, int value, int bUpdate)
{
if (!toAttr(hAttr) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toAttr(hAttr)->DeclareItem(lpszName, P3PmsgData(value), bUpdate != 0);
} catch (...) { delete pResult; return nullptr; }
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_attr_declare_double(MsgAttrHandle hAttr, const wchar_t* lpszName, double value, int bUpdate)
{
if (!toAttr(hAttr) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toAttr(hAttr)->DeclareItem(lpszName, P3PmsgData(value), bUpdate != 0);
} catch (...) { delete pResult; return nullptr; }
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_attr_declare_wstr(MsgAttrHandle hAttr, const wchar_t* lpszName, const wchar_t* lpszValue, int bUpdate)
{
if (!toAttr(hAttr) || !lpszName || !lpszValue) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toAttr(hAttr)->DeclareItem(lpszName, P3PmsgData(lpszValue), bUpdate != 0);
} catch (...) { delete pResult; return nullptr; }
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API int
msgcore_attr_delete(MsgAttrHandle hAttr, const wchar_t* lpszName)
{
if (!toAttr(hAttr) || !lpszName) return 0;
return toAttr(hAttr)->Delete(lpszName) ? 1 : 0;
}
MSGCORE_C_API void
msgcore_attr_truncate(MsgAttrHandle hAttr)
{
if (toAttr(hAttr)) toAttr(hAttr)->Truncate();
}
// ---------------------------------------------------------------------------
// P3PmsgDesc
// ---------------------------------------------------------------------------
MSGCORE_C_API MsgDescHandle
msgcore_desc_from_field(MsgFieldHandle hField, int bCreate)
{
if (!toField(hField)) return nullptr;
P3PmsgDesc* pDesc = new P3PmsgDesc(toField(hField));
if (bCreate && pDesc->IsEmpty())
pDesc->Create();
return MsgcoreHandleAdd(pDesc, MsgcoreHandle_Desc);
}
MSGCORE_C_API void
msgcore_desc_destroy(MsgDescHandle hDesc)
{
P3PmsgDesc* p = toDesc(hDesc);
// Forget first, and act only on the caller that removed the entry -
// a double destroy then deletes once and refuses the second time.
if (!p || !MsgcoreHandleForget(hDesc, MsgcoreHandle_Desc)) return;
delete p;
}
MSGCORE_C_API int
msgcore_desc_get_count(MsgDescHandle hDesc)
{
if (!toDesc(hDesc)) return 0;
return (int)toDesc(hDesc)->GetCount();
}
MSGCORE_C_API int
msgcore_desc_is_empty(MsgDescHandle hDesc)
{
if (!toDesc(hDesc)) return 1;
return toDesc(hDesc)->IsEmpty() ? 1 : 0;
}
MSGCORE_C_API int
msgcore_desc_exists(MsgDescHandle hDesc, const wchar_t* lpszName)
{
if (!toDesc(hDesc) || !lpszName) return 0;
return toDesc(hDesc)->Exists(lpszName) ? 1 : 0;
}
MSGCORE_C_API MsgFieldHandle
msgcore_desc_select_item(MsgDescHandle hDesc, const wchar_t* lpszName)
{
if (!toDesc(hDesc) || !lpszName) return nullptr;
P3PmsgField* pResult = new P3PmsgField();
try {
*pResult = toDesc(hDesc)->SelectItem(lpszName);
} catch (...) { delete pResult; return nullptr; }
return MsgcoreHandleAdd(pResult, MsgcoreHandle_Field);
}
MSGCORE_C_API MsgFieldHandle
msgcore_desc_declare_int(MsgDescHandle hDesc, const wchar_t* lpszName, int value, int bUpdate)