yash_env/system/virtual/fd_set.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2026 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! File descriptor set for the virtual system
18//!
19//! This module defines the [`FdSet` struct](FdSet), which is an implementation
20//! of the [`FdSet` trait](FdSetTrait) for the
21//! [`VirtualSystem`](super::VirtualSystem). The module also provides iterators
22//! for iterating over the file descriptors in an `FdSet`.
23
24use crate::io::{Fd, RawFd};
25use crate::system::FdSet as FdSetTrait;
26use std::collections::BTreeSet;
27
28/// File descriptor set for the virtual system
29///
30/// This is an implementation of the [`FdSet` trait](FdSetTrait) for the
31/// [`VirtualSystem`](super::VirtualSystem). It represents a set of file
32/// descriptors that can be monitored for events such as readability or
33/// writability in the virtual system. Currently, the `FdSet` struct internally
34/// uses a `BTreeSet` to store the file descriptors, which allows for efficient
35/// insertion, removal, and lookup of file descriptors.
36#[derive(Clone, Debug, Default, Eq, PartialEq)]
37pub struct FdSet(BTreeSet<Fd>);
38
39impl FdSetTrait for FdSet {
40 const MAX_FD: Fd = Fd(RawFd::MAX);
41
42 #[inline(always)]
43 fn insert(&mut self, fd: Fd) {
44 if fd.0 >= 0 {
45 self.0.insert(fd);
46 }
47 }
48
49 #[inline(always)]
50 fn remove(&mut self, fd: Fd) {
51 self.0.remove(&fd);
52 }
53
54 #[inline(always)]
55 fn contains(&self, fd: Fd) -> bool {
56 self.0.contains(&fd)
57 }
58}
59
60impl From<Fd> for FdSet {
61 #[inline(always)]
62 fn from(fd: Fd) -> Self {
63 let mut set = Self::new();
64 set.insert(fd);
65 set
66 }
67}
68
69impl FromIterator<Fd> for FdSet {
70 #[inline(always)]
71 fn from_iter<I: IntoIterator<Item = Fd>>(iter: I) -> Self {
72 Self(FromIterator::from_iter(iter))
73 }
74}
75
76/// Iterator over the file descriptors in an `FdSet`
77///
78/// Use `FdSet::into_iter` to create an iterator from an `FdSet`.
79#[derive(Debug)]
80pub struct IntoIter(std::collections::btree_set::IntoIter<Fd>);
81
82impl Iterator for IntoIter {
83 type Item = Fd;
84
85 #[inline(always)]
86 fn next(&mut self) -> Option<Self::Item> {
87 self.0.next()
88 }
89}
90
91impl IntoIterator for FdSet {
92 type Item = Fd;
93 type IntoIter = IntoIter;
94
95 #[inline(always)]
96 fn into_iter(self) -> Self::IntoIter {
97 IntoIter(self.0.into_iter())
98 }
99}
100
101/// Iterator over the file descriptors in an `FdSet` by reference
102///
103/// Use `FdSet::iter` to create an iterator from a reference to an `FdSet`.
104#[derive(Debug)]
105pub struct Iter<'a>(std::collections::btree_set::Iter<'a, Fd>);
106
107impl<'a> Iterator for Iter<'a> {
108 type Item = &'a Fd;
109
110 #[inline(always)]
111 fn next(&mut self) -> Option<Self::Item> {
112 self.0.next()
113 }
114}
115
116impl<'a> IntoIterator for &'a FdSet {
117 type Item = &'a Fd;
118 type IntoIter = Iter<'a>;
119
120 #[inline(always)]
121 fn into_iter(self) -> Self::IntoIter {
122 Iter(self.0.iter())
123 }
124}
125
126impl Extend<Fd> for FdSet {
127 fn extend<I: IntoIterator<Item = Fd>>(&mut self, iter: I) {
128 self.0.extend(iter);
129 }
130}
131
132impl<'a> Extend<&'a Fd> for FdSet {
133 fn extend<I: IntoIterator<Item = &'a Fd>>(&mut self, iter: I) {
134 self.0.extend(iter);
135 }
136}
137
138impl FdSet {
139 /// Returns the number of file descriptors in the set.
140 #[inline(always)]
141 pub fn len(&self) -> usize {
142 self.0.len()
143 }
144
145 /// Returns whether the set is empty.
146 #[inline(always)]
147 pub fn is_empty(&self) -> bool {
148 self.0.is_empty()
149 }
150
151 /// Returns an iterator over the file descriptors in the set.
152 #[inline(always)]
153 pub fn iter(&self) -> Iter<'_> {
154 self.into_iter()
155 }
156}