高效的XML——XDS By MikeFeng 最近在遊戲編程精粹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 cacheextern 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_DEFTYPEvoid 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_DEFTYPEvoid 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)的話就會導致衝突。