pascal_string/
lib.rs

1#![warn(missing_docs, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications,
2        unused_results)]
3
4//! # Pascal strings in Rust.
5//!
6//! A `PascalString`, or `ShortString` is a String which stores its data on the stack. Because of this, it has
7//! a fixed maximum size, which cannot be changed. Traditionally, the size of a `PascalString` is 256 bytes -
8//! the first byte stores the length, which means that each remaining byte is indexable using only that byte.
9//!
10//! This is a very niche string type - generally, you are better off using `std::string::String`, or the
11//! `AsciiString` type from the `ascii` crate if you need an ascii string. They have no upper size limit, and
12//! are cheaper to pass around as they are only 64 bytes on the stack. Generally, you should only use `PascalString` if:
13//!
14//! * You know that you absolutely, certainly cannot do without heap allocation.
15//! * You need to store your string data inline into your `struct` type - for example if you will allocate a bunch
16//!   of these custom `struct` types into a pool allocator, and cannot afford the heap fragmentation.
17//! * You will keep, allocate, and deallocate a *lot* of short strings in your program.
18
19extern crate ascii;
20
21mod pascal_str;
22mod pascal_string;
23
24pub use pascal_str::{Chars, CharsMut, Lines, PascalStr};
25pub use pascal_string::{IntoChars, PascalString, PascalStringAppendError, PascalStringCreateError, AsciiError};
26use std::u8;
27
28const PASCAL_STRING_BUF_SIZE: usize = u8::MAX as usize;
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use ascii::*;
34    use std::borrow::Cow;
35    use std::ffi::{CStr, CString};
36    use std::iter::IntoIterator;
37
38    #[test]
39    fn test_string_creation() {
40        let test = "Hello, my world!".to_owned();
41        let test_pascal = PascalString::from(&test).unwrap();
42        assert_eq!(&test, test_pascal.as_str());
43
44        let too_many_bytes = vec![12u8; 256];
45        assert!(match PascalString::from(&too_many_bytes) {
46            Err(PascalStringCreateError::InputTooLong) => true,
47            _ => false
48        });
49    }
50
51    #[test]
52    fn test_character_append() {
53        let mut string = PascalString::new();
54        assert!(string.try_push('h').is_ok());
55        string.push('e');
56        string.push(76u8);
57        string.push('l');
58        string.push(AsciiChar::L);
59        string.push('o');
60        string.push(AsciiChar::Null);
61
62        assert_eq!(string.as_str(), "heLlLo\0");
63    }
64
65    #[test]
66    fn test_string_append() {
67        let mut string = PascalString::new();
68        string.push_str("Hola, ");
69        string.push_str("Senor!");
70        assert_eq!(string.as_str(), "Hola, Senor!");
71    }
72
73    #[test]
74    fn test_string_indexing_and_char_iteration() {
75        {
76            let string = PascalString::from("q").unwrap();
77            assert_eq!(string[0u8], AsciiChar::q);
78        }
79
80        {
81            let string2 = PascalString::from("WASD").unwrap();
82            {
83                let mut chars_iter = string2.chars();
84                assert_eq!(chars_iter.next(), Some(&AsciiChar::W));
85                assert_eq!(chars_iter.next(), Some(&AsciiChar::A));
86                assert_eq!(chars_iter.next(), Some(&AsciiChar::S));
87                assert_eq!(chars_iter.next(), Some(&AsciiChar::D));
88                assert_eq!(chars_iter.next(), None);
89            }
90
91            let mut into_chars = string2.into_iter();
92            assert_eq!(into_chars.next(), Some(AsciiChar::W));
93            assert_eq!(into_chars.next(), Some(AsciiChar::A));
94            assert_eq!(into_chars.next(), Some(AsciiChar::S));
95            assert_eq!(into_chars.next(), Some(AsciiChar::D));
96            assert_eq!(into_chars.next(), None);
97        }
98    }
99
100    #[test]
101    fn test_lines_iteration() {
102        let string = "hello\n,\nmy\ndarling".to_string();
103        let pstring = PascalString::from(&string).unwrap();
104        for (s0, s1) in pstring.lines().zip(string.split_whitespace()) {
105            assert_eq!(s0.as_str(), s1);
106        }
107    }
108
109    #[test]
110    fn test_as_cstr() {
111        {
112            let msg = "I am your favourite cookie monster >:-)\0";
113            let pstr = PascalString::from(&msg).unwrap();
114            let cstr = CStr::from_bytes_with_nul(msg.as_bytes()).unwrap();
115            let pstr_as_cstr = pstr.as_cstr().unwrap();
116            assert!(match pstr_as_cstr {
117                Cow::Borrowed(_) => true,
118                _ => false
119            });
120            assert_eq!(&*pstr_as_cstr, cstr);
121        }
122
123        {
124            let oversized = ['l'; 255];
125            let string_oversized = {
126                let mut s = String::new();
127                for i in 0..oversized.len() {
128                    s.push(oversized[i]);
129                }
130                s
131            };
132            let pstr_oversized = PascalString::from_fixed_ascii_array(255, oversized).unwrap();
133            let cstr_from_pstr_oversized = pstr_oversized.as_cstr().unwrap();
134            let cstr_from_string_oversized = CString::new(string_oversized).unwrap();
135            assert!(match cstr_from_pstr_oversized {
136                Cow::Owned(_) => true,
137                _ => false
138            });
139            assert_eq!(cstr_from_pstr_oversized.into_owned(), cstr_from_string_oversized);
140        }
141
142        {
143            let has_interior_null = "lol\0hi";
144            let pstr = PascalString::from(&has_interior_null).unwrap();
145            let err = match pstr.as_cstr() {
146                Err(err) => err,
147                _ => panic!("incorrect result")
148            };
149            assert_eq!(err.interior_null_index(), 3);
150        }
151    }
152}