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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
#![allow(clippy::len_zero)]
#![deny(warnings)]
#![deny(missing_docs)]
#![allow(clippy::needless_late_init)]

/*!
This is the documentation for `savefile-abi`

# Welcome to savefile-abi!

Savefile-abi is a crate that is primarily meant to help building binary plugins using Rust.

Note! This is a work-in-progress.

# Example

Let's say we have a crate that defines this trait for adding u32s:

*InterfaceCrate*
```
extern crate savefile_derive;
use savefile_derive::savefile_abi_exportable;

#[savefile_abi_exportable(version=0)]
pub trait AdderInterface {
    fn add(&self, x: u32, y: u32) -> u32;
}

```

Now, we want to implement addition in a different crate, compile it to a shared library
(.dll or .so), and use it in the first crate (or some other crate):

*ImplementationCrate*
```
 # extern crate savefile_derive;
 # use savefile_derive::{savefile_abi_exportable};
 # #[savefile_abi_exportable(version=0)]
 # pub trait AdderInterface {
 #   fn add(&self, x: u32, y: u32) -> u32;
 # }
 #
use savefile_derive::{savefile_abi_export};
#[derive(Default)]
struct MyAdder { }

impl AdderInterface for MyAdder {
    fn add(&self, x: u32, y: u32) -> u32 {
        x + y
    }
}

/// Export this implementation as the default-implementation for
/// the interface 'AdderInterface', for the current library.
savefile_abi_export!(MyAdder, AdderInterface);

```

We add the following to Cargo.toml in our implementation crate:

```toml
[lib]
crate-type = ["cdylib"]
```

Now, in our application, we add a dependency to *InterfaceCrate*, but not
to *ImplementationCrate*.

We then load the implementation dynamically at runtime:

*ApplicationCrate*

```rust,no_run
 # extern crate savefile_derive;
 # mod adder_interface {
 #   use savefile_derive::savefile_abi_exportable;
 #   #[savefile_abi_exportable(version=0)]
 #   pub trait AdderInterface {
 #     fn add(&self, x: u32, y: u32) -> u32;
 #   }
 # }
 #
use adder_interface::AdderInterface;
use savefile_abi::AbiConnection;


// Load the implementation of `dyn AdderInterface` that was published
// using the `savefile_abi_export!` above.
let connection = AbiConnection::<dyn AdderInterface>
        ::load_shared_library("ImplementationCrate.so").unwrap();

// The type `AbiConnection::<dyn AdderInterface>` implements
// the `AdderInterface`-trait, so we can use it to call its methods.
assert_eq!(connection.add(1, 2), 3);

```

# More advanced examples

Interface containing closure arguments:
```
 # extern crate savefile_derive;
 # use savefile_derive::savefile_abi_exportable;
#[savefile_abi_exportable(version=0)]
pub trait CallMeBack {
    fn call_me(&self, x: &dyn Fn(u32) -> u32) -> u32;
    fn call_me_mut(&self, x: &mut dyn FnMut(u32) -> u32) -> u32;
}

```

Interface containing more complex types:
```
 # extern crate savefile_derive;
 # use savefile_derive::savefile_abi_exportable;
 # use std::collections::{HashMap, BinaryHeap};
#[savefile_abi_exportable(version=0)]
pub trait Processor {
    fn process(&self, x: &HashMap<String,String>, parameters: f32) -> BinaryHeap<u32>;
}

```

Interface containing user defined types:
```
 # extern crate savefile_derive;
 # use savefile_derive::{Savefile,savefile_abi_exportable};
 # use std::collections::{HashMap, BinaryHeap};

#[derive(Savefile)]
pub struct MyCustomType {
    pub name: String,
    pub age: u8,
    pub length: f32,
}

#[savefile_abi_exportable(version=0)]
pub trait Processor {
    fn insert(&self, x: &MyCustomType) -> Result<u32, String>;
}

```

# Versioning

Let's say the last example from the previous chapter needed to be evolved.
The type now needs a 'city' field.

We can add this while retaining compatibility with clients expecting the old API:

```
extern crate savefile_derive;

 # use savefile::prelude::SavefileError;
 # use savefile_derive::{Savefile,savefile_abi_exportable};
 # use savefile_abi::verify_compatiblity;
 # use std::collections::{HashMap, BinaryHeap};

#[derive(Savefile)]
pub struct MyCustomType {
    pub name: String,
    pub age: u8,
    pub length: f32,
    #[savefile_versions="1.."]
    pub city: String,
}

#[savefile_abi_exportable(version=1)]
pub trait Processor {
    fn insert(&self, x: &MyCustomType) -> Result<u32, String>;
}

#[cfg(test)]
{
    #[test]
    pub fn test_backward_compatibility() {
       // Automatically verify backward compatibility isn't broken.
       // Schemas for each version are stored in directory 'schemas',
       // and consulted on next run to ensure no change.
       // You should check the schemas in to source control.
       // If check fails for an unreleased version, just remove the schema file from
       // within 'schemas' directory.
       verify_compatiblity::<dyn Processor>("schemas").unwrap()
    }
}


```

Older clients, not aware of the 'city' field, can still call newer implementations. The 'city'
field will receive an empty string (Default::default()). Newer clients, calling older implementations,
will simply, automatically, omit the 'city' field.


# Background

Savefile-abi is a crate that is primarily meant to help building binary plugins using Rust.

The primary usecase is that a binary rust program is to be shipped to some customer,
who should then be able to load various binary modules into the program at runtime.
Savefile-abi defines ABI-stable rust-to-rust FFI for calling between a program and
a runtime-loaded shared library.

For now, both the main program and the plugins need to be written in rust. They can,
however, be written using different versions of the rust compiler, and the API may
be allowed to evolve. That is, data structures can be modified, and methods can be added
(or removed).

The reason savefile-abi is needed, is that rust does not have a stable 'ABI'. This means that
if shared libraries are built using rust, all libraries must be compiled by the same version of
rust, using the exact same source code. This means that rust cannot, 'out of the box', support
a binary plugin system, without something like savefile-abi. This restriction may be lifted
in the future, which would make this crate (savefile-abi) mostly redundant.

Savefile-abi does not solve the general 'stable ABI'-problem. Rather, it defines a limited
set of features, which allows useful calls between shared libraries, without allowing any
and all rust construct.

# Why another stable ABI-crate for Rust?

There are other crates also providing ABI-stability. Savefile-abi has the following properties:

 * It is able to completely specify the protocol used over the FFI-boundary. I.e, it can
isolate two shared libraries completely, making minimal assumptions about data type
memory layouts.

 * When it cannot prove that memory layouts are identical, it falls back to (fast) serialization.
This has a performance penalty, and may require heap allocation.

 * It tries to require a minimum of configuration needed by the user, while still being safe.

 * It supports versioning of data structures (with a performance penalty).

 * It supports trait objects as arguments, including FnMut() and Fn().

 * Boxed trait objects, including Fn-traits, can be transferred across FFI-boundaries, passing
   ownership, while still not invoking UB if the object is dropped on the other side of the
   FFI-boundary.

 * It requires enums to be `#[repr(uX)]` in order to pass them by reference. Other enums
will still work correctly, but will be serialized under the hood at a performance penalty.

 * It places severe restrictions on types of arguments, since they must be serializable
using the Savefile-crate for serialization. Basically, arguments must be 'simple', in that
they must own all their contents, and free of cycles. I.e, the type of the arguments must
have lifetime `&'static`. Note, arguments may still be references, and the contents of the
argument types may include Box, Vec etc, so this does not mean that only primitive types are
supporte or anything like that.

Arguments cannot be mutable, since if serialization is needed, it would be impractical to detect and
handle updates to arguments made by callee. This said, arguments can still have types such as
HashMap, IndexMap, Vec, String and custom defined structs or enums.

# How it all works

The basic principle is that savefile-abi makes sure to send function parameters in a way
that is certain to be understood by the code on the other end of the FFI-boundary.
It analyses if memory layouts of reference-parameters are the same on both sides of the
FFI-boundary, and if they are, the references are simply copied. In all other cases, including
all non-reference parameters, the data is simply serialized and sent as a binary buffer.

The callee cannot rely on any particular lifetimes of arguments, since if the arguments
were serialized, the arguments the callee sees will only have a lifetime of a single call,
regardless of the original lifetime. Savefile-abi inspects all lifetimes and ensures
that reference parameters don't have non-default lifetimes. Argument types must have static
lifetimes (otherwise they can't be serialized). The only exception is that the argument
can be reference types, but the type referenced must itself be `&'static`.

# About Safety

Savefile-Abi uses copious amounts of unsafe code. It has a test suite, and the
test suite passes with miri.

One thing to be aware of is that, at present, the AbiConnection::load_shared_library-method
is not marked as unsafe. However, if the .so-file given as argument is corrupt, using this
method can cause any amount of UB. Thus, it could be argued that it should be marked unsafe.

However, the same is true for _any_ shared library used by a rust program, including the
system C-library. It is also true that rust programs rely on the rust
compiler being implemented correctly. Thus, it has been
judged that the issue of corrupt binary files is beyond the scope of safety for Savefile-Abi.

As long as the shared library is a real Savefile-Abi shared library, it should be sound to use,
even if it contains code that is completely incompatible. This will be detected at runtime,
and either AbiConnection::load_shared_library will panic, or any calls made after will panic.

# About Vec and String references

Savefile-Abi allows passing references containing Vec and/or String across the FFI-boundary.
This is not guaranteed to be sound. However, Savefile-Abi uses heuristics to determine
the actual memory layout of both Vec and String, and verifies that the two libraries agree
on the layout of Vec. If they do not, the data is serialized instead. Also, since
parameters can never be mutable in Savefile-abi (except for closures), we know
the callee is not going to be freeing something allocated by the caller. Parameters
called by value are always serialized.


*/

