tree schema NEW unique validation

Several other fixes in lyd_path()
and XML parser are included.
Tests also included.
diff --git a/src/validation.c b/src/validation.c
index e232883..618e096 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -18,6 +18,7 @@
 #include "common.h"
 #include "xpath.h"
 #include "tree_data_internal.h"
+#include "tree_schema_internal.h"
 
 /**
  * @brief Evaluate a single "when" condition.
@@ -211,11 +212,227 @@
     return LY_SUCCESS;
 }
 
+static struct lyd_node *
+lyd_val_uniq_find_leaf(const struct lysc_node_leaf *uniq_leaf, struct lyd_node *list)
+{
+    struct ly_set *set;
+    struct lyd_node *node = NULL;
+
+    set = lyd_find_instance(lyd_node_children(list), (struct lysc_node *)uniq_leaf);
+    if (set && set->count) {
+        /* we were looking for a leaf */
+        assert(set->count == 1);
+        node = set->objs[0];
+    }
+    ly_set_free(set, NULL);
+    return node;
+}
+
+/*
+ * actions (cb_data):
+ * 0  - compare all uniques
+ * n  - compare n-th unique
+ */
+static int
+lyd_val_uniq_list_equal(void *val1_p, void *val2_p, int UNUSED(mod), void *cb_data)
+{
+    struct ly_ctx *ctx;
+    struct lysc_node_list *slist;
+    struct lyd_node *diter, *first, *second;
+    struct lyd_value *val1, *val2;
+    char *path1, *path2, *uniq_str, *ptr;
+    uint32_t i, j, action;
+
+    assert(val1_p && val2_p);
+
+    first = *((struct lyd_node **)val1_p);
+    second = *((struct lyd_node **)val2_p);
+    action = (uintptr_t)cb_data;
+
+    assert(first && (first->schema->nodetype == LYS_LIST));
+    assert(second && (second->schema == first->schema));
+
+    ctx = first->schema->module->ctx;
+
+    slist = (struct lysc_node_list *)first->schema;
+
+    /* compare unique leaves */
+    if (action > 0) {
+        i = action - 1;
+        if (i < LY_ARRAY_SIZE(slist->uniques)) {
+            goto uniquecheck;
+        }
+    }
+    LY_ARRAY_FOR(slist->uniques, i) {
+uniquecheck:
+        LY_ARRAY_FOR(slist->uniques[i], j) {
+            /* first */
+            diter = lyd_val_uniq_find_leaf(slist->uniques[i][j], first);
+            if (diter) {
+                val1 = &((struct lyd_node_term *)diter)->value;
+            } else {
+                /* use default value */
+                val1 = slist->uniques[i][j]->dflt;
+            }
+
+            /* second */
+            diter = lyd_val_uniq_find_leaf(slist->uniques[i][j], second);
+            if (diter) {
+                val2 = &((struct lyd_node_term *)diter)->value;
+            } else {
+                /* use default value */
+                val2 = slist->uniques[i][j]->dflt;
+            }
+
+            if (!val1 || !val2 || val1->realtype->plugin->compare(val1, val2)) {
+                /* values differ or either one is not set */
+                break;
+            }
+        }
+        if (j && (j == LY_ARRAY_SIZE(slist->uniques[i]))) {
+            /* all unique leafs are the same in this set, create this nice error */
+            path1 = lyd_path(first, LYD_PATH_LOG, NULL, 0);
+            path2 = lyd_path(second, LYD_PATH_LOG, NULL, 0);
+
+            /* use buffer to rebuild the unique string */
+            uniq_str = malloc(1024);
+            uniq_str[0] = '\0';
+            ptr = uniq_str;
+            LY_ARRAY_FOR(slist->uniques[i], j) {
+                if (j) {
+                    strcpy(ptr, " ");
+                    ++ptr;
+                }
+                ptr = lysc_path_until((struct lysc_node *)slist->uniques[i][j], (struct lysc_node *)slist, LYSC_PATH_LOG,
+                                      ptr, 1024 - (ptr - uniq_str));
+                if (!ptr) {
+                    /* path will be incomplete, whatever */
+                    break;
+                }
+
+                ptr += strlen(ptr);
+            }
+            LOGVAL(ctx, LY_VLOG_LYD, second, LY_VCODE_NOUNIQ, uniq_str, path1, path2);
+
+            free(path1);
+            free(path2);
+            free(uniq_str);
+            return 1;
+        }
+
+        if (action > 0) {
+            /* done */
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
 static LY_ERR
 lyd_validate_unique(const struct lysc_node *snode, struct lysc_node_leaf ***uniques, struct lyd_node *sibling)
 {
-    /* TODO check uniques in siblings and children */
-    return LY_SUCCESS;
+    struct lyd_node *diter;
+    struct ly_set *set;
+    uint32_t i, j, n = 0;
+    LY_ERR ret = LY_SUCCESS;
+    uint32_t hash, u, usize = 0;
+    int dynamic;
+    const char *str;
+    struct hash_table **uniqtables = NULL;
+    struct lyd_value *val;
+    struct ly_ctx *ctx = snode->module->ctx;
+
+    assert(uniques);
+
+    /* get all list instances */
+    set = lyd_find_instance(sibling, snode);
+    LY_CHECK_RET(!set, LY_EINT);
+
+    if (set->count == 2) {
+        /* simple comparison */
+        if (lyd_val_uniq_list_equal(&set->objs[0], &set->objs[1], 0, (void *)0)) {
+            /* instance duplication */
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+    } else if (set->count > 2) {
+        /* use hashes for comparison */
+        /* first, allocate the table, the size depends on number of items in the set */
+        for (u = 31; u > 0; u--) {
+            usize = set->count << u;
+            usize = usize >> u;
+            if (usize == set->count) {
+                break;
+            }
+        }
+        LY_CHECK_ERR_GOTO(!u, LOGINT(ctx); ret = LY_EINT, cleanup);
+        u = 32 - u;
+        usize = 1 << u;
+
+        uniqtables = malloc(LY_ARRAY_SIZE(uniques) * sizeof *uniqtables);
+        LY_CHECK_ERR_GOTO(!uniqtables, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+        n = LY_ARRAY_SIZE(uniques);
+        for (j = 0; j < n; j++) {
+            uniqtables[j] = lyht_new(usize, sizeof(struct lyd_node *), lyd_val_uniq_list_equal, (void *)(j + 1L), 0);
+            LY_CHECK_ERR_GOTO(!uniqtables[j], LOGMEM(ctx); ret = LY_EMEM, cleanup);
+        }
+
+        for (u = 0; u < set->count; u++) {
+            /* loop for unique - get the hash for the instances */
+            for (i = 0; i < n; i++) {
+                val = NULL;
+                for (j = hash = 0; j < LY_ARRAY_SIZE(uniques[i]); j++) {
+                    diter = lyd_val_uniq_find_leaf(uniques[i][j], set->objs[u]);
+                    if (diter) {
+                        val = &((struct lyd_node_term *)diter)->value;
+                    } else {
+                        /* use default value */
+                        val = uniques[i][j]->dflt;
+                    }
+                    if (!val) {
+                        /* unique item not present nor has default value */
+                        break;
+                    }
+
+                    /* get canonical string value */
+                    str = val->realtype->plugin->print(val, LYD_JSON, json_print_get_prefix, NULL, &dynamic);
+                    hash = dict_hash_multi(hash, str, strlen(str));
+                    if (dynamic) {
+                        free((char *)str);
+                    }
+                }
+                if (!val) {
+                    /* skip this list instance since its unique set is incomplete */
+                    continue;
+                }
+
+                /* finish the hash value */
+                hash = dict_hash_multi(hash, NULL, 0);
+
+                /* insert into the hashtable */
+                ret = lyht_insert(uniqtables[i], &set->objs[u], hash, NULL);
+                if (ret == LY_EEXIST) {
+                    /* instance duplication */
+                    ret = LY_EVALID;
+                }
+                LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+            }
+        }
+    }
+
+cleanup:
+    ly_set_free(set, NULL);
+    for (j = 0; j < n; j++) {
+        if (!uniqtables[j]) {
+            /* failed when allocating uniquetables[j], following j are not allocated */
+            break;
+        }
+        lyht_free(uniqtables[j]);
+    }
+    free(uniqtables);
+
+    return ret;
 }
 
 static LY_ERR
@@ -274,9 +491,7 @@
 {
     struct lyd_node *node;
 
-    /* validate schema-based restrictions */
-    LY_CHECK_RET(lyd_validate_siblings_schema_r(sibling, sparent, mod, options));
-
+    /* validate all restrictions of nodes themselves */
     LY_LIST_FOR(sibling, node) {
         /* TODO node's must */
         /* TODO node instance duplicites */
@@ -284,9 +499,14 @@
         /* TODO node's if-features */
         /* TODO node list keys */
         /* node value including if-feature is checked by plugins */
+    }
 
-        /* validate all children */
-        LY_CHECK_RET(lyd_validate_siblings_r((struct lyd_node *)lyd_node_children(sibling), node->schema, mod, options));
+    /* validate schema-based restrictions */
+    LY_CHECK_RET(lyd_validate_siblings_schema_r(sibling, sparent, mod, options));
+
+    LY_LIST_FOR(sibling, node) {
+        /* validate all children recursively */
+        LY_CHECK_RET(lyd_validate_siblings_r((struct lyd_node *)lyd_node_children(node), node->schema, mod, options));
     }
 
     return LY_SUCCESS;