hash table FEATURE make hash table public
diff --git a/src/common.h b/src/common.h
index bb4e49d..82b0603 100644
--- a/src/common.h
+++ b/src/common.h
@@ -23,7 +23,7 @@
 #include "compat.h"
 #include "config.h"
 #include "context.h"
-#include "hash_table.h"
+#include "hash_table_internal.h"
 #include "log.h"
 #include "schema_compile.h"
 #include "set.h"
diff --git a/src/context.c b/src/context.c
index b0d8dcc..7a14203 100644
--- a/src/context.c
+++ b/src/context.c
@@ -680,25 +680,25 @@
 
     while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
         /* name */
-        hash = dict_hash_multi(hash, mod->name, strlen(mod->name));
+        hash = lyht_hash_multi(hash, mod->name, strlen(mod->name));
 
         /* revision */
         if (mod->revision) {
-            hash = dict_hash_multi(hash, mod->revision, strlen(mod->revision));
+            hash = lyht_hash_multi(hash, mod->revision, strlen(mod->revision));
         }
 
         /* enabled features */
         while ((f = lysp_feature_next(f, mod->parsed, &fi))) {
             if (f->flags & LYS_FENABLED) {
-                hash = dict_hash_multi(hash, f->name, strlen(f->name));
+                hash = lyht_hash_multi(hash, f->name, strlen(f->name));
             }
         }
 
         /* imported/implemented */
-        hash = dict_hash_multi(hash, (char *)&mod->implemented, sizeof mod->implemented);
+        hash = lyht_hash_multi(hash, (char *)&mod->implemented, sizeof mod->implemented);
     }
 
-    hash = dict_hash_multi(hash, NULL, 0);
+    hash = lyht_hash_multi(hash, NULL, 0);
     return hash;
 }
 