extern crate savefile;
extern crate savefile_derive;
use savefile::{
    diff_schema, load_file_noschema, load_noschema, save_file_noschema, AbiMethodInfo, AbiTraitDefinition, Deserialize,
    Deserializer, LittleEndian, SavefileError, Schema, Serializer, CURRENT_SAVEFILE_LIB_VERSION,
};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::io::{Cursor, Read, Write};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::panic::catch_unwind;
use std::path::Path;
use std::ptr::null;
use std::sync::{Mutex, MutexGuard};
use std::{ptr, slice};
use std::any::TypeId;

use byteorder::ReadBytesExt;
use libloading::{Library, Symbol};

/// This trait is meant to be exported for a 'dyn SomeTrait'.
/// It can be automatically implemented by using the
/// macro `#[savefile_abi_exportable(version=0)]` on
/// a trait that is to be exportable.
///
/// NOTE!
/// If trait `MyExampleTrait` is to be exportable, the trait `AbiExportable` must
/// be implemented for `dyn MyExampleTrait`.
///
/// # Safety
/// The implementor must:
///  * Make sure that the ABI_ENTRY function implements all parts of AbiProtocol
///    in a correct manner
///  * Has a correct 'get_definition' function, which must return a AbiTraitDefinition instance
///    that is truthful.
///  * Implement 'call' correctly
pub unsafe trait AbiExportable {
    /// A function which implements the savefile-abi contract.
    const ABI_ENTRY: unsafe extern "C" fn(AbiProtocol);
    /// Must return a truthful description about all the methods in the
    /// `dyn trait` that AbiExportable is implemented for (i.e, `Self`).
    fn get_definition(version: u32) -> AbiTraitDefinition;
    /// Must return the current latest version of the interface. I.e,
    /// the version which Self represents. Of course, there may be future higher versions,
    /// but none such are known by the code.
    fn get_latest_version() -> u32;
    /// Implement method calling. Must deserialize data from 'data', and
    /// must return an outcome (result) by calling `receiver`.
    ///
    /// The return value is either Ok, or an error if the method to be called could
    /// not be found or for some reason not called (mismatched actual ABI, for example).
    ///
    /// `receiver` must be given 'abi_result' as its 'result_receiver' parameter, so that
    /// the receiver may set the result. The receiver executes at the caller-side of the ABI-divide,
    /// but it receives as first argument an RawAbiCallResult that has been created by the callee.
    fn call(
        trait_object: TraitObject,
        method_number: u16,
        effective_version: u32,
        compatibility_mask: u64,
        data: &[u8],
        abi_result: *mut (),
        receiver: unsafe extern "C" fn(
            outcome: *const RawAbiCallResult,
            result_receiver: *mut (), /* actual type: Result<T,SaveFileError>>*/
        ),
    ) -> Result<(), SavefileError>;
}

/// Trait that is to be implemented for the implementation of a trait whose `dyn trait` type
/// implements AbiExportable.
///
/// If `MyExampleTrait` is an ABI-exportable trait, and `MyExampleImplementation` is an
/// implementation of `MyExampleTrait`, then:
///  * The `AbiInterface` associated type must be `dyn MyExampleTrait`
///  * `AbiExportableImplementation` must be implemented for `MyExampleImplementation`
///
/// # Safety
/// The following must be fulfilled:
/// * ABI_ENTRY must be a valid function, implementing the AbiProtocol-protocol.
/// * AbiInterface must be 'dyn SomeTrait', where 'SomeTrait' is an exported trait.
///
pub unsafe trait AbiExportableImplementation {
    /// An entry point which implements the AbiProtocol protocol
    const ABI_ENTRY: unsafe extern "C" fn(AbiProtocol);
    /// The type 'dyn SomeTrait'.
    type AbiInterface: ?Sized + AbiExportable;
    /// A method which must be able to return a default-implementation of `dyn SomeTrait`.
    /// I.e, the returned box is a boxed dyn trait, not 'Self' (the actual implementation type).
    fn new() -> Box<Self::AbiInterface>;
}

/// Given a boxed trait object pointer, expressed as a data ptr and a vtable pointer,
/// of type T (which must be a `dyn SomeTrait` type), drop the boxed trait object.
/// I.e, `trait_object` is a type erased instance of Box<T> , where T is for example
/// `dyn MyTrait`.
/// # Safety
/// The given `trait_object` must be a boxed trait object.
unsafe fn destroy_trait_obj<T: AbiExportable + ?Sized>(trait_object: TraitObject) {
    let mut raw_ptr: MaybeUninit<*mut T> = MaybeUninit::uninit();
    ptr::copy(
        &trait_object as *const TraitObject as *const MaybeUninit<*mut T>,
        &mut raw_ptr as *mut MaybeUninit<*mut T>,
        1,
    );

    let _ = Box::from_raw(raw_ptr.assume_init());
}

