/******************************************************************************* * This file is part of SWIFT. * Copyright (c) 2019 Peter W. Draper (p.w.draper@durham.ac.uk) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * ******************************************************************************/ /** * @file memuse_rnodes.c * @brief file of routines used for radix nodes in memory loggers. */ /* Config parameters. */ #include /* Standard includes. */ #include #include #include #include #include #include /* Local defines. */ #include "memuse_rnodes.h" /* Local includes. */ #include "atomic.h" #include "clocks.h" #include "error.h" /** * @brief Return the position of a keypart for a list of children. * If not found returns where it would be inserted. * * @param keypart the keypart to locate. * @param children the list of sorted children. * @param count the number of children * * @return the index of key or where it should be inserted. */ static unsigned int memuse_rnode_bsearch(uint8_t keypart, struct memuse_rnode **children, unsigned int count) { /* Search for lower bound. */ unsigned int lower = 0; unsigned int upper = count; while (lower < upper) { unsigned int middle = (upper + lower) / 2; if (keypart > children[middle]->keypart) lower = middle + 1; else upper = middle; } return lower; } /** * @brief Insert a child, if needed, into a list of children. Assumes * we have sufficient room. * * @param child the child to insert, if needed. * @param children the list of sorted children. * @param count the number of children */ static void memuse_rnode_binsert_child(struct memuse_rnode *child, struct memuse_rnode **children, unsigned int *count) { unsigned int pos = 0; if (*count > 0) { /* Find the child or insertion point. */ pos = memuse_rnode_bsearch(child->keypart, children, *count); /* If not found move all children to make a space, unless we're inserting * after the end. */ if (pos < *count && children[pos]->keypart != child->keypart) { memmove(&children[pos + 1], &children[pos], (*count - pos) * sizeof(struct memuse_rnode *)); } } /* Insert new child */ children[pos] = child; *count += 1; } /** * @brief Add a child rnode to an rnode. Making sure we have room and keeping * the sort order. * * @param node the parent node. * @param child the node to add to the parent, */ static void memuse_rnode_add_child(struct memuse_rnode *node, struct memuse_rnode *child) { /* Extend the children list to include a new entry .*/ void *mem = realloc(node->children, (node->count + 1) * sizeof(struct memuse_rnode *)); if (mem == NULL) error("Failed to reallocate rnodes\n"); node->children = (struct memuse_rnode **)mem; /* Insert the new child. */ memuse_rnode_binsert_child(child, node->children, &node->count); } /** * @brief Find a child of a node with the given key part. * * @param node the node to search. * @param keypart the key part of the child. * @return NULL if not found. */ static struct memuse_rnode *memuse_rnode_lookup(const struct memuse_rnode *node, uint8_t keypart) { /* Locate the key, or where it would be inserted. */ if (node->count > 0) { unsigned int index = memuse_rnode_bsearch(keypart, node->children, node->count); if (index < node->count && keypart == node->children[index]->keypart) { return node->children[index]; } } return NULL; } /** * @brief insert a child into a node's children list and add a pointer, iff * this is the destination node for the given key. * * @param node the parent node. * @param depth the depth of the parent node. * @param key the full key of the eventual leaf node. * @param keylen the numbers of bytes in the full key. * @param value a value to be stored at the leaf node. Note -1 is used as the * NULL value, so storing signed values needs care and we limit the * range of unsigned values. */ void memuse_rnode_insert_child(struct memuse_rnode *node, uint8_t depth, uint8_t *key, uint8_t keylen, int64_t value) { /* Check if keypart this already exists at this level and add new child if * not. */ uint8_t keypart = key[depth]; struct memuse_rnode *child = memuse_rnode_lookup(node, keypart); if (child == NULL) { child = (struct memuse_rnode *)calloc(1, sizeof(struct memuse_rnode)); child->value = -1; child->keypart = keypart; memuse_rnode_add_child(node, child); } /* Are we at the lowest level yet? */ depth++; if (depth == keylen) { /* Our destination node. */ #if SWIFT_DEBUG_CHECKS if (child->value != -1) message("Overwriting rnode value: %ld with %ld", child->value, value); #endif child->value = value; return; } /* Down we go to the next level. */ memuse_rnode_insert_child(child, depth, key, keylen, value); return; } /** * @brief Find a child node for the given full key. * * @param node the current parent node. * @param depth the depth of the parent node, 0 for first call. * @param key the full key of the expected child node. * @param keylen the number of bytes in the key. */ struct memuse_rnode *memuse_rnode_find_child(struct memuse_rnode *node, uint8_t depth, uint8_t *key, uint8_t keylen) { uint8_t keypart = key[depth]; struct memuse_rnode *child = NULL; if (node->count > 0) child = memuse_rnode_lookup(node, keypart); if (child != NULL && (depth + 1) < keylen) { return memuse_rnode_find_child(child, depth + 1, key, keylen); } return child; } /** * @brief Free all resources associated with a node. * * @param node the rnode. */ void memuse_rnode_cleanup(struct memuse_rnode *node) { if (!node) return; for (size_t k = 0; k < node->count; k++) { memuse_rnode_cleanup(node->children[k]); free(node->children[k]); } if (node->count > 0) free(node->children); } /** * @brief Dump a representation of the radix tree rooted at a node to stdout. * * Debugging code. * * @param depth the depth of the node in the tree, root is 0. * @param node the node at which to start dumping. * @param full if not zero then nodes that are not storing a value * are also reported. */ void memuse_rnode_dump(int depth, struct memuse_rnode *node, int full) { /* Value of the full key, to this depth. Assumes full key is a pointer, * so uncomment when using strings. */ static union { // uint8_t key[MEMUSE_MAXLABLEN]; // char ptr[MEMUSE_MAXLABLEN]; uint8_t key[sizeof(uintptr_t)]; void *ptr; } keyparts = {{0}}; /* Record keypart at this depth. Root has no keypart. */ if (depth != 0) keyparts.key[depth - 1] = node->keypart; // if (node->ptr != NULL || full) { // keyparts.key[depth] = '\0'; // // /* Gather children's keys if full. */ // char fullkey[MEMUSE_MAXLABLEN]; // if (full) { // for (size_t k = 0; k < node->count; k++) { // fullkey[k] = node->children[k]->keypart; // } // fullkey[node->count] = '\0'; // printf("dump @ depth: %d keypart: %d key: %s value: %p fullkey: %s\n", // depth, node->keypart, keyparts.ptr, node->ptr, fullkey); // } else { // printf("dump @ depth: %d keypart: %d key: %s value: %p\n", depth, // node->keypart, keyparts.ptr, node->ptr); // } //} if ((node->value != -1) || full) { printf("dump @ depth: %d keypart: %d key: %p value: %" PRId64 "\n", depth, node->keypart, keyparts.ptr, node->value); } /* Recurse to all children. */ for (size_t k = 0; k < node->count; k++) { memuse_rnode_dump(depth + 1, node->children[k], full); } }