diff --git a/src/dict.c b/src/dict.c
new file mode 100644
index 0000000..07631f6
--- /dev/null
+++ b/src/dict.c
@@ -0,0 +1,265 @@
+/**
+ * @file dict.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang dictionary for storing strings
+ *
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "dict.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+
+/* starting size of the dictionary */
+#define LYDICT_MIN_SIZE 1024
+
+/**
+ * @brief Comparison callback for dictionary's hash table
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
+{
+    LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
+
+    const char *str1 = ((struct ly_dict_rec *)val1_p)->value;
+    const char *str2 = ((struct ly_dict_rec *)val2_p)->value;
+
+    LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+    LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+    if (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
+        return 1;
+    }
+
+    return 0;
+}
+
+void
+lydict_init(struct ly_dict *dict)
+{
+    LY_CHECK_ARG_RET(NULL, dict, );
+
+    dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct ly_dict_rec), lydict_val_eq, NULL, 1);
+    LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
+    pthread_mutex_init(&dict->lock, NULL);
+}
+
+void
+lydict_clean(struct ly_dict *dict)
+{
+    struct ly_dict_rec *dict_rec = NULL;
+    struct ly_ht_rec *rec = NULL;
+
+    LY_CHECK_ARG_RET(NULL, dict, );
+
+    for (uint32_t i = 0; i < dict->hash_tab->size; i++) {
+        /* get ith record */
+        rec = (struct ly_ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
+        if (rec->hits == 1) {
+            /*
+             * this should not happen, all records inserted into
+             * dictionary are supposed to be removed using lydict_remove()
+             * before calling lydict_clean()
+             */
+            dict_rec = (struct ly_dict_rec *)rec->val;
+            LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
+            /* if record wasn't removed before free string allocated for that record */
+#ifdef NDEBUG
+            free(dict_rec->value);
+#endif
+        }
+    }
+
+    /* free table and destroy mutex */
+    lyht_free(dict->hash_tab, NULL);
+    pthread_mutex_destroy(&dict->lock);
+}
+
+static ly_bool
+lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *cb_data)
+{
+    LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
+
+    const char *str1 = ((struct ly_dict_rec *)val1_p)->value;
+    const char *str2 = ((struct ly_dict_rec *)val2_p)->value;
+
+    LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+    LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+    if (mod) {
+        /* used when inserting new values */
+        if (strcmp(str1, str2) == 0) {
+            return 1;
+        }
+    } else {
+        /* used when finding the original value again in the resized table */
+        return lydict_val_eq(val1_p, val2_p, mod, cb_data);
+    }
+
+    return 0;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_remove(const struct ly_ctx *ctx, const char *value)
+{
+    LY_ERR ret = LY_SUCCESS;
+    size_t len;
+    uint32_t hash;
+    struct ly_dict_rec rec, *match = NULL;
+    char *val_p;
+
+    if (!ctx || !value) {
+        return LY_SUCCESS;
+    }
+
+    LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
+
+    len = strlen(value);
+    hash = lyht_hash(value, len);
+
+    /* create record for lyht_find call */
+    rec.value = (char *)value;
+    rec.refcount = 0;
+
+    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+    /* set len as data for compare callback */
+    lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+    /* check if value is already inserted */
+    ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
+
+    if (ret == LY_SUCCESS) {
+        LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
+
+        /* if value is already in dictionary, decrement reference counter */
+        match->refcount--;
+        if (match->refcount == 0) {
+            /*
+             * remove record
+             * save pointer to stored string before lyht_remove to
+             * free it after it is removed from hash table
+             */
+            val_p = match->value;
+            ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
+            free(val_p);
+            LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
+        }
+    } else if (ret == LY_ENOTFOUND) {
+        LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
+    } else {
+        LOGINT(ctx);
+    }
+
+finish:
+    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+    return ret;
+}
+
+LY_ERR
+dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct ly_dict_rec *match = NULL, rec;
+    uint32_t hash;
+
+    LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
+
+    hash = lyht_hash(value, len);
+    /* set len as data for compare callback */
+    lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+    /* create record for lyht_insert */
+    rec.value = value;
+    rec.refcount = 1;
+
+    ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
+    if (ret == LY_EEXIST) {
+        match->refcount++;
+        if (zerocopy) {
+            free(value);
+        }
+        ret = LY_SUCCESS;
+    } else if (ret == LY_SUCCESS) {
+        if (!zerocopy) {
+            /*
+             * allocate string for new record
+             * record is already inserted in hash table
+             */
+            match->value = malloc(sizeof *match->value * (len + 1));
+            LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
+            if (len) {
+                memcpy(match->value, value, len);
+            }
+            match->value[len] = '\0';
+        }
+    } else {
+        /* lyht_insert returned error */
+        if (zerocopy) {
+            free(value);
+        }
+        return ret;
+    }
+
+    if (str_p) {
+        *str_p = match->value;
+    }
+
+    return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
+{
+    LY_ERR result;
+
+    LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+    if (!value) {
+        *str_p = NULL;
+        return LY_SUCCESS;
+    }
+
+    if (!len) {
+        len = strlen(value);
+    }
+
+    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+    result = dict_insert(ctx, (char *)value, len, 0, str_p);
+    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+    return result;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
+{
+    LY_ERR result;
+
+    LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+    if (!value) {
+        *str_p = NULL;
+        return LY_SUCCESS;
+    }
+
+    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+    result = dict_insert(ctx, value, strlen(value), 1, str_p);
+    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+    return result;
+}
diff --git a/src/hash_table.c b/src/hash_table.c
index 90915f0..0523d8e 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -1,9 +1,10 @@
 /**
  * @file hash_table.c
  * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief libyang dictionary for storing strings and generic hash table
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang generic hash table implementation
  *
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -25,80 +26,8 @@
 #include "dict.h"
 #include "log.h"
 
-#define LYDICT_MIN_SIZE 1024
-
-/**
- * @brief Comparison callback for dictionary's hash table
- *
- * Implementation of ::lyht_value_equal_cb.
- */
-static ly_bool
-lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
-{
-    LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
-
-    const char *str1 = ((struct ly_dict_rec *)val1_p)->value;
-    const char *str2 = ((struct ly_dict_rec *)val2_p)->value;
-
-    LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
-    LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
-
-    if (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
-        return 1;
-    }
-
-    return 0;
-}
-
-void
-lydict_init(struct ly_dict *dict)
-{
-    LY_CHECK_ARG_RET(NULL, dict, );
-
-    dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct ly_dict_rec), lydict_val_eq, NULL, 1);
-    LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
-    pthread_mutex_init(&dict->lock, NULL);
-}
-
-void
-lydict_clean(struct ly_dict *dict)
-{
-    struct ly_dict_rec *dict_rec = NULL;
-    struct ly_ht_rec *rec = NULL;
-
-    LY_CHECK_ARG_RET(NULL, dict, );
-
-    for (uint32_t i = 0; i < dict->hash_tab->size; i++) {
-        /* get ith record */
-        rec = (struct ly_ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
-        if (rec->hits == 1) {
-            /*
-             * this should not happen, all records inserted into
-             * dictionary are supposed to be removed using lydict_remove()
-             * before calling lydict_clean()
-             */
-            dict_rec = (struct ly_dict_rec *)rec->val;
-            LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
-            /* if record wasn't removed before free string allocated for that record */
-#ifdef NDEBUG
-            free(dict_rec->value);
-#endif
-        }
-    }
-
-    /* free table and destroy mutex */
-    lyht_free(dict->hash_tab, NULL);
-    pthread_mutex_destroy(&dict->lock);
-}
-
-/*
- * Usage:
- * - init hash to 0
- * - repeatedly call dict_hash_multi(), provide hash from the last call
- * - call dict_hash_multi() with key_part = NULL to finish the hash
- */
-uint32_t
-dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash_multi(uint32_t hash, const char *key_part, size_t len)
 {
     uint32_t i;
 
@@ -117,191 +46,13 @@
     return hash;
 }
 
-/*
- * Bob Jenkin's one-at-a-time hash
- * http://www.burtleburtle.net/bob/hash/doobs.html
- *
- * Spooky hash is faster, but it works only for little endian architectures.
- */
-uint32_t
-dict_hash(const char *key, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash(const char *key, size_t len)
 {
     uint32_t hash;
 
-    hash = dict_hash_multi(0, key, len);
-    return dict_hash_multi(hash, NULL, len);
-}
-
-static ly_bool
-lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *cb_data)
-{
-    LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
-
-    const char *str1 = ((struct ly_dict_rec *)val1_p)->value;
-    const char *str2 = ((struct ly_dict_rec *)val2_p)->value;
-
-    LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
-    LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
-
-    if (mod) {
-        /* used when inserting new values */
-        if (strcmp(str1, str2) == 0) {
-            return 1;
-        }
-    } else {
-        /* used when finding the original value again in the resized table */
-        return lydict_val_eq(val1_p, val2_p, mod, cb_data);
-    }
-
-    return 0;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_remove(const struct ly_ctx *ctx, const char *value)
-{
-    LY_ERR ret = LY_SUCCESS;
-    size_t len;
-    uint32_t hash;
-    struct ly_dict_rec rec, *match = NULL;
-    char *val_p;
-
-    if (!ctx || !value) {
-        return LY_SUCCESS;
-    }
-
-    LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
-
-    len = strlen(value);
-    hash = dict_hash(value, len);
-
-    /* create record for lyht_find call */
-    rec.value = (char *)value;
-    rec.refcount = 0;
-
-    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
-    /* set len as data for compare callback */
-    lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
-    /* check if value is already inserted */
-    ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
-
-    if (ret == LY_SUCCESS) {
-        LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
-
-        /* if value is already in dictionary, decrement reference counter */
-        match->refcount--;
-        if (match->refcount == 0) {
-            /*
-             * remove record
-             * save pointer to stored string before lyht_remove to
-             * free it after it is removed from hash table
-             */
-            val_p = match->value;
-            ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
-            free(val_p);
-            LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
-        }
-    } else if (ret == LY_ENOTFOUND) {
-        LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
-    } else {
-        LOGINT(ctx);
-    }
-
-finish:
-    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-    return ret;
-}
-
-LY_ERR
-dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
-{
-    LY_ERR ret = LY_SUCCESS;
-    struct ly_dict_rec *match = NULL, rec;
-    uint32_t hash;
-
-    LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
-
-    hash = dict_hash(value, len);
-    /* set len as data for compare callback */
-    lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
-    /* create record for lyht_insert */
-    rec.value = value;
-    rec.refcount = 1;
-
-    ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
-    if (ret == LY_EEXIST) {
-        match->refcount++;
-        if (zerocopy) {
-            free(value);
-        }
-        ret = LY_SUCCESS;
-    } else if (ret == LY_SUCCESS) {
-        if (!zerocopy) {
-            /*
-             * allocate string for new record
-             * record is already inserted in hash table
-             */
-            match->value = malloc(sizeof *match->value * (len + 1));
-            LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
-            if (len) {
-                memcpy(match->value, value, len);
-            }
-            match->value[len] = '\0';
-        }
-    } else {
-        /* lyht_insert returned error */
-        if (zerocopy) {
-            free(value);
-        }
-        return ret;
-    }
-
-    if (str_p) {
-        *str_p = match->value;
-    }
-
-    return ret;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
-{
-    LY_ERR result;
-
-    LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
-    if (!value) {
-        *str_p = NULL;
-        return LY_SUCCESS;
-    }
-
-    if (!len) {
-        len = strlen(value);
-    }
-
-    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
-    result = dict_insert(ctx, (char *)value, len, 0, str_p);
-    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
-    return result;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
-{
-    LY_ERR result;
-
-    LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
-    if (!value) {
-        *str_p = NULL;
-        return LY_SUCCESS;
-    }
-
-    pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
-    result = dict_insert(ctx, value, strlen(value), 1, str_p);
-    pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
-    return result;
+    hash = lyht_hash_multi(0, key, len);
+    return lyht_hash_multi(hash, NULL, len);
 }
 
 struct ly_ht_rec *
@@ -310,7 +61,7 @@
     return (struct ly_ht_rec *)&recs[idx * rec_size];
 }
 
-struct ly_ht *
+LIBYANG_API_DEF struct ly_ht *
 lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize)
 {
     struct ly_ht *ht;
@@ -342,7 +93,7 @@
     return ht;
 }
 
-lyht_value_equal_cb
+LIBYANG_API_DEF lyht_value_equal_cb
 lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal)
 {
     lyht_value_equal_cb prev;
@@ -352,7 +103,7 @@
     return prev;
 }
 
-void *
+LIBYANG_API_DEF void *
 lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data)
 {
     void *prev;
@@ -362,7 +113,7 @@
     return prev;
 }
 