/// Call the given method, on the trait object.
///
/// trait_object - Type erased version of Box<dyn SomeTrait>
/// method_number - The method to be called. This is an ordinal number, with 0 being the first method in definition order in the trait.
/// effective_version - The version number in the serialized format, negotiated previously.
/// compatibility_mask - For each method, one bit which says if the argument can be sent as just a reference, without having to use serialization to do a deep copy
/// data - All the arguments, in a slice
/// abi_result - Pointer to a place which will receiver the return value. This points to a Result<T, SaveFileError>, but since that type may have a different layout in callee and caller, we can't just use that type.
/// receiver - A function which will receiver the actual serialized return value, and an error code.
///
/// If the callee panics, this will be encoded into the RawAbiCallResult given to the `receiver`. The `reveiver` will always be called with the return value/return status.
///
/// # Safety
/// Every detail of all the arguments must be correct. Any little error is overwhelmingly likely to cause
/// a segfault or worse.
unsafe fn call_trait_obj<T: AbiExportable + ?Sized>(
    trait_object: TraitObject,
    method_number: u16,
    effective_version: u32,
    compatibility_mask: u64,
    data: &[u8],
    abi_result: *mut (),
    receiver: unsafe extern "C" fn(
        outcome: *const RawAbiCallResult,
        result_receiver: *mut (), /*Result<T,SaveFileError>>*/
    ),
) -> Result<(), SavefileError> {
    <T>::call(
        trait_object,
        method_number,
        effective_version,
        compatibility_mask,
        data,
        abi_result,
        receiver,
    )
}

/// Describes a method in a trait
#[derive(Debug)]
pub struct AbiConnectionMethod {
    /// The name of the method
    pub method_name: String,
    /// This is mostly for debugging, it's not actually used
    pub caller_info: AbiMethodInfo,
    /// The ordinal number of this method at the callee, or None if callee doesn't have
    /// method.
    pub callee_method_number: Option<u16>,
    /// For each of the up to 64 different arguments,
    /// a bit value of 1 means layout is identical, and in such a way that
    /// references can be just binary copied (owned arguments must still be cloned, and
    /// we can just as well do that using serialization, it will be approx as fast).
    pub compatibility_mask: u64,
}

/// Type erased carrier of a dyn trait fat pointer
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct TraitObject {
    ptr: *const (),
    vtable: *const (),
}

unsafe impl Sync for TraitObject {}
unsafe impl Send for TraitObject {}

impl TraitObject {
    /// Returns a TraitObject with two null ptrs. This value must never be used,
    /// but can serve as a default before the real value is written.
    pub fn zero() -> TraitObject {
        TraitObject {
            ptr: null(),
            vtable: null(),
        }
    }

    /// Interpret this TraitObject as *mut T.
    /// *mut T *MUST* be a fat pointer of the same type as was used to create this TraitObject
    /// instance.
    pub fn as_mut_ptr<T: ?Sized>(self) -> *mut T {
        assert_eq!(
            std::mem::size_of::<*mut T>(),
            16,
            "TraitObject must only be used with dyn trait, not any other kind of trait"
        );

        let mut target: MaybeUninit<*mut T> = MaybeUninit::zeroed();
        unsafe {
            ptr::copy(
                &self as *const TraitObject as *const MaybeUninit<*mut T>,
                &mut target as *mut MaybeUninit<*mut T>,
                1,
            );
            target.assume_init()
        }
    }
    /// Interpret this TraitObject as *const T.
    /// *const T *MUST* be a fat pointer of the same type as was used to create this TraitObject
    /// instance.
    pub fn as_const_ptr<T: ?Sized>(self) -> *const T {
        assert_eq!(
            std::mem::size_of::<*const T>(),
            16,
            "TraitObject must only be used with dyn trait, not any other kind of trait"
        );

        let mut target: MaybeUninit<*const T> = MaybeUninit::zeroed();
        unsafe {
            ptr::copy(
                &self as *const TraitObject as *const MaybeUninit<*const T>,
                &mut target as *mut MaybeUninit<*const T>,
                1,
            );
            target.assume_init()
        }
    }
    /// Convert the given fat pointer to a TraitObject instance.
    pub fn new_from_ptr<T: ?Sized>(raw: *const T) -> TraitObject {
        assert_eq!(
            std::mem::size_of::<*const T>(),
            16,
            "TraitObject::new_from_ptr() must only be used with dyn trait, not any other kind of trait"
        );
        assert_eq!(std::mem::size_of::<TraitObject>(), 16);

        let mut trait_object = TraitObject::zero();

        unsafe {
            ptr::copy(
                &raw as *const *const T,
                &mut trait_object as *mut TraitObject as *mut *const T,
                1,
            )
        };
        trait_object
    }
    /// Note: This only works for boxed dyn Trait.
    /// T must be `dyn SomeTrait`.
    pub fn new<T: ?Sized>(input: Box<T>) -> TraitObject {
        let raw = Box::into_raw(input);
        assert_eq!(
            std::mem::size_of::<*mut T>(),
            16,
            "TraitObject::new() must only be used with Boxed dyn trait, not any other kind of Box"
        );
        assert_eq!(std::mem::size_of::<TraitObject>(), 16);

        let mut trait_object = TraitObject::zero();

        unsafe {
            ptr::copy(
                &raw as *const *mut T,
                &mut trait_object as *mut TraitObject as *mut *mut T,
                1,
            )
        };
        trait_object
    }
}

/// Information about an entry point and the trait
/// it corresponds to.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct AbiConnectionTemplate {
    /// The negotiated effective serialization version.
    /// See 'savefile' crate for more information about version handling.
    #[doc(hidden)]
    pub effective_version: u32,
    /// All the methods of the trait.
    #[doc(hidden)]
    pub methods: &'static [AbiConnectionMethod],
    /// The entry point which will actually be used for calls. Typically,
    /// this entry point points into a different shared object/dll compared to
    /// the caller.
    #[doc(hidden)]
    pub entry: unsafe extern "C" fn(flag: AbiProtocol),
}

/// Information about an ABI-connection. I.e,
/// a caller and callee. The caller is in one
/// particular shared object, the callee in another.
/// Any modifiable state is stored in this object,
/// and the actual callee is stateless (allowing multiple
/// different incoming 'connections').
///
/// The fields are public, so that they can be easily written by the
/// proc macros. But the user should never interact with them directly,
/// so they are marked as doc(hidden).
#[repr(C)]
#[derive(Debug)]
pub struct AbiConnection<T: ?Sized> {
    /// Cachable information about the interface
    #[doc(hidden)]
    pub template: AbiConnectionTemplate,
    /// Information on whether we *own* the trait object.
    /// If we do, we must arrange for the foreign library code to drop it when we're done.
    /// Otherwise, we must not drop it.
    #[doc(hidden)]
    pub owning: Owning,
    /// The concrete trait object for this instance.
    /// I.e, type erased trait object in the foreign library
    #[doc(hidden)]
    pub trait_object: TraitObject,
    /// Phantom, to make this valid rust (since we don't otherwise carry a T).
    #[doc(hidden)]
    pub phantom: PhantomData<*const T>,
}
unsafe impl<T: ?Sized> Sync for AbiConnection<T> {}
unsafe impl<T: ?Sized> Send for AbiConnection<T> {}

/// A trait object together with its entry point
#[repr(C)]
#[derive(Debug)]
pub struct PackagedTraitObject {
    /// Type erased trait object for an ABI-exported trait
    pub trait_object: TraitObject,
    /// The low level entry point
    pub entry: unsafe extern "C" fn(flag: AbiProtocol),
}

impl PackagedTraitObject {
    /// Create a PackagedTraitObject from a `Box<T>`    . T must be a trait object.
    /// T must implement AbiExportable, which means it has an ::ABI_ENTRY associated
    /// type that gives the entry point.
    pub fn new<T: AbiExportable + ?Sized>(boxed: Box<T>) -> PackagedTraitObject {
        let trait_object = TraitObject::new(boxed);
        let entry = T::ABI_ENTRY;
        PackagedTraitObject { trait_object, entry }
    }

