ocl_core/
util.rs

1//! Utility and debugging functions.
2//!
3//! ## Stability
4//!
5//! Printing functions may be moved/renamed/removed at any time.
6use crate::{OclPrm, OclScl};
7use num_traits::PrimInt;
8use std::iter;
9use std::mem;
10use std::ops::Range;
11use std::ptr;
12use std::string::FromUtf8Error;
13
14//=============================================================================
15//================================= MACROS ====================================
16//=============================================================================
17
18//=============================================================================
19//================================ STATICS ====================================
20//=============================================================================
21
22pub mod colors {
23    //! ASCII Color Palette
24    //!
25    //! Used for printing functions.
26    //
27    // TODO: Remove or feature gate printing related code.
28
29    pub static TAB: &'static str = "    ";
30
31    pub static C_DEFAULT: &'static str = "\x1b[0m";
32    pub static C_UNDER: &'static str = "\x1b[1m";
33
34    // 30–37
35    pub static C_RED: &'static str = "\x1b[31m";
36    pub static C_BRED: &'static str = "\x1b[1;31m";
37    pub static C_GRN: &'static str = "\x1b[32m";
38    pub static C_BGRN: &'static str = "\x1b[1;32m";
39    pub static C_ORA: &'static str = "\x1b[33m";
40    pub static C_DBL: &'static str = "\x1b[34m";
41    pub static C_PUR: &'static str = "\x1b[35m";
42    pub static C_CYA: &'static str = "\x1b[36m";
43    pub static C_LGR: &'static str = "\x1b[37m";
44    // [ADDME] 38: Extended Colors
45    // pub static C_EXT38: &'static str = "\x1b[38m";
46    pub static C_DFLT: &'static str = "\x1b[39m";
47
48    // 90-97
49    pub static C_DGR: &'static str = "\x1b[90m";
50    pub static C_LRD: &'static str = "\x1b[91m";
51    pub static C_YEL: &'static str = "\x1b[93m";
52    pub static C_BLU: &'static str = "\x1b[94m";
53    pub static C_LBL: &'static str = "\x1b[94m";
54    pub static C_MAG: &'static str = "\x1b[95m";
55    // [ADDME] 38: Extended Colors
56    // pub static C_EXT38: &'static str = "\x1b[38m";
57
58    pub static BGC_DEFAULT: &'static str = "\x1b[49m";
59    pub static BGC_GRN: &'static str = "\x1b[42m";
60    pub static BGC_PUR: &'static str = "\x1b[45m";
61    pub static BGC_LGR: &'static str = "\x1b[47m";
62    pub static BGC_DGR: &'static str = "\x1b[100m";
63}
64
65//=============================================================================
66//=========================== UTILITY FUNCTIONS ===============================
67//=============================================================================
68
69/// An error caused by a utility function.
70#[derive(Debug, thiserror::Error)]
71pub enum UtilError {
72    #[error(
73        "The size of the source byte slice ({src} bytes) does not match \
74        the size of the destination type ({dst} bytes)."
75    )]
76    BytesTo { src: usize, dst: usize },
77    #[error(
78        "The size of the source byte vector ({src} bytes) does not match \
79        the size of the destination type ({dst} bytes)."
80    )]
81    BytesInto { src: usize, dst: usize },
82    #[error(
83        "The size of the source byte vector ({src} bytes) is not evenly \
84        divisible by the size of the destination type ({dst} bytes)."
85    )]
86    BytesIntoVec { src: usize, dst: usize },
87    #[error(
88        "The size of the source byte slice ({src} bytes) is not evenly \
89        divisible by the size of the destination type ({dst} bytes)."
90    )]
91    BytesToVec { src: usize, dst: usize },
92    #[error("Unable to convert bytes into string: {0}")]
93    BytesIntoString(#[from] FromUtf8Error),
94}
95
96/// Copies a byte slice to a new `u32`.
97///
98/// ### Stability
99///
100/// May depricate in favor of `bytes_to`
101///
102pub fn bytes_to_u32(bytes: &[u8]) -> u32 {
103    debug_assert!(bytes.len() == 4);
104
105    u32::from(bytes[0])
106        | (u32::from(bytes[1]) << 8)
107        | (u32::from(bytes[2]) << 16)
108        | (u32::from(bytes[3]) << 24)
109}
110
111/// Copies a slice of bytes to a new value of arbitrary type.
112///
113/// ### Safety
114///
115/// You may want to wear a helmet.
116///
117pub unsafe fn bytes_to<T>(bytes: &[u8]) -> Result<T, UtilError> {
118    if mem::size_of::<T>() == bytes.len() {
119        let mut new_val = mem::MaybeUninit::<T>::uninit();
120        ptr::copy(bytes.as_ptr(), new_val.as_mut_ptr() as *mut u8, bytes.len());
121        Ok(new_val.assume_init())
122    } else {
123        Err(UtilError::BytesTo {
124            src: bytes.len(),
125            dst: mem::size_of::<T>(),
126        })
127    }
128}
129
130/// Converts a vector of bytes into a value of arbitrary type.
131///
132/// ### Safety
133///
134/// Roughly equivalent to a weekend in Tijuana.
135///
136// [NOTE]: Not sure this is the best or simplest way to do this but whatever.
137// Would be nice to not even have to copy anything and just basically
138// transmute the vector into the result type. [TODO]: Fiddle with this
139// at some point.
140//
141pub unsafe fn bytes_into<T>(vec: Vec<u8>) -> Result<T, UtilError> {
142    if mem::size_of::<T>() == vec.len() {
143        let mut new_val = mem::MaybeUninit::<T>::uninit();
144        ptr::copy(vec.as_ptr(), new_val.as_mut_ptr() as *mut u8, vec.len());
145        Ok(new_val.assume_init())
146    } else {
147        Err(UtilError::BytesInto {
148            src: vec.len(),
149            dst: mem::size_of::<T>(),
150        })
151    }
152}
153
154/// Converts a vector of bytes into a vector of arbitrary type.
155///
156/// ### Safety
157///
158/// Ummm... Say what?
159///
160/// TODO: Consider using `alloc::heap::reallocate_inplace` equivalent.
161///
162pub unsafe fn bytes_into_vec<T>(mut vec: Vec<u8>) -> Result<Vec<T>, UtilError> {
163    // debug_assert!(vec.len() % mem::size_of::<T>() == 0);
164    if vec.len() % mem::size_of::<T>() == 0 {
165        let new_len = vec.len() / mem::size_of::<T>();
166        let new_cap = vec.capacity() / mem::size_of::<T>();
167        let ptr = vec.as_mut_ptr();
168        mem::forget(vec);
169        let mut new_vec: Vec<T> = Vec::from_raw_parts(ptr as *mut T, new_len, new_cap);
170        new_vec.shrink_to_fit();
171        Ok(new_vec)
172    } else {
173        Err(UtilError::BytesIntoVec {
174            src: vec.len(),
175            dst: mem::size_of::<T>(),
176        })
177    }
178}
179
180/// Copies a slice of bytes into a vector of arbitrary type.
181///
182/// ### Safety
183///
184/// Negative.
185///
186pub unsafe fn bytes_to_vec<T>(bytes: &[u8]) -> Result<Vec<T>, UtilError> {
187    // debug_assert!(bytes.len() % mem::size_of::<T>() == 0);
188    if bytes.len() % mem::size_of::<T>() == 0 {
189        let new_len = bytes.len() / mem::size_of::<T>();
190        let mut new_vec: Vec<T> = Vec::with_capacity(new_len);
191        ptr::copy(
192            bytes.as_ptr(),
193            new_vec.as_mut_ptr() as *mut _ as *mut u8,
194            bytes.len(),
195        );
196        new_vec.set_len(new_len);
197        Ok(new_vec)
198    } else {
199        Err(UtilError::BytesToVec {
200            src: bytes.len(),
201            dst: mem::size_of::<T>(),
202        })
203    }
204}
205
206/// Converts a byte Vec into a string, removing the trailing null byte if it
207/// exists.
208pub fn bytes_into_string(mut bytes: Vec<u8>) -> Result<String, UtilError> {
209    if bytes.last() == Some(&0u8) {
210        bytes.pop();
211    }
212
213    String::from_utf8(bytes)
214        .map(|str| String::from(str.trim()))
215        .map_err(UtilError::BytesIntoString)
216}
217
218/// [UNTESTED] Copies an arbitrary primitive or struct into core bytes.
219///
220/// ### Depth
221///
222/// This is not a deep copy, will only copy the surface of primitives, structs,
223/// etc. Not 100% sure about what happens with other types but should copy
224/// everything zero levels deep.
225///
226/// ### Endianness
227///
228/// 98% sure (speculative) this will always be correct due to the driver
229/// automatically taking it into account.
230///
231/// ### Safety
232///
233/// Don't ask.
234///
235/// [FIXME]: Evaluate the ins and outs of this and lock this down a bit.
236pub unsafe fn into_bytes<T>(val: T) -> Vec<u8> {
237    // let big_endian = false;
238    let size = mem::size_of::<T>();
239    let mut new_vec: Vec<u8> = iter::repeat(0).take(size).collect();
240
241    ptr::copy(&val as *const _ as *const u8, new_vec.as_mut_ptr(), size);
242
243    // if big_endian {
244    //     new_vec = new_vec.into_iter().rev().collect();
245    // }
246
247    new_vec
248}
249
250/// Pads `len` to make it evenly divisible by `incr`.
251pub fn padded_len(len: usize, incr: usize) -> usize {
252    let len_mod = len % incr;
253
254    if len_mod == 0 {
255        len
256    } else {
257        let pad = incr - len_mod;
258        let padded_len = len + pad;
259        debug_assert_eq!(padded_len % incr, 0);
260        padded_len
261    }
262}
263
264/// An error caused by `util::vec_remove_rebuild`.
265#[derive(thiserror::Error, Debug)]
266pub enum VecRemoveRebuildError {
267    #[error("Remove list is longer than source vector.")]
268    TooLong,
269    #[error(
270        "'remove_list' contains at least one out of range index: [{idx}] \
271        ('orig_vec' length: {orig_len})."
272    )]
273    OutOfRange { idx: usize, orig_len: usize },
274}
275
276/// Batch removes elements from a vector using a list of indices to remove.
277///
278/// Will create a new vector and do a streamlined rebuild if
279/// `remove_list.len()` > `rebuild_threshold`. Threshold should typically be
280/// set very low (less than probably 5 or 10) as it's expensive to remove one
281/// by one.
282///
283pub fn vec_remove_rebuild<T: Clone + Copy>(
284    orig_vec: &mut Vec<T>,
285    remove_list: &[usize],
286    rebuild_threshold: usize,
287) -> Result<(), VecRemoveRebuildError> {
288    if remove_list.len() > orig_vec.len() {
289        return Err(VecRemoveRebuildError::TooLong);
290    }
291    let orig_len = orig_vec.len();
292
293    // If the list is below threshold
294    if remove_list.len() <= rebuild_threshold {
295        for &idx in remove_list.iter().rev() {
296            if idx < orig_len {
297                orig_vec.remove(idx);
298            } else {
299                return Err(VecRemoveRebuildError::OutOfRange { idx, orig_len });
300            }
301        }
302    } else {
303        unsafe {
304            let mut remove_markers: Vec<bool> = iter::repeat(true).take(orig_len).collect();
305
306            // Build a sparse list of which elements to remove:
307            for &idx in remove_list.iter() {
308                if idx < orig_len {
309                    *remove_markers.get_unchecked_mut(idx) = false;
310                } else {
311                    return Err(VecRemoveRebuildError::OutOfRange { idx, orig_len });
312                }
313            }
314
315            let mut new_len = 0usize;
316
317            // Iterate through remove_markers and orig_vec, pushing when the marker is false:
318            for idx in 0..orig_len {
319                if *remove_markers.get_unchecked(idx) {
320                    *orig_vec.get_unchecked_mut(new_len) = *orig_vec.get_unchecked(idx);
321                    new_len += 1;
322                }
323            }
324
325            debug_assert_eq!(new_len, orig_len - remove_list.len());
326            orig_vec.set_len(new_len);
327        }
328    }
329
330    Ok(())
331}
332
333/// Wraps (`%`) each value in the list `vals` if it equals or exceeds `val_n`.
334pub fn wrap_vals<T: OclPrm + PrimInt>(vals: &[T], val_n: T) -> Vec<T> {
335    vals.iter().map(|&v| v % val_n).collect()
336}
337
338// /// Converts a length in `T` to a size in bytes.
339// #[inline]
340// pub fn len_to_size<T>(len: usize) -> usize {
341//     len * mem::size_of::<T>()
342// }
343
344// /// Converts lengths in `T` to sizes in bytes for a `[usize; 3]`.
345// #[inline]
346// pub fn len3_to_size3<T>(lens: [usize; 3]) -> [usize; 3] {
347//     [len_to_size::<T>(lens[0]), len_to_size::<T>(lens[1]), len_to_size::<T>(lens[2])]
348// }
349
350// /// Converts lengths in `T` to sizes in bytes for a `&[usize]`.
351// pub fn lens_to_sizes<T>(lens: &[usize]) -> Vec<usize> {
352//     lens.iter().map(|len| len * mem::size_of::<T>()).collect()
353// }
354
355//=============================================================================
356//=========================== PRINTING FUNCTIONS ==============================
357//=============================================================================
358
359/// Prints bytes as hex.
360pub fn print_bytes_as_hex(bytes: &[u8]) {
361    print!("0x");
362
363    for &byte in bytes.iter() {
364        print!("{:x}", byte);
365    }
366}
367
368#[allow(unused_assignments, unused_variables)]
369/// [UNSTABLE]: MAY BE REMOVED AT ANY TIME
370/// Prints a vector to stdout. Used for debugging.
371//
372// TODO: Remove or feature gate printing related code.
373//
374pub fn print_slice<T: OclScl>(
375    vec: &[T],
376    every: usize,
377    val_range: Option<(T, T)>,
378    idx_range: Option<Range<usize>>,
379    show_zeros: bool,
380) {
381    print!(
382        "{cdgr}[{cg}{}{cdgr}/{}",
383        vec.len(),
384        every,
385        cg = colors::C_GRN,
386        cdgr = colors::C_DGR
387    );
388
389    let (vr_start, vr_end) = match val_range {
390        Some(vr) => {
391            print!(";({}-{})", vr.0, vr.1);
392            vr
393        }
394        None => (Default::default(), Default::default()),
395    };
396
397    let (ir_start, ir_end) = match idx_range {
398        Some(ref ir) => {
399            print!(";[{}..{}]", ir.start, ir.end);
400            (ir.start, ir.end)
401        }
402        None => (0usize, 0usize),
403    };
404
405    print!("]:{cd} ", cd = colors::C_DEFAULT,);
406
407    let mut ttl_nz = 0usize;
408    let mut ttl_ir = 0usize;
409    let mut within_idx_range = true;
410    let mut within_val_range = true;
411    let mut hi: T = vr_start;
412    let mut lo: T = vr_end;
413    let mut sum: i64 = 0;
414    let mut ttl_prntd: usize = 0;
415    let len = vec.len();
416
417    let mut color: &'static str = colors::C_DEFAULT;
418    let mut prnt: bool = false;
419
420    // Yes, this clusterfuck needs rewriting someday
421    for (i, item) in vec.iter().enumerate() {
422        prnt = false;
423
424        if every != 0 {
425            if i % every == 0 {
426                prnt = true;
427            } else {
428                prnt = false;
429            }
430        }
431
432        if idx_range.is_some() {
433            let ir = idx_range.as_ref().expect("ocl::buffer::print_vec()");
434
435            if i < ir_start || i >= ir_end {
436                prnt = false;
437                within_idx_range = false;
438            } else {
439                within_idx_range = true;
440            }
441        } else {
442            within_idx_range = true;
443        }
444
445        if val_range.is_some() {
446            if *item < vr_start || *item > vr_end {
447                prnt = false;
448                within_val_range = false;
449            } else {
450                if within_idx_range {
451                    // if *item == Default::default() {
452                    //     ttl_ir += 1;
453                    // } else {
454                    //     ttl_ir += 1;
455                    // }
456                    ttl_ir += 1;
457                }
458
459                within_val_range = true;
460            }
461        }
462
463        if within_idx_range && within_val_range {
464            sum += item.to_i64().expect("ocl::buffer::print_vec(): vec[i]");
465
466            if *item > hi {
467                hi = *item
468            };
469
470            if *item < lo {
471                lo = *item
472            };
473
474            if vec[i] != Default::default() {
475                ttl_nz += 1usize;
476                color = colors::C_ORA;
477            } else if show_zeros {
478                color = colors::C_DEFAULT;
479            } else {
480                prnt = false;
481            }
482        }
483
484        if prnt {
485            print!(
486                "{cg}[{cd}{}{cg}:{cc}{}{cg}]{cd}",
487                i,
488                vec[i],
489                cc = color,
490                cd = colors::C_DEFAULT,
491                cg = colors::C_DGR
492            );
493            ttl_prntd += 1;
494        }
495    }
496
497    let mut anz: f32 = 0f32;
498    let mut nz_pct: f32 = 0f32;
499
500    let mut ir_pct: f32 = 0f32;
501    let mut avg_ir: f32 = 0f32;
502
503    if ttl_nz > 0 {
504        anz = sum as f32 / ttl_nz as f32;
505        nz_pct = (ttl_nz as f32 / len as f32) * 100f32;
506        //print!( "[ttl_nz: {}, nz_pct: {:.0}%, len: {}]", ttl_nz, nz_pct, len);
507    }
508
509    if ttl_ir > 0 {
510        avg_ir = sum as f32 / ttl_ir as f32;
511        ir_pct = (ttl_ir as f32 / len as f32) * 100f32;
512        //print!( "[ttl_nz: {}, nz_pct: {:.0}%, len: {}]", ttl_nz, nz_pct, len);
513    }
514
515    println!(
516        "{cdgr}; (nz:{clbl}{}{cdgr}({clbl}{:.2}%{cdgr}),\
517        ir:{clbl}{}{cdgr}({clbl}{:.2}%{cdgr}),hi:{},lo:{},anz:{:.2},prntd:{}){cd} ",
518        ttl_nz,
519        nz_pct,
520        ttl_ir,
521        ir_pct,
522        hi,
523        lo,
524        anz,
525        ttl_prntd,
526        cd = colors::C_DEFAULT,
527        clbl = colors::C_LBL,
528        cdgr = colors::C_DGR
529    );
530}
531
532pub fn print_simple<T: OclScl>(slice: &[T]) {
533    print_slice(slice, 1, None, None, true);
534}
535
536pub fn print_val_range<T: OclScl>(slice: &[T], every: usize, val_range: Option<(T, T)>) {
537    print_slice(slice, every, val_range, None, true);
538}
539
540#[cfg(test)]
541mod tests {
542    // use std::iter;
543
544    #[test]
545    fn remove_rebuild() {
546        let mut primary_vals: Vec<u32> = (0..(1 << 18)).map(|v| v).collect();
547        let orig_len = primary_vals.len();
548
549        let mut bad_indices: Vec<usize> = Vec::<usize>::with_capacity(1 << 16);
550        let mut idx = 0;
551
552        // Mark every whateverth value 'bad':
553        for &val in primary_vals.iter() {
554            if (val % 19 == 0) || (val % 31 == 0) || (val % 107 == 0) {
555                bad_indices.push(idx);
556            }
557
558            idx += 1;
559        }
560
561        println!(
562            "util::tests::remove_rebuild(): bad_indices: {}",
563            bad_indices.len()
564        );
565
566        // Remove the bad values:
567        super::vec_remove_rebuild(&mut primary_vals, &bad_indices[..], 3)
568            .expect("util::tests::remove_rebuild()");
569
570        // Check:
571        for &val in primary_vals.iter() {
572            if (val % 19 == 0) || (val % 31 == 0) || (val % 107 == 0) {
573                panic!(
574                    "util::tests::remove_rebuild(): Value: '{}' found in list!",
575                    val
576                );
577            }
578        }
579
580        assert_eq!(orig_len, primary_vals.len() + bad_indices.len());
581    }
582}