feat(ida): IDA内部改为使用XArray实现 (#934)

目前可以记录哪些ID已经分配,支持了ID释放的功能.

Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
LoGin
2024-09-25 11:20:52 +08:00
committed by GitHub
parent 9ad34ef277
commit 013ffb708f
20 changed files with 273 additions and 83 deletions

View File

@ -0,0 +1,9 @@
[package]
name = "ida"
version = "0.1.0"
edition = "2021"
authors = ["longjin <longjin@dragonos.org>"]
description = "一个基于XArray的ID分配器"
[dependencies]
kdepends = { path = "../kdepends" }

View File

@ -0,0 +1,3 @@
# IDA
一个基于XArray的ID分配器

View File

@ -0,0 +1,210 @@
#![no_std]
#![feature(core_intrinsics)]
#![allow(internal_features)]
#![allow(clippy::needless_return)]
#[cfg(test)]
#[macro_use]
extern crate std;
use core::cmp::min;
use core::intrinsics::unlikely;
use core::marker::PhantomData;
use core::ops::Deref;
struct EmptyIdaItemRef<'a> {
_marker: PhantomData<&'a EmptyIdaItem>,
}
impl<'a> Deref for EmptyIdaItemRef<'a> {
type Target = EmptyIdaItem;
fn deref(&self) -> &Self::Target {
&EmptyIdaItem
}
}
struct EmptyIdaItem;
unsafe impl kdepends::xarray::ItemEntry for EmptyIdaItem {
type Ref<'a> = EmptyIdaItemRef<'a> where Self: 'a;
fn into_raw(self) -> *const () {
core::ptr::null()
}
unsafe fn from_raw(_raw: *const ()) -> Self {
EmptyIdaItem
}
unsafe fn raw_as_ref<'a>(_raw: *const ()) -> Self::Ref<'a> {
EmptyIdaItemRef {
_marker: PhantomData,
}
}
}
/// id分配器
pub struct IdAllocator {
current_id: usize,
min_id: usize,
max_id: usize,
used: usize,
xarray: kdepends::xarray::XArray<EmptyIdaItem>,
}
impl IdAllocator {
/// 创建一个新的id分配器
pub const fn new(initial_id: usize, max_id: usize) -> Option<Self> {
if initial_id >= max_id {
return None;
}
Some(Self {
current_id: initial_id,
min_id: initial_id,
max_id,
used: 0,
xarray: kdepends::xarray::XArray::new(),
})
}
/// 可用的id数量
#[inline]
pub fn available(&self) -> usize {
self.max_id - self.min_id - self.used
}
/// 分配一个新的id
///
/// ## 返回
///
/// 如果分配成功返回Some(id)否则返回None
pub fn alloc(&mut self) -> Option<usize> {
if unlikely(self.available() == 0) {
return None;
}
if let Some(try1) = self.do_find_first_free_index(self.current_id, self.max_id) {
self.current_id = try1;
self.xarray.store(try1 as u64, EmptyIdaItem);
self.used += 1;
return Some(try1);
}
// 从头开始找
if let Some(try2) =
self.do_find_first_free_index(self.min_id, min(self.current_id, self.max_id))
{
self.current_id = try2;
self.xarray.store(try2 as u64, EmptyIdaItem);
self.used += 1;
return Some(try2);
}
return None;
}
/// 检查id是否存在
///
/// ## 参数
///
/// - `id`要检查的id
///
/// ## 返回
///
/// 如果id存在返回true否则返回false
pub fn exists(&self, id: usize) -> bool {
if id < self.min_id || id >= self.max_id {
return false;
}
self.xarray.load(id as u64).is_some()
}
fn do_find_first_free_index(&self, start_id: usize, end: usize) -> Option<usize> {
(start_id..end).find(|&i| !self.exists(i))
}
/// 释放一个id
///
/// ## 参数
///
/// - `id`要释放的id
pub fn free(&mut self, id: usize) {
if id < self.min_id || id >= self.max_id {
return;
}
if self.xarray.remove(id as u64).is_some() {
self.used -= 1;
}
}
/// 返回已经使用的id数量
pub fn used(&self) -> usize {
self.used
}
}
impl core::fmt::Debug for IdAllocator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("IdAllocator")
.field("current_id", &self.current_id)
.field("min_id", &self.min_id)
.field("max_id", &self.max_id)
.field("used", &self.used)
.field("xarray", &"xarray<()>")
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_fail() {
assert_eq!(IdAllocator::new(10, 10).is_none(), true);
assert_eq!(IdAllocator::new(11, 10).is_none(), true);
}
#[test]
fn test_new_success() {
assert_eq!(IdAllocator::new(9, 10).is_some(), true);
assert_eq!(IdAllocator::new(0, 10).is_some(), true);
}
#[test]
fn test_id_allocator() {
let mut ida = IdAllocator::new(0, 10).unwrap();
assert_eq!(ida.alloc(), Some(0));
assert_eq!(ida.alloc(), Some(1));
assert_eq!(ida.alloc(), Some(2));
assert_eq!(ida.alloc(), Some(3));
assert_eq!(ida.alloc(), Some(4));
assert_eq!(ida.alloc(), Some(5));
assert_eq!(ida.alloc(), Some(6));
assert_eq!(ida.alloc(), Some(7));
assert_eq!(ida.alloc(), Some(8));
assert_eq!(ida.alloc(), Some(9));
assert_eq!(ida.alloc(), None);
for i in 0..10 {
assert_eq!(ida.exists(i), true);
}
ida.free(5);
for i in 0..10 {
if i == 5 {
assert_eq!(ida.exists(i), false);
} else {
assert_eq!(ida.exists(i), true);
}
}
assert_eq!(ida.used(), 9);
assert_eq!(ida.alloc(), Some(5));
assert_eq!(ida.alloc(), None);
assert_eq!(ida.used(), 10);
for i in 0..10 {
ida.free(i);
}
assert_eq!(ida.used(), 0);
}
}

View File

@ -7,9 +7,10 @@ description = "需要导出的依赖项(为保持内核依赖版本与调试
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ringbuffer = "0.15.0"
memoffset = "0.9.0"
crc = { path = "../crc" }
memoffset = "0.9.0"
ringbuffer = "0.15.0"
xarray = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/xarray", rev = "de93b57c34", features = ["slab-friendly"] }
# 一个无锁MPSC队列
[dependencies.thingbuf]

View File

@ -7,3 +7,4 @@ pub extern crate memoffset;
pub extern crate ringbuffer;
pub extern crate crc;
pub extern crate xarray;