1use std::alloc::Allocator;
7use std::cmp::Ordering;
8use std::io::Read;
9use std::mem::{self, MaybeUninit};
10use std::ops::{Bound, Range, RangeBounds};
11use std::{fmt, ptr, slice, str};
12
13use crate::apperr;
14
15pub const KILO: usize = 1000;
16pub const MEGA: usize = 1000 * 1000;
17pub const GIGA: usize = 1000 * 1000 * 1000;
18
19pub const KIBI: usize = 1024;
20pub const MEBI: usize = 1024 * 1024;
21pub const GIBI: usize = 1024 * 1024 * 1024;
22
23pub struct MetricFormatter<T>(pub T);
24
25impl fmt::Display for MetricFormatter<usize> {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 let mut value = self.0;
28 let mut suffix = "B";
29 if value >= GIGA {
30 value /= GIGA;
31 suffix = "GB";
32 } else if value >= MEGA {
33 value /= MEGA;
34 suffix = "MB";
35 } else if value >= KILO {
36 value /= KILO;
37 suffix = "kB";
38 }
39 write!(f, "{value}{suffix}")
40 }
41}
42
43pub type CoordType = isize;
45
46pub const COORD_TYPE_SAFE_MAX: CoordType = (1 << (CoordType::BITS / 2 - 1)) - 1;
52
53#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
55pub struct Point {
56 pub x: CoordType,
57 pub y: CoordType,
58}
59
60impl Point {
61 pub const MIN: Self = Self { x: CoordType::MIN, y: CoordType::MIN };
62 pub const MAX: Self = Self { x: CoordType::MAX, y: CoordType::MAX };
63}
64
65impl PartialOrd<Self> for Point {
66 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
67 Some(self.cmp(other))
68 }
69}
70
71impl Ord for Point {
72 fn cmp(&self, other: &Self) -> Ordering {
73 self.y.cmp(&other.y).then(self.x.cmp(&other.x))
74 }
75}
76
77#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
79pub struct Size {
80 pub width: CoordType,
81 pub height: CoordType,
82}
83
84impl Size {
85 pub fn as_rect(&self) -> Rect {
86 Rect { left: 0, top: 0, right: self.width, bottom: self.height }
87 }
88}
89
90#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
92pub struct Rect {
93 pub left: CoordType,
94 pub top: CoordType,
95 pub right: CoordType,
96 pub bottom: CoordType,
97}
98
99impl Rect {
100 pub fn one(value: CoordType) -> Self {
102 Self { left: value, top: value, right: value, bottom: value }
103 }
104
105 pub fn two(top_bottom: CoordType, left_right: CoordType) -> Self {
108 Self { left: left_right, top: top_bottom, right: left_right, bottom: top_bottom }
109 }
110
111 pub fn three(top: CoordType, left_right: CoordType, bottom: CoordType) -> Self {
114 Self { left: left_right, top, right: left_right, bottom }
115 }
116
117 pub fn is_empty(&self) -> bool {
119 self.left >= self.right || self.top >= self.bottom
120 }
121
122 pub fn width(&self) -> CoordType {
124 self.right - self.left
125 }
126
127 pub fn height(&self) -> CoordType {
129 self.bottom - self.top
130 }
131
132 pub fn contains(&self, point: Point) -> bool {
134 point.x >= self.left && point.x < self.right && point.y >= self.top && point.y < self.bottom
135 }
136
137 pub fn intersect(&self, rhs: Self) -> Self {
139 let l = self.left.max(rhs.left);
140 let t = self.top.max(rhs.top);
141 let r = self.right.min(rhs.right);
142 let b = self.bottom.min(rhs.bottom);
143
144 let r = l.max(r);
147 let b = t.max(b);
148
149 Self { left: l, top: t, right: r, bottom: b }
150 }
151}
152
153pub fn minmax<T>(v1: T, v2: T) -> [T; 2]
155where
156 T: Ord,
157{
158 if v2 < v1 { [v2, v1] } else { [v1, v2] }
159}
160
161#[inline(always)]
162#[allow(clippy::ptr_eq)]
163fn opt_ptr<T>(a: Option<&T>) -> *const T {
164 unsafe { mem::transmute(a) }
165}
166
167#[inline(always)]
170#[allow(clippy::ptr_eq)]
171pub fn opt_ptr_eq<T>(a: Option<&T>, b: Option<&T>) -> bool {
172 opt_ptr(a) == opt_ptr(b)
173}
174
175#[inline]
183#[must_use]
184pub const unsafe fn str_from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str {
185 unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
186}
187
188pub fn slice_copy_safe<T: Copy>(dst: &mut [T], src: &[T]) -> usize {
191 let len = src.len().min(dst.len());
192 unsafe { ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), len) };
193 len
194}
195
196pub trait ReplaceRange<T: Copy> {
199 fn replace_range<R: RangeBounds<usize>>(&mut self, range: R, src: &[T]);
200}
201
202impl<T: Copy, A: Allocator> ReplaceRange<T> for Vec<T, A> {
203 fn replace_range<R: RangeBounds<usize>>(&mut self, range: R, src: &[T]) {
204 let start = match range.start_bound() {
205 Bound::Included(&start) => start,
206 Bound::Excluded(start) => start + 1,
207 Bound::Unbounded => 0,
208 };
209 let end = match range.end_bound() {
210 Bound::Included(end) => end + 1,
211 Bound::Excluded(&end) => end,
212 Bound::Unbounded => usize::MAX,
213 };
214 vec_replace_impl(self, start..end, src);
215 }
216}
217
218fn vec_replace_impl<T: Copy, A: Allocator>(dst: &mut Vec<T, A>, range: Range<usize>, src: &[T]) {
219 unsafe {
220 let dst_len = dst.len();
221 let src_len = src.len();
222 let off = range.start.min(dst_len);
223 let del_len = range.end.saturating_sub(off).min(dst_len - off);
224
225 if del_len == 0 && src_len == 0 {
226 return; }
228
229 let tail_len = dst_len - off - del_len;
230 let new_len = dst_len - del_len + src_len;
231
232 if src_len > del_len {
233 dst.reserve(src_len - del_len);
234 }
235
236 let ptr = dst.as_mut_ptr().add(off);
240
241 if tail_len > 0 && src_len != del_len {
243 ptr::copy(ptr.add(del_len), ptr.add(src_len), tail_len);
244 }
245
246 ptr::copy_nonoverlapping(src.as_ptr(), ptr, src_len);
248 dst.set_len(new_len);
249 }
250}
251
252pub fn file_read_uninit<T: Read>(
254 file: &mut T,
255 buf: &mut [MaybeUninit<u8>],
256) -> apperr::Result<usize> {
257 unsafe {
258 let buf_slice = slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len());
259 let n = file.read(buf_slice)?;
260 Ok(n)
261 }
262}
263
264#[inline(always)]
266pub const fn slice_as_uninit_ref<T>(slice: &[T]) -> &[MaybeUninit<T>] {
267 unsafe { slice::from_raw_parts(slice.as_ptr() as *const MaybeUninit<T>, slice.len()) }
268}
269
270#[inline(always)]
272pub const fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
273 unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut MaybeUninit<T>, slice.len()) }
274}
275
276pub trait AsciiStringHelpers {
278 fn starts_with_ignore_ascii_case(&self, prefix: &str) -> bool;
283}
284
285impl AsciiStringHelpers for str {
286 fn starts_with_ignore_ascii_case(&self, prefix: &str) -> bool {
287 let s = self.as_bytes();
290 let p = prefix.as_bytes();
291 p.len() <= s.len() && s[..p.len()].eq_ignore_ascii_case(p)
292 }
293}