-struct ly_ht *
+LIBYANG_API_DEF struct ly_ht *
 lyht_dup(const struct ly_ht *orig)
 {
     struct ly_ht *ht;
@@ -380,7 +131,7 @@
     return ht;
 }
 
-void
+LIBYANG_API_DEF void
 lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p))
 {
     struct ly_ht_rec *rec;
@@ -621,7 +372,7 @@
     return LY_ENOTFOUND;
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_find(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
 {
     struct ly_ht_rec *rec;
@@ -634,7 +385,7 @@
     return rec ? LY_SUCCESS : LY_ENOTFOUND;
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_find_next_with_collision_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
         lyht_value_equal_cb collision_val_equal, void **match_p)
 {
@@ -680,13 +431,13 @@
     return LY_ENOTFOUND;
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_find_next(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
 {
     return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
         void **match_p)
 {
@@ -773,13 +524,13 @@
     return ret;
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
 {
     return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
 {
     struct ly_ht_rec *rec, *crec;
@@ -862,13 +613,13 @@
     return ret;
 }
 
-LY_ERR
+LIBYANG_API_DEF LY_ERR
 lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash)
 {
     return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
 }
 
-uint32_t
+LIBYANG_API_DEF uint32_t
 lyht_get_fixed_size(uint32_t item_count)
 {
     uint32_t i, size = 0;
diff --git a/src/hash_table.h b/src/hash_table.h
index 170f5b9..c45a6da 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -4,7 +4,7 @@
  * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief libyang hash table
  *
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -20,6 +20,10 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include "compat.h"
 #include "log.h"
 
@@ -28,15 +32,29 @@
  *
  * Usage:
  * - init hash to 0
- * - repeatedly call ::dict_hash_multi(), provide hash from the last call
- * - call ::dict_hash_multi() with key_part = NULL to finish the hash
+ * - repeatedly call ::lyht_hash_multi(), provide hash from the last call
+ * - call ::lyht_hash_multi() with key_part = NULL to finish the hash
+ *
+ * @param[in] hash Previous hash.
+ * @param[in] key_part Next key to hash,
+ * @param[in] len Length of @p key_part.
+ * @return Hash with the next key.
  */
-uint32_t dict_hash_multi(uint32_t hash, const char *key_part, size_t len);
+LIBYANG_API_DECL uint32_t lyht_hash_multi(uint32_t hash, const char *key_part, size_t len);
 
 /**
  * @brief Compute hash from a string.
+ *
+ * Bob Jenkin's one-at-a-time hash
+ * http://www.burtleburtle.net/bob/hash/doobs.html
+ *
+ * Spooky hash is faster, but it works only for little endian architectures.
+ *
+ * @param[in] key Key to hash.
+ * @param[in] len Length of @p key.
+ * @return Hash of the key.
  */
-uint32_t dict_hash(const char *key, size_t len);
+LIBYANG_API_DECL uint32_t lyht_hash(const char *key, size_t len);
 
 /**
  * @brief Callback for checking hash table values equivalence.
@@ -49,82 +67,6 @@
  */
 typedef ly_bool (*lyht_value_equal_cb)(void *val1_p, void *val2_p, ly_bool mod, void *cb_data);
 
-/** reference value for 100% */
-#define LYHT_HUNDRED_PERCENTAGE 100
-
-/** when the table is at least this much percent full, it is enlarged (double the size) */
-#define LYHT_ENLARGE_PERCENTAGE 75
-
-/** only once the table is this much percent full, enable shrinking */
-#define LYHT_FIRST_SHRINK_PERCENTAGE 50
-
-/** when the table is less than this much percent full, it is shrunk (half the size) */
-#define LYHT_SHRINK_PERCENTAGE 25
-
-/** when the table has less than this much percent empty records, it is rehashed to get rid of all the invalid records */
-#define LYHT_REHASH_PERCENTAGE 2
-
-/** never shrink beyond this size */
-#define LYHT_MIN_SIZE 8
-
-/**
- * @brief Generic hash table record.
- */
-struct ly_ht_rec {
-    uint32_t hash;        /* hash of the value */
-    int32_t hits;         /* collision/overflow value count - 1 (a filled entry has 1 hit,
-                           * special value -1 means a deleted record) */
-    unsigned char val[1]; /* arbitrary-size value */
-} _PACKED;
-
-/**
- * @brief (Very) generic hash table.
- *
- * Hash table with open addressing collision resolution and
- * linear probing of interval 1 (next free record is used).
- * Removal is lazy (removed records are only marked), but
- * if possible, they are fully emptied.
- */
-struct ly_ht {
-    uint32_t used;        /* number of values stored in the hash table (filled records) */
-    uint32_t size;        /* always holds 2^x == size (is power of 2), actually number of records allocated */
-    uint32_t invalid;     /* number of invalid records (deleted) */
-    lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
-    void *cb_data;        /* user data callback arbitrary value */
-    uint16_t resize;      /* 0 - resizing is disabled, *
-                           * 1 - enlarging is enabled, *
-                           * 2 - both shrinking and enlarging is enabled */
-    uint16_t rec_size;    /* real size (in bytes) of one record for accessing recs array */
-    unsigned char *recs;  /* pointer to the hash table itself (array of struct ht_rec) */
-};
-
-struct ly_dict_rec {
-    char *value;
-    uint32_t refcount;
-};
-
-/**
- * dictionary to store repeating strings
- */
-struct ly_dict {
-    struct ly_ht *hash_tab;
-    pthread_mutex_t lock;
-};
-
-/**
- * @brief Initiate content (non-zero values) of the dictionary
- *
- * @param[in] dict Dictionary table to initiate
- */
-void lydict_init(struct ly_dict *dict);
-
-/**
- * @brief Cleanup the dictionary content
- *
- * @param[in] dict Dictionary table to cleanup
- */
-void lydict_clean(struct ly_dict *dict);
-
 /**
  * @brief Create new hash table.
  *
@@ -135,7 +77,8 @@
  * @param[in] resize Whether to resize the table on too few/too many records taken.
  * @return Empty hash table, NULL on error.
  */
-struct ly_ht *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize);
+LIBYANG_API_DECL struct ly_ht *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data,
+        uint16_t resize);
 
 /**
  * @brief Set hash table value equal callback.
@@ -144,7 +87,7 @@
  * @param[in] new_val_equal New callback for checking value equivalence.
  * @return Previous callback for checking value equivalence.
  */
-lyht_value_equal_cb lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal);
+LIBYANG_API_DECL lyht_value_equal_cb lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal);
 
 /**
  * @brief Set hash table value equal callback user data.
@@ -153,7 +96,7 @@
  * @param[in] new_cb_data New data for values callback.
  * @return Previous data for values callback.
  */
-void *lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data);
+LIBYANG_API_DECL void *lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data);
 
 /**
  * @brief Make a duplicate of an existing hash table.
@@ -161,7 +104,7 @@
  * @param[in] orig Original hash table to duplicate.
  * @return Duplicated hash table @p orig, NULL on error.
  */
