meminfo/entry.rs
1// -----------------------------------------------------------------------------------------------
2// -- Standard library imports --
3// -----------------------------------------------------------------------------------------------
4use std::num::ParseIntError;
5use std::ops::{Deref, DerefMut, Range};
6use std::{error, fmt, str};
7
8// -----------------------------------------------------------------------------------------------
9// -- Module error types --
10// -----------------------------------------------------------------------------------------------
11/// The error type for [`MemInfoEntry`] **size** parsing.
12#[derive(Debug)]
13pub struct ParseSizeError {
14 /// **Label** of the [`MemInfoEntry`] whose size could not be parsed.
15 pub(crate) label: String,
16 /// **Size** of the [`MemInfoEntry`] that could not be parsed.
17 pub(crate) size: String,
18 /// The error **source** (i.e. the reson why [`MemInfoEntry`] size could not be parsed).
19 pub(crate) source: ParseIntError,
20}
21
22impl ParseSizeError {
23 /// Constructs a new instance of the error from the `label` of the entry whose `size` could
24 /// not be parsed as `usize`, the string representation of the entry `size` that could not be
25 /// parsed and the `source` of the error (i.e. the error cause).
26 #[inline]
27 fn new(label: &str, size: &str, source: ParseIntError) -> Self {
28 Self {
29 label: label.to_owned(),
30 size: size.to_owned(),
31 source,
32 }
33 }
34}
35
36impl fmt::Display for ParseSizeError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 f.write_fmt(format_args!(
39 "failed to parse `{size}` as usize in `/proc/meminfo` entry `{label}`",
40 size = self.size,
41 label = self.label
42 ))
43 }
44}
45
46impl error::Error for ParseSizeError {
47 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
48 Some(&self.source)
49 }
50}
51
52// -----------------------------------------------------------------------------------------------
53// -- Module types --
54// -----------------------------------------------------------------------------------------------
55/// A parsed `/proc/meminfo` entry.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct MemInfoEntry<'m> {
58 /// Entry **label** (e.g. `MemAvailable`).
59 pub(crate) label: &'m str,
60 /// Entry **size** (e.g. `3911344`).
61 pub(crate) size: &'m str,
62 /// Entry **unit**, if present (e.g. `kB`).
63 ///
64 /// # Notes
65 ///
66 /// Unit might be missing for entries which do not represent memory sizes
67 /// (e.g. `HugePages_Total`, `HugePages_Free`, etc.).
68 pub(crate) unit: Option<&'m str>,
69}
70
71impl<'m> MemInfoEntry<'m> {
72 /// The delimiter between a `/proc/meminfo` entry label and its size.
73 pub(crate) const DELIMITER: u8 = b':';
74
75 /// Constructs a new instance from entry `label`, `size`, and `unit`, converting fields from
76 /// UTF-8 bytes to string slices.
77 ///
78 /// # Panics
79 ///
80 /// This functions panics if `bytes` do not represent valid UTF-8.
81 #[inline]
82 #[cfg(not(feature = "utf8-unchecked"))]
83 pub(crate) fn new(label: &'m [u8], size: &'m [u8], unit: Option<&'m [u8]>) -> Self {
84 /// Converts a slice of bytes to a string slice, validating its content to be UTF-8.
85 ///
86 /// # Panics
87 ///
88 /// This functions panics if `bytes` do not represent valid UTF-8.
89 #[inline(always)]
90 fn from_utf8(bytes: &[u8]) -> &str {
91 str::from_utf8(bytes).expect("`/proc/meminfo` to contain valid UTF-8")
92 }
93
94 Self {
95 label: from_utf8(label),
96 size: from_utf8(size),
97 unit: unit.map(from_utf8),
98 }
99 }
100
101 /// Constructs a new instance from entry `label`, `size` and `unit`, converting fields from
102 /// UTF-8 bytes to string slices.
103 ///
104 /// # Safety
105 ///
106 /// This function assumes `label`, `size` and `unit` (`/proc/meminfo` data) to be valid
107 /// UTF-8 bytes. If this is not the case (e.g. malformed `/proc/meminfo` data), calling this
108 /// function will result in undefined behaviour. In case of doubt, please use
109 /// [`MemInfoEntry::new`] instead, since performs UTF-8 validation and panics on malformed
110 /// data.
111 #[inline]
112 #[must_use]
113 #[cfg(feature = "utf8-unchecked")]
114 pub(crate) unsafe fn new_unchecked(
115 label: &'m [u8],
116 size: &'m [u8],
117 unit: Option<&'m [u8]>,
118 ) -> Self {
119 Self {
120 label: unsafe { str::from_utf8_unchecked(label) },
121 size: unsafe { str::from_utf8_unchecked(size) },
122 unit: unit.map(|unit| unsafe { str::from_utf8_unchecked(unit) }),
123 }
124 }
125
126 /// Returns the entry label (e.g. `MemAvailable`).
127 #[inline]
128 #[must_use]
129 pub fn label(&self) -> &str {
130 self.label
131 }
132
133 /// Returns the entry size (e.g. `3911344`).
134 ///
135 /// # Errors
136 ///
137 /// This method returns an error if the entry size could not be parsed as `usize`.
138 #[inline]
139 pub fn size(&self) -> Result<usize, ParseSizeError> {
140 self.size
141 .parse()
142 .map_err(|source| ParseSizeError::new(self.label, self.size, source))
143 }
144
145 /// Returns the entry unit, if present (e.g. `kB`).
146 #[inline]
147 #[must_use]
148 pub fn unit(&self) -> Option<&str> {
149 self.unit
150 }
151}
152
153/// A parsed `/proc/meminfo` entry with additional information about its position in the file
154/// stream.
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct MemInfoEntryExtended<'m> {
157 /// The actual `/proc/meminfo` entry.
158 pub(crate) entry: MemInfoEntry<'m>,
159 /// The range of bytes this entry was parsed from in the `/proc/meminfo` file stream.
160 pub(crate) range: Range<usize>,
161}
162
163impl<'m> MemInfoEntryExtended<'m> {
164 /// Constructs a new instance from a plain `entry`, and the `start` and `end` byte positions
165 /// of the entry in the `/proc/meminfo` file stream.
166 #[inline]
167 pub(crate) fn new(entry: MemInfoEntry<'m>, start: usize, end: usize) -> Self {
168 Self {
169 entry,
170 range: start..end,
171 }
172 }
173
174 /// Returns the byte range of the entry in the `/proc/meminfo` file stream.
175 #[inline]
176 #[must_use]
177 pub fn byte_range(&self) -> &Range<usize> {
178 &self.range
179 }
180
181 /// Returns the start position of the entry in the `/proc/meminfo` file stream.
182 #[inline]
183 #[must_use]
184 pub fn start_pos(&self) -> usize {
185 self.range.start
186 }
187
188 /// Returns the end position of the entry in the `/proc/meminfo` file stream.
189 #[inline]
190 #[must_use]
191 pub fn end_pos(&self) -> usize {
192 self.range.end
193 }
194
195 /// Returns the required buffer capacity to read fit the entry raw bytes.
196 #[inline]
197 #[must_use]
198 pub fn required_capacity(&self) -> usize {
199 self.range.end - self.range.start
200 }
201}
202
203impl<'m> Deref for MemInfoEntryExtended<'m> {
204 type Target = MemInfoEntry<'m>;
205
206 fn deref(&self) -> &Self::Target {
207 &self.entry
208 }
209}
210
211impl<'m> DerefMut for MemInfoEntryExtended<'m> {
212 fn deref_mut(&mut self) -> &mut Self::Target {
213 &mut self.entry
214 }
215}