| /**************************************************************************** |
| * 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 |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: idr_find |
| ****************************************************************************/ |
| |
| FAR void *idr_find(FAR struct idr_s *idr, unsigned int id) |
| { |
| FAR struct idr_node_s *node; |
| struct idr_node_s search; |
| |
| search.id = id; |
| nxmutex_lock(&idr->lock); |
| node = RB_FIND(idr_tree_s, &idr->alloced, &search); |
| if (node == NULL) |
| { |
| nxmutex_unlock(&idr->lock); |
| return NULL; |
| } |
| |
| nxmutex_unlock(&idr->lock); |
| return node->data; |
| } |
| |
| /**************************************************************************** |
| * Name: idr_get_next |
| ****************************************************************************/ |
| |
| 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); |
| } |