-struct ly_ht *lyht_dup(const struct ly_ht *orig);
+LIBYANG_API_DECL struct ly_ht *lyht_dup(const struct ly_ht *orig);
 
 /**
  * @brief Free a hash table.
@@ -169,7 +112,7 @@
  * @param[in] ht Hash table to be freed.
  * @param[in] val_free Optional callback for freeing all the stored values, @p val_p is a pointer to a stored value.
  */
-void lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p));
+LIBYANG_API_DECL void lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p));
 
 /**
  * @brief Find a value in a hash table.
@@ -181,7 +124,7 @@
  * @return LY_SUCCESS if value was found,
  * @return LY_ENOTFOUND if not found.
  */
-LY_ERR lyht_find(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_find(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
 
 /**
  * @brief Find another equal value in the hash table.
@@ -193,7 +136,7 @@
  * @return LY_SUCCESS if value was found,
  * @return LY_ENOTFOUND if not found.
  */
-LY_ERR lyht_find_next(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_find_next(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
 
 /**
  * @brief Find another equal value in the hash table. Same functionality as ::lyht_find_next()
@@ -207,7 +150,7 @@
  * @return LY_SUCCESS if value was found,
  * @return LY_ENOTFOUND if not found.
  */
-LY_ERR lyht_find_next_with_collision_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+LIBYANG_API_DECL LY_ERR lyht_find_next_with_collision_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
         lyht_value_equal_cb collision_val_equal, void **match_p);
 
 /**
@@ -222,7 +165,7 @@
  * @return LY_EEXIST in case the value is already present.
  * @return LY_EMEM in case of memory allocation failure.
  */
-LY_ERR lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
 
 /**
  * @brief Insert a value into hash table. Same functionality as ::lyht_insert()
@@ -239,8 +182,8 @@
  * @return LY_EEXIST in case the value is already present.
  * @return LY_EMEM in case of memory allocation failure.
  */
-LY_ERR lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
-        void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+        lyht_value_equal_cb resize_val_equal, void **match_p);
 
 /**
  * @brief Remove a value from a hash table.
@@ -252,7 +195,7 @@
  * @return LY_SUCCESS on success,
  * @return LY_ENOTFOUND if value was not found.
  */
-LY_ERR lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash);
+LIBYANG_API_DECL LY_ERR lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash);
 
 /**
  * @brief Remove a value from a hash table. Same functionality as ::lyht_remove()
@@ -267,7 +210,8 @@
  * @return LY_SUCCESS on success,
  * @return LY_ENOTFOUND if value was not found.
  */