    /// Create a PackagedTraitObject from a &T. T must be a trait object.
    /// T must implement AbiExportable, which means it has an ::ABI_ENTRY associated
    /// type that gives the entry point.
    /// Note, we use `*const T` here even for mutable cases, but it doesn't matter
    /// since it's never used, it's just cast to other stuff and then finally
    /// back to the right type.
    pub fn new_from_ptr<T>(r: *const T) -> PackagedTraitObject
    where
        T: AbiExportable + ?Sized,
    {
        assert_eq!(std::mem::size_of::<*const T>(), 16);
        let trait_object = TraitObject::new_from_ptr(r);
        let entry = T::ABI_ENTRY;
        PackagedTraitObject { trait_object, entry }
    }

    /// 'Serialize' this object. I.e, write it to a binary buffer, so that we can send it
    /// to a foreign library.
    pub fn serialize(self, serializer: &mut Serializer<impl Write>) -> Result<(), SavefileError> {
        serializer.write_ptr(self.trait_object.ptr)?;
        serializer.write_ptr(self.trait_object.vtable)?;
        serializer.write_ptr(self.entry as *const ())?;

        Ok(())
    }
    /// 'Deserialize' this object. I.e, read it from a binary buffer, so that we can receive it
    /// from a foreign library.
    ///
    /// # Safety
    /// The data available to read from Deserializer must be correct, and contain
    /// a valid serialized PackagedTraitObject.
    pub unsafe fn deserialize(
        deserializer: &mut Deserializer<impl Read>,
    ) -> Result<PackagedTraitObject, SavefileError> {
        let mut trait_object = TraitObject::zero();
        trait_object.ptr = deserializer.read_ptr()? as *mut ();
        trait_object.vtable = deserializer.read_ptr()? as *mut ();
        let entry = deserializer.read_ptr()? as *mut ();
        assert_eq!(std::mem::size_of::<unsafe extern "C" fn(flag: AbiProtocol)>(), 8);
        Ok(PackagedTraitObject {
            trait_object,
            entry: unsafe { std::mem::transmute(entry) },
        })
    }
}

impl<T: ?Sized> Drop for AbiConnection<T> {
    fn drop(&mut self) {
        match &self.owning {
            Owning::Owned => unsafe {
                (self.template.entry)(AbiProtocol::DropInstance {
                    trait_object: self.trait_object,
                });
            },
            Owning::NotOwned => {}
        }
    }
}

/// Helper struct carrying a pointer and length to an utf8 message.
/// We use this instead of &str, to guard against the hypothetical possibility
/// that the layout of &str would ever change.
#[repr(C)]
pub struct AbiErrorMsg {
    /// Pointer to utf8 error message
    pub error_msg_utf8: *const u8,
    /// The length of the message, in bytes
    pub len: usize,
}
impl AbiErrorMsg {
    /// Attempt to convert the given data to a String.
    /// Any invalid UTF8-chars are replaced.
    pub fn convert_to_string(&self) -> String {
        if self.len == 0 {
            return "".to_string();
        }
        let data = unsafe { slice::from_raw_parts(self.error_msg_utf8, self.len) };
        String::from_utf8_lossy(data).into()
    }
}

/// The result of calling a method in a foreign library.
#[repr(C, u8)]
pub enum RawAbiCallResult {
    /// Successful operation
    Success {
        /// A pointer to the return value, serialized
        data: *const u8,
        /// The size of the serialized return value, in bytes
        len: usize,
    },
    /// The method that was called, panicked. Since the way panic unwinding in Rust
    /// could change between rust-versions, we can't allow any panics to unwind
    /// across the boundary between two different libraries.
    Panic(AbiErrorMsg),
    /// There was an error in the ABI-framework. This will happen if code tries
    /// to call a method that is not actually available on the target, or if method
    /// signatures change in non ABI-compatible ways.
    AbiError(AbiErrorMsg),
}

/// This struct carries all information between different libraries.
/// I.e, it is the sole carrier of information accross an FFI-boundary.
#[repr(C, u8)]
pub enum AbiProtocol {
    /// Call a method on a trait object.
    RegularCall {
        /// Type-erased actual trait object. This is the 16 bytes o trait fat pointer.
        trait_object: TraitObject,
        /// For every argument, a bit '1' if said argument is a reference that can just
        /// be binary copied, as a pointer
        compatibility_mask: u64,
        /// Data for parameters, possibly serialized
        data: *const u8,
        /// Length of parameters-data
        data_length: usize,
        /// Instance of type `AbiCallResult<T>`, to which the return-value callback will
        /// write deserialized results or panic-message.
        abi_result: *mut (),
        /// Callback which will be called by callee in order to supply the return value
        /// (without having to allocate heap-memory)
        receiver: unsafe extern "C" fn(
            outcome: *const RawAbiCallResult,
            result_receiver: *mut (), /*Result<T,SaveFileError>>*/
        ),
        /// The negotiated protocol version
        effective_version: u32,
        /// The method to call. This is the method number using the
        /// numbering of the callee.
        method_number: u16,
    },
    /// Get callee version
    InterrogateVersion {
        /// The version of the callee savefile schema. This can only change if the savefile library
        /// is upgraded.
        schema_version_receiver: *mut u16,
        /// The version of the data schema, on the callee.
        abi_version_receiver: *mut u32,
    },
    /// Get schema
    InterrogateMethods {
        /// The version of the schema that the caller expects.
        schema_version_required: u16,
        /// The schema version that the caller expects the callee to communicate using.
        /// I.e, if callee has a later version of the 'savefile' library, this can be used
        /// to arrange for it to speak an older dialect. In theory, but savefile is still
        /// involving and there is always a risk that ABI-breaks will be necessary.
        callee_schema_version_interrogated: u32,
        /// A pointer pointing at the location that that caller will expect the return value to be written.
        /// Note, callee does not actually write to this, it just calls `callback`, which allows caller
        /// to write to the result_receiver. The field is still needed here, since the `callback` is a bare function,
        /// and cannot capture any data.
        result_receiver: *mut AbiTraitDefinition,
        /// Called by callee to convey information back to caller.
        /// `receiver` is place the caller will want to write the result.
        callback: unsafe extern "C" fn(
            receiver: *mut AbiTraitDefinition,
            callee_schema_version: u16,
            data: *const u8,
            len: usize,
        ),
    },
    /// Create a new trait object.
    CreateInstance {
        /// Pointer which will receive the fat pointer to the dyn trait object, allocated on heap using Box.
        trait_object_receiver: *mut TraitObject,
        /// Opaque pointer to callers representation of error (String)
        error_receiver: *mut (), /*String*/
        /// Called by callee if instance creation fails (by panic)
        error_callback: unsafe extern "C" fn(error_receiver: *mut (), error: *const AbiErrorMsg),
    },
    /// Drop a trait object.
    DropInstance {
        /// dyn trait fat pointer
        trait_object: TraitObject,
    },
}

/// Parse the given RawAbiCallResult. If it concerns a success, then deserialize a return value using the given closure.
pub fn parse_return_value_impl<T>(
    outcome: &RawAbiCallResult,
    deserialize_action: impl FnOnce(&mut Deserializer<Cursor<&[u8]>>) -> Result<T, SavefileError>,
) -> Result<T, SavefileError> {
    match outcome {
        RawAbiCallResult::Success { data, len } => {
            let data = unsafe { std::slice::from_raw_parts(*data, *len) };
            let mut reader = Cursor::new(data);
            let file_version = reader.read_u32::<LittleEndian>()?;
            let mut deserializer = Deserializer {
                reader: &mut reader,
                file_version,
                ephemeral_state: HashMap::new(),
            };
            deserialize_action(&mut deserializer)
            //T::deserialize(&mut deserializer)
        }
        RawAbiCallResult::Panic(AbiErrorMsg { error_msg_utf8, len }) => {
            let errdata = unsafe { std::slice::from_raw_parts(*error_msg_utf8, *len) };
            Err(SavefileError::CalleePanic {
                msg: String::from_utf8_lossy(errdata).into(),
            })
        }
        RawAbiCallResult::AbiError(AbiErrorMsg { error_msg_utf8, len }) => {
            let errdata = unsafe { std::slice::from_raw_parts(*error_msg_utf8, *len) };
            Err(SavefileError::GeneralError {
                msg: String::from_utf8_lossy(errdata).into(),
            })
        }
    }
}

