#include "core.h" #include "namespace.h" /** @brief Prefix / Namespace pair. */ typedef struct ns_entry_t { ns_pfx pfx; // Namespace prefix. char * ns; // Fully qualified NS. } NSEntry; typedef struct hashmap NSMap; /** @brief Iterator for dumping NS map. */ struct dump_iter_t { size_t i; // Iterator counter. const char *** data; // Stored data. }; /** * Callbacks. */ static int nsmap_comp_fn (const void *a, const void *b, void *udata) { const NSEntry *nsa = a; const NSEntry *nsb = b; return strncmp (nsa->pfx, nsb->pfx, PFX_LEN); } static uint64_t nsmap_hash_fn ( const void *item, uint64_t seed0, uint64_t seed1) { const NSEntry *nse = item; return (uint64_t) LSUP_HASH64 (nse->pfx, PFX_LEN, seed0); } static void nsmap_free_fn (void *item) { free (((NSEntry *) item)->ns); } static bool nsmap_dump_ns_iter_fn (const void *item, void *udata) { const NSEntry *entry = item; struct dump_iter_t *cur = udata; cur->data[cur->i][0] = (const char *)entry->pfx; cur->data[cur->i++][1] = (const char *)entry->ns; return true; } /** * API. */ NSMap * LSUP_nsmap_new (void) { return hashmap_new ( sizeof (NSEntry), 0, LSUP_HASH_SEED, 0, nsmap_hash_fn, nsmap_comp_fn, nsmap_free_fn, NULL); } void LSUP_nsmap_free (NSMap *map) { hashmap_free (map); } LSUP_rc LSUP_nsmap_add (NSMap *map, const char *pfx, const char *nsstr) { NSEntry entry_s; if (strlen(pfx) >= PFX_LEN) log_warn( "Prefix `%s` is longer than the maximum allowed size " "(%d characters). Truncating.", pfx, PFX_LEN - 1); strncpy (entry_s.pfx, pfx, sizeof (entry_s.pfx)); char *ns = strdup (nsstr); NSEntry *ret = hashmap_get (map, &entry_s); if (!ret) { entry_s.ns = ns; hashmap_set (map, &entry_s); if (hashmap_oom (map)) return LSUP_MEM_ERR; log_debug ("Added prefix '%s' to NS map.", entry_s.pfx); } else { log_debug ( "Replacing NS '%s' with '%s' for preix '%s'.", ret->ns, ns, entry_s.pfx); free (ret->ns); ret->ns = ns; } // Free replaced NS string. return LSUP_OK; } LSUP_rc LSUP_nsmap_remove (NSMap *map, const char *pfx) { NSEntry entry_s; strncpy (entry_s.pfx, pfx, PFX_LEN); NSEntry *entry = hashmap_delete (map, &entry_s); if (!entry) return LSUP_NOACTION; free (entry->ns); return LSUP_OK; } const char * LSUP_nsmap_get_ns (NSMap *map, const char *pfx) { NSEntry entry_s; strncpy (entry_s.pfx, pfx, PFX_LEN); NSEntry *entry = hashmap_get (map, &entry_s); return (entry) ? entry->ns : NULL; } const char * LSUP_nsmap_get_pfx (NSMap *map, const char *ns) { const NSEntry *entry; size_t i = 0; while (hashmap_iter (map, &i, (void **) &entry)) { if (strncmp (entry->ns, ns, strlen (ns)) == 0) return entry->pfx; } return NULL; } LSUP_rc LSUP_nsmap_normalize_uri ( NSMap *map, const char *pfx_uri, char **fq_uri_p) { char *fq_uri = NULL; size_t pfx_len = strcspn (pfx_uri, ":"); if (pfx_len >= PFX_LEN) { log_warn( "Prefix in `%s` is longer than the maximum allowed size " "(%d characters). Truncating.", pfx_uri, PFX_LEN - 1); pfx_len = PFX_LEN - 1; } ns_pfx pfx; strncpy (pfx, pfx_uri, pfx_len); pfx[pfx_len] = 0; /* Namespace *entry; for (entry = map; entry != NULL; entry = entry->hh.next) { if (strncmp (entry->pfx, pfx_uri, strlen (entry->pfx)) == 0) break; } */ const char *ns = LSUP_nsmap_get_ns (map, pfx); if (ns) { // -1 for :, +1 for terminator. size_t fq_size = strlen (ns) + strlen (pfx_uri) - pfx_len; fq_uri = malloc (fq_size); if (UNLIKELY (! (fq_uri))) return LSUP_MEM_ERR; strcpy (fq_uri, ns); strcat (fq_uri, pfx_uri + pfx_len + 1); } else fq_uri = strdup (pfx_uri); *fq_uri_p = fq_uri; return LSUP_OK; } LSUP_rc LSUP_nsmap_denormalize_uri ( NSMap *map, const char *fq_uri, char **pfx_uri_p) { /* * This is different from LSUP_nsmap_get_ns, in that the URI being looked * at will unlikely match exactly the full namespace stored in the map. * This function has to count the characters left over from the match in * order to add the URI suffix. */ const NSEntry *entry; const char *pfx = NULL; size_t i = 0, offset; while (hashmap_iter (map, &i, (void **) &entry)) { offset = strlen(entry->ns); if (strncmp (entry->ns, fq_uri, offset) == 0) { pfx = entry->pfx; break; } } char *pfx_uri = NULL; if (pfx) { // +2: one for terminating \x00, one for the colon. pfx_uri = malloc (strlen (pfx) + strlen (fq_uri) - offset + 2); if (UNLIKELY (! (pfx_uri))) return LSUP_MEM_ERR; sprintf (pfx_uri, "%s:%s", pfx, fq_uri + offset); } else pfx_uri = strdup (fq_uri); *pfx_uri_p = pfx_uri; return LSUP_OK; } const char *** LSUP_nsmap_dump (const NSMap *map) { size_t i = hashmap_count ((NSMap *) map); const char ***data = malloc (2 * (i + 1) * sizeof (char *)); if (UNLIKELY (!data)) return NULL; for (size_t j = 0; j < i; j++) { data[j] = malloc (2 * sizeof (char *)); if (UNLIKELY (!data[j])) return NULL; } struct dump_iter_t cur = {.i = 0, .data = data}; hashmap_scan ((NSMap *) map, nsmap_dump_ns_iter_fn, &cur); data[i] = NULL; // Sentinel return data; }