-LY_ERR lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal);
+LIBYANG_API_DECL LY_ERR lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+        lyht_value_equal_cb resize_val_equal);
 
 /**
  * @brief Get suitable size of a hash table for a fixed number of items.
@@ -275,6 +219,10 @@
  * @param[in] item_count Number of stored items.
  * @return Hash table size.
  */
-uint32_t lyht_get_fixed_size(uint32_t item_count);
+LIBYANG_API_DECL uint32_t lyht_get_fixed_size(uint32_t item_count);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* LY_HASH_TABLE_H_ */
diff --git a/src/hash_table_internal.h b/src/hash_table_internal.h
new file mode 100644
index 0000000..eaadbc7
--- /dev/null
+++ b/src/hash_table_internal.h
@@ -0,0 +1,105 @@
+/**
+ * @file hash_table_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang hash table internal header
+ *
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_HASH_TABLE_INTERNAL_H_
+#define LY_HASH_TABLE_INTERNAL_H_
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "compat.h"
+#include "hash_table.h"
+
+/** reference value for 100% */
+#define LYHT_HUNDRED_PERCENTAGE 100
+
+/** when the table is at least this much percent full, it is enlarged (double the size) */
+#define LYHT_ENLARGE_PERCENTAGE 75
+
+/** only once the table is this much percent full, enable shrinking */
+#define LYHT_FIRST_SHRINK_PERCENTAGE 50
+
+/** when the table is less than this much percent full, it is shrunk (half the size) */
+#define LYHT_SHRINK_PERCENTAGE 25
+
+/** when the table has less than this much percent empty records, it is rehashed to get rid of all the invalid records */
+#define LYHT_REHASH_PERCENTAGE 2
+
+/** never shrink beyond this size */
+#define LYHT_MIN_SIZE 8
+
+/**
+ * @brief Generic hash table record.
+ */
+struct ly_ht_rec {
+    uint32_t hash;        /* hash of the value */
+    int32_t hits;         /* collision/overflow value count - 1 (a filled entry has 1 hit,
+                           * special value -1 means a deleted record) */
+    unsigned char val[1]; /* arbitrary-size value */
+} _PACKED;
+
+/**
+ * @brief (Very) generic hash table.
+ *
+ * Hash table with open addressing collision resolution and
+ * linear probing of interval 1 (next free record is used).
+ * Removal is lazy (removed records are only marked), but
+ * if possible, they are fully emptied.
+ */
+struct ly_ht {
+    uint32_t used;        /* number of values stored in the hash table (filled records) */
+    uint32_t size;        /* always holds 2^x == size (is power of 2), actually number of records allocated */
+    uint32_t invalid;     /* number of invalid records (deleted) */
+    lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
+    void *cb_data;        /* user data callback arbitrary value */
+    uint16_t resize;      /* 0 - resizing is disabled, *
+                           * 1 - enlarging is enabled, *
+                           * 2 - both shrinking and enlarging is enabled */
+    uint16_t rec_size;    /* real size (in bytes) of one record for accessing recs array */
+    unsigned char *recs;  /* pointer to the hash table itself (array of struct ht_rec) */
+};
+
+/**
+ * @brief Dictionary hash table record.
+ */
+struct ly_dict_rec {
+    char *value;        /**< stored string */
+    uint32_t refcount;  /**< reference count of the string */
+};
+
+/**
+ * @brief Dictionary for storing repeated strings.
+ */
+struct ly_dict {
+    struct ly_ht *hash_tab;
+    pthread_mutex_t lock;
+};
+
+/**
+ * @brief Initiate content (non-zero values) of the dictionary
+ *
+ * @param[in] dict Dictionary table to initiate
+ */
+void lydict_init(struct ly_dict *dict);
+
+/**
+ * @brief Cleanup the dictionary content
+ *
+ * @param[in] dict Dictionary table to cleanup
+ */
+void lydict_clean(struct ly_dict *dict);
+
+#endif /* LY_HASH_TABLE_INTERNAL_H_ */
diff --git a/src/libyang.h b/src/libyang.h
index 2bfc6be..f992a78 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -39,6 +39,7 @@
 
 /*
  * The following headers are supposed to be included explicitly:
+ * - hash_table.h
  * - metadata.h
  * - plugins_types.h
  * - plugins_exts.h
diff --git a/src/log.c b/src/log.c
index 3c02351..1a75dec 100644
--- a/src/log.c
+++ b/src/log.c
@@ -188,7 +188,7 @@
     rec.tid = pthread_self();
 
     /* get the pointer to the matching record */