/// Parse an RawAbiCallResult instance into a `Result<Box<dyn T>, SavefileError>` .
/// This is used on the caller side, and the type T will always be statically known.
/// TODO: There's some duplicated code here, compare parse_return_value
pub fn parse_return_boxed_trait<T:'static>(outcome: &RawAbiCallResult) -> Result<Box<AbiConnection<T>>, SavefileError>
where
    T: AbiExportable + ?Sized,
{
    parse_return_value_impl(outcome, |deserializer| {
        let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? };
        unsafe {
            Ok(Box::new(AbiConnection::<T>::from_raw_packaged(
                packaged,
                Owning::Owned,
            )?))
        }
    })
}
/// We never unload libraries which have been dynamically loaded, because of all the problems with
/// doing so.
static LIBRARY_CACHE: Mutex<Option<HashMap<String /*filename*/, Library>>> = Mutex::new(None);
static ENTRY_CACHE: Mutex<
    Option<HashMap<(String /*filename*/, String /*trait name*/), unsafe extern "C" fn(flag: AbiProtocol)>>,
> = Mutex::new(None);

static ABI_CONNECTION_TEMPLATES: Mutex<
    Option<HashMap<(TypeId,unsafe extern "C" fn(flag: AbiProtocol)), AbiConnectionTemplate>>,
> = Mutex::new(None);

struct Guard<'a, K: Hash + Eq, V> {
    guard: MutexGuard<'a, Option<HashMap<K, V>>>,
}

impl<K: Hash + Eq, V> std::ops::Deref for Guard<'_, K, V> {
    type Target = HashMap<K, V>;
    fn deref(&self) -> &HashMap<K, V> {
        self.guard.as_ref().unwrap()
    }
}

impl<K: Hash + Eq, V> std::ops::DerefMut for Guard<'_, K, V> {
    fn deref_mut(&mut self) -> &mut HashMap<K, V> {
        &mut *self.guard.as_mut().unwrap()
    }
}

// Avoid taking a dependency on OnceCell or lazy_static or something, just for this little thing
impl<'a, K: Hash + Eq, V> Guard<'a, K, V> {
    pub fn lock(map: &'a Mutex<Option<HashMap<K /*filename*/, V>>>) -> Guard<'a, K, V> {
        let mut guard = map.lock().unwrap();
        if guard.is_none() {
            *guard = Some(HashMap::new());
        }
        Guard { guard }
    }
}

/// Helper to determine if something is owned, or not
#[derive(Debug, Clone, Copy)]
pub enum Owning {
    /// The object is owned
    Owned,
    /// The object is not owned
    NotOwned,
}

const FLEX_BUFFER_SIZE: usize = 64;
/// Stack allocated buffer that overflows on heap if needed
#[doc(hidden)]
pub enum FlexBuffer {
    /// Allocated on stack>
    Stack {
        /// The current write position. This is the same as
        /// the logical size of the buffer, since we can only write at the end.
        position: usize,
        /// The data backing this buffer, on the stack
        data: MaybeUninit<[u8; FLEX_BUFFER_SIZE]>,
    },
    /// Allocated on heap
    Spill(Vec<u8>),
}
impl Write for FlexBuffer {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        match self {
            FlexBuffer::Stack { position, data } => {
                if *position + buf.len() <= FLEX_BUFFER_SIZE {
                    let rawdata = data as *mut MaybeUninit<_> as *mut u8;
                    unsafe { ptr::copy(buf.as_ptr(), rawdata.add(*position), buf.len()) };
                    *position += buf.len();
                } else {
                    let mut spill = Vec::with_capacity(2 * FLEX_BUFFER_SIZE + buf.len());
                    let rawdata = data as *mut MaybeUninit<_> as *mut u8;
                    let dataslice = unsafe { slice::from_raw_parts(rawdata, *position) };
                    spill.extend(dataslice);
                    spill.extend(buf);
                    *self = FlexBuffer::Spill(spill);
                }
            }
            FlexBuffer::Spill(v) => v.extend(buf),
        }
        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Raw entry point for receiving return values from other shared libraries
#[doc(hidden)]
pub unsafe extern "C" fn abi_result_receiver<T: Deserialize>(
    outcome: *const RawAbiCallResult,
    result_receiver: *mut (),
) {
    let outcome = unsafe { &*outcome };
    let result_receiver = unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit<Result<T, SavefileError>>) };
    result_receiver.write(parse_return_value_impl(outcome, |deserializer| {
        T::deserialize(deserializer)
    }));
}

/// Raw entry point for receiving return values from other shared libraries
#[doc(hidden)]
pub unsafe extern "C" fn abi_boxed_trait_receiver<T:'static>(outcome: *const RawAbiCallResult, result_receiver: *mut ())
where
    T: AbiExportable + ?Sized,
{
    let outcome = unsafe { &*outcome };
    let result_receiver =
        unsafe { &mut *(result_receiver as *mut std::mem::MaybeUninit<Result<Box<AbiConnection<T>>, SavefileError>>) };
    result_receiver.write(parse_return_value_impl(outcome, |deserializer| {
        let packaged = unsafe { PackagedTraitObject::deserialize(deserializer)? };
        unsafe {
            Ok(Box::new(AbiConnection::<T>::from_raw_packaged(
                packaged,
                Owning::Owned,
            )?))
        }
    }));
}

// Flex buffer is only used internally, and we don't need to provide
// any of the regular convenience.
#[allow(clippy::new_without_default)]
#[allow(clippy::len_without_is_empty)]
impl FlexBuffer {
    /// Create a new buffer instance, allocated from the stack
    pub fn new() -> FlexBuffer {
        FlexBuffer::Stack {
            position: 0,
            data: MaybeUninit::uninit(),
        }
    }
    /// Get a pointer to the buffer contents
    pub fn as_ptr(&self) -> *const u8 {
        match self {
            FlexBuffer::Stack { data, .. } => data as *const MaybeUninit<_> as *const u8,
            FlexBuffer::Spill(v) => v.as_ptr(),
        }
    }
    /// Get the number of bytes in the buffer
    pub fn len(&self) -> usize {
        match self {
            FlexBuffer::Stack { position, .. } => *position,
            FlexBuffer::Spill(v) => v.len(),
        }
    }
}

/// Arguments are layout compatible if their native versions are layout_compatible,
/// or if they are traits and the effective version of the traits are compatible.
/// For traits, the actual fat pointer is always compatible, so can always be used.
/// The trait-objects themselves can never be serialized, so they can only be used as references.
fn arg_layout_compatible(
    a_native: &Schema,
    b_native: &Schema,
    a_effective: &Schema,
    b_effective: &Schema,
    effective_version: u32,
) -> bool {
    match (a_native, b_native) {
        (Schema::FnClosure(a1, _a2), Schema::FnClosure(b1, _b2)) => {
            let (Schema::FnClosure(effective_a1, effective_a2), Schema::FnClosure(effective_b1, effective_b2)) =
                (a_effective, b_effective)
            else {
                return false;
            };

            a1 == b1
                && a1 == effective_a1
                && a1 == effective_b1
                && effective_a2
                    .verify_backward_compatible(effective_version, effective_b2)
                    .is_ok()
        }
        (Schema::Boxed(native_a), Schema::Boxed(native_b)) => {
            let (Schema::Boxed(effective_a2), Schema::Boxed(effective_b2)) = (a_effective, b_effective) else {
                return false;
            };
            arg_layout_compatible(
                &**native_a,
                &**native_b,
                &**effective_a2,
                &**effective_b2,
                effective_version,
            )
        }
        (Schema::Trait(s_a, _), Schema::Trait(s_b, _)) => {
            if s_a != s_b {
                return false;
            }
            let (Schema::Trait(e_a2, effective_a2), Schema::Trait(e_b2, effective_b2)) = (a_effective, b_effective)
            else {
                return false;
            };
            if e_a2 != e_b2 {
                return false;
            }

            effective_a2
                .verify_backward_compatible(effective_version, effective_b2)
                .is_ok()
        }
        (a, b) => a.layout_compatible(b),
    }
}

