diff --git a/include/nuttx/idr.h b/include/nuttx/idr.h new file mode 100644 index 0000000000..9d15588236 --- /dev/null +++ b/include/nuttx/idr.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * include/nuttx/idr.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_IDR_H +#define __INCLUDE_NUTTX_IDR_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define idr_for_each_entry(idr, node, id) \ + for ((id) = 0; ((node) = idr_get_next(idr, &(id))) != NULL; id++) + +#define idr_init() idr_init_base(0) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct idr_s; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +FAR void *idr_get_next(FAR struct idr_s *idr, FAR int *id); +FAR struct idr_s *idr_init_base(int base); +void idr_destroy(FAR struct idr_s *idr); +int idr_alloc_u32(FAR struct idr_s *idr, FAR void *ptr, FAR uint32_t *result, + uint32_t max); +int idr_alloc(FAR struct idr_s *idr, FAR void *ptr, int start, int end); +FAR void *idr_remove(FAR struct idr_s *idr, unsigned int id); + +#endif diff --git a/libs/libc/misc/CMakeLists.txt b/libs/libc/misc/CMakeLists.txt index 3beabec7f1..9a2c55bd9d 100644 --- a/libs/libc/misc/CMakeLists.txt +++ b/libs/libc/misc/CMakeLists.txt @@ -32,6 +32,7 @@ list( lib_tea_encrypt.c lib_tea_decrypt.c lib_cxx_initialize.c + lib_idr.c lib_impure.c lib_memfd.c lib_mutex.c diff --git a/libs/libc/misc/Make.defs b/libs/libc/misc/Make.defs index c5a1bda008..77d7838f6a 100644 --- a/libs/libc/misc/Make.defs +++ b/libs/libc/misc/Make.defs @@ -25,6 +25,7 @@ CSRCS += lib_xorshift128.c lib_tea_encrypt.c lib_tea_decrypt.c CSRCS += lib_cxx_initialize.c lib_impure.c lib_memfd.c lib_mutex.c CSRCS += lib_fchmodat.c lib_fstatat.c lib_getfullpath.c lib_openat.c CSRCS += lib_mkdirat.c lib_utimensat.c lib_mallopt.c lib_memoryregion.c +CSRCS += lib_idr.c # Support for platforms that do not have long long types diff --git a/libs/libc/misc/lib_idr.c b/libs/libc/misc/lib_idr.c new file mode 100644 index 0000000000..88f8138984 --- /dev/null +++ b/libs/libc/misc/lib_idr.c @@ -0,0 +1,315 @@ +/**************************************************************************** + * 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 +#include +#include + +/**************************************************************************** + * 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); +}