static tb_void_t tb_test_strncmp(tb_char_t const* s1, tb_char_t const* s2, tb_size_t size) { __tb_volatile__ tb_long_t n = 1000000; __tb_volatile__ tb_long_t r = 0; tb_hong_t t = tb_mclock(); while (n--) { r = tb_strncmp(s1, s2, size); } t = tb_mclock() - t; tb_printf("%lld ms, tb_test_strncmp(%s, %s, %u) = %ld\n", t, s1, s2, size, r); }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_int_t xm_string_startswith(lua_State* lua) { // check tb_assert_and_check_return_val(lua, 0); // get the string and prefix size_t prefix_size = 0; tb_char_t const* string = luaL_checkstring(lua, 1); tb_char_t const* prefix = luaL_checklstring(lua, 2, &prefix_size); tb_check_return_val(string && prefix, 0); // string:startswith(prefix)? lua_pushboolean(lua, !tb_strncmp(string, prefix, (tb_size_t)prefix_size)); // ok return 1; }
tb_char_t const* tb_path_relative_to(tb_char_t const* root, tb_char_t const* path, tb_char_t* data, tb_size_t maxn) { // check tb_assert_and_check_return_val(path && data && maxn, tb_null); // trace tb_trace_d("path: %s", path); // the root is the current and the path is absolute? return path directly if (!root && !tb_path_is_absolute(path)) { // copy it tb_strlcpy(data, path, maxn); // translate it return tb_path_translate(data, 0, maxn)? data : tb_null; } // get the absolute path tb_size_t path_size = 0; tb_char_t path_absolute[TB_PATH_MAXN]; tb_size_t path_maxn = sizeof(path_absolute); path = tb_path_absolute(path, path_absolute, path_maxn); path_size = tb_strlen(path); tb_assert_and_check_return_val(path && path_size && path_size < path_maxn, tb_null); // trace tb_trace_d("path_absolute: %s", path); // get the absolute root tb_size_t root_size = 0; tb_char_t root_absolute[TB_PATH_MAXN]; tb_size_t root_maxn = sizeof(root_absolute); if (root) { // get the absolute root root = tb_path_absolute(root, root_absolute, root_maxn); root_size = tb_strlen(root); } else { // get the current directory if (!(root_size = tb_directory_current(root_absolute, root_maxn))) return tb_null; // translate it if (!(root_size = tb_path_translate(root_absolute, root_size, root_maxn))) return tb_null; root = root_absolute; } tb_assert_and_check_return_val(root && root_size && root_size < root_maxn, tb_null); // trace tb_trace_d("root_absolute: %s", root); // same directory? return "." if (path_size == root_size && !tb_strncmp(path, root, root_size)) { // check tb_assert_and_check_return_val(maxn >= 2, "."); // return "." data[0] = '.'; data[1] = '\0'; return data; } // append separator if (path_size + 1 < path_maxn) { path_absolute[path_size++] = TB_PATH_SEPARATOR; path_absolute[path_size] = '\0'; } if (root_size + 1 < root_maxn) { root_absolute[root_size++] = TB_PATH_SEPARATOR; root_absolute[root_size] = '\0'; } // find the common leading directory tb_char_t const* p = path; tb_char_t const* q = root; tb_long_t last = -1; for (; *p && *q && *p == *q; q++, p++) { // save the last separator if (*p == TB_PATH_SEPARATOR) last = q - root; } // is different directory or outside the windows drive root? using the absolute path if (last <= 0 || (last == 2 && root[1] == ':')) { // the path size tb_size_t size = tb_min(path_size - 1, maxn); // copy it tb_strncpy(data, path, size); data[size] = '\0'; } // exists same root? else { // count the remaining levels in root tb_size_t count = 0; tb_char_t const* l = root + last + 1; for (; *l; l++) { if (*l == TB_PATH_SEPARATOR) count++; } // append "../" or "..\\" tb_char_t* d = data; tb_char_t* e = data + maxn; while (count--) { if (d + 3 < e) { d[0] = '.'; d[1] = '.'; d[2] = TB_PATH_SEPARATOR; d += 3; } } // append the left path l = path + last + 1; while (*l && d < e) *d++ = *l++; // remove the last separator if (d > data) d--; // end *d = '\0'; } // trace tb_trace_d("relative: %s", data); // ok? return data; }
tb_long_t tb_string_cstrncmp(tb_string_ref_t string, tb_char_t const* s, tb_size_t n) { // check tb_assert_and_check_return_val(string && s, 0); return tb_strncmp(tb_string_cstr(string), s, n); }
static tb_object_ref_t tb_object_bplist_reader_done(tb_stream_ref_t stream) { // check tb_assert_and_check_return_val(stream, tb_null); // init root tb_object_ref_t root = tb_null; // init reader tb_object_bplist_reader_t reader = {0}; reader.stream = stream; // init size tb_hize_t size = tb_stream_size(stream); tb_assert_and_check_return_val(size, tb_null); // init data tb_byte_t data[32] = {0}; // read magic & version if (!tb_stream_bread(stream, data, 8)) return tb_null; // check magic & version if (tb_strncmp((tb_char_t const*)data, "bplist00", 8)) return tb_null; // seek to tail if (!tb_stream_seek(stream, size - 26)) return tb_null; // read offset size tb_size_t offset_size = tb_stream_bread_u8(stream); tb_trace_d("offset_size: %lu", offset_size); // read item size for array and dictionary tb_size_t item_size = tb_stream_bread_u8(stream); tb_trace_d("item_size: %lu", item_size); // read object count tb_size_t object_count = (tb_size_t)tb_stream_bread_u64_be(stream); tb_trace_d("object_count: %lu", object_count); // read root object tb_size_t root_object = (tb_size_t)tb_stream_bread_u64_be(stream); tb_trace_d("root_object: %lu", root_object); // read offset table index tb_size_t offset_table_index = (tb_size_t)tb_stream_bread_u64_be(stream); tb_trace_d("offset_table_index: %lu", offset_table_index); // check tb_assert_and_check_return_val(item_size && offset_size && object_count, tb_null); // init object hash tb_object_ref_t* object_hash = (tb_object_ref_t*)tb_malloc0(sizeof(tb_object_ref_t) * object_count); tb_assert_and_check_return_val(object_hash, tb_null); // done tb_bool_t failed = tb_false; do { // walk tb_size_t i = 0; for (i = 0; i < object_count; i++) { // seek to the offset entry if (!tb_stream_seek(stream, offset_table_index + i * offset_size)) { failed = tb_true; break; } // read the object offset tb_hize_t offset = 0; switch (offset_size) { case 1: offset = tb_stream_bread_u8(stream); break; case 2: offset = tb_stream_bread_u16_be(stream); break; case 4: offset = tb_stream_bread_u32_be(stream); break; case 8: offset = tb_stream_bread_u64_be(stream); break; default: return tb_null; break; } // seek to the object offset if (!tb_stream_seek(stream, offset)) { failed = tb_true; break; } // read object object_hash[i] = tb_object_bplist_reader_func_object(&reader, item_size); // if (object_hash[i]) tb_object_dump(object_hash[i]); } // failed? tb_check_break(!failed); // build array & dictionary items for (i = 0; i < object_count; i++) { tb_object_ref_t object = object_hash[i]; if (object) { switch (tb_object_type(object)) { case TB_OBJECT_TYPE_ARRAY: { // the priv data tb_byte_t* priv = (tb_byte_t*)tb_object_getp(object); if (priv) { // count tb_size_t count = (tb_size_t)tb_bits_get_u32_ne(priv); if (count) { // goto item data tb_byte_t const* p = priv + sizeof(tb_uint32_t); // walk items tb_size_t j = 0; for (i = 0; j < count; j++) { // the item index tb_size_t item = tb_object_bplist_bits_get(p + j * item_size, item_size); tb_assert(item < object_count && object_hash[item]); // tb_trace_d("item: %d", item); // append item if (item < object_count && object_hash[item]) { tb_object_inc(object_hash[item]); tb_object_array_append(object, object_hash[item]); } } } // exit priv tb_free(priv); tb_object_setp(object, tb_null); // tb_object_dump(object); } } break; case TB_OBJECT_TYPE_DICTIONARY: { // the priv data tb_byte_t* priv = (tb_byte_t*)tb_object_getp(object); if (priv) { // count tb_size_t count = (tb_size_t)tb_bits_get_u32_ne(priv); if (count) { // goto item data tb_byte_t const* p = priv + sizeof(tb_uint32_t); // walk items tb_size_t j = 0; for (i = 0; j < count; j++) { // the key & val tb_size_t key = tb_object_bplist_bits_get(p + j * item_size, item_size); tb_size_t val = tb_object_bplist_bits_get(p + (count + j) * item_size, item_size); tb_assert(key < object_count && object_hash[key]); tb_assert(val < object_count && object_hash[val]); // tb_trace_d("key_val: %u => %lu", key, val); // append the key & val if (key < object_count && val < object_count && object_hash[key] && object_hash[val]) { // key must be string now. tb_assert(tb_object_type(object_hash[key]) == TB_OBJECT_TYPE_STRING); if (tb_object_type(object_hash[key]) == TB_OBJECT_TYPE_STRING) { // set key => val tb_char_t const* skey = tb_object_string_cstr(object_hash[key]); if (skey) { tb_object_inc(object_hash[val]); tb_object_dictionary_set(object, skey, object_hash[val]); } tb_assert(skey); } } } } // exit priv tb_free(priv); tb_object_setp(object, tb_null); // tb_object_dump(object); } } break; default: break; } } } } while (0); // exit object hash if (object_hash) { // root if (root_object < object_count) root = object_hash[root_object]; // refn-- tb_size_t i; for (i = 0; i < object_count; i++) { if (object_hash[i] && i != root_object) tb_object_dec(object_hash[i]); } // exit object hash tb_free(object_hash); object_hash = tb_null; } // ok? return root; }
tb_size_t tb_xml_reader_next(tb_xml_reader_ref_t reader) { // check tb_xml_reader_impl_t* impl = (tb_xml_reader_impl_t*)reader; tb_assert_and_check_return_val(impl && impl->rstream, TB_XML_READER_EVENT_NONE); // reset event impl->event = TB_XML_READER_EVENT_NONE; // next while (!impl->event) { // peek character tb_char_t* pc = tb_null; if (!tb_stream_need(impl->rstream, (tb_byte_t**)&pc, 1) || !pc) break; // is element? if (*pc == '<') { // parse element: <...> tb_char_t const* element = tb_xml_reader_element_parse(impl); tb_assert_and_check_break(element); // is document begin: <?xml version="..." charset=".." ?> tb_size_t size = tb_string_size(&impl->element); if (size > 4 && !tb_strnicmp(element, "?xml", 4)) { // update event impl->event = TB_XML_READER_EVENT_DOCUMENT; // update version & charset tb_xml_node_ref_t attr = (tb_xml_node_ref_t)tb_xml_reader_attributes(reader); for (; attr; attr = attr->next) { if (!tb_string_cstricmp(&attr->name, "version")) tb_string_strcpy(&impl->version, &attr->data); if (!tb_string_cstricmp(&attr->name, "encoding")) tb_string_strcpy(&impl->charset, &attr->data); } // transform stream => utf-8 if (tb_string_cstricmp(&impl->charset, "utf-8") && tb_string_cstricmp(&impl->charset, "utf8")) { // charset tb_size_t charset = TB_CHARSET_TYPE_UTF8; if (!tb_string_cstricmp(&impl->charset, "gb2312") || !tb_string_cstricmp(&impl->charset, "gbk")) charset = TB_CHARSET_TYPE_GB2312; else tb_trace_e("the charset: %s is not supported", tb_string_cstr(&impl->charset)); // init transform stream if (charset != TB_CHARSET_TYPE_UTF8) { #ifdef TB_CONFIG_MODULE_HAVE_CHARSET // init the filter stream if (!impl->fstream) impl->fstream = tb_stream_init_filter_from_charset(impl->istream, charset, TB_CHARSET_TYPE_UTF8); else { // ctrl stream if (!tb_stream_ctrl(impl->fstream, TB_STREAM_CTRL_FLTR_SET_STREAM, impl->istream)) break; // the filter tb_stream_filter_ref_t filter = tb_null; if (!tb_stream_ctrl(impl->fstream, TB_STREAM_CTRL_FLTR_GET_FILTER, &filter)) break; tb_assert_and_check_break(filter); // ctrl filter if (!tb_stream_filter_ctrl(filter, TB_STREAM_FILTER_CTRL_CHARSET_SET_FTYPE, charset)) break; } // open the filter stream if (impl->fstream && tb_stream_open(impl->fstream)) impl->rstream = impl->fstream; tb_string_cstrcpy(&impl->charset, "utf-8"); #else // trace tb_trace_e("unicode type is not supported, please enable charset module config if you want to use it!"); #endif } } } // is document type: <!DOCTYPE ... > else if (size > 8 && !tb_strnicmp(element, "!DOCTYPE", 8)) { // update event impl->event = TB_XML_READER_EVENT_DOCUMENT_TYPE; } // is element end: </name> else if (size > 1 && element[0] == '/') { // check tb_check_break(impl->level); // update event impl->event = TB_XML_READER_EVENT_ELEMENT_END; // leave impl->level--; } // is comment: <!-- text --> else if (size >= 3 && !tb_strncmp(element, "!--", 3)) { // no comment end? if (element[size - 2] != '-' || element[size - 1] != '-') { // patch '>' tb_string_chrcat(&impl->element, '>'); // seek to comment end tb_char_t ch = '\0'; tb_int_t n = 0; while ((ch = tb_stream_bread_s8(impl->rstream))) { // --> if (n == 2 && ch == '>') break; else { // append it tb_string_chrcat(&impl->element, ch); if (ch == '-') n++; else n = 0; } } // update event if (ch != '\0') impl->event = TB_XML_READER_EVENT_COMMENT; } else impl->event = TB_XML_READER_EVENT_COMMENT; } // is cdata: <![CDATA[ text ]]> else if (size >= 8 && !tb_strnicmp(element, "![CDATA[", 8)) { if (element[size - 2] != ']' || element[size - 1] != ']') { // patch '>' tb_string_chrcat(&impl->element, '>'); // seek to cdata end tb_char_t ch = '\0'; tb_int_t n = 0; while ((ch = tb_stream_bread_s8(impl->rstream))) { // ]]> if (n == 2 && ch == '>') break; else { // append it tb_string_chrcat(&impl->element, ch); if (ch == ']') n++; else n = 0; } } // update event if (ch != '\0') impl->event = TB_XML_READER_EVENT_CDATA; } else impl->event = TB_XML_READER_EVENT_CDATA; } // is empty element: <name/> else if (size > 1 && element[size - 1] == '/') { // update event impl->event = TB_XML_READER_EVENT_ELEMENT_EMPTY; } // is element begin: <name> else { // update event impl->event = TB_XML_READER_EVENT_ELEMENT_BEG; // enter impl->level++; } // trace // tb_trace_d("<%s>", element); } // is text: <> text </> else if (*pc) { // parse text: <> ... <> tb_char_t const* text = tb_xml_reader_text_parse(impl); if (text && tb_string_cstrcmp(&impl->text, "\r\n") && tb_string_cstrcmp(&impl->text, "\n")) impl->event = TB_XML_READER_EVENT_TEXT; // trace // tb_trace_d("%s", text); } else { // skip the invalid character if (!tb_stream_skip(impl->rstream, 1)) break; } } // ok? return impl->event; }