最近在游戏编程精粹4(Game Programming Gems 4)中看到了对于XDS的介绍,解开了我对于XML低效的困惑。也许在小型的XML应用中不觉得,但是在大数据量的应用中XML的速度甚至无法和普通的.ini相提并论。首先让我们来看看XDS是什么吧。
XDS技术由DSD和XDS两种文件格式组成。前者跟XSD相似,后者跟XML相似,只不过这两种格式都是二进制的。正是采用了二进制格式,无论是在体积还是在速度上XDS的性能比XML都有明显的提升。目前支持XDS的免费库主要有XDSToolkit,现在可以下载到1.03版本。这是一个开源项目,解压后我们可以看到它由两个工具一个API包组成,另外还附一个例子。两个工具的名字分别叫做xdsConvert和xdsMakeSchema,分别是用来进行XML和XDS相互转换,以及生成DSD文件的。
在一个C/C++项目中,我们经常需要用struct定义一系列数据结构。xdsMakeSchema就可以通过输入数据结构的定义文件.h来生成DSD和相应的c头文件。在一个项目的初期,你可能需要用XML编辑器来编写这个项目所需要的XML数据,然后在程序中通过XDSLiteAPI来进行解析。这套API有两个Paser,一个服务于XML,另一个服务于XDS。当你的项目完全可以自动生成XML的时候就可以由XML转向XDS了。游戏编程精粹中解释的很详细,这边就说说需要注意的地方了。
要利用API对XDS进行解析需要以下步骤:
① 以struct定义的C数据类型
② XDS的数据类型定义,可以在DSD中,也可以在程序中定义
③ 回调函数的编写,主要是XDS_PROCESSNODE函数
以该工具包附带的Powerup为例,struct看起来是这样的:
struct PowerUp_t {
char szName[10]; // display name
char szImage[16]; // image file name
// health increase/decrease (-128 to 127)
signed char iHealth;
// temporary abilities/penalties
// (value is duration in seconds)
unsigned char iInvulnerability;
unsigned char iFastMove;
unsigned char iHighJump;
unsigned char iStunPlayer;
// extra life (count)
unsigned char iLifeUp;
};
// global power-up definition cache
extern struct PowerUp_t *g_PowerUps;
可以通过使用xdsMakeSchema来生成dsd,同时生成的xxxx_dsd.h只是为了免除将dsd文件读入内存,查看它的内容就可以看到它定义了一个dsd数组:
// XDS DSD literal -- use this in calls to xdsInit()
//
#ifdef DEFINE_DSD
const unsigned char XDSDSD_Powerups[216] = {
0x58, 0x44, 0x53, 0x21, 0x30, 0x33, 。。。
};
#else
extern const unsigned char XDSDSD_Powerups[216];
#endif
// XDS DSD IDs -- use these in implementation of XDS_PROCESSNODE()
//
#define XDS_Powerups_Powerup 0x0100 // Record
#define XDS_Powerups_PowerUp_t 0x0101 // Type
#define XDS_Powerups__xdsType1 0x0102 // Type
#define XDS_Powerups_g_PowerUps 0x0103 // Element
同时还定义了一些常量,这些常量在解析xds中会用到。
除了在dsd中对于xds格式的定义之外,我们还可以在main.cpp中看到程序内的定义:
#ifdef XDS_SUPPORT_DEFTYPE
void regDsd(struct xdsHandle *hXds)
{
// Register my types (test only)
xdsDefRecord(hXds, "Powerup", 2);
unsigned short iStructType = xdsDefStructType(hXds, "PowerUp_t");
xdsDefStructField(hXds, iStructType, "szName", XDS_TYPE_CHAR, 10);
xdsDefStructField(hXds, iStructType, "szImage", XDS_TYPE_CHAR, 16);
xdsDefStructField(hXds, iStructType, "iHealth", XDS_TYPE_CHAR, 0);
xdsDefStructField(hXds, iStructType, "iInvulnerability", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iFastMove", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iHighJump", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iStunPlayer", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iLifeUp", XDS_TYPE_BYTE, 0);
xdsDefStructDone(hXds, iStructType);
unsigned short iArrayType = xdsDefArrayType(hXds, "_xdsType1", iStructType, 0, 2);
xdsDefElement(hXds, "g_PowerUps", iArrayType, 0);
}
#endif
注意:交叉使用dsd定义和程序定义容易造成一个错误,就是在程序和dsd可能在定义的时候冲突,数据类型冲突,或者数据长度冲突,从而导致程序的崩溃。附带的例子中程序定义数据类型如下:
#ifdef XDS_SUPPORT_DEFTYPE
void regDsd(struct xdsHandle *hXds)
{
// Register my types (test only)
xdsDefRecord(hXds, "Powerup", 4);
unsigned short iStructType = xdsDefStructType(hXds, "PowerUp_t");
xdsDefStructField(hXds, iStructType, "szName", XDS_TYPE_CHAR, 10);
xdsDefStructField(hXds, iStructType, "szImage", XDS_TYPE_CHAR, 16);
xdsDefStructField(hXds, iStructType, "iHealth", XDS_TYPE_CHAR, 0);
xdsDefStructField(hXds, iStructType, "iInvulnerability", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iFastMove", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iHighJump", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iStunPlayer", XDS_TYPE_BYTE, 0);
xdsDefStructField(hXds, iStructType, "iLifeUp", XDS_TYPE_BYTE, 0);
xdsDefStructDone(hXds, iStructType);
unsigned short iArrayType = xdsDefArrayType(hXds, "_xdsType1", iStructType, 0, 2);
xdsDefElement(hXds, "g_PowerUps", iArrayType, 0);
}
#endif
要是在生成dsd时用参数-r Powerup:2而这里用xdsDefRecord(hXds, "Powerup", 4)的话就会导致冲突。