浏览代码

add hfile iniparser

ithewei 7 年之前
父节点
当前提交
3a71ce015d
共有 8 个文件被更改,包括 608 次插入85 次删除
  1. 1 0
      h.h
  2. 51 43
      hbuf.h
  3. 53 3
      hdef.h
  4. 76 0
      hfile.h
  5. 52 33
      hstring.cpp
  6. 9 6
      hstring.h
  7. 251 0
      iniparser.cpp
  8. 115 0
      iniparser.h

+ 1 - 0
h.h

@@ -25,6 +25,7 @@
 
 #include "hbuf.h"
 #include "hbytearray.h"
+#include "hfile.h"
 #include "hgui.h"
 
 #include "hframe.h"

+ 51 - 43
hbuf.h

@@ -6,62 +6,70 @@
 #include <string.h>
 #include <mutex>
 
-typedef struct hbuf_s{
+typedef struct hbuf_s {
     uint8* base;
     size_t len;
+    bool   cleanup_;
 
-    hbuf_s(){
+    hbuf_s() {
         base = NULL;
         len  = 0;
+        cleanup_ = false;
     }
 
-    hbuf_s(uint8* base, size_t len){
+    hbuf_s(uint8* base, size_t len) {
         this->base = base;
         this->len  = len;
+        cleanup_ = false;
+    }
+
+    virtual ~hbuf_s() {
+        cleanup();
     }
 
     // warn: must call cleanup when no longer in use, otherwise memory leak.
-    void init(size_t cap){
+    void init(size_t cap) {
         if (cap == len) return;
 
-        if (!base){
+        if (!base) {
             base = (uint8*)malloc(cap);
             memset(base, 0, cap);
-        }else{
+        } else {
             base = (uint8*)realloc(base, cap);
         }
         len = cap;
+        cleanup_ = true;
     }
 
-    void resize(size_t cap){
+    void resize(size_t cap) {
         init(cap);
     }
 
-    void cleanup(){
-        SAFE_FREE(base);
-        len = 0;
+    void cleanup() {
+        if (cleanup_) {
+            SAFE_FREE(base);
+            len = 0;
+            cleanup_ = false;
+        }
     }
 
-    bool isNull(){
+    bool isNull() {
         return base == NULL || len == 0;
     }
 }hbuf_t;
 
-class HBuf : public hbuf_t{
+class HBuf : public hbuf_t {
 public:
-    HBuf() : hbuf_t(){}
+    HBuf() : hbuf_t() {}
     HBuf(size_t cap) {init(cap);}
-    HBuf(void* data, size_t len){
+    HBuf(void* data, size_t len) {
         init(len);
         memcpy(base, data, len);
     }
-    virtual ~HBuf() {cleanup();}
-
-    std::mutex mutex; // used in multi-thread
 };
 
 // VL: Variable-Length
-class HVLBuf : public HBuf{
+class HVLBuf : public HBuf {
 public:
     HVLBuf() : HBuf() {_offset = _size = 0;}
     HVLBuf(size_t cap) : HBuf(cap) {_offset = _size = 0;}
@@ -71,13 +79,13 @@ public:
     uint8* data() {return base+_offset;}
     size_t size() {return _size;}
 
-    void push_front(void* ptr, size_t len){
-        if (len > this->len - _size){
+    void push_front(void* ptr, size_t len) {
+        if (len > this->len - _size) {
             this->len = MAX(this->len, len)*2;
             base = (uint8*)realloc(base, this->len);
         }
         
-        if (_offset < len){
+        if (_offset < len) {
             // move => end
             memmove(base+this->len-_size, data(), _size);
             _offset = this->len-_size;
@@ -88,11 +96,11 @@ public:
         _size += len;
     }
 
-    void push_back(void* ptr, size_t len){
-        if (len > this->len - _size){
+    void push_back(void* ptr, size_t len) {
+        if (len > this->len - _size) {
             this->len = MAX(this->len, len)*2;
             base = (uint8*)realloc(base, this->len);
-        }else if (len > this->len - _offset - _size){
+        }else if (len > this->len - _offset - _size) {
             // move => start
             memmove(base, data(), _size);
             _offset = 0;
@@ -101,8 +109,8 @@ public:
         _size += len;
     }
 
-    void pop_front(void* ptr, size_t len){
-        if (len <= _size){
+    void pop_front(void* ptr, size_t len) {
+        if (len <= _size) {
             if (ptr){
                 memcpy(ptr, data(), len);
             }
@@ -112,32 +120,32 @@ public:
         }
     }
 
-    void pop_back(void* ptr, size_t len){
-        if (len <= _size){
-            if (ptr){
+    void pop_back(void* ptr, size_t len) {
+        if (len <= _size) {
+            if (ptr) {
                 memcpy(ptr, data()+_size-len, len);
             }
             _size -= len;
         }
     }
 
-    void clear(){
+    void clear() {
         _offset = _size = 0;
     }
 
-    void prepend(void* ptr, size_t len){
+    void prepend(void* ptr, size_t len) {
         push_front(ptr, len);
     }
 
-    void append(void* ptr, size_t len){
+    void append(void* ptr, size_t len) {
         push_back(ptr, len);
     }
 
-    void insert(void* ptr, size_t len){
+    void insert(void* ptr, size_t len) {
         push_back(ptr, len);
     }
 
-    void remove(size_t len){
+    void remove(size_t len) {
         pop_front(NULL, len);
     }
 
@@ -146,26 +154,26 @@ private:
     size_t _size;
 };
 
-class HRingBuf : public HBuf{
+class HRingBuf : public HBuf {
 public:
     HRingBuf() : HBuf() {_head = _tail = _size = 0;}
     HRingBuf(size_t cap) : HBuf(cap) {_head = _tail = _size = 0;}
 
-    uint8* alloc(size_t len){
+    uint8* alloc(size_t len) {
         uint8* ret = NULL;
-        if (_head < _tail || _size == 0){
+        if (_head < _tail || _size == 0) {
             // [_tail, this->len) && [0, _head)
-            if (this->len - _tail >= len){
+            if (this->len - _tail >= len) {
                 ret = base + _tail;
                 _tail += len;
                 if (_tail == this->len) _tail = 0;
-            }else if(_head >= len){
+            } else if(_head >= len) {
                 ret = base;
                 _tail = len;
             }
         }else{
             // [_tail, _head)
-            if (_head - _tail >= len){
+            if (_head - _tail >= len) {
                 ret = base + _tail;
                 _tail += len;
             }
@@ -174,12 +182,12 @@ public:
         return ret;
     }
 
-    void   free(size_t len){
+    void   free(size_t len) {
         _size -= len;
-        if (len <= this->len - _head){
+        if (len <= this->len - _head) {
             _head += len;
             if (_head == this->len) _head = 0;
-        }else{
+        } else {
             _head = len;
         }
     }

+ 53 - 3
hdef.h

@@ -14,21 +14,51 @@ typedef long long           int64;
 typedef float               float32;
 typedef double              float64;
 
+typedef unsigned char       BYTE;
+typedef unsigned short      WORD;
 typedef int                 BOOL;
 
 typedef int (*method_t)(void* userdata);
 typedef void (*procedure_t)(void* userdata);
 
+#ifndef MAX_PATH
+#define MAX_PATH          260
+#endif
+
 #ifndef NULL
+#ifdef __cplusplus
 #define NULL    0
+#else
+#define NULL    ((void *)0)
+#endif
+#endif
+
+#ifndef FALSE
+#define FALSE               0
 #endif
 
 #ifndef TRUE
-#define TRUE    1L
+#define TRUE                1
 #endif
 
-#ifndef FALSE
-#define FALSE   0L
+#ifndef IN
+#define IN
+#endif
+
+#ifndef OUT
+#define OUT
+#endif
+
+#ifndef OPTIONAL
+#define OPTIONAL
+#endif
+
+#ifndef REQUIRED
+#define REQUIRED
+#endif
+
+#ifndef REPEATED
+#define REPEATED
 #endif
 
 #ifndef MAX
@@ -51,6 +81,26 @@ typedef void (*procedure_t)(void* userdata);
 ( ((uint32)d) | ( ((uint32)c) << 8 ) | ( ((uint32)b) << 16 ) | ( ((uint32)a) << 24 ) )
 #endif
 
+#ifndef MAKE_WORD
+#define MAKE_WORD(h,l)  ( (((WORD)h) << 8) | (l & 0xff) )
+#endif
+
+#ifndef HIBYTE
+#define HIBYTE(w) ( (BYTE)(((WORD)w) >> 8) )
+#endif
+
+#ifndef LOBYTE
+#define LOBYTE(w) ( (BYTE)(w & 0xff) )
+#endif
+
+#define MAKE_INT32(h,l)   ( ((int32)h) << 16 | (l & 0xffff) )
+#define HIINT16(n)        ( (int16)(((int32)n) >> 16) )
+#define LOINI16(n)        ( (int16)(n & 0xffff) )
+
+#define MAKE_INT64(h,l)   ( ((int64)h) << 32 | (l & 0xffffffff) )
+#define HIINT32(n)        ( (int32)(((int64)n) >> 32) )
+#define LOINI32(n)        ( (int32)(n & 0xffffffff) )
+
 #define FLOAT_PRECISION 1e-6
 #define FLOAT_EQUAL_ZERO(f) (-FLOAT_PRECISION < (f) && (f) < FLOAT_PRECISION)
 

+ 76 - 0
hfile.h

@@ -0,0 +1,76 @@
+#ifndef HW_FILE_H
+#define HW_FILE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "hdef.h"
+#include "hbuf.h"
+#include "herr.h"
+
+class HFile {
+public:
+    HFile() {
+        _fp = NULL;
+    }
+
+    ~HFile() {
+        close();
+    }
+
+    int open(const char* filepath, const char* mode) {
+        close();
+        strncpy(_filepath, filepath, MAX_PATH);
+        _fp = fopen(filepath, mode);
+        if (_fp == NULL) {
+            return ERR_OPEN_FILE;
+        }
+        return 0;
+    }
+
+    void close() {
+        if (_fp) {
+            fclose(_fp);
+            _fp = NULL;
+        }
+    }
+
+    size_t size() {
+        struct _stat stat;
+        _stat(_filepath, &stat);
+        return stat.st_size;       
+    }
+
+    size_t read(void* ptr, size_t len) {
+        return fread(ptr, 1, len, _fp);
+    }
+
+    size_t readall(hbuf_t& buf) {
+        size_t filesize = size();
+        buf.init(filesize);
+        return fread(buf.base, 1, buf.len, _fp);
+    }
+
+    bool readline(string& str) {
+        str.clear();
+        char ch;
+        while (fread(&ch, 1, 1, _fp)) {
+            if (ch == '\n') {
+                return true;
+            }
+            str += ch;
+        }
+        return str.length() != 0;
+    }
+
+    size_t write(const void* ptr, size_t len) {
+        return fwrite(ptr, 1, len, _fp);
+    }
+
+    char  _filepath[MAX_PATH];
+    FILE* _fp;
+};
+
+#endif // HW_FILE_H

+ 52 - 33
hstring.cpp

@@ -1,15 +1,20 @@
 #include "hstring.h"
+
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <stdarg.h>
+
 #include <iostream>
 #include <sstream>
 
+#define SPACE_CHARS     " \t\r\n"
+
 int vscprintf(const char* fmt, va_list ap) {
 	return vsnprintf(NULL, 0, fmt, ap);
 }
 
-std::string asprintf(const char* fmt, ...) {
+string asprintf(const char* fmt, ...) {
 	va_list ap;
 	va_start(ap, fmt);
 	int len = vscprintf(fmt, ap) + 1;
@@ -17,101 +22,115 @@ std::string asprintf(const char* fmt, ...) {
 	// must recall va_start in linux
 	va_start(ap, fmt);
 	char* buf = (char*)malloc(len);
+    memset(buf, 0, len);
 	vsnprintf(buf, len, fmt, ap);
 	va_end(ap);
-	buf[len-1] = '\0';
-	std::string res(buf);
+
+	string res(buf);
 	free(buf);
 	return res;
 }
 
-StringList split(std::string& str, char delim){
+StringList split(const string& str, char delim) {
 	std::stringstream ss;
 	ss << str;
-	std::string item;
+	string item;
 	StringList res;
-	while (std::getline(ss, item, delim)){
+	while (std::getline(ss, item, delim)) {
 		res.push_back(item);
 	}
 	return res;
 }
 
-std::string trim(std::string& str){
-	std::string::size_type pos1 = str.find_first_not_of(" \t\r\n");
-	if (pos1 == std::string::npos)	return "";
+string trim(const string& str){
+	string::size_type pos1 = str.find_first_not_of(SPACE_CHARS);
+	if (pos1 == string::npos)	return "";
 
-	std::string::size_type pos2 = str.find_last_not_of(" \t\r\n");
+	string::size_type pos2 = str.find_last_not_of(SPACE_CHARS);
 	return str.substr(pos1, pos2-pos1+1);
 }
 
-std::string replace(std::string& str, std::string& find, std::string& rep){
-	std::string::size_type pos = 0;
-	std::string::size_type a = find.size();
-	std::string::size_type b = rep.size();
-	while ((pos = str.find(find,pos)) != std::string::npos){
-	    str.replace(pos, a, rep);
+string trimL(const string& str) {
+    string::size_type pos = str.find_first_not_of(SPACE_CHARS);
+    if (pos == string::npos)    return "";
+    return str.substr(pos);
+}
+
+string trimR(const string& str) {
+    string::size_type pos = str.find_last_not_of(SPACE_CHARS);
+    return str.substr(0, pos+1);
+}
+
+string replace(const string& str, const string& find, const string& rep) {
+	string::size_type pos = 0;
+	string::size_type a = find.size();
+	string::size_type b = rep.size();
+
+    string res(str);
+	while ((pos = res.find(find,pos)) != string::npos){
+	    res.replace(pos, a, rep);
 		pos += b;
 	}
-    return str;
+    return res;
 }
 
-string basename(string& str){
+string basename(const string& str) {
     string::size_type pos1 = str.find_last_not_of("/\\");
-    if (pos1 == string::npos){
+    if (pos1 == string::npos) {
         return "/";
     }
     string::size_type pos2 = str.find_last_of("/\\", pos1);
-    if (pos2 == string::npos){
+    if (pos2 == string::npos) {
         pos2 = 0;
-    }else{
+    } else {
         pos2++;
     }
 
     return str.substr(pos2, pos1-pos2+1);
 }
 
-string dirname(string& str){
+string dirname(const string& str) {
     string::size_type pos1 = str.find_last_not_of("/\\");
-    if (pos1 == string::npos){
+    if (pos1 == string::npos) {
         return "/";
     }
     string::size_type pos2 = str.find_last_of("/\\", pos1);
-    if (pos2 == string::npos){
+    if (pos2 == string::npos) {
         return ".";
-    }else if (pos2 == 0){
+    } else if (pos2 == 0) {
         pos2 = 1;
     }
 
     return str.substr(0, pos2);
 }
 
-string filename(string& str){
+string filename(const string& str) {
     string::size_type pos1 = str.find_last_of("/\\");
-    if (pos1 == string::npos){
+    if (pos1 == string::npos) {
         pos1 = 0;
-    }else{
+    } else {
         pos1++;
     }
     string file = str.substr(pos1, -1);
 
     string::size_type pos2 = file.find_last_of(".");
-    if (pos2 == string::npos){
+    if (pos2 == string::npos) {
         return file;
     }
     return file.substr(0, pos2);
 }
 
-string suffixname(string& str){
+string suffixname(const string& str) {
     string::size_type pos1 = str.find_last_of("/\\");
-    if (pos1 == string::npos){
+    if (pos1 == string::npos) {
         pos1 = 0;
-    }else{
+    } else {
         pos1++;
     }
     string file = str.substr(pos1, -1);
 
     string::size_type pos2 = file.find_last_of(".");
-    if (pos2 == string::npos){
+    if (pos2 == string::npos) {
         return "";
     }
     return file.substr(pos2+1, -1);

+ 9 - 6
hstring.h

@@ -8,12 +8,15 @@ using std::string;
 typedef std::vector<string> StringList;
 
 string asprintf(const char* fmt, ...);
-StringList split(string& str, char delim);
-string trim(string& str);
+StringList split(const string& str, char delim);
+string trim(const string& str);
+string trimL(const string& str);
+string trimR(const string& str);
+string replace(const string& str, const string& find, const string& rep);
 
-string basename(string& str);
-string dirname(string& str);
-string filename(string& str);
-string suffixname(string& str);
+string basename(const string& str);
+string dirname(const string& str);
+string filename(const string& str);
+string suffixname(const string& str);
 
 #endif // H_STRING_H

+ 251 - 0
iniparser.cpp

@@ -0,0 +1,251 @@
+#include "iniparser.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sstream>
+
+#include "hlog.h"
+#include "herr.h"
+#include "hfile.h"
+
+IniParser::IniParser() {
+    _comment = DEFAULT_INI_COMMENT;
+    _delim = DEFAULT_INI_DELIM;
+    root_ = NULL;
+}
+
+IniParser::~IniParser() {
+    Unload();
+}
+
+int IniParser::Unload() {
+    SAFE_DELETE(root_);
+    return 0;
+}
+
+int IniParser::LoadFromFile(const char* filepath) {
+    _filepath = filepath;
+
+    HFile file;
+    if (file.open(filepath, "r") != 0) {
+        hloge("Open file [%s] failed!", filepath);
+        return ERR_OPEN_FILE;
+    }
+
+    hbuf_t buf;
+    file.readall(buf);
+    hlogi("filesize=%d", buf.len);
+    hlogi("%s", buf.base);
+
+    return LoadFromMem((const char*)buf.base);
+}
+
+/********************************************
+# test.ini
+# this is a test ini file.
+
+[section] # tail_comment
+# key
+key = value # tail_comment
+
+# other
+# end
+********************************************/
+int IniParser::LoadFromMem(const char* data) {
+    Unload();
+
+    root_ = new IniNode;
+    root_->type = IniNode::INI_NODE_TYPE_ROOT;
+
+    std::stringstream ss;
+    ss << data;
+    std::string strLine;
+    int line = 0;
+    string::size_type pos;
+
+    string      content;
+    string      comment;
+    string      strDiv;
+    IniNode* pScopeNode = root_;
+    IniNode* pNewNode = NULL;
+    while (std::getline(ss, strLine)) {
+        ++line;
+
+        content = trimL(strLine);
+        if (content.length() == 0)  {
+            // blank line
+            strDiv += '\n';
+            continue;
+        }
+
+        // trim_comment
+        comment = "";
+        pos = content.find_first_of(_comment);
+        if (pos != string::npos) {
+            comment = content.substr(pos);
+            content = content.substr(0, pos);
+            hlogi("pos=%d content=%s comment=%s", pos, content.c_str(), comment.c_str());
+        }
+
+        content = trimR(content);
+        if (content.length() == 0) {
+            strDiv += strLine;
+            strDiv += '\n';
+            continue;
+        } else if (strDiv.length() != 0) {
+            IniNode* pNode = new IniNode;
+            pNode->type = IniNode::INI_NODE_TYPE_DIV;
+            pNode->label = strDiv;
+            pScopeNode->Add(pNode);
+            strDiv = "";
+        }
+
+        if (content[0] == '[') {
+            if (content[content.length()-1] == ']') {
+                // section
+                content = trim(content.substr(1,content.length()-2));
+                pNewNode = new IniNode;
+                pNewNode->type = IniNode::INI_NODE_TYPE_SECTION;
+                pNewNode->label = content;
+                root_->Add(pNewNode);
+                pScopeNode = pNewNode;
+            } else {
+                hlogw("format error, line:%d", line);
+                continue;   // ignore    
+            }
+        } else {
+            pos = content.find_first_of(_delim);
+            if (pos != string::npos) {
+                // key-value
+                pNewNode = new IniNode;
+                pNewNode->type = IniNode::INI_NODE_TYPE_KEY_VALUE;
+                pNewNode->label = trim(content.substr(0,pos));
+                pNewNode->value = trim(content.substr(pos+_delim.length()));
+                pScopeNode->Add(pNewNode);
+            } else {
+                hlogw("format error, line:%d", line);
+                continue;   // ignore
+            }
+        }
+
+        if (comment.length() != 0) {
+            // tail_comment
+            IniNode* pNode = new IniNode;
+            pNode->type = IniNode::INI_NODE_TYPE_SPAN;
+            pNode->label = comment;
+            pNewNode->Add(pNode);
+            comment = "";
+        }
+    }
+
+    // file end comment
+    if (strDiv.length() != 0) {
+        IniNode* pNode = new IniNode;
+        pNode->type = IniNode::INI_NODE_TYPE_DIV;
+        pNode->label = strDiv;
+        root_->Add(pNode);
+    }
+
+    return 0;
+}
+
+void IniParser::DumpString(IniNode* pNode, string& str) {
+    if (pNode == NULL)  return;
+
+    if (pNode->type != IniNode::INI_NODE_TYPE_SPAN) {
+        if (str.length() > 0 && str[str.length()-1] != '\n') {
+            str += '\n';
+        }
+    }
+
+    switch (pNode->type) {
+    case IniNode::INI_NODE_TYPE_SECTION: {
+        str += '[';
+        str += pNode->label;
+        str += ']';
+    }
+    break;
+    case IniNode::INI_NODE_TYPE_KEY_VALUE: {
+        str += asprintf("%s %s %s", pNode->label.c_str(), _delim.c_str(), pNode->value.c_str());
+    }
+    break;
+    case IniNode::INI_NODE_TYPE_DIV: {
+        str += pNode->label;
+    }
+    break;
+    case IniNode::INI_NODE_TYPE_SPAN: {
+        str += '\t';
+        str += pNode->label;
+    }
+    break;
+    default:
+    break;
+    }
+
+    for (auto p : pNode->children) {
+        DumpString(p, str);
+    }
+}
+
+string IniParser::DumpString() {
+    string str;
+    DumpString(root_, str);
+    return str;
+}
+
+int IniParser::Save() {
+    return SaveAs(_filepath.c_str());
+}
+
+int IniParser::SaveAs(const char* filepath) {
+    string str = DumpString();
+    if (str.length() == 0) {
+        return 0;
+    }
+
+    HFile file;
+    if (file.open(filepath, "w") != 0) {
+        hloge("Save file [%s] failed.", filepath);
+        return ERR_SAVE_FILE;
+    }
+    file.write(str.c_str(), str.length());
+
+    return 0;
+}
+
+string IniParser::GetValue(const string& key, const string& section) {
+    IniNode* pSection = root_;
+    if (section.length() != 0) {
+        pSection = root_->Get(section, IniNode::INI_NODE_TYPE_SECTION);
+        if (pSection == NULL)   return "";
+    }
+
+    IniNode* pKV = pSection->Get(key, IniNode::INI_NODE_TYPE_KEY_VALUE);
+    if (pKV == NULL)    return "";
+    
+    return pKV->value;
+}
+
+void   IniParser::SetValue(const string& key, const string& value, const string& section) {
+    IniNode* pSection = root_;
+    if (section.length() != 0) {
+        pSection = root_->Get(section, IniNode::INI_NODE_TYPE_SECTION);
+        if (pSection == NULL) {
+            pSection = new IniNode;
+            pSection->type = IniNode::INI_NODE_TYPE_SECTION;
+            pSection->label = section;
+            root_->Add(pSection);
+        }
+    }
+
+    IniNode* pKV = pSection->Get(key, IniNode::INI_NODE_TYPE_KEY_VALUE);
+    if (pKV == NULL) {
+        pKV = new IniNode;
+        pKV->type = IniNode::INI_NODE_TYPE_KEY_VALUE;
+        pKV->label = key;
+        pSection->Add(pKV);
+    }
+    pKV->value = value;
+}

+ 115 - 0
iniparser.h

@@ -0,0 +1,115 @@
+#ifndef HW_INI_PARSER_H
+#define HW_INI_PARSER_H
+
+#include <list>
+
+#include "hdef.h"
+#include "hstring.h"
+
+#define DEFAULT_INI_COMMENT "#"
+#define DEFAULT_INI_DELIM   "="
+
+/**********************************
+# div
+
+[section]
+
+key = value # span
+
+# div
+***********************************/
+
+// class IniComment;
+// class IniSection;
+// class IniKeyValue;
+// for simplicity, we add a member value.
+class IniNode {
+public:
+    enum Type {
+        INI_NODE_TYPE_UNKNOWN,
+        INI_NODE_TYPE_ROOT,
+        INI_NODE_TYPE_SECTION,
+        INI_NODE_TYPE_KEY_VALUE,
+        INI_NODE_TYPE_DIV,
+        INI_NODE_TYPE_SPAN,
+    } type;
+    string  label;  
+    string  value;  
+    std::list<IniNode*>    children;
+
+    virtual ~IniNode() {
+        for (auto& item : children) {
+            SAFE_DELETE(item);
+        }
+        children.clear();
+    }
+
+    void Add(IniNode* pNode) {
+        children.push_back(pNode);
+    }
+
+    void Del(IniNode* pNode) {
+        for (auto iter = children.begin(); iter != children.end(); ++iter) {
+            if ((*iter) == pNode) {
+                delete (*iter);
+                children.erase(iter);
+                return;
+            }
+        }
+    }
+
+    IniNode* Get(const string& label, Type type = INI_NODE_TYPE_KEY_VALUE) {
+        for (auto& pNode : children) {
+            if (pNode->type == type && pNode->label == label) {
+                return pNode;
+            }
+        }
+        return NULL;
+    }
+};
+
+// class IniComment : public IniNode {
+// public:
+//     string comment;
+// };
+
+// class IniSection : public IniNode {
+// public:
+//     string section;
+// };
+
+// class IniKeyValue : public IniNode {
+// public:
+//     string key;
+//     string value;
+// };
+
+class IniParser {
+public:
+    IniParser();
+    ~IniParser();
+
+    void SetIniComment(const string& comment) {_comment = comment;}
+    void SetIniDilim(const string& delim) {_delim = delim;}
+
+    int LoadFromFile(const char* filepath);
+    int LoadFromMem(const char* data);
+    int Unload();
+
+    void DumpString(IniNode* pNode, string& str);
+    string DumpString();
+    int Save();
+    int SaveAs(const char* filepath);
+
+    string GetValue(const string& key, const string& section = "");
+    void   SetValue(const string& key, const string& value, const string& section = "");
+
+private:
+    string  _comment;
+    string  _delim;
+    string  _filepath;
+
+    IniNode* root_;
+};
+
+#endif // HW_INI_PARSER_H