Skip to main content

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