impl<T: AbiExportable + ?Sized + 'static> AbiConnection<T> {
    /// Analyse the difference in definitions between the two sides,
    /// and create an AbiConnection
    #[allow(clippy::too_many_arguments)]
    fn analyze_and_create(
        trait_name: &str,
        remote_entry: unsafe extern "C" fn(flag: AbiProtocol),
        effective_version: u32,
        caller_effective_definition: AbiTraitDefinition,
        callee_effective_definition: AbiTraitDefinition,
        caller_native_definition: AbiTraitDefinition,
        callee_native_definition: AbiTraitDefinition,
    ) -> Result<AbiConnectionTemplate, SavefileError> {
        let mut methods = Vec::with_capacity(caller_native_definition.methods.len());
        if caller_native_definition.methods.len() > 64 {
            panic!("Too many method arguments, max 64 are supported!");
        }
        for caller_native_method in caller_native_definition.methods.into_iter() {
            let Some((callee_native_method_number, callee_native_method)) = callee_native_definition
                .methods
                .iter()
                .enumerate()
                .find(|x| x.1.name == caller_native_method.name)
            else {
                methods.push(AbiConnectionMethod {
                    method_name: caller_native_method.name,
                    caller_info: caller_native_method.info,
                    callee_method_number: None,
                    compatibility_mask: 0,
                });
                continue;
            };

            let Some(callee_effective_method) = callee_effective_definition
                .methods
                .iter()
                .find(|x| x.name == caller_native_method.name)
            else {
                return Err(SavefileError::GeneralError {msg: format!("Internal error - missing method definition {} in signature when calculating serializable version of call (1).", caller_native_method.name)});
            };

            let Some(caller_effective_method) = caller_effective_definition
                .methods
                .iter()
                .find(|x| x.name == caller_native_method.name)
            else {
                return Err(SavefileError::GeneralError {msg: format!("Internal error - missing method definition {} in signature when calculating serializable version of call (2).", caller_native_method.name)});
            };

            if caller_native_method.info.arguments.len() != callee_native_method.info.arguments.len() {
                return Err(SavefileError::GeneralError {msg: format!("Number of arguments for method {} was expected by caller to be {} but was {} in implementation.", caller_native_method.name, caller_native_method.info.arguments.len(), callee_native_method.info.arguments.len())});
            }

            if caller_native_method.info.arguments.len() != caller_effective_method.info.arguments.len() {
                return Err(SavefileError::GeneralError {
                    msg: format!(
                        "Internal error - number of arguments for method {} has differs between {} to {} (1).",
                        caller_native_method.name,
                        caller_native_method.info.arguments.len(),
                        caller_effective_method.info.arguments.len()
                    ),
                });
            }

            if caller_native_method.info.arguments.len() != callee_effective_method.info.arguments.len() {
                return Err(SavefileError::GeneralError {
                    msg: format!(
                        "Internal error - number of arguments for method {} has differs between {} to {} (2).",
                        caller_native_method.name,
                        caller_native_method.info.arguments.len(),
                        callee_effective_method.info.arguments.len()
                    ),
                });
            }

            if caller_native_method.info.arguments.len() > 64 {
                return Err(SavefileError::TooManyArguments);
            }

            let retval_effective_schema_diff = diff_schema(
                &caller_effective_method.info.return_value,
                &callee_effective_method.info.return_value,
                "".to_string(),
            );
            if let Some(diff) = retval_effective_schema_diff {
                return Err(SavefileError::IncompatibleSchema {
                    message: format!(
                        "Incompatible ABI detected. Trait: {}, method: {}, return value error: {}",
                        trait_name, &caller_native_method.name, diff
                    ),
                });
            }
            let mut mask = 0;
            let mut verify_compatibility = |effective1, effective2, native1, native2, index: Option<usize>| {
                let effective_schema_diff = diff_schema(effective1, effective2, "".to_string());
                if let Some(diff) = effective_schema_diff {
                    return Err(SavefileError::IncompatibleSchema {
                        message: if let Some(index) = index {
                            format!(
                                "Incompatible ABI detected. Trait: {}, method: {}, argument: #{}: {}",
                                trait_name, &caller_native_method.name, index, diff
                            )
                        } else {
                            format!(
                                "Incompatible ABI detected. Trait: {}, method: {}, return value differs: {}",
                                trait_name, &caller_native_method.name, diff
                            )
                        },
                    });
                }

                let comp = arg_layout_compatible(native1, native2, effective1, effective2, effective_version);

                if comp {
                    if let Some(index) = index {
                        mask |= 1 << index;
                    }
                }
                Ok(())
            };

            for index in 0..caller_native_method.info.arguments.len() {
                let effective1 = &caller_effective_method.info.arguments[index].schema;
                let effective2 = &callee_effective_method.info.arguments[index].schema;
                let native1 = &caller_native_method.info.arguments[index].schema;
                let native2 = &callee_native_method.info.arguments[index].schema;
                verify_compatibility(effective1, effective2, native1, native2, Some(index))?;
            }

            verify_compatibility(
                &caller_effective_method.info.return_value,
                &callee_effective_method.info.return_value,
                &caller_native_method.info.return_value,
                &callee_native_method.info.return_value,
                None, /*return value*/
            )?;

            methods.push(AbiConnectionMethod {
                method_name: caller_native_method.name,
                caller_info: caller_native_method.info,
                callee_method_number: Some(callee_native_method_number as u16),
                compatibility_mask: mask,
            })
        }

        Ok(AbiConnectionTemplate {
            effective_version,
            methods: Box::leak(methods.into_boxed_slice()),
            entry: remote_entry,
        })
    }

    /// Gets the function pointer for the entry point of the given interface, in the given
    /// shared library.
    fn get_symbol_for(
        shared_library_path: &str,
        trait_name: &str,
    ) -> Result<unsafe extern "C" fn(flag: AbiProtocol), SavefileError> {
        let mut entry_guard = Guard::lock(&ENTRY_CACHE);
        let mut lib_guard = Guard::lock(&LIBRARY_CACHE);

        if let Some(item) = entry_guard.get(&(shared_library_path.to_string(), trait_name.to_string())) {
            return Ok(*item);
        }

        let filename = shared_library_path.to_string();
        let trait_name = trait_name.to_string();
        let library;
        match lib_guard.entry(filename.clone()) {
            Entry::Occupied(item) => {
                library = item.into_mut();
            }
            Entry::Vacant(vacant) => unsafe {
                library = vacant.insert(Library::new(&filename).map_err(|x| SavefileError::LoadLibraryFailed {
                    libname: filename.to_string(),
                    msg: x.to_string(),
                })?);
            },
        }

        match entry_guard.entry((filename.clone(), trait_name.clone())) {
            Entry::Occupied(item) => {
                return Ok(*item.get());
            }
            Entry::Vacant(vacant) => {
                let symbol_name = format!("abi_entry_{}\0", trait_name);
                let symbol: Symbol<unsafe extern "C" fn(flag: AbiProtocol)> = unsafe {
                    library
                        .get(symbol_name.as_bytes())
                        .map_err(|x| SavefileError::LoadSymbolFailed {
                            libname: filename.to_string(),
                            symbol: symbol_name,
                            msg: x.to_string(),
                        })?
                };
                let func: unsafe extern "C" fn(flag: AbiProtocol) =
                    unsafe { std::mem::transmute(symbol.into_raw().into_raw()) };
                vacant.insert(func);
                Ok(func)
            }
        }
    }

    /// Determines the name, without namespace, of the implemented
    /// trait.
    fn trait_name() -> &'static str {
        let n = std::any::type_name::<T>();
        let n = n.split("::").last().unwrap();
        n
    }
    /// Load the shared library given by 'filename', and find a savefile-abi-implementation of
    /// the trait 'T'. Returns an object that implements the
    ///
    /// # Safety
    /// The shared library referenced by 'filename' must be safely implemented,
    /// and must contain an ABI-exported implementation of T, which must be a dyn trait.
    /// However, this kind of guarantee is really needed for all execution of any rust code,
    /// so we don't mark this as unsafe. Symbols are unlikely to match by mistake.
    pub fn load_shared_library(filename: &str) -> Result<AbiConnection<T>, SavefileError> {
        let remote_entry = Self::get_symbol_for(filename, Self::trait_name())?;
        Self::new_internal(remote_entry, None, Owning::Owned)
    }

    /// Creates an AbiConnection from a PackagedTraitObject
    /// This is the way the derive macro crates AbiConnection instances.
    ///
    /// # Safety
    /// * entry_point of `packed` must implement AbiProtocol
    /// * trait_object of `packed` must be a type erased trait object reference
    /// * owning must be correct
    #[doc(hidden)]
    pub unsafe fn from_raw_packaged(
        packed: PackagedTraitObject,
        owning: Owning,
    ) -> Result<AbiConnection<T>, SavefileError> {
        Self::from_raw(packed.entry, packed.trait_object, owning)
    }

    /// Check if the given argument 'arg' in method 'method' is memory compatible such that
    /// it will be sent as a reference, not copied. This will depend on the memory layout
    /// of the code being called into. It will not change during the lifetime of an
    /// AbiConnector, but it may change if the target library is recompiled.
    pub fn get_arg_passable_by_ref(&self, method: &str, arg: usize) -> bool {
        if let Some(found) = self.template.methods.iter().find(|var| var.method_name == method) {
            let abi_method: &AbiConnectionMethod = found;
            if arg >= abi_method.caller_info.arguments.len() {
                panic!(
                    "Method '{}' has only {} arguments, so there is no argument #{}",
                    method,
                    abi_method.caller_info.arguments.len(),
                    arg
                );
            }
            (abi_method.compatibility_mask & (1 << (arg as u64))) != 0
        } else {
            let arg_names: Vec<_> = self.template.methods.iter().map(|x| x.method_name.as_str()).collect();
            panic!(
                "Trait has no method with name '{}'. Available methods: {}",
                method,
                arg_names.join(", ")
            );
        }
    }

    /// This routine is mostly for tests.
    /// It allows using a raw external API entry point directly.
    /// This is mostly useful for internal testing of the savefile-abi-library.
    /// 'miri' does not support loading dynamic libraries. Using this function
    /// from within the same image as the implementation, can be a workaround for this.
    ///
    /// # Safety
    /// * entry_point must implement AbiProtocol
    /// * trait_object must be a type erased trait object reference
    /// * owning must be correct
    #[doc(hidden)]
    pub unsafe fn from_raw(
        entry_point: unsafe extern "C" fn(AbiProtocol),
        trait_object: TraitObject,
        owning: Owning,
    ) -> Result<AbiConnection<T>, SavefileError> {
        Self::new_internal(entry_point, Some(trait_object), owning)
    }

    /// Crate a AbiConnection from an entry point and a boxed trait object.
    /// This is undocumented, since it's basically useless except for tests.
    /// If you have a Box<dyn Example>, you'd want to just use it directly,
    /// not make an AbiConnection wrapping it.
    ///
    /// This method is still useful during testing.
    ///
    /// # Safety
    ///  * The entry point must contain a correct implementation matching the type T.
    ///  * T must be a dyn trait object
    #[doc(hidden)]
    pub fn from_boxed_trait(trait_object: Box<T>) -> Result<AbiConnection<T>, SavefileError> {
        let trait_object = TraitObject::new(trait_object);
        Self::new_internal(T::ABI_ENTRY, Some(trait_object), Owning::Owned)
    }

    /// Crate a AbiConnection from an entry point and a boxed trait object.
    /// This allows using a different interface trait for the backing implementation, for
    /// test cases which want to test version evolution.
    ///
    /// # Safety
    ///  * The entry point must contain a correct implementation matching the type T.
    ///  * T must be a dyn trait object
    #[doc(hidden)]
    pub unsafe fn from_boxed_trait_for_test<O: AbiExportable + ?Sized>(
        entry_point: unsafe extern "C" fn(AbiProtocol),
        trait_object: Box<O>,
    ) -> Result<AbiConnection<T>, SavefileError> {
        let trait_object = TraitObject::new(trait_object);
        Self::new_internal(entry_point, Some(trait_object), Owning::Owned)
    }

    fn new_internal(
        remote_entry: unsafe extern "C" fn(AbiProtocol),
        trait_object: Option<TraitObject>,
        owning: Owning,
    ) -> Result<AbiConnection<T>, SavefileError> {
        let mut templates = Guard::lock(&ABI_CONNECTION_TEMPLATES);

        let typeid = TypeId::of::<T>();
        // In principle, it would be enough to key 'templates' based on 'remote_entry'.
        // However, if we do, and the user ever uses AbiConnection<T> with the _wrong_ entry point,
        // we risk poisoning the cache with erroneous data.
        let template = match templates.entry((typeid,remote_entry)) {
            Entry::Occupied(template) => template.get().clone(),
            Entry::Vacant(vacant) => {
                let own_version = T::get_latest_version();
                let own_native_definition = T::get_definition(own_version);

                let mut callee_abi_version = 0u32;
                let mut callee_schema_version = 0u16;
                unsafe {
                    (remote_entry)(AbiProtocol::InterrogateVersion {
                        schema_version_receiver: &mut callee_schema_version as *mut _,
                        abi_version_receiver: &mut callee_abi_version as *mut _,
                    });
                }

                if callee_schema_version > CURRENT_SAVEFILE_LIB_VERSION {
                    return Err(SavefileError::IncompatibleSavefileLibraryVersion);
                }

                let effective_version = own_version.min(callee_abi_version);

                let mut callee_abi_native_definition = AbiTraitDefinition {
                    name: "".to_string(),
                    methods: vec![],
                };
                let mut callee_abi_effective_definition = AbiTraitDefinition {
                    name: "".to_string(),
                    methods: vec![],
                };
                unsafe extern "C" fn definition_receiver(
                    receiver: *mut AbiTraitDefinition,
                    schema_version: u16,
                    data: *const u8,
                    len: usize,
                ) {
                    let receiver = unsafe { &mut *receiver };
                    let slice = unsafe { slice::from_raw_parts(data, len) };
                    let mut cursor = Cursor::new(slice);

                    let schema = load_noschema(&mut cursor, schema_version.into());
                    *receiver = schema.unwrap_or(Default::default());
                }

                unsafe {
                    (remote_entry)(AbiProtocol::InterrogateMethods {
                        schema_version_required: callee_schema_version,
                        callee_schema_version_interrogated: callee_abi_version,
                        result_receiver: &mut callee_abi_native_definition as *mut _,
                        callback: definition_receiver,
                    });
                }

                unsafe {
                    (remote_entry)(AbiProtocol::InterrogateMethods {
                        schema_version_required: callee_schema_version,
                        callee_schema_version_interrogated: effective_version,
                        result_receiver: &mut callee_abi_effective_definition as *mut _,
                        callback: definition_receiver,
                    });
                }

                let own_effective_definition = T::get_definition(effective_version);
                let trait_name = Self::trait_name();
                let template = Self::analyze_and_create(
                    trait_name,
                    remote_entry,
                    effective_version,
                    own_effective_definition,
                    callee_abi_effective_definition,
                    own_native_definition,
                    callee_abi_native_definition,
                )?;
                vacant.insert(template).clone()
            }
        };

        let trait_object = if let Some(obj) = trait_object {
            obj
        } else {
            let mut trait_object = TraitObject::zero();
            let mut error_msg: String = Default::default();
            unsafe extern "C" fn error_callback(error_receiver: *mut (), error: *const AbiErrorMsg) {
                let error_msg = unsafe { &mut *(error_receiver as *mut String) };
                *error_msg = unsafe { &*error }.convert_to_string();
            }
            unsafe {
                (remote_entry)(AbiProtocol::CreateInstance {
                    trait_object_receiver: &mut trait_object as *mut _,
                    error_receiver: &mut error_msg as *mut String as *mut _,
                    error_callback,
                });
            }

            if error_msg.len() > 0 {
                return Err(SavefileError::CalleePanic { msg: error_msg });
            }
            trait_object
        };

        Ok(AbiConnection {
            template,
            owning,
            trait_object,
            phantom: PhantomData,
        })
    }
}

/// Helper implementation of ABI entry point.
/// The actual low level `extern "C"` functions call into this.
/// This is an entry point meant to be used by the derive macro.
///
/// This version, the 'light version', does not support instance
/// creation.
///
/// # Safety
/// The 'AbiProtocol' protocol must only contain valid data.
pub unsafe fn abi_entry_light<T: AbiExportable + ?Sized>(flag: AbiProtocol) {
    match flag {
        AbiProtocol::RegularCall {
            trait_object,
            method_number,
            effective_version,
            compatibility_mask,
            data,
            data_length,
            abi_result,
            receiver,
        } => {
            let result = catch_unwind(|| {
                let data = unsafe { slice::from_raw_parts(data, data_length) };

                match unsafe {
                    call_trait_obj::<T>(
                        trait_object,
                        method_number,
                        effective_version,
                        compatibility_mask,
                        data,
                        abi_result,
                        receiver,
                    )
                } {
                    Ok(_) => {}
                    Err(err) => {
                        let msg = format!("{:?}", err);
                        let err = RawAbiCallResult::AbiError(AbiErrorMsg {
                            error_msg_utf8: msg.as_ptr(),
                            len: msg.len(),
                        });
                        receiver(&err, abi_result)
                    }
                }
            });
            match result {
                Ok(()) => {}
                Err(err) => {
                    let msg: &str;
                    let temp;
                    if let Some(err) = err.downcast_ref::<&str>() {
                        msg = err;
                    } else {
                        temp = format!("{:?}", err);
                        msg = &temp;
                    }
                    let err = RawAbiCallResult::Panic(AbiErrorMsg {
                        error_msg_utf8: msg.as_ptr(),
                        len: msg.len(),
                    });
                    receiver(&err, abi_result)
                }
            }
        }
        AbiProtocol::InterrogateVersion {
            schema_version_receiver,
            abi_version_receiver,
        } => {
            // # SAFETY
            // The pointers come from another savefile-implementation, and are known to be valid
            unsafe {
                *schema_version_receiver = CURRENT_SAVEFILE_LIB_VERSION;
                *abi_version_receiver = <T as AbiExportable>::get_latest_version();
            }
        }
        AbiProtocol::InterrogateMethods {
            schema_version_required,
            callee_schema_version_interrogated,
            result_receiver,
            callback,
        } => {
            // Note! Any conforming implementation must send a 'schema_version_required' number that is
            // within the ability of the receiving implementation. It can interrogate this using 'AbiProtocol::InterrogateVersion'.
            let abi = <T as AbiExportable>::get_definition(callee_schema_version_interrogated);
            let mut temp = vec![];
            let Ok(_) = Serializer::save_noschema(&mut temp, schema_version_required as u32, &abi) else {
                return;
            };
            callback(result_receiver, schema_version_required, temp.as_ptr(), temp.len());
        }
        AbiProtocol::CreateInstance {
            trait_object_receiver: _,
            error_receiver,
            error_callback,
        } => {
            let msg = format!("Internal error - attempt to create an instance of {} using the interface crate, not an implementation crate", std::any::type_name::<T>());
            let err = AbiErrorMsg {
                error_msg_utf8: msg.as_ptr(),
                len: msg.len(),
            };
            error_callback(error_receiver, &err as *const _)
        }
        AbiProtocol::DropInstance { trait_object } => unsafe {
            destroy_trait_obj::<T>(trait_object);
        },
    }
}
/// Helper implementation of ABI entry point.
/// The actual low level `extern "C"` functions call into this.
/// This is an entry point meant to be used by the derive macro.
///
/// This version, the 'full version', does support instance
/// creation.
///
/// # Safety
/// The 'AbiProtocol' protocol must only contain valid data.
pub unsafe fn abi_entry<T: AbiExportableImplementation>(flag: AbiProtocol) {
    match flag {
        AbiProtocol::CreateInstance {
            trait_object_receiver,
            error_receiver,
            error_callback,
        } => {
            let result = catch_unwind(|| {
                let obj: Box<T::AbiInterface> = T::new();
                let raw = Box::into_raw(obj);
                assert_eq!(std::mem::size_of::<*mut T::AbiInterface>(), 16);
                assert_eq!(std::mem::size_of::<TraitObject>(), 16);

                let mut trait_object = TraitObject::zero();

                unsafe {
                    ptr::copy(
                        &raw as *const *mut T::AbiInterface,
                        &mut trait_object as *mut TraitObject as *mut *mut T::AbiInterface,
                        1,
                    )
                };

                unsafe {
                    *trait_object_receiver = trait_object;
                }
            });
            match result {
                Ok(_) => {}
                Err(err) => {
                    let msg: &str;
                    let temp;
                    if let Some(err) = err.downcast_ref::<&str>() {
                        msg = err;
                    } else {
                        temp = format!("{:?}", err);
                        msg = &temp;
                    }
                    let err = AbiErrorMsg {
                        error_msg_utf8: msg.as_ptr(),
                        len: msg.len(),
                    };
                    error_callback(error_receiver, &err as *const _)
                }
            }
        }
        flag => {
            abi_entry_light::<T::AbiInterface>(flag);
        }
    }
}

/// If files representing the given AbiExportable definition is not already present,
/// create one file per supported version, with the definition of the ABI.
/// If files are present, verify that the definition is the same as that in the files.
///
/// This allows us to detect if the data structure as we've declared it is modified
/// in a non-backward compatible way.
///
/// 'path' is a path where files defining the Abi schema are stored. These files
/// should be checked in to version control.
pub fn verify_compatiblity<T: AbiExportable + ?Sized>(path: &str) -> Result<(), SavefileError> {
    std::fs::create_dir_all(path)?;
    for version in 0..=T::get_latest_version() {
        let def = T::get_definition(version);
        let schema_file_name = Path::join(Path::new(path), format!("savefile_{}_{}.schema", def.name, version));
        if std::fs::metadata(&schema_file_name).is_ok() {
            let previous_schema = load_file_noschema(&schema_file_name, 1)?;

            def.verify_backward_compatible(version, &previous_schema)?;
        } else {
            save_file_noschema(&schema_file_name, 1, &def)?;
        }
    }
    Ok(())
}