fyrox_core/
lib.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Core data structures and algorithms used throughout Fyrox.
22//!
23//! Some of them can be useful separately outside the engine.
24
25#![allow(clippy::upper_case_acronyms)]
26#![allow(clippy::from_over_into)]
27#![allow(clippy::doc_lazy_continuation)]
28
29#[macro_use]
30extern crate memoffset;
31#[macro_use]
32extern crate lazy_static;
33
34pub use arrayvec;
35pub use byteorder;
36pub use nalgebra as algebra;
37pub use num_traits;
38pub use parking_lot;
39pub use rand;
40pub use sstorage::ImmutableString;
41pub use uuid;
42
43use crate::visitor::{Visit, VisitResult, Visitor};
44use bytemuck::Pod;
45use fxhash::FxHashMap;
46use std::any::Any;
47use std::collections::hash_map::Entry;
48use std::ffi::OsString;
49use std::hash::Hasher;
50use std::{
51    borrow::Borrow,
52    cmp,
53    hash::Hash,
54    path::{Path, PathBuf},
55};
56pub mod color;
57pub mod color_gradient;
58pub mod early;
59pub mod io;
60pub mod log;
61pub mod math;
62pub mod net;
63pub mod numeric_range;
64pub mod pool;
65pub mod quadtree;
66pub mod rectpack;
67pub mod reflect;
68pub mod sparse;
69pub mod sstorage;
70pub mod task;
71pub mod type_traits;
72pub mod variable;
73pub mod visitor;
74pub mod watcher;
75
76pub use futures;
77pub use instant;
78
79pub use notify;
80
81#[cfg(target_arch = "wasm32")]
82pub use js_sys;
83use std::marker::PhantomData;
84#[cfg(target_arch = "wasm32")]
85pub use wasm_bindgen;
86#[cfg(target_arch = "wasm32")]
87pub use wasm_bindgen_futures;
88#[cfg(target_arch = "wasm32")]
89pub use web_sys;
90
91pub use type_traits::prelude::*;
92/// Defines as_(variant), as_mut_(variant) and is_(variant) methods.
93#[macro_export]
94macro_rules! define_is_as {
95    ($typ:tt : $kind:ident -> ref $result:path => fn $is:ident, fn $as_ref:ident, fn $as_mut:ident) => {
96        /// Returns true if node is instance of given type.
97        pub fn $is(&self) -> bool {
98            match self {
99                $typ::$kind(_) => true,
100                _ => false,
101            }
102        }
103
104        /// Tries to cast shared reference to a node to given type, panics if
105        /// cast is not possible.
106        pub fn $as_ref(&self) -> &$result {
107            match self {
108                $typ::$kind(ref val) => val,
109                _ => panic!("Cast to {} failed!", stringify!($kind)),
110            }
111        }
112
113        /// Tries to cast mutable reference to a node to given type, panics if
114        /// cast is not possible.
115        pub fn $as_mut(&mut self) -> &mut $result {
116            match self {
117                $typ::$kind(ref mut val) => val,
118                _ => panic!("Cast to {} failed!", stringify!($kind)),
119            }
120        }
121    };
122}
123
124/// Utility function that replaces back slashes \ to forward /. Internally, it converts the input
125/// path to string (lossy - see [`Path::to_string_lossy`]) and replaces the slashes in the string.
126/// Finally, it converts the string to the PathBuf and returns it. This method is intended to be
127/// used only for paths, that does not contain non-unicode characters.
128pub fn replace_slashes<P: AsRef<Path>>(path: P) -> PathBuf {
129    PathBuf::from(
130        path.as_ref()
131            .to_string_lossy()
132            .to_string()
133            .replace('\\', "/"),
134    )
135}
136
137/// Appends specified extension to the path.
138///
139/// # Examples
140///
141/// ```rust
142/// # use std::path::Path;
143/// # use fyrox_core::append_extension;
144/// let path = Path::new("foo.bar");
145/// let new_path = append_extension(path, "baz");
146/// assert_eq!(new_path, Path::new("foo.bar.baz"))
147/// ```
148#[must_use]
149pub fn append_extension<P: AsRef<Path>, E: AsRef<str>>(
150    path: P,
151    additional_extension: E,
152) -> PathBuf {
153    let mut final_path = path.as_ref().to_path_buf();
154    let new_extension = final_path
155        .extension()
156        .map(|e| {
157            let mut ext = e.to_owned();
158            ext.push(".");
159            ext.push(additional_extension.as_ref());
160            ext
161        })
162        .unwrap_or_else(|| OsString::from(additional_extension.as_ref()));
163    final_path.set_extension(new_extension);
164    final_path
165}
166
167#[derive(Clone, Debug)]
168pub struct BiDirHashMap<K, V> {
169    forward_map: FxHashMap<K, V>,
170    backward_map: FxHashMap<V, K>,
171}
172
173impl<K: Hash + Eq + Clone, V: Hash + Eq + Clone> BiDirHashMap<K, V> {
174    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
175        let existing = self.forward_map.insert(key.clone(), value.clone());
176        self.backward_map.insert(value, key);
177        existing
178    }
179
180    pub fn remove_by_key(&mut self, key: &K) -> Option<V> {
181        if let Some(value) = self.forward_map.remove(key) {
182            self.backward_map.remove(&value);
183            Some(value)
184        } else {
185            None
186        }
187    }
188
189    pub fn contains_key<Q: ?Sized + Hash + Eq>(&self, key: &Q) -> bool
190    where
191        K: Borrow<Q>,
192    {
193        self.forward_map.contains_key(key)
194    }
195
196    pub fn remove_by_value(&mut self, value: &V) -> Option<K> {
197        if let Some(key) = self.backward_map.remove(value) {
198            self.forward_map.remove(&key);
199            Some(key)
200        } else {
201            None
202        }
203    }
204
205    pub fn contains_value<Q: ?Sized + Hash + Eq>(&self, value: &Q) -> bool
206    where
207        V: Borrow<Q>,
208    {
209        self.backward_map.contains_key(value)
210    }
211
212    pub fn value_of(&self, node: &K) -> Option<&V> {
213        self.forward_map.get(node)
214    }
215
216    pub fn key_of(&self, value: &V) -> Option<&K> {
217        self.backward_map.get(value)
218    }
219
220    pub fn len(&self) -> usize {
221        self.forward_map.len()
222    }
223
224    pub fn is_empty(&self) -> bool {
225        self.forward_map.is_empty()
226    }
227
228    pub fn clear(&mut self) {
229        self.forward_map.clear();
230        self.backward_map.clear();
231    }
232
233    pub fn forward_map(&self) -> &FxHashMap<K, V> {
234        &self.forward_map
235    }
236
237    pub fn backward_map(&self) -> &FxHashMap<V, K> {
238        &self.backward_map
239    }
240
241    pub fn into_inner(self) -> (FxHashMap<K, V>, FxHashMap<V, K>) {
242        (self.forward_map, self.backward_map)
243    }
244}
245
246impl<K, V> Default for BiDirHashMap<K, V> {
247    fn default() -> Self {
248        Self {
249            forward_map: Default::default(),
250            backward_map: Default::default(),
251        }
252    }
253}
254
255impl<K: Hash + Eq + Clone, V: Hash + Eq + Clone> From<FxHashMap<K, V>> for BiDirHashMap<K, V> {
256    fn from(forward_map: FxHashMap<K, V>) -> Self {
257        let mut backward_map = FxHashMap::default();
258        for (k, v) in forward_map.iter() {
259            backward_map.insert(v.clone(), k.clone());
260        }
261        Self {
262            forward_map,
263            backward_map,
264        }
265    }
266}
267
268impl<K, V> Visit for BiDirHashMap<K, V>
269where
270    K: Hash + Eq + Clone + Default + Visit,
271    V: Hash + Eq + Clone + Default + Visit,
272{
273    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
274        let mut region = visitor.enter_region(name)?;
275
276        self.forward_map.visit("ForwardMap", &mut region)?;
277        self.backward_map.visit("BackwardMap", &mut region)?;
278
279        Ok(())
280    }
281}
282
283impl<K, V> FromIterator<(K, V)> for BiDirHashMap<K, V>
284where
285    K: Hash + Eq + Clone,
286    V: Hash + Eq + Clone,
287{
288    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
289        let mut hm = Self::default();
290        for (k, v) in iter {
291            hm.forward_map.insert(k.clone(), v.clone());
292            hm.backward_map.insert(v, k);
293        }
294        hm
295    }
296}
297
298#[inline]
299pub fn hash_combine(lhs: u64, rhs: u64) -> u64 {
300    lhs ^ (rhs
301        .wrapping_add(0x9e3779b9)
302        .wrapping_add(lhs << 6)
303        .wrapping_add(lhs >> 2))
304}
305
306/// Strip working directory from file name. The function may fail for one main reason -
307/// input path is not valid, does not exist, or there is some other issues with it.
308pub fn make_relative_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, std::io::Error> {
309    match path
310        .as_ref()
311        .canonicalize()?
312        .strip_prefix(std::env::current_dir()?.canonicalize()?)
313    {
314        Ok(relative_path) => Ok(replace_slashes(relative_path)),
315        Err(_) => Err(std::io::Error::new(
316            std::io::ErrorKind::Other,
317            "unable to strip prefix!",
318        )),
319    }
320}
321
322/// "Transmutes" array of any sized type to a slice of bytes.
323pub fn array_as_u8_slice<T: Sized + Pod>(v: &[T]) -> &'_ [u8] {
324    // SAFETY: It is safe to reinterpret data to read it.
325    unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) }
326}
327
328/// "Transmutes" array of any sized type to a slice of bytes.
329pub fn array_as_u8_slice_mut<T: Sized + Pod>(v: &mut [T]) -> &'_ mut [u8] {
330    // SAFETY: It is safe to reinterpret memory region of POD type.
331    unsafe { std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut u8, std::mem::size_of_val(v)) }
332}
333
334/// "Transmutes" array of any sized type to a slice of some other type.
335pub fn transmute_slice<T: Sized, U: Sized>(v: &[T]) -> &'_ [U] {
336    // SAFETY: It is safe to reinterpret data to read it.
337    unsafe {
338        std::slice::from_raw_parts(
339            v.as_ptr() as *const U,
340            std::mem::size_of_val(v) / std::mem::size_of::<U>(),
341        )
342    }
343}
344
345/// "Transmutes" value of any sized type to a slice of bytes.
346pub fn value_as_u8_slice<T: Sized + Pod>(v: &T) -> &'_ [u8] {
347    // SAFETY: It is safe to reinterpret data to read it.
348    unsafe { std::slice::from_raw_parts(v as *const T as *const u8, std::mem::size_of::<T>()) }
349}
350
351/// Takes a vector of trivially-copyable values and turns it into a vector of bytes.
352pub fn transmute_vec_as_bytes<T: Pod>(vec: Vec<T>) -> Vec<u8> {
353    unsafe {
354        let mut vec = std::mem::ManuallyDrop::new(vec);
355        Vec::from_raw_parts(
356            vec.as_mut_ptr() as *mut u8,
357            vec.len() * std::mem::size_of::<T>(),
358            vec.capacity() * std::mem::size_of::<T>(),
359        )
360    }
361}
362
363/// Performs hashing of a sized value by interpreting it as raw memory.
364pub fn hash_as_bytes<T: Sized + Pod, H: Hasher>(value: &T, hasher: &mut H) {
365    hasher.write(value_as_u8_slice(value))
366}
367
368/// Compares two strings using case-insensitive comparison. This function does not allocate any
369/// any memory and significantly faster than `a.to_lowercase() == b.to_lowercase()`.
370pub fn cmp_strings_case_insensitive(a: impl AsRef<str>, b: impl AsRef<str>) -> bool {
371    let a_ref = a.as_ref();
372    let b_ref = b.as_ref();
373
374    if a_ref.len() != b_ref.len() {
375        return false;
376    }
377
378    a_ref
379        .chars()
380        .zip(b_ref.chars())
381        .all(|(ca, cb)| ca.to_lowercase().eq(cb.to_lowercase()))
382}
383
384pub fn make_pretty_type_name(type_name: &str) -> &str {
385    let mut colon_position = None;
386    let mut byte_pos = 0;
387    for c in type_name.chars() {
388        byte_pos += c.len_utf8();
389        if c == ':' {
390            colon_position = Some(byte_pos);
391        } else if c == '<' {
392            break;
393        }
394    }
395    if let Some(colon_position) = colon_position {
396        type_name.split_at(colon_position).1
397    } else {
398        type_name
399    }
400}
401
402#[repr(transparent)]
403#[derive(Debug)]
404pub struct PhantomDataSendSync<T: ?Sized>(PhantomData<T>);
405
406// SAFETY: PhantomDataSendSync does not hold any data.
407unsafe impl<T: ?Sized> Send for PhantomDataSendSync<T> {}
408// SAFETY: PhantomDataSendSync does not hold any data.
409unsafe impl<T: ?Sized> Sync for PhantomDataSendSync<T> {}
410
411impl<T: ?Sized> Hash for PhantomDataSendSync<T> {
412    #[inline]
413    fn hash<H: Hasher>(&self, _: &mut H) {}
414}
415
416impl<T: ?Sized> PartialEq for PhantomDataSendSync<T> {
417    fn eq(&self, _other: &PhantomDataSendSync<T>) -> bool {
418        true
419    }
420}
421
422impl<T: ?Sized> Eq for PhantomDataSendSync<T> {}
423
424impl<T: ?Sized> PartialOrd for PhantomDataSendSync<T> {
425    fn partial_cmp(&self, _other: &PhantomDataSendSync<T>) -> Option<cmp::Ordering> {
426        Some(self.cmp(_other))
427    }
428}
429
430impl<T: ?Sized> Ord for PhantomDataSendSync<T> {
431    fn cmp(&self, _other: &PhantomDataSendSync<T>) -> cmp::Ordering {
432        cmp::Ordering::Equal
433    }
434}
435
436impl<T: ?Sized> Copy for PhantomDataSendSync<T> {}
437
438impl<T: ?Sized> Clone for PhantomDataSendSync<T> {
439    fn clone(&self) -> Self {
440        *self
441    }
442}
443
444impl<T: ?Sized> Default for PhantomDataSendSync<T> {
445    fn default() -> Self {
446        Self(PhantomData)
447    }
448}
449
450/// A trait for entities that have name.
451pub trait NameProvider {
452    /// Returns a reference to the name of the entity.
453    fn name(&self) -> &str;
454}
455
456/// Tries to find an entity by its name in a series of entities produced by an iterator.
457pub fn find_by_name_ref<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a T)>
458where
459    T: NameProvider,
460    I: Iterator<Item = (K, &'a T)>,
461    S: AsRef<str>,
462{
463    iter.find(|(_, value)| value.name() == name.as_ref())
464}
465
466/// Tries to find an entity by its name in a series of entities produced by an iterator.
467pub fn find_by_name_mut<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a mut T)>
468where
469    T: NameProvider,
470    I: Iterator<Item = (K, &'a mut T)>,
471    S: AsRef<str>,
472{
473    iter.find(|(_, value)| value.name() == name.as_ref())
474}
475
476/// Swaps the content of a hash map entry with the content of an `Option`.
477pub fn swap_hash_map_entry<K, V>(entry: Entry<K, V>, value: &mut Option<V>) {
478    match (entry, value) {
479        (Entry::Occupied(entry), p @ None) => *p = Some(entry.remove()),
480        (Entry::Occupied(mut entry), Some(p)) => std::mem::swap(entry.get_mut(), p),
481        (Entry::Vacant(_), None) => (),
482        (Entry::Vacant(entry), p @ Some(_)) => drop(entry.insert(p.take().unwrap())),
483    }
484}
485
486/// Swaps the content of two hash map entries.
487pub fn swap_hash_map_entries<K0, K1, V>(entry0: Entry<K0, V>, entry1: Entry<K1, V>) {
488    match (entry0, entry1) {
489        (Entry::Occupied(e0), Entry::Vacant(e1)) => drop(e1.insert(e0.remove())),
490        (Entry::Occupied(mut e0), Entry::Occupied(mut e1)) => {
491            std::mem::swap(e0.get_mut(), e1.get_mut())
492        }
493        (Entry::Vacant(_), Entry::Vacant(_)) => (),
494        (Entry::Vacant(e0), Entry::Occupied(e1)) => drop(e0.insert(e1.remove())),
495    }
496}
497
498/// A simple trait for downcasting through [`Any`] trait.
499pub trait Downcast: Any {
500    /// Converts self reference as a reference to [`Any`]. Could be used to downcast a trait object
501    /// to a particular type.
502    fn as_any(&self) -> &dyn Any;
503
504    /// Converts self reference as a reference to [`Any`]. Could be used to downcast a trait object
505    /// to a particular type.
506    fn as_any_mut(&mut self) -> &mut dyn Any;
507
508    fn into_any(self: Box<Self>) -> Box<dyn Any>;
509}
510
511impl<T: Any> Downcast for T {
512    fn as_any(&self) -> &dyn Any {
513        self
514    }
515
516    fn as_any_mut(&mut self) -> &mut dyn Any {
517        self
518    }
519
520    fn into_any(self: Box<Self>) -> Box<dyn Any> {
521        self
522    }
523}
524
525#[cfg(test)]
526mod test {
527    use std::path::Path;
528
529    use crate::{
530        append_extension, cmp_strings_case_insensitive, combine_uuids, hash_combine,
531        make_relative_path, transmute_vec_as_bytes,
532        visitor::{Visit, Visitor},
533        BiDirHashMap,
534    };
535    use fxhash::FxHashMap;
536    use std::mem::size_of;
537    use uuid::uuid;
538
539    #[test]
540    fn test_combine_uuids() {
541        let a = uuid!("d1a45bd5-5066-4b28-b103-95c59c230e77");
542        let b = uuid!("0a06591a-1c66-4299-ba6f-2b205b795575");
543
544        assert_ne!(combine_uuids(a, b), a);
545        assert_ne!(combine_uuids(a, b), b);
546    }
547
548    #[test]
549    fn test_append_extension() {
550        let path = Path::new("foo.bar");
551        let new_path = append_extension(path, "baz");
552        assert_eq!(new_path, Path::new("foo.bar.baz"));
553    }
554
555    #[test]
556    fn bi_dir_hash_map_insert() {
557        let mut map = BiDirHashMap::<u32, u32>::default();
558
559        assert!(map.forward_map.is_empty());
560        assert!(map.backward_map.is_empty());
561
562        let result = map.insert(1, 42);
563
564        assert_eq!(result, None);
565        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
566        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
567    }
568
569    #[test]
570    fn bi_dir_hash_map_remove_by_key() {
571        let mut map = BiDirHashMap::<u32, u32>::default();
572        map.insert(1, 42);
573
574        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
575        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
576
577        let result = map.remove_by_key(&42);
578        assert_eq!(result, None);
579
580        let result = map.remove_by_key(&1);
581        assert_eq!(result, Some(42));
582        assert!(map.forward_map.is_empty());
583        assert!(map.backward_map.is_empty());
584    }
585
586    #[test]
587    fn bi_dir_hash_map_remove_by_value() {
588        let mut map = BiDirHashMap::<u32, u32>::default();
589        map.insert(1, 42);
590
591        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
592        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
593
594        let result = map.remove_by_value(&1);
595        assert_eq!(result, None);
596
597        let result = map.remove_by_value(&42);
598        assert_eq!(result, Some(1));
599        assert!(map.forward_map.is_empty());
600        assert!(map.backward_map.is_empty());
601    }
602
603    #[test]
604    fn bi_dir_hash_map_contains_key() {
605        let mut map = BiDirHashMap::<u32, u32>::default();
606        map.insert(1, 42);
607
608        assert!(map.contains_key(&1));
609        assert!(!map.contains_key(&42));
610    }
611
612    #[test]
613    fn bi_dir_hash_map_contains_value() {
614        let mut map = BiDirHashMap::<u32, u32>::default();
615        map.insert(1, 42);
616
617        assert!(map.contains_value(&42));
618        assert!(!map.contains_value(&1));
619    }
620
621    #[test]
622    fn bi_dir_hash_map_value_of() {
623        let mut map = BiDirHashMap::<u32, u32>::default();
624        map.insert(1, 42);
625
626        assert_eq!(map.value_of(&1), Some(&42));
627        assert_eq!(map.value_of(&42), None);
628    }
629
630    #[test]
631    fn bi_dir_hash_map_key_of() {
632        let mut map = BiDirHashMap::<u32, u32>::default();
633        map.insert(1, 42);
634
635        assert_eq!(map.key_of(&1), None);
636        assert_eq!(map.key_of(&42), Some(&1));
637    }
638
639    #[test]
640    fn bi_dir_hash_map_getters() {
641        let mut map = BiDirHashMap::<u32, u32>::default();
642        assert!(map.is_empty());
643
644        map.insert(1, 42);
645        assert_eq!(map.len(), 1);
646
647        assert!(map.forward_map().eq(&map.forward_map));
648        assert!(map.backward_map().eq(&map.backward_map));
649
650        map.clear();
651        assert!(map.is_empty());
652    }
653
654    #[test]
655    fn bi_dir_hash_map_into_inner() {
656        let mut map = BiDirHashMap::<u32, u32>::default();
657        map.insert(1, 42);
658
659        let (f, b) = map.clone().into_inner();
660        assert!(map.forward_map().eq(&f));
661        assert!(map.backward_map().eq(&b));
662    }
663
664    #[test]
665    fn from_fx_hash_map_for_bi_dir_hash_map() {
666        let mut h = FxHashMap::default();
667        h.insert(1, 42);
668
669        let map = BiDirHashMap::from(h);
670        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
671        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
672    }
673
674    #[test]
675    fn test_visit_for_bi_dir_hash_map() {
676        let mut map = BiDirHashMap::<u32, u32>::default();
677        let mut visitor = Visitor::default();
678
679        assert!(map.visit("name", &mut visitor).is_ok());
680    }
681
682    #[test]
683    fn from_iter_for_bi_dir_hash_map() {
684        let map = BiDirHashMap::from_iter(vec![(1, 42)]);
685
686        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
687        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
688    }
689
690    #[test]
691    fn test_hash_combine() {
692        assert_eq!(hash_combine(1, 1), 0x9E3779FB);
693    }
694
695    #[test]
696    fn test_make_relative_path() {
697        assert!(make_relative_path(Path::new("foo.txt")).is_err());
698        assert!(make_relative_path(Path::new("Cargo.toml")).is_ok());
699    }
700
701    #[test]
702    fn tests_case_insensitive_str_comparison() {
703        assert!(cmp_strings_case_insensitive("FooBar", "FOOBaR"));
704        assert!(!cmp_strings_case_insensitive("FooBaz", "FOOBaR"));
705        assert!(cmp_strings_case_insensitive("foobar", "foobar"));
706    }
707
708    #[test]
709    fn test_transmute_vec_as_bytes_length_new_f32() {
710        let vec = vec![1.0f32, 2.0, 3.0];
711        let byte_vec = transmute_vec_as_bytes(vec.clone());
712        let expected_length = vec.len() * size_of::<f32>();
713        assert_eq!(byte_vec.len(), expected_length);
714    }
715
716    #[test]
717    fn test_transmute_vec_as_bytes_length_new_usize() {
718        let vec = vec![1usize, 2, 3];
719        let byte_vec = transmute_vec_as_bytes(vec.clone());
720        let expected_length = vec.len() * size_of::<usize>();
721        assert_eq!(byte_vec.len(), expected_length);
722    }
723}