-    if (lyht_find(ctx->err_ht, &rec, dict_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match)) {
+    if (lyht_find(ctx->err_ht, &rec, lyht_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match)) {
         return NULL;
     }
 
@@ -215,7 +215,7 @@
     /* LOCK */
     pthread_mutex_lock((pthread_mutex_t *)&ctx->lyb_hash_lock);
 
-    r = lyht_insert(ctx->err_ht, &new, dict_hash((void *)&new.tid, sizeof new.tid), (void **)&rec);
+    r = lyht_insert(ctx->err_ht, &new, lyht_hash((void *)&new.tid, sizeof new.tid), (void **)&rec);
 
     /* UNLOCK */
     pthread_mutex_unlock((pthread_mutex_t *)&ctx->lyb_hash_lock);
diff --git a/src/lyb.c b/src/lyb.c
index 87806c8..59d379c 100644
--- a/src/lyb.c
+++ b/src/lyb.c
@@ -39,8 +39,8 @@
     LYB_HASH hash;
 
     /* generate full hash */
-    full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
-    full_hash = dict_hash_multi(full_hash, node->name, strlen(node->name));
+    full_hash = lyht_hash_multi(0, mod->name, strlen(mod->name));
+    full_hash = lyht_hash_multi(full_hash, node->name, strlen(node->name));
     if (collision_id) {
         size_t ext_len;
 
@@ -51,9 +51,9 @@
             /* use one more byte from the module name than before */
             ext_len = collision_id;
         }
-        full_hash = dict_hash_multi(full_hash, mod->name, ext_len);
+        full_hash = lyht_hash_multi(full_hash, mod->name, ext_len);
     }
