ndarray_npy/npy/elements/
bool.rs

1//! Trait implementations for `bool`.
2
3use super::{bytes_as_mut_slice, bytes_as_slice, check_for_extra_bytes};
4use crate::{ReadDataError, ReadableElement, ViewDataError, ViewElement, ViewMutElement};
5use py_literal::Value as PyValue;
6use std::error::Error;
7use std::fmt;
8use std::io;
9use std::mem;
10
11/// An error parsing a `bool` from a byte.
12#[derive(Debug)]
13struct ParseBoolError {
14    bad_value: u8,
15}
16
17impl Error for ParseBoolError {
18    fn source(&self) -> Option<&(dyn Error + 'static)> {
19        None
20    }
21}
22
23impl fmt::Display for ParseBoolError {
24    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25        write!(f, "error parsing value {:#04x} as a bool", self.bad_value)
26    }
27}
28
29impl From<ParseBoolError> for ReadDataError {
30    fn from(err: ParseBoolError) -> ReadDataError {
31        ReadDataError::ParseData(Box::new(err))
32    }
33}
34
35impl From<ParseBoolError> for ViewDataError {
36    fn from(err: ParseBoolError) -> ViewDataError {
37        ViewDataError::InvalidData(Box::new(err))
38    }
39}
40
41/// Returns `Ok(_)` iff each of the bytes is a valid bitwise representation for
42/// `bool`.
43///
44/// In other words, this checks that each byte is `0x00` or `0x01`, which is
45/// important for the bytes to be reinterpreted as `bool`, since creating a
46/// `bool` with an invalid value is undefined behavior. Rust guarantees that
47/// `false` is represented as `0x00` and `true` is represented as `0x01`.
48fn check_valid_for_bool(bytes: &[u8]) -> Result<(), ParseBoolError> {
49    for &byte in bytes {
50        if byte > 1 {
51            return Err(ParseBoolError { bad_value: byte });
52        }
53    }
54    Ok(())
55}
56
57impl ReadableElement for bool {
58    fn read_to_end_exact_vec<R: io::Read>(
59        mut reader: R,
60        type_desc: &PyValue,
61        len: usize,
62    ) -> Result<Vec<Self>, ReadDataError> {
63        match *type_desc {
64            PyValue::String(ref s) if s == "|b1" => {
65                // Read the data.
66                let mut bytes: Vec<u8> = vec![0; len];
67                reader.read_exact(&mut bytes)?;
68                check_for_extra_bytes(&mut reader)?;
69
70                // Check that the data is valid for interpretation as `bool`.
71                check_valid_for_bool(&bytes)?;
72
73                // Cast the `Vec<u8>` to `Vec<bool>`.
74                {
75                    let ptr: *mut u8 = bytes.as_mut_ptr();
76                    let len: usize = bytes.len();
77                    let cap: usize = bytes.capacity();
78                    mem::forget(bytes);
79                    // This is safe because:
80                    //
81                    // * All elements are valid `bool`s. (See the call to
82                    //   `check_valid_for_bool` above.)
83                    //
84                    // * `ptr` was originally allocated by `Vec`.
85                    //
86                    // * `bool` has the same size and alignment as `u8`.
87                    //
88                    // * `len` and `cap` are copied directly from the
89                    //   `Vec<u8>`, so `len <= cap` and `cap` is the capacity
90                    //   `ptr` was allocated with.
91                    Ok(unsafe { Vec::from_raw_parts(ptr.cast::<bool>(), len, cap) })
92                }
93            }
94            ref other => Err(ReadDataError::WrongDescriptor(other.clone())),
95        }
96    }
97}
98
99// Rust guarantees that `bool` is one byte, the bitwise representation of
100// `false` is `0x00`, and the bitwise representation of `true` is `0x01`, so we
101// can just cast the data in-place.
102impl_writable_element_always_valid_cast!(bool, "|b1", "|b1");
103
104impl ViewElement for bool {
105    fn bytes_as_slice<'a>(
106        bytes: &'a [u8],
107        type_desc: &PyValue,
108        len: usize,
109    ) -> Result<&'a [Self], ViewDataError> {
110        match *type_desc {
111            PyValue::String(ref s) if s == "|b1" => {
112                check_valid_for_bool(bytes)?;
113                unsafe { bytes_as_slice(bytes, len) }
114            }
115            ref other => Err(ViewDataError::WrongDescriptor(other.clone())),
116        }
117    }
118}
119
120impl ViewMutElement for bool {
121    fn bytes_as_mut_slice<'a>(
122        bytes: &'a mut [u8],
123        type_desc: &PyValue,
124        len: usize,
125    ) -> Result<&'a mut [Self], ViewDataError> {
126        match *type_desc {
127            PyValue::String(ref s) if s == "|b1" => {
128                check_valid_for_bool(bytes)?;
129                unsafe { bytes_as_mut_slice(bytes, len) }
130            }
131            ref other => Err(ViewDataError::WrongDescriptor(other.clone())),
132        }
133    }
134}