+void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value, struct tlvdb **tlvdb_elm)
+{
+ struct tlvdb *telm = tlvdb_find_full(tlvdb, tag);
+ if (telm == NULL) {
+ // new tlv element
+ struct tlvdb *elm = tlvdb_fixed(tag, len, value);
+ tlvdb_add(tlvdb, elm);
+ if (tlvdb_elm)
+ *tlvdb_elm = elm;
+ } else {
+ // the same tlv structure
+ if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len))
+ return;
+
+ // replace tlv element
+ struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value);
+ tnewelm->next = telm->next;
+ tnewelm->parent = telm->parent;
+
+ // if telm stayed first in children chain
+ if (telm->parent && telm->parent->children == telm) {
+ telm->parent->children = tnewelm;
+ }
+
+ // if telm have previous element
+ if (telm != tlvdb) {
+ // elm in root
+ struct tlvdb *celm = tlvdb;
+ // elm in child list of node
+ if (telm->parent && telm->parent->children)
+ celm = telm->parent->children;
+
+ // find previous element
+ for (; celm; celm = celm->next) {
+ if (celm->next == telm) {
+ celm->next = tnewelm;
+ break;
+ }
+ }
+ }
+
+ // free old element with childrens
+ telm->next = NULL;
+ tlvdb_free(telm);
+
+ if (tlvdb_elm)
+ *tlvdb_elm = tnewelm;
+ }
+
+ return;
+}
+
+void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value)
+{
+ tlvdb_change_or_add_node_ex(tlvdb, tag, len, value, NULL);
+}
+