-    full_hash = dict_hash_multi(full_hash, NULL, 0);
+    full_hash = lyht_hash_multi(full_hash, NULL, 0);
 
     /* use the shortened hash */
     hash = full_hash & (LYB_HASH_MASK >> collision_id);
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
index 4f1255e..316fad4 100644
--- a/src/tree_data_common.c
+++ b/src/tree_data_common.c
@@ -1158,9 +1158,9 @@
     parent = siblings->parent;
     if (parent && parent->schema && parent->children_ht) {
         /* calculate our hash */
-        hash = dict_hash_multi(0, schema->module->name, strlen(schema->module->name));
-        hash = dict_hash_multi(hash, schema->name, strlen(schema->name));
-        hash = dict_hash_multi(hash, NULL, 0);
+        hash = lyht_hash_multi(0, schema->module->name, strlen(schema->module->name));
+        hash = lyht_hash_multi(hash, schema->name, strlen(schema->name));
+        hash = lyht_hash_multi(hash, NULL, 0);
 
         /* use special hash table function */
         ht_cb = lyht_set_cb(parent->children_ht, lyd_hash_table_schema_val_equal);
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
index 79e0031..c8ea2f2 100644
--- a/src/tree_data_hash.c
+++ b/src/tree_data_hash.c
@@ -39,15 +39,15 @@
     }
 
     /* hash always starts with the module and schema name */
