nuttx/libs/libc/misc/lib_idr.c
chenrun1 a1ccf15e39 idr:Tool for associating discrete ids with addresses
This is a tool for associating discrete IDs with addresses.
This tool is implemented through the red-black tree method provided by <sys/tree.h>, and the time complexity when calling, searching, and deleting is optimized to O(logn)
The implementation is the moving node operation of two red-black trees
1. When applying for a node, it will first check whether there is an available node in the "removed" tree. If so, the memory address of the node will be reused and moved to the "alloced" tree.
2. If the "removed" tree is an "empty tree", then the node will be requested from the memory and added to the "alloced" tree
3. Similarly, when removing a node, we set the address pointed to in the node to "NULL" and move it to the "removed" tree. Next time we alloc the node, we can reduce the overhead caused by memory application
For now, we still have something that can be optimized, and that is the memory elimination mechanism of the "removed tree". The current implementation will only release all the content under the "removed" tree when the idtree is destroyed.

Signed-off-by: chenrun1 <chenrun1@xiaomi.com>
2024-08-19 11:05:40 -03:00

316 lines
8.0 KiB
C

/****************************************************************************
* libs/libc/misc/lib_idr.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/lib/lib.h>
#include <nuttx/idr.h>
#include <sys/tree.h>
/****************************************************************************
* Private Types
****************************************************************************/
RB_HEAD(idr_tree_s, idr_node_s);
struct idr_node_s
{
RB_ENTRY(idr_node_s) link;
unsigned int id;
FAR void *data;
};
struct idr_s
{
mutex_t lock;
unsigned int lastid;
unsigned int base;
struct idr_tree_s alloced;
struct idr_tree_s removed;
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: idr_compare
****************************************************************************/
static int idr_compare(FAR struct idr_node_s *node,
FAR struct idr_node_s *root)
{
/* When node < root, it should be placed on the left side of the tree */
return node->id - root->id;
}
/****************************************************************************
* Name: idr_create_node
****************************************************************************/
FAR static struct idr_node_s *idr_create_node(FAR void *ptr, unsigned int id)
{
FAR struct idr_node_s *node;
node = lib_malloc(sizeof(struct idr_node_s));
if (node == NULL)
{
return NULL;
}
node->id = id;
node->data = ptr;
return node;
}
/****************************************************************************
* Name: RB_GENERATE_STATIC
****************************************************************************/
RB_GENERATE_STATIC(idr_tree_s, idr_node_s, link, idr_compare)
/****************************************************************************
* Public Functions
****************************************************************************/
FAR void *idr_get_next(FAR struct idr_s *idr, FAR int *id)
{
FAR struct idr_node_s *node;
struct idr_node_s search;
search.id = *id;
nxmutex_lock(&idr->lock);
node = RB_NFIND(idr_tree_s, &idr->alloced, &search);
if (node == NULL)
{
nxmutex_unlock(&idr->lock);
return NULL;
}
/* Update id */
*id = node->id;
nxmutex_unlock(&idr->lock);
return node->data;
}
/****************************************************************************
* Name: idr_remove
****************************************************************************/
FAR void *idr_remove(FAR struct idr_s *idr, unsigned int id)
{
FAR struct idr_node_s *node;
struct idr_node_s search;
FAR void *ptr;
search.id = id;
nxmutex_lock(&idr->lock);
node = RB_FIND(idr_tree_s, &idr->alloced, &search);
if (node == NULL)
{
/* We did not find the corresponding node from the allocated tree */
nxmutex_unlock(&idr->lock);
return NULL;
}
ptr = node->data;
node->data = NULL;
/* We move the node from the allocated tree to the removed tree so that
* it can be used the next time it is allocated to avoid multiple
* allocations and reduce efficiency.
*/
RB_REMOVE(idr_tree_s, &idr->alloced, node);
RB_INSERT(idr_tree_s, &idr->removed, node);
nxmutex_unlock(&idr->lock);
return ptr;
}
/****************************************************************************
* Name: idr_alloc_u32
****************************************************************************/
int idr_alloc_u32(FAR struct idr_s *idr, FAR void *ptr, FAR uint32_t *result,
uint32_t max)
{
FAR struct idr_node_s *node;
FAR struct idr_node_s *temp;
struct idr_node_s search;
nxmutex_lock(&idr->lock);
/* Check ID Value */
if (*result < idr->base)
{
/* If it is less than the set base, then the search starts from
* the base.
*/
*result = idr->base;
}
/* Check if there are available blocks from the removed tree */
RB_FOREACH_SAFE(node, idr_tree_s, &idr->removed, temp)
{
if (node->id >= *result && node->id <= max)
{
RB_REMOVE(idr_tree_s, &idr->removed, node);
node->data = ptr;
*result = node->id;
RB_INSERT(idr_tree_s, &idr->alloced, node);
goto out;
}
}
/* Branch prediction: do we check if the saved lastid is in range?
* This can improve performance.
*/
if (idr->lastid >= *result && idr->lastid < max)
{
search.id = ++idr->lastid;
temp = RB_FIND(idr_tree_s, &idr->alloced, &search);
if (temp == NULL)
{
/* We can use this value, reduced the traversal search
* available id
*/
*result = search.id;
goto create;
}
}
/* Cache miss, then we need to traverse the tree */
for (; *result <= max; (*result)++)
{
search.id = *result;
temp = RB_FIND(idr_tree_s, &idr->alloced, &search);
if (temp == NULL)
{
/* We found it */
goto create;
}
}
/* There are no available IDs in this range. */
nxmutex_unlock(&idr->lock);
return -ENOSPC;
create:
node = idr_create_node(ptr, *result);
if (node == NULL)
{
nxmutex_unlock(&idr->lock);
return -ENOMEM;
}
RB_INSERT(idr_tree_s, &idr->alloced, node);
/* Update lastid */
idr->lastid = *result;
out:
nxmutex_unlock(&idr->lock);
return OK;
}
/****************************************************************************
* Name: idr_alloc
****************************************************************************/
int idr_alloc(FAR struct idr_s *idr, FAR void *ptr, int start, int end)
{
uint32_t id = start;
int ret;
if (start < 0)
{
return -EINVAL;
}
ret = idr_alloc_u32(idr, ptr, &id, end > 0 ? end - 1 : INT32_MAX);
if (ret < 0)
{
return ret;
}
return id;
}
/****************************************************************************
* Name: idr_init_base
****************************************************************************/
FAR struct idr_s *idr_init_base(int base)
{
FAR struct idr_s *idr;
idr = lib_zalloc(sizeof(struct idr_s));
if (idr == NULL)
{
return NULL;
}
idr->base = base;
RB_INIT(&idr->alloced);
RB_INIT(&idr->removed);
nxmutex_init(&idr->lock);
return idr;
}
/****************************************************************************
* Name: idr_destroy
****************************************************************************/
void idr_destroy(FAR struct idr_s *idr)
{
FAR struct idr_node_s *node;
FAR struct idr_node_s *temp;
/* Release the space occupied by each node in the RB tree */
nxmutex_lock(&idr->lock);
RB_FOREACH_SAFE(node, idr_tree_s, &idr->removed, temp)
{
lib_free(node);
}
RB_FOREACH_SAFE(node, idr_tree_s, &idr->alloced, temp)
{
lib_free(node);
}
nxmutex_unlock(&idr->lock);
nxmutex_destroy(&idr->lock);
lib_free(idr);
}