namespace { template TreePtr dtree_resolve_reference(TreePtr tree) { u32 depth = 0; while(tree && tree->type == 'R' && depth < 16) { TreePtr target = reinterpret_cast(tree->_ptr); if(target == 0 || target == tree) break; tree = target; depth += 1; } return(tree); } } void DTree::each(std::function f) { const DTree& target = deref(); switch(target.type) { case('M'): for (auto it = target._map.begin(); it != target._map.end(); ++it) { f(it->second, it->first); } break; default: f(*this, ""); break; } } bool DTree::is_array() { return(deref().type == 'M'); } String DTree::to_string() { const DTree& target = deref(); switch(target.type) { case('S'): return(target._String); break; case('F'): return(std::to_string(target._float)); break; case('B'): return(target._bool ? "(true)" : "(false)"); break; case('M'): return(""); break; case('P'): return(std::to_string((u64)target._ptr)); break; case('R'): return(""); break; } return(""); } String DTree::to_json(char quote_char) { const DTree& target = deref(); switch(target.type) { case('S'): return(json_escape(target._String, quote_char)); break; case('F'): return(std::to_string(target._float)); break; case('B'): return(target._bool ? "true" : "false"); break; case('M'): return("\"(array)\""); break; case('P'): return("\"(pointer)\""); break; case('R'): return("\"(reference)\""); break; } return("\"(unknown)\""); } String DTree::get_type_name() { const DTree& target = deref(); switch(target.type) { case('S'): return("String"); break; case('F'): return("f64"); break; case('B'): return("bool"); break; case('M'): return("array"); break; case('P'): return("pointer"); break; case('R'): return("reference"); break; } return("unknown"); } DTree DTree::get_by_path(String path, String delim) { const DTree* current = &deref(); if(path == "") return(*current); size_t start = 0; while(start <= path.length()) { size_t end = path.find(delim, start); String segment; if(end == String::npos) segment = path.substr(start); else segment = path.substr(start, end - start); if(segment == "") { if(end == String::npos) break; start = end + delim.length(); continue; } current = ¤t->deref(); if(current->type != 'M') return(DTree()); auto it = current->_map.find(segment); if(it == current->_map.end()) return(DTree()); current = &it->second; if(end == String::npos) break; start = end + delim.length(); } return(current->deref()); } bool DTree::is_reference() { return(type == 'R'); } DTree* DTree::reference_target() { if(type != 'R') return(0); DTree* target = dtree_resolve_reference(this); if(target == 0 || target == this || target->type == 'R') return(0); return(target); } const DTree* DTree::reference_target() const { if(type != 'R') return(0); const DTree* target = dtree_resolve_reference(this); if(target == 0 || target == this || target->type == 'R') return(0); return(target); } DTree& DTree::deref() { DTree* target = dtree_resolve_reference(this); if(target == 0) return(*this); return(*target); } const DTree& DTree::deref() const { const DTree* target = dtree_resolve_reference(this); if(target == 0) return(*this); return(*target); } void DTree::set_type(char t) { DTree* target = reference_target(); if(target) { target->set_type(t); return; } if(type != t) { type = t; switch(type) { case('M'): _map.clear(); _array_index = 0; break; } } } void DTree::set(String s) { DTree* target = reference_target(); if(target) { target->set(s); return; } set_type('S'); _String = s; } void DTree::set(void* p) { DTree* target = reference_target(); if(target) { target->set(p); return; } set_type('P'); _ptr = p; } void DTree::set(f64 f) { DTree* target = reference_target(); if(target) { target->set(f); return; } set_type('F'); _float = f; } void DTree::set_bool(bool b) { DTree* target = reference_target(); if(target) { target->set_bool(b); return; } set_type('B'); _bool = b; } void DTree::set(DTree source) { DTree* target = reference_target(); if(target) { target->set(source); return; } set_type(source.type); switch(type) { case('S'): _String = source._String; break; case('F'): _float = source._float; break; case('B'): _bool = source._bool; break; case('M'): _map = source._map; break; case('P'): _ptr = source._ptr; break; case('R'): _ptr = source._ptr; break; } } void DTree::set(StringMap source) { DTree* target = reference_target(); if(target) { target->set(source); return; } set_type('M'); for (auto it = source.begin(); it != source.end(); ++it) { _map[it->first] = it->second; } } void DTree::set_reference(DTree* target) { type = 'R'; _ptr = target; } DTree* DTree::key(String s) { DTree* target = reference_target(); if(target) return(target->key(s)); set_type('M'); return(&_map[s]); } DTree& DTree::operator [] (String s) { DTree* target = reference_target(); if(target) return((*target)[s]); set_type('M'); return(_map[s]); } void DTree::operator = (String v) { set(v); } void DTree::operator = (f64 v) { set(v); } void DTree::operator = (void* v) { set(v); } void DTree::operator = (DTree v) { set(v); } void DTree::operator = (StringMap v) { set(v); } void DTree::push(DTree& child) { DTree* target = reference_target(); if(target) { target->push(child); return; } set_type('M'); _map[std::to_string(_array_index)] = child; _array_index += 1; } DTree DTree::pop() { DTree* target = reference_target(); if(target) return(target->pop()); set_type('M'); auto last = _map.rbegin(); DTree result = last->second; _map.erase(last->first); return(result); } void DTree::remove(String s) { DTree* target = reference_target(); if(target) { target->remove(s); return; } set_type('M'); _map.erase(s); } void DTree::clear() { DTree* target = reference_target(); if(target) { target->clear(); return; } set_type('M'); _map.clear(); } String to_String(DTree t) { return(t.to_string()); } String var_dump(DTree map, String prefix, String postfix) { String result = ""; if(!map.is_array()) return(map.to_string()); map.each([&] (DTree item, String key) { result += prefix + key + ": " + item.to_string() + postfix; if(item.is_array()) result += var_dump(item, prefix + "\t"); }); return(result); } String json_escape(String s, char quote_char) { //return(String("\"")+s+"\""); String result; u32 i = 0; result.append(1, quote_char); while(i < s.length()) { char c = s[i]; if(c == quote_char) { result.append(1, '\\'); result.append(1, quote_char); } else switch(c) { case('\t'): result.append("\\t"); break; case('\n'): result.append("\\n"); break; case('"'): result.append("\\\""); break; case('\r'): result.append("\\r"); break; case('\\'): result.append("\\\\"); break; case('\b'): result.append("\\b"); break; case('\f'): result.append("\\f"); break; default: result.append(1, c); break; } i += 1; } result.append(1, quote_char); return(result); }