-    node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
-    node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
+    node->hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+    node->hash = lyht_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
 
     if (node->schema->nodetype == LYS_LIST) {
         if (node->schema->flags & LYS_KEYLESS) {
             /* key-less list simply calls hash function again with empty key,
              * just so that it differs from the first-instance hash
              */
-            node->hash = dict_hash_multi(node->hash, NULL, 0);
+            node->hash = lyht_hash_multi(node->hash, NULL, 0);
         } else {
             struct lyd_node_inner *list = (struct lyd_node_inner *)node;
 
@@ -56,7 +56,7 @@
                 struct lyd_node_term *key = (struct lyd_node_term *)iter;
 
                 hash_key = key->value.realtype->plugin->print(NULL, &key->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
-                node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+                node->hash = lyht_hash_multi(node->hash, hash_key, key_len);
                 if (dyn) {
                     free((void *)hash_key);
                 }
@@ -67,14 +67,14 @@
         struct lyd_node_term *llist = (struct lyd_node_term *)node;
 
         hash_key = llist->value.realtype->plugin->print(NULL, &llist->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
-        node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+        node->hash = lyht_hash_multi(node->hash, hash_key, key_len);
         if (dyn) {
             free((void *)hash_key);
         }
     }
 
     /* finish the hash */
-    node->hash = dict_hash_multi(node->hash, NULL, 0);
+    node->hash = lyht_hash_multi(node->hash, NULL, 0);
 
     return LY_SUCCESS;
 }
@@ -136,9 +136,9 @@
     if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
             (!node->prev->next || (node->prev->schema != node->schema))) {
         /* get the simple hash */
-        hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
-        hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
-        hash = dict_hash_multi(hash, NULL, 0);
+        hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+        hash = lyht_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+        hash = lyht_hash_multi(hash, NULL, 0);
 
         /* remove any previous stored instance, only if we did not start with an empty HT */
         if (!empty_ht && node->next && (node->next->schema == node->schema)) {
@@ -216,9 +216,9 @@
     /* first instance of the (leaf-)list, needs to be removed from HT */
     if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
         /* get the simple hash */
-        hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
-        hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
-        hash = dict_hash_multi(hash, NULL, 0);
+        hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+        hash = lyht_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+        hash = lyht_hash_multi(hash, NULL, 0);
 
         /* remove the instance */
         if (lyht_remove(node->parent->children_ht, &node, hash)) {
diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c
index 42032a7..5580809 100644
--- a/src/tree_schema_common.c
+++ b/src/tree_schema_common.c
@@ -361,7 +361,7 @@
     LY_ERR ret;
     uint32_t hash;
 
-    hash = dict_hash(name, strlen(name));
+    hash = lyht_hash(name, strlen(name));
     ret = lyht_insert(ht, &name, hash, NULL);
     if (ret == LY_EEXIST) {
         if (err_detail) {
@@ -433,7 +433,7 @@
 
     /* check collision with the top-level typedefs */
     if (node) {
-        hash = dict_hash(name, name_len);
+        hash = lyht_hash(name, name_len);
         if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
             LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
                     "Duplicate identifier \"%s\" of typedef statement - scoped type collide with a top-level type.", name);
@@ -566,7 +566,7 @@
 
     /* check collision with the top-level groupings */
     if (node) {
-        hash = dict_hash(name, name_len);
+        hash = lyht_hash(name, name_len);
         if (!lyht_find(grps_global, &name, hash, NULL)) {
             LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
                     "Duplicate identifier \"%s\" of grouping statement - scoped grouping collide with a top-level grouping.", name);
diff --git a/src/validation.c b/src/validation.c
index 89ba74a..73ae19c 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -1307,7 +1307,7 @@
 
                     /* get hash key */
                     hash_key = val->realtype->plugin->print(NULL, val, LY_VALUE_LYB, NULL, &dyn, &key_len);
-                    hash = dict_hash_multi(hash, hash_key, key_len);
+                    hash = lyht_hash_multi(hash, hash_key, key_len);
                     if (dyn) {
                         free((void *)hash_key);
                     }
@@ -1318,7 +1318,7 @@
                 }
 
                 /* finish the hash value */
-                hash = dict_hash_multi(hash, NULL, 0);
+                hash = lyht_hash_multi(hash, NULL, 0);
 
                 /* insert into the hashtable */
                 ret = lyht_insert(uniqtables[u], &set->objs[i], hash, NULL);
diff --git a/src/xpath.c b/src/xpath.c
index 44ae922..b3e4c70 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -695,9 +695,9 @@
             hnode.node = set->val.nodes[i].node;
             hnode.type = set->val.nodes[i].type;
 
-            hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
-            hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
-            hash = dict_hash_multi(hash, NULL, 0);
+            hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+            hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+            hash = lyht_hash_multi(hash, NULL, 0);
 
             r = lyht_insert(set->ht, &hnode, hash, NULL);
             assert(!r);
@@ -715,9 +715,9 @@
         hnode.node = node;
         hnode.type = type;
 
-        hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
-        hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
-        hash = dict_hash_multi(hash, NULL, 0);
+        hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+        hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+        hash = lyht_hash_multi(hash, NULL, 0);
 
         r = lyht_insert(set->ht, &hnode, hash, NULL);
         assert(!r);
@@ -743,9 +743,9 @@
         hnode.node = node;
         hnode.type = type;
 
-        hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
-        hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
-        hash = dict_hash_multi(hash, NULL, 0);
+        hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+        hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+        hash = lyht_hash_multi(hash, NULL, 0);
 
         r = lyht_remove(set->ht, &hnode, hash);
         assert(!r);
@@ -776,9 +776,9 @@
     hnode.node = node;
     hnode.type = type;
 
-    hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
-    hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
-    hash = dict_hash_multi(hash, NULL, 0);
+    hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+    hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+    hash = lyht_hash_multi(hash, NULL, 0);
 
     if (!lyht_find(set->ht, &hnode, hash, (void **)&match_p)) {
         if ((skip_idx > -1) && (set->val.nodes[skip_idx].node == match_p->node) && (set->val.nodes[skip_idx].type == match_p->type)) {
@@ -1789,9 +1789,9 @@
             hnode.node = set->val.nodes[i].node;
             hnode.type = set->val.nodes[i].type;
 
-            hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
-            hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
-            hash = dict_hash_multi(hash, NULL, 0);
+            hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+            hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+            hash = lyht_hash_multi(hash, NULL, 0);
 
             assert(!lyht_find(set->ht, &hnode, hash, NULL));
         }