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
|
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// copyright : (C) 2008 by Eran Ifrah
// file name : wxSerialize.h
//
// -------------------------------------------------------------------------
// A
// _____ _ _ _ _
// / __ \ | | | | (_) |
// | / \/ ___ __| | ___| | _| |_ ___
// | | / _ \ / _ |/ _ \ | | | __/ _ )
// | \__/\ (_) | (_| | __/ |___| | || __/
// \____/\___/ \__,_|\___\_____/_|\__\___|
//
// F i l e
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------------------
// $RCSfile: wxSerialize.h $
// $Source: include/wx/wxSerialize.h $
// $Revision: 1.16 $
// $Date: 7-sep-2007 11:29:08 $
//---------------------------------------------------------------------------
// Author: Jorgen Bodde
// Christian Buhtz
// Copyright: (c) Jorgen Bodde <jorgb@xs4all.nl>
// License: see LICENSE for details
//---------------------------------------------------------------------------
#ifndef _WXSERIALIZE_H_
#define _WXSERIALIZE_H_
#include <wx/sstream.h>
#include <wx/buffer.h>
#include <wx/math.h>
#include <wx/datetime.h>
#include <wx/gdicmn.h> // for wxSize, wxPoint
#define WXSERIALIZE_MAJOR_VERSION 1
#define WXSERIALIZE_MINOR_VERSION 1
/** \author Jorgen Bodde
\mainpage
This is the wxSerialize documentation guide. wxSerialize is a serializing class meant for serializing / de-serializing
a complete (or partial) object model to a stream. The streaming is made type safe and reliable. This means when an
error occurs (wrong data read back or unexpected end of file) the wxSerialize class logs an error, and refuses to
read from the stream, leaving all variables that follow the error untouched, or in a default state.
With the use of a version number and a header, the stream is checked when reading it back. When, for example you
wrote a stream with version 1000, and upon reading you expect version 900, the wxSerialize class logs an error that
the expected version is lower then the encountered version, avoiding a mis-read of data that is not supported in the
code. With the use of a header string, you can double check if the stream offered is really recognised by the
application.
Because wxSerialize uses wxInputStream and wxOutputStream for reading and writing, the contents of the serialization
can be written to a memory based stream, a compression stream (like wxZLibOutputStream), a tcp/ip socket, from
an wxURL (from the internet, handy for updates), or any other stream based upon wxInputStream / wxOutputStream.
The streaming is binary compatible on Linux / Windows and MacOSX which means that if you read back the binary data
from disk, the same results are read back, the values are properly converted from little to big endian.
\section serializing_exactly Ok but what is serializing exactly?
Serializing is putting the contents of a class to an output file. Microsoft's MFC used CArchive (which I attempted
to keep as API compatible as possible) and boost now also has a binary serializer. So, how does it work? Well it
is really simple. The power of the serializing lies in the fact that the classes themselves are responsible for
serializing their data. It is very short in code, and the destination of the serialization stream doesn't really
matter.
\section how_work So how does it work?
Consider the following class:
\code
class MyDataClass
{
public:
// the serialization class, that streams from and to the
// specified stream.
Serialize(wxSerialize &a);
private:
wxString m_name;
wxString m_address;
wxUint8 m_age;
wxArrayString m_siblings;
};
\endcode
How does the serializing work .. We simply implement the Serialize method:
\code
bool MyDataClass::Serialize(wxSerialize &a)
{
if(a.IsStoring())
{
a << m_name << m_address << m_age;
a << m_siblings;
}
else
{
a >> m_name >> m_address >> m_age;
a >> m_siblings;
}
// return false when the archive encountered an error
return a.IsOk();
}
\endcode
This is all there is to it! This small piece of code, serializes the 4 used member vars to the stream, on disk
or where it needs to go. The following code shows how the class is serialized. First when it is saved to disk:
\code
wxFileOutputStream file("somefile.dat");
wxSerialize a(file);
m_ourClass->Serialize(a);
\endcode
That's all! When the file and archive are out of scope, the file is closed and the object is persistent on disk.
Now to read the data back:
\code
wxFileInputStream file("somefile.dat");
wxSerialize a(file);
// let's say we just create it and initialize it
m_ourClass = new MyDataClass();
m_ourClass->Serialize(a);
\endcode
Nearly identical code isn't it ? Even the new MyDataClass() is not needed, it is simply there to illustrate how
newly created objects can be serialized back into. Only one method is needed, I called it Serialize() but the
name does not matter.
\section why_using So why not write using wxConfig, wxFFile, XML or use XTI?
This serializer class was designed, keeping in mind a small footprint in memory and disk. When serializing to XML
you gain flexibility and even can hand-edit it, but the overhead in the class is enormous. You have to figure out
how the data needs to be stored, attribute names, nested hierarchies, handle errors, etc. XML is basically better
to write to, if user editing or portability is a big issue.
Serializing to a binary file can be more useful in the following situations:
- Writing cache data (e.g. an object model that represents a state in memory). By writing this in a serialized
stream to disk, the object model can be quickly read back between sessions, or heavy preprocessing (e.g storing
a directory structure to disk) can be avoided.
- Undo / Redo mechanism. Usually undo / redo mechanisms require a lot of overhead in the object model. For example
when the user deletes a data item in a GUI window, this item should not be really deleted, only taken apart from the
object model. When using a serializer class, the object can be removed, and later on re-created back on it's
rightful place. The good thing is that the serialize methods can both handle undo / redo, but also save the
data to disk.
- Small applications that do not require overhead of XML, SQL, or use wxConfig because these all need name / value
conventions. With very small applications that only perform minor tasks, the developer is more distracted by
the writing to disk then it's really needed.
- Writing config. This sounds odd, but when you have a global config object, and it is not really necessary to
keep all that data in a text file, serializing the object containing the config is rather easy.
So when is it not useful?
- Ofcourse when people want to edit the data afterwards.
- When the data needs to be exchanged between applications using a standard format
- When the application needs to process huge data, or store entered data on disk to be queried later on (SQL or
XML).
\section what_dp What about data flexibility?
People who spent some time serializing data, know that when for example a binary serialized file is written to disk
and the object is extended with more data, you have a problem with serializing it back.
Why? The data in the stream is only compatible with the old data makeup, and when adding new member vars how are
you going to read them back? They are not in the stream to begin with, and you don't even know if they are or not.
Well I deviced two ways of doing this. Let's for example extend our rather trivial MyDataClass class:
\code
class MyDataClass
{
public:
// the serialization class, that streams from and to the
// specified stream.
Serialize(wxSerialize &a);
private:
wxString m_name;
wxString m_address;
wxUint8 m_age;
wxArrayString m_siblings;
// NEW DATA ADDED, THIS NEEDS SERIALIZING TOO!
wxUint32 m_monthlyPay;
bool m_healthInsurance;
};
\endcode
\subsection version_number Using a version number
The version number you can give to the stream when you write it back. This version number is stored and checked upon
reading. When creating the stream you specify the version of the stream. You increment this version when there will
be binary incompatibility with older streams:
\code
wxFileOutputStream file("somefile.dat");
wxSerialize a(file, 1001); // latest version is 1001 (was 1000)
m_ourClass->Serialize(a);
\endcode
Now in the code we can take this in account when reading. When we save, we simply save our new data back. And
as you guessed the drawback, older applications still in the field cannot read the newer stream:
\code
bool MyDataClass::Serialize(wxSerialize &a)
{
if(a.IsStoring())
{
// simply write all
a << m_name << m_address << m_age;
a << m_siblings;
// always append new data here, so reading back goes in
// the same exact order as it is written
a << m_monthlyPay << m_healthInsurance;
}
else
{
a >> m_name >> m_address >> m_age;
a >> m_siblings;
// reading back poses a problem. We can have the data but in
// an older stream we have not. So we need the version check:
if(a.GetVersion() > 1000)
{
a >> m_monthlyPay >> m_healthInsurance;
}
}
// return false when the archive encountered an error
return a.IsOk();
}
\endcode
As you might have guessed, this can be some overhead. While developing the app, this can happen a lot
as the classes grow. So the disadvantages are:
- Older releases you wrote (and are in the field) cannot read the newer data back. So when you use serialization
as part of packages or updates, people are obliged to update, or you must provide multiple versions)
- All the newer data require a version check at read time, and will bloat your application as you must take in
account all older streams. A small overhead, but lots of changes will simply bloat your code with GetVersion()
checks which is not really elegant.
\subsection enter_leave Using EnterObject() and LeaveObject()
To reduce the code overhead, and maintain binary read compatiblity between older and newer releases of your
application, I deviced a way which allows you to add member data to your serializing routines and not break
the reading of older or even newer streams. This is how it works;
\code
bool MyDataClass::Serialize(wxSerialize &a)
{
a.EnterObject();
if(a.IsStoring())
{
// simply write all
a << m_name << m_address << m_age;
a << m_siblings;
a << m_monthlyPay << m_healthInsurance;
}
else
{
a >> m_name >> m_address >> m_age;
a >> m_siblings;
a >> m_monthlyPay >> m_healthInsurance;
}
a.LeaveObject();
return a.IsOk();
}
\endcode
Done! These two powerful boundaries (EnterObject() and LeaveObject()) tackle two situations:
- <b>Reading an old stream in a newer application</b>. So what happens, when for example the older stream does not contain the
m_monthlyPay and m_healthInsurance, the LeaveObject() boundary was already written in the old code. The stream
recognises this, and refuses to read data into m_monthlyPay and m_healthInsurance. In these cases the default
value will be kept in these members. Data that follows after the boundary when LeaveObject was not called,
will be kept default.
- <b>Reading a newer stream with an older application</b>. When the application you released in the field is older then the stream
being written by the newer release, more data is present in the stream then the old application knows about.
Here the same situation applies. The LeaveObject() in the old code comes sooner then the expected leave boundary
in the stream, so wxSerialize will go look for the boundary and ignore all data that is newer.
The EnterObject() and LeaveObject() construction keeps the stream in sync. The only important thing to remember is
that you MUST USE these boundaries from the first moment and not when you are adding new member objects later on.
It is a small sacrifice to gain a lot of flexibility! The overhead in the stream is also very small, because the
footprint of the boundaries is exactly 1 byte for the enter and 1 byte for the leave marker.
EnterObject() and LeaveObject() can be used multiple times after eachother, and also nested. So when you serialize
a class hierarchy and in one of your child objects also data changes, this will be possible.
The only drawback that is left with this approach (but is also present in <b>all</b> data writing when older applications
write back data that contained newer information) is that newer data is being overwritten. You must take special
caution (for example using the version tag or wxSerializeStatus object) in your application to warn the user that
the data being written will destroy newer information in the serializing stream. The newer release of your
application can still read this stream, but newer data is defaulted again.
Also another caveat is that when you replace data being read back it will not work, not with EnterObject() /
LeaveObject() but also not with a versioned stream. Consider this example:
\code
bool MyDataClass::Serialize(wxSerialize &a)
{
a.EnterObject();
if(a.IsStoring())
{
// simply write all
a << m_name << m_address << m_age;
a << m_siblings;
//a << m_monthlyPay << m_healthInsurance;
// REPLACED NOW with:
a << m_surName << m_middleName;
}
else
{
a >> m_name >> m_address >> m_age;
a >> m_siblings;
// WON'T WORK! In older versions the two vars m_monthlypay and m_healthInsurance
// were stored here, instead we read back this. If the stream we read back do not
// contain these new replaced variables yet, we are hitting an error state.
a >> m_surName >> m_middleName;
}
a.LeaveObject();
return a.IsOk();
}
\endcode
\subsection archive_status wxSerializeStatus Helper Class
This class is especially designed to keep the status of the stream between reading and writing. It contains the
last version being written or read, the header, and more important, an NewDataLoss() flag. This flag will be set
when the stream being read back is newer then the data model it's beaing serialized into. Writing the data back
using the wxSerializeStatus object earlier retrieved when reading will prevent the write if the flag NewDataLoss()
is not reset using OverwriteNew() flag. How is the flag determined? Well, if one time in the data serializing
process the LeaveObject() method needs to search for the marker (thus skipping newer data in the stream) this
flag is set.
This wxSerializeStatus class will also remove the need of writing the same header back as being specified when
reading, and also the version will be preserved. The member wxSerializeStatus::SetVersion() can be used to specify
a newer version. Here is an example, first when reading we store the last status and keep it:
\code
wxFileInputStream file("somefile.dat");
wxSerialize a(file);
// let's say we just create it and initialize it
m_ourClass = new MyDataClass();
m_ourClass->Serialize(a);
// store the status (copy class)
m_ourStatus = a.GetArchiveStatus();
\endcode
When writing back, and this application is older then the newer application, thus an overwrite will result
in newer information loss:
\code
// if the last time we read a newer stream, tell the user
if(m_ourStatus.NewDataLoss())
{
if(::wxMessageDialog( ... "ask if we should overwrite, it will result in dataloss" ...) == wxID_YES)
m_ourStatus.OverwriteNew(); // reset the flag
}
// if the flag is reset we continue else we skip
if(!m_ourStatus.NewDataLoss())
{
wxFileOutputStream file("somefile.dat");
wxSerialize a(file, m_ourStatus.GetVersion(), m_ourStatus.GetHeader());
// serialize data
m_ourClass->Serialize(a);
}
else
{
wxLogError("Data is not saved!");
}
\endcode
This construction prevents the application of accidentally writing data back and destroying newer serialized data.
The user must explicitly agree upon this, or the developer must provide a mechanism to backup the old file so that
newer data is not lost when reading it back in the newer application.
*/
/** \class wxSerializeStatus
\brief A simple helper class that keeps track of the current version of the archive, the header and
more important, when at reading time it appears the stream contains newer data then expected.
It will help the developer in asking the user if the stream might be saved back or not. See
\ref archive_status overview for more information.
*/
class wxSerializeStatus
{
public:
/** Constructor of the wxSerializeStatus object. This is the default constructor so you can
define it in the class itself without having to do anything for it */
wxSerializeStatus()
: m_version(0)
, m_header(wxT(""))
, m_newDataLoss(false)
{
};
/** Constructor initializer of the wxSerializeStatus object. Used by wxSerialize to fill in the goods
so they can be used later */
wxSerializeStatus(size_t version, const wxString &header)
: m_version(version)
, m_header(header)
, m_newDataLoss(false)
{
};
~wxSerializeStatus() {
};
/** Copy operator to get the latest copy of it from the wxSerialize class, and assign it to your copy */
void operator=(const wxSerializeStatus &s) {
m_version = s.m_version;
m_header = s.m_header;
m_newDataLoss = s.m_newDataLoss;
};
/** With this method you tell wxSerialize that it is ok to overwrite the newer data stream with an older
version of it. This needs to be set implicitly. Check out \ref archive_status for more information.
*/
void OverwriteNew() {
m_newDataLoss = false;
};
/** This method returns the flag of the current NewDataLoss status. It will be set when the wxSerialize
encounters an out of sync where there is more data then can be read back. See \ref enter_leave
*/
bool NewDataLoss() const {
return m_newDataLoss;
};
/** Sets the NewDataLoss flag again. This is used by wxSerialize only, or you can use it to prevent other
parts of the your code to be executed later when new data loss still occurs. The counterpart of this
flag is OverwriteNew() */
void SetNewDataLoss() {
m_newDataLoss = true;
};
/** Returns the version of the last read archive */
size_t GetVersion() const {
return m_version;
};
/** Returns the header of the last read archive */
const wxString &GetHeader() const {
return m_header;
};
private:
size_t m_version;
wxString m_header;
bool m_newDataLoss;
};
/** \class wxSerialize
\brief A serializer class that can serialize the contents of your class to or from a stream. It uses wxInputStream
and wxOutputStream derived classes for input and output.
See the \ref mainpage for more information
*/
class wxSerialize
{
public:
/** Construct an input stream archive. The stream will be used to retrieve the data from, which can be any
derived stream like a file, zip stream etc. When constructed as input, the wxSerialize object cannot
be written to.
The first integer and string are always read from the stream. This contains the version number (or 0 when
not used) and the header which is empty if not used.
When setting the version parameter, wxSerialize will check if the version read back is at least the expected
version. For example, when version argument is set to 1000, wxSerialize will accept versions 0 .. 1000 for reading
but will give an error if version 1001 and up is read from the stream. You can use this to set the version to
at least the supported version for your code. When extending the serialization routines after your app has
been deployed, the version info can be used to dynamically skip parts while loading from an older stream,
and warn users that the version of the read stream is older which means incompatibility with
the older application will occur. This is not the recommended method though, see EnterObject() and LeaveObject()
for a more flexible solution.
The header tag will also be compared when it is not empty. When an empty header is passed as parameter, the read
back header tag is ignored. When the header argument is set, and the read back header is empty or different,
an error will occur and the stream cannot be read back from. This can provide an extra check if the stream
will contain the data needed.
The partialMode flag will do nothing else but setting a flag (IsFullSerialize()) which can be used in
serializing to skip some relational hierarchies and stuff not needed when the object is only partially
serialized to the stream (handy for undo / redo mechanisms). When set to true, IsFullSerialize() will
return false.
\code
// open and write
wxFileInputStream in_stream("somefile.dat");
wxSerialize a(in_stream, 1000, _T("My Stream"));
if(a.IsOk())
{
a >> m_name;
a >> m_age;
a >> m_string >> m_count;
}
else
wxLogError(wxT("Archive cannot be opened because : ") + a.GetLastErrorString());
// end the archive
a.Close();
\endcode
See also \ref enter_leave and read about data flexibility.
\sa IsFullSerialize(), wxSerializeStatus
*/
wxSerialize(wxInputStream &stream, size_t version = 0, const wxString &header = wxEmptyString, bool partialMode = false);
/** Construct an output stream object. The stream will be used to save the data to, which can be any derived
stream like a file, zip stream etc. When constructed as output, the wxSerialize object cannot be read from.
\code
// open and write
wxFileOutputStream out_file("somefile.dat");
wxSerialize a(out_file, 1000, _T("My Stream"));
a << m_name;
a << m_age;
a << m_string << m_count;
// end all writing (does not close the stream)
a.Close();
\endcode
The version (1000 in example) is saved in the stream, also the header. At reading time these two are checked
against what the expected version and header are, so that you can be sure the stream is recognised and
supported.
Always save your latest supported version here. This means when you extended your application's serialization,
increment the version. This will allow you to read back older streams and save them with newer information.
See \ref enter_leave for more ways to make your serialization more flexible.
*/
wxSerialize(wxOutputStream &stream, size_t version = 1, const wxString &header = wxEmptyString, bool partialMode = false);
/** Destructor. The passed stream ombject will not be destroyed, because it is used by reference */
virtual ~wxSerialize();
/** Closes the archive. This means all writing or reading done to it will not affect the archive or variables
passed by reference. A closed archive will not produce any errors when read from and will not advance in
searching. A closed archive can be re-opened with Open() to allow reading or writing again. */
void Close() {
m_opened = false;
};
/** Re-opens the archive. But only when the archive is in OK state (IsOk() == true). By default the archive is
opened when the constructor is created and everything went ok */
void Open() {
// it can be as simple as this
m_opened = IsOk();
};
/** In reading mode, it returns true when the stream we are reading from returns true on Eof().
It means that it returns true AFTER an attempt has been made to read past the end of the stream.
In writing mode Eof() returns always false. When the stream is closed, true is always returned,
we can't read or write anymore in that case anyway. Reading more from a stream that is at it's
end will produce errors.
*/
bool Eof();
/** Returns true when the wxSerialize is in OK condition. This means no error occurred. Errors that can occur are
for example:
- The underlying stream reports that it is not OK (not able to write, not able to read)
- Reading an unexpected type of variable (e.g. expecting int and reading back string)
- Reading from a wxSerialize with a higher version then supported
- Reading from a wxSerialize with a wrong header
- Writing to the wxSerialize when we are in reading mode
- Reading from the wxSerialize when we are in writing mode
See GetLastError() to retrieve the error, and GetLastErrorString() for an explanation. When the wxSerialize
encountered errors, it will remain in an error state, subsequent reads and writes will not have effect to
either the member variables to read to, or write anything to the stream when writing, for example:
\code
wxSerialize a(in_stream);
// .. some code
if(a.IsStoring())
{
// when wxSerialize is not ok, this does nothing
// so we can do this safely, and report Ok status below
a << m_name << m_address << m_age;
// when we are not ok, we can use a.IsOk() to skip
// the count loop right away (we cannot write and it saves time)
a << GetCount();
for(int i = 0; i < GetCount() && a.IsOk(); i++)
a << m_value[i];
}
else
{
// reset your object, in case we
// get errors, all vars will be initialized
ResetDefaults();
// reading back from not ok stream also does not affect
// the passed member vars they will remain default
a >> m_name >> m_address >> m_age;
// be careful here, because count is not set by default
// so we set it to 0. Now the for loop will quit right away
int count = 0;
a >> count;
for(int i = 0; i < count; i++)
a >> m_value;
}
// return status to lower part
return a.IsOk();
\endcode
*/
bool IsOk() {
bool noerr = (m_errorCode == wxSERIALIZE_ERR_OK);
if(m_writeMode)
return m_odstr.IsOk() && noerr;
else
return m_idstr.IsOk() && noerr;
};
/** Returns the last error set in this string. */
const wxString &GetLastErrorStr() const {
return m_errMsg;
};
/** This method allows you to specify regions of an object that wxSerialize can identify. This allows
you to read back old streams, and still serialize all data back from the stream. All the data that
is not present in the stream reading back is skipped. This will reduce the problem that old streams
do not contain all data that is needed, it will simply be skipped. See \ref enter_leave for a bigger
explanation, basically when using this, you ensure older applications can read newer streams, and
newer applications can also read older streams.
The only restriction is that you keep the data in the same order. This means the new added data
always needs to be read and written at the end of the original data.
The method returns false when the object entering did not work (always returns true in write mode
but can return false plus an error when the current level could not be found. If this is the case
the wxSerialize class stays in an error state so it won't be harmful trying to read or write anyway.
For example:
\code
wxSerialize a(in_stream);
if(a.EnterObject())
{
// we succesfully entered the object level
if(a.IsLoading())
{
a >> m_name;
a >> m_age;
// these vars are not present in the older stream. At this point,
// all serializing into the variables will not take place. They will
// be left default, so initializing before the construction gives them
// a default value, but the stream reader does not give errors, here
// it is best to use the ReadInt and ReadString directly with a default
// specifier:
a.ReadInt(m_peopeCount, 0); // default no people when not present
a.ReadUint8(m_numberOfPersons, 1); // default 1 person
}
else
{
// save the stream back, now all vars can be read back. The interesting
// thing is that older programs can still read the streams back as they
// scan for the LeaveObject() marker at the end of their serializing round
a << m_name;
a << m_age;
a << m_peopeCount;
a << m_numberOfPersons;
}
a.LeaveObject();
}
\endcode
*/
bool EnterObject();
/** This the leave method that belongs to the EnterObject() method. Please always close the
section with the LeaveObject() when an EnterObject() is used. These two constructions can
be nested also. See \ref enter_leave for more information.
\sa EnterObject()
*/
bool LeaveObject();
/** Indicates current state is writing. This flag will always be true if wxSerialize is constructed with a
wxOutputStream. The IsStoring() does not check wether the state of the stream is ok or not,
to prevent problems do:
\code
wxSerialize a(stream);
// without the a.IsOk() it will work too, but serializing will
// go on writing to dev/null without aborting when an error occurred.
if(a.IsOk())
{
if(a.IsStoring())
{
// write contents
}
else
{
// read contents
}
}
\endcode
*/
bool IsStoring() const {
return m_writeMode;
};
/** Indicates current state is reading. This flag will always be true if wxSerialize is constructed with a
wxInputStream. The IsLoading() does not check wether the state of the stream is ok or not, to prevent
problems do:
\code
wxSerialize a(stream);
// without the a.IsOk() it will work too, but serializing will
// go on without aborting when an error occurred.
if(a.IsOk())
{
if(a.IsLoading())
{
// read contents
}
else
{
// write contents
}
}
\endcode
*/
bool IsLoading() const {
return !m_writeMode;
};
/** This is an extra flag independent of IsStoring() or IsLoading(). It can be set to indicate that certain
parts of the serialized objects must be omitted, can be useful when not the complete hierarchy needs
serializing, but only a single object, for example to a temporary stream (undo / redo mechanism). See
the argument 'partialMode' at the constructors for this class.
\code
bool SomeObject::Serialize(wxSerialize &a)
{
if(a.IsStoring())
{
// store our data
a << m_name << m_address << m_age;
// in partial mode, the following will return false, not saving
// any hierachie information of objects
if(a.IsFullSerialize())
{
a << m_items.Count();
for(size_t i = 0 ; a.IsOk() && i < m_items.Count(); i++)
m_items[a]->Serialize(a);
}
}
else
{
// load our data
a >> m_name >> m_address >> m_age;
// in partial mode, the following will return false, not restoring
// any hierachie information of objects
if(a.IsFullSerialize())
{
size_t count = 0;
a >> count;
m_items.DeleteAll();
for(size_t i = 0 ; a.IsOk() && i < count; i++)
{
SomeChild *child = new SomeChild();
child->Serialize(a);
m_items.Add(child);
}
}
}
}
\endcode
*/
bool IsFullSerialize() const {
return !m_partialMode;
};
/** Returns true when the stream is open. When opened, reading and writing can take place. When closed,
no reading and writing can occur, but also no error will be reported when tried anyway */
bool IsOpen() const {
return m_opened;
};
// -- saving serializers --
/** Convenience operator for serializing. See WriteUInt8() */
virtual wxSerialize &operator <<(wxUint8 value) {
WriteUint8(value);
return *this;
};
/** Convenience operator for serializing. See WriteUInt16() */
virtual wxSerialize &operator <<(wxUint16 value) {
WriteUint16(value);
return *this;
};
/** Convenience operator for serializing. See WriteUInt32() */
virtual wxSerialize &operator <<(wxUint32 value) {
WriteUint32(value);
return *this;
};
/** Convenience operator for serializing. See WriteUInt64() */
virtual wxSerialize &operator <<(wxUint64 value) {
WriteUint64(value);
return *this;
};
/** Convenience operator for serializing. See WriteInt() */
virtual wxSerialize &operator <<(int value) {
WriteInt(value);
return *this;
};
/** Convenience operator for serializing. See WriteString() */
virtual wxSerialize &operator <<(const wxString& value) {
WriteString(value);
return *this;
};
/** Convenience operator for serializing. See WriteArrayString() */
virtual wxSerialize &operator <<(const wxArrayString& value) {
WriteArrayString(value);
return *this;
};
/** Convenience operator for serializing. See WriteString() */
virtual wxSerialize &operator <<(const wxChar* pvalue) {
wxString value(pvalue);
WriteString(value);
return *this;
};
/** Convenience operator for serializing. See WriteBool() */
virtual wxSerialize &operator <<(bool value) {
WriteBool(value);
return *this;
};
/** Convenience operator for serializing. See WriteDouble() */
virtual wxSerialize &operator <<(wxFloat64 value) {
WriteDouble(value);
return *this;
};
/** Convenience operator for serializing. See WriteDateTime() */
virtual wxSerialize &operator <<(const wxDateTime &value) {
WriteDateTime(value);
return *this;
};
/** Convenience operator for serializing. See WriteIntInt() */
virtual wxSerialize &operator <<(const wxSize& value) {
WriteIntInt(value.GetWidth(), value.GetHeight());
return *this;
};
/** Convenience operator for serializing. See WriteIntInt() */
virtual wxSerialize &operator <<(const wxPoint& value) {
WriteIntInt(value.x, value.y);
return *this;
};
// -- loading serializers --
/** Convenience operator for serializing. See ReadUint8() */
virtual wxSerialize &operator >>(wxUint8& value) {
ReadUint8(value);
return *this;
};
/** Convenience operator for serializing. See ReadUint16() */
virtual wxSerialize &operator >>(wxUint16& value) {
ReadUint16(value);
return *this;
};
/** Convenience operator for serializing. See ReadUInt32() */
virtual wxSerialize &operator >>(wxUint32& value) {
ReadUint32(value);
return *this;
};
/** Convenience operator for serializing. See ReadUInt64() */
virtual wxSerialize &operator >>(wxUint64& value) {
ReadUint64(value);
return *this;
};
/** Convenience operator for serializing. See ReadInt() */
virtual wxSerialize &operator >>(int &value) {
ReadInt(value);
return *this;
};
/** Convenience operator for serializing. See ReadString() */
virtual wxSerialize &operator >>(wxString& value) {
ReadString(value);
return *this;
};
/** Convenience operator for serializing. See ReadArrayString() */
virtual wxSerialize &operator >>(wxArrayString& value) {
ReadArrayString(value);
return *this;
};
/** Convenience operator for serializing. See ReadBool() */
virtual wxSerialize &operator >>(bool& value) {
ReadBool(value);
return *this;
};
/** Convenience operator for serializing. See ReadDouble() */
virtual wxSerialize &operator >>(wxFloat64& value) {
ReadDouble(value);
return *this;
};
/** Convenience operator for serializing. See ReadDateTime() */
virtual wxSerialize &operator >>(wxDateTime& value) {
ReadDateTime(value);
return *this;
};
/** Convenience operator for serializing. See ReadIntInt() */
virtual wxSerialize &operator >>(wxSize& value) {
int iWidth, iHeight;
if (ReadIntInt(iWidth, iHeight)) {
value.SetWidth(iWidth);
value.SetHeight(iHeight);
}
return *this;
};
/** Convenience operator for serializing. See ReadIntInt() */
virtual wxSerialize &operator >>(wxPoint& value) {
ReadIntInt(value.x, value.y);
return *this;
};
/** Loads a boolean from stream. When the next record to be read is not a bool, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
*/
bool ReadBool(bool& value);
/** Variant of ReadBool() that will provide a default if the read was not succesful */
bool ReadBool(bool& value, bool defval) {
bool val = ReadBool(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxUint8 from stream. When the next record to be read is not a wxUint8, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
*/
bool ReadUint8(wxUint8& value);
/** Variant of ReadUint8() that will provide a default if the read was not succesful */
bool ReadUint8(wxUint8& value, wxUint8 defval) {
bool val = ReadUint8(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxUint16 from stream. When the next record to be read is not a wxUint16, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
*/
bool ReadUint16(wxUint16& value);
/** Variant of ReadUint16() that will provide a default if the read was not succesful */
bool ReadUint16(wxUint16& value, wxUint16 defval) {
bool val = ReadUint16(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxUint32 from stream. When the next record to be read is not a wxUint32, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
*/
bool ReadUint32(wxUint32& value);
/** Variant of ReadUint32() that will provide a default if the read was not succesful */
bool ReadUint32(wxUint32& value, wxUint32 defval) {
bool val = ReadUint32(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxUint64 from stream. When the next record to be read is not a wxUint64, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
*/
bool ReadUint64(wxUint64& value);
/** Variant of ReadUint64() that will provide a default if the read was not succesful */
bool ReadUint64(wxUint64& value, wxUint64 defval) {
bool val = ReadUint64(value);
if(!val)
value = defval;
return val;
};
/** Loads an int from stream. When the next record to be read is not an int, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
<b>SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN:</b>
Reading an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits
to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is
using the wxUint16, wxUint32, wxUint64 data types. However, this method is made as safe as possible
by also storing the size of the int on the stream. This means reading a stored integer of 4 bytes
(32 bits) it will be read back as 32 bits, and casted to a 64 bits integer. In theory, this should
work as the sign flag for a 32 bits container will be extended to match the 64 bits container. The
only worst case scenario that exists is when an 64 bits int is written, and it needs to be casted
to a 32 bits integer, problems can occur because the destination container may be too small. No error
will be logged so be careful storing extreme values that might not fit in smaller integers.
*/
bool ReadInt(int& value);
/** Loads an pair of ints from stream. When the next record to be read is not an int, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read.
<b>SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN:</b>
Reading an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits
to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is
using the wxUint16, wxUint32, wxUint64 data types. However, this method is made as safe as possible
by also storing the size of the int on the stream. This means reading a stored integer of 4 bytes
(32 bits) it will be read back as 32 bits, and casted to a 64 bits integer. In theory, this should
work as the sign flag for a 32 bits container will be extended to match the 64 bits container. The
only worst case scenario that exists is when an 64 bits int is written, and it needs to be casted
to a 32 bits integer, problems can occur because the destination container may be too small. No error
will be logged so be careful storing extreme values that might not fit in smaller integers.
*/
bool ReadIntInt(int& value1, int& value2);
/** Variant of ReadIntInt() that will provide a default if the read was not succesful */
bool ReadIntInt(int& value1, int& value2, int defval1, int defval2) {
bool val = ReadIntInt(value1, value2);
if(!val) {
value1 = defval1;
value2 = defval2;
}
return val;
};
/** Variant of ReadInt() that will provide a default if the read was not succesful */
bool ReadInt(int& value, int defval) {
bool val = ReadInt(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxFloat64 from stream. When the next record to be read is not a wxFloat64, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read. The wxFloat64 is
stored as a IEEE converted standard wxFloat64, and is x-platform so it can be shared between other os'es
*/
bool ReadDouble(wxFloat64& value);
/** Variant of ReadDouble() that will provide a default if the read was not succesful */
bool ReadDouble(wxFloat64& value, wxFloat64 defval) {
bool val = ReadDouble(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxString from stream. When the next record to be read is not a wxString, an error will occur.
When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read. For the string
first the count is stored and then the string itself. When storing an empty string, only a 0 count is
stored.
*/
bool ReadString(wxString& value);
/** Variant of ReadString() that will provide a default if the read was not succesful */
bool ReadString(wxString& value, const wxString &defval) {
bool val = ReadString(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxArrayString from stream. When the next record to be read is not a wxArrayString, an error
will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read. For the string
first the count is stored and then the string itself. When storing an empty string, only a 0 count is
stored.
*/
bool ReadArrayString(wxArrayString& value);
/** Variant of ReadArrayString() that will provide a default if the read was not succesful */
bool ReadString(wxArrayString& value, const wxArrayString &defval) {
bool val = ReadArrayString(value);
if(!val)
value = defval;
return val;
};
/** Loads a wxDateTime from stream. When the next record to be read is not a wxDateTime, an error
will occur. When wxSerialize encountered the end of object boundary (see \ref enter_leave) or the archive is
in an error state, the value is not touched.
Please note that when an error occurs, all subsequent loading from the stream will result in an
error. This is to provide a save mechanism out of a read error. So instead of checking every value
you read back yourself, they can be default and won't produce erratic behaviour.
The value returned is true when the read was succesful, false when nothing was read. For the string
first the count is stored and then the string itself. When storing an empty string, only a 0 count is
stored.
*/
bool ReadDateTime(wxDateTime& value);
/** Reads a record from the stream. The wxMemoryBuffer is a growable buffer class, which will contain the data
being read. The length of the buffer will be the length present so there is no need to define a
pre-allocated buffer. When the referenced buffer runs out of scope in your code, it is destroyed safely.
The read buffer can be up to 4 Gb (count is 32 bits). The buffer will be considered an atomic element
so when the read fails for the specified number of counts, the buffer is corrupted. This will ofcourse only
happen when the stream unexpectedly terminates and will also occur with other atomic values like reading
an integer etc.
NOTICE FOR PLATFORM BINARY COMPATIBILITY: No conversion takes place when writing the buffer. It is written as
a sequence of bytes to the stream. So be careful storing the whole data back into a structure (little to big endian)
*/
bool Read(wxMemoryBuffer &buffer);
/** Saves a bool to stream. It writes a header type first, and after that the value. This means when the boolean
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
*/
bool WriteBool(bool value);
/** Saves a double as a wxFloat64 to stream. It writes a header type first, and after that the value. This means when the wxFloat64
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
The wxFloat64 is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems.
*/
bool WriteDouble(wxFloat64 value);
/** Saves a wxUint8 to stream. It writes a header type first, and after that the value. This means when the wxUint8
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
The wxUint8 is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems.
*/
bool WriteUint8(wxUint8 value);
/** Saves a wxUint16 to stream. It writes a header type first, and after that the value. This means when the wxUint16
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
The wxUint16 is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems.
*/
bool WriteUint16(wxUint16 value);
/** Saves a wxUint32 to stream. It writes a header type first, and after that the value. This means when the wxUint32
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
The wxUint32 is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems.
*/
bool WriteUint32(wxUint32 value);
/** Saves a wxUint64 to stream. It writes a header type first, and after that the value. This means when the wxUint64
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
The wxUint64 is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems.
*/
bool WriteUint64(wxUint64 value);
/** Saves an int to stream. It writes a header type first, and after that the value. This means when the int
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
<b>SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN:</b>
Writing an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits
to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is
using the wxUint16, wxUint32, wxUint64 data types. See ReadInt() for more information.
*/
bool WriteInt(int value);
/** Saves a pair of ints to a stream. It is used for wxSize and wxPoint. It writes a header type first,
and after that the value. This means when the int is read back when it is not expected, an error occurs
and the program can terminate gracefully because no bogus values are read back. True is returned when
the value is saved properly, and false when it's not.
<b>SPECIAL CARE ABOUT THIS FUNCTION MUST BE TAKEN:</b>
Writing an integer x-platform is potentially unsafe. The size of the integer may vary from 16 bits
to 64 bits depending on the platform. When sharing data streams, the preferred way of streaming is
using the wxUint16, wxUint32, wxUint64 data types. See ReadInt() for more information.
*/
bool WriteIntInt(int value1, int value2);
/** Saves a wxString to stream. It writes a header type first, and after that the value. This means when the wxString
is read back when it is not expected, an error occurs and the program can terminate gracefully because no
bogus values are read back. True is returned when the value is saved properly, and false when it's not.
The wxString is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems. First the count is stored, then the string. With an empty string only
the 0 count is stored.
*/
bool WriteString(const wxString& value);
/** Saves a wxArrayString to stream. It writes a header type first, and after that the value. This means when
the wxArrayString is read back when it is not expected, an error occurs and the program can terminate
gracefully because no bogus values are read back. True is returned when the value is saved properly, and
false when it's not.
The wxString is saved in a cross platform compatible mode, which means the streams can be interchanged between
MacOS, Linux and Windows without problems. First the count is stored, then the string. With an empty string only
the 0 count is stored.
*/
bool WriteArrayString(const wxArrayString& value);
/** Saves a wxDateTime to stream. It writes a header type first, and after that the value. This means when
the wxDateTime is read back when it is not expected, an error occurs and the program can terminate
gracefully because no bogus values are read back. True is returned when the value is saved properly, and
false when it's not.
The wxDateTime is saved in a cross platform compatible mode (e.g. the number of ticks). Please be aware that no
timezone information or daylight saving information is stored. So if the date read back on a different PC is
mis interpreted because the PC is in a different time zone you must find other means of syncing them.
*/
bool WriteDateTime(const wxDateTime& value);
/** Saves an arbitrary amount of bytes to the stream using wxMemoryBuffer. It writes a header type first, and after
that the count to be saved and the bytes in the wxMemoryBuffer. This means when the data is read back when
it is not expected, an error occurs and the program can terminate gracefully because no bogus values are
read back. True is returned when the value is saved properly, and false when it's not.
Data can be saved up to 4Gb. This is not recommended (nor physically possible I think) but the limit is 32 bits for
the count.
NOTICE FOR PLATFORM BINARY COMPATIBILITY: No conversion takes place when writing the buffer. It is written as
a sequence of bytes to the stream. So be careful storing the whole data back into a structure (little to big endian)
*/
bool Write(const wxMemoryBuffer &buffer);
// -- arbitrary functions --
/** Returns the header that was read by this stream. When writing, the header string contains
the passed header by the constructor. See GetArchiveStatus() for more information. */
wxString GetHeaderString() const {
return m_headerStr;
};
/** Returns the version of the stream currently beaing read, or else the version of the stream when it
was created. This can be used to exclude parts while reading, if the stream version does not
support newly added veriables like;
\code
// we expect version 1001 at least (lower versions are possible)
wxSerialize a(in_stream, 1001);
if(a.IsStoring())
{
a << m_name;
// added since 1001
a << m_addr;
}
else
{
a >> m_name;
// only read when we are above 1000
if(a.GetVersion() > 1000)
a >> m_addr;
}
\endcode
*/
size_t GetVersion() const {
return m_version;
};
/** Returns the wxSerializeStatus object. You usually do this when you just read a stream, to get more
information about it later, like the version, header, and if the stream contained newer data which
means writing it back with the older data model you might be using, you destroy information */
const wxSerializeStatus &GetArchiveStatus() const {
return m_status;
};
/// return the version string of the class wxSerialize
static wxString GetLibVersionString ();
/// return the major version number of the class wxSerialize
static long GetLibVersionMajor ();
/// return the minor version number of the class wxSerialize
static long GetLibVersionMinor ();
private:
// error types
enum
{
wxSERIALIZE_ERR_OK = 0,
wxSERIALIZE_ERR_EOF = -1,
wxSERIALIZE_ERR_ILL = -2,
wxSERIALIZE_ERR_MEM = -3
};
// all errors that can occur
enum
{
wxSERIALIZE_ERR_STR_HEADER_s1_s2, /// Illegal header in start of stream
wxSERIALIZE_ERR_STR_WRONGVERSION_s1_s2, /// Not a proper version
wxSERIALIZE_ERR_STR_BADISTREAM, /// Bad input stream
wxSERIALIZE_ERR_STR_BADOSTREAM, /// Bad output stream
wxSERIALIZE_ERR_STR_NOVERHDR, /// Cannot write version or header info to stream
wxSERIALIZE_ERR_STR_NOHEADER_s1, /// Header expected but not found
wxSERIALIZE_ERR_STR_NOVERSION, /// No version found in stream
wxSERIALIZE_ERR_STR_NOWRITE, /// Cannot write in read mode
wxSERIALIZE_ERR_STR_NOREAD, /// Cannot read in write mode
wxSERIALIZE_ERR_STR_EOF, /// End of file error in read mode
wxSERIALIZE_ERR_STR_WRONGCHUNK_s1_s2, /// Expected header x but got y
wxSERIALIZE_ERR_STR_MEMORY_s1, /// Cannot allocate x bytes of memory
wxSERIALIZE_ERR_STR_READSIZE, /// Read size mismatch
wxSERIALIZE_ERR_STR_RINTSIZE, /// Integer size mismatch
wxSERIALIZE_ERR_STR_SINTSIZE, /// Ditto
wxSERIALIZE_ERR_STR_ILL_LEAVE, /// Illegal leave marker found
wxSERIALIZE_ERR_STR_ILL_UNKNOWN_HDR_s1, /// Unknown header error
wxSERIALIZE_ERR_STR_ILL_LEVEL /// Sync level failure!
};
// headers
enum
{
wxSERIALIZE_HDR_BOOL = 'b',
wxSERIALIZE_HDR_INT8 = 'c',
wxSERIALIZE_HDR_INT16 = 'w',
wxSERIALIZE_HDR_INT32 = 'l',
wxSERIALIZE_HDR_INT64 = 'q',
wxSERIALIZE_HDR_DOUBLE = 'd',
wxSERIALIZE_HDR_STRING = 's',
wxSERIALIZE_HDR_RECORD = 'r',
wxSERIALIZE_HDR_INT = 'i',
wxSERIALIZE_HDR_ARRSTRING = 'a',
wxSERIALIZE_HDR_DATETIME = 't',
wxSERIALIZE_HDR_ENTER = '<',
wxSERIALIZE_HDR_LEAVE = '>',
wxSERIALIZE_HDR_INTINT = 'I'
};
// pre-check if stream is capable of writing, returns errors
// when condition is not good (like input stream is defined
// instead of output)
bool CanStore();
// pre-check if stream is capable of reading, returns errors
// when condition is not good (like output stream is defined
// instead of input)
bool CanLoad();
// loads the header, and sets an error when the expected header is not the same
// as the header given
int LoadChunkHeader(int expheader);
// writes a single char to the stream
void WriteChar(wxUint8 value);
// sets an error with arbitrary message using wxLogError
int LogError(int err, int msgcode, const wxString &s1 = wxEmptyString,
const wxString &s2 = wxEmptyString);
// attempts to find the marker that belongs to this
// current level. We assume the current count but increment when we
// find one, decrement when we find another ..
void FindCurrentEnterLevel();
// finds current leave level. This means all enter levels we encounter
// we will skip, until we have the level of our object - 1
void FindCurrentLeaveLevel();
// skip the data belonging by the header
void SkipData(wxUint8 hdr);
// load the buffer from a stream
void Load(wxMemoryBuffer &buf);
// reads a string (without header)
wxString LoadString();
// reads an array string without header
wxArrayString LoadArrayString();
// reads a datetime without header
wxDateTime LoadDateTime();
// reads a single char from the stream, returns '\0' when none can be read
wxUint8 LoadChar();
// see wxUint8 LoadChar()
wxUint8 LoadUint8();
// reads a wxUint16 (without header)
wxUint16 LoadUint16();
// reads a wxUint32 (without header)
wxUint32 LoadUint32();
// reads a wxUint64 (without header)
wxUint64 LoadUint64();
// reads a wxFloat64 (without header)
wxFloat64 LoadDouble();
// loads integer
int LoadInt();
// loads pair of integers
void LoadIntInt (int& value1, int& value2);
// reads a bool (without header)
bool LoadBool();
// writes a wxUint8 without header
void SaveChar(wxUint8 value);
// see SaveChar(wxUint8)
void SaveUint8(wxUint8 value);
// writes a wxUint16 (without header)
void SaveUint16(wxUint16 value);
// writes a wxUint32 (without header)
void SaveUint32(wxUint32 value);
// writes a wxUint64 (without header)
void SaveUint64(wxUint64 value);
// writes a string (without header)
void SaveString(const wxString &str);
// translates header name to something readable for errors
wxString GetHeaderName(int headername);
/** Sets the error to a specific condition. Valid error conditions are
- wxSERIALIZE_OK
- wxSERIALIZE_ERR_EOF
- wxSERIALIZE_ERR_ILL
- wxSERIALIZE_ERR_MEM
The error will not change from the last error condition when it is
already set. Meaning it will keep the last error, even when others
follow.
*/
void SetError(int err) {
// only set error when we are still ok
if(m_errorCode == wxSERIALIZE_ERR_OK)
m_errorCode = err;
};
// initialize function
void InitAll();
private:
// last error code issued in wxSerialize
int m_errorCode;
// the last logged error message
wxString m_errMsg;
// the header received
wxString m_headerStr;
// The version written or read
size_t m_version;
// opened status (true if open) Close() will close it
bool m_opened;
// partial save or load mode
bool m_partialMode;
// write or read mode of the stream
bool m_writeMode;
// under the hood output data stream (this stream is maintained by us)
wxOutputStream &m_odstr;
// under the hood output data stream (this stream is maintained by us)
wxInputStream &m_idstr;
// object marker level. Any level above 0 means we have encountered
// an EnterObject and LeaveObject.
int m_objectLevel;
// flag to indicate that we encountered a LEAVE or ENTER marker. This means
// no read should be done until we reached the LeaveObject() that
// belongs to our level.
bool m_haveBoundary;
// last boundary encountered
unsigned char m_lastBoundary;
// temp vars that have only the purpose to initialize streams that
// need to be initialized by ref. By using wxStringStream variants
// we can safely read and load from them when this error might occur
wxString m_tmpostr, m_tmpistr;
wxStringOutputStream m_otmp;
wxStringInputStream m_itmp;
// The archive status object
wxSerializeStatus m_status;
};
#endif
|