Files
@ 379cffa23df8
Branch filter:
Location: CSY/reowolf/src/protocol/containers.rs
379cffa23df8
4.0 KiB
application/rls-services+xml
WIP on type table
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | /// Containers.rs
///
/// Contains specialized containers for the parser/compiler
/// TODO: Actually implement, I really want to remove all of the identifier
/// allocations.
use std::collections::LinkedList;
const PAGE_SIZE: usize = 4096;
struct StringPage {
buffer: [u8; PAGE_SIZE],
remaining: usize,
next_page: Option<Box<StringPage>>,
}
impl StringPage {
fn new() -> Self{
Self{
buffer: [0; PAGE_SIZE],
remaining: PAGE_SIZE,
next_page: None
}
}
}
/// Custom allocator for strings that are copied and remain valid during the
/// complete compilation phase. May perform multiple allocations but the
/// previously allocated strings remain valid. Because we will usually allocate
/// quite a number of these we will allocate a buffer upon construction of the
/// StringAllocator.
pub(crate) struct StringAllocator {
first_page: Box<StringPage>,
last_page: *mut StringPage,
}
unsafe impl Send for StringAllocator {}
impl StringAllocator {
pub(crate) fn new() -> StringAllocator {
let mut page = Box::new(StringPage::new());
let page_ptr = unsafe { page.as_mut() as *mut StringPage };
StringAllocator{
first_page: page,
last_page: page_ptr,
}
}
pub(crate) fn alloc(&mut self, data: &[u8]) -> Result<&'static str, String> {
let data_len = data.len();
if data_len > PAGE_SIZE {
return Err(format!(
"string is too large ({} bytes exceeds the maximum of {})",
data_len, PAGE_SIZE
));
}
// Because we're doing a copy anyway, we might as well perform the
// UTF-8 checking now. Such that it is safe to do an unchecked
// `from_utf8_unchecked` later.
let data = std::str::from_utf8(data);
if let Err(_) = data {
return Err(format!("invalid utf8-string"));
}
let data = data.unwrap();
unsafe {
if data_len > (*self.last_page).remaining {
// Allocate new page
let mut new_page = Box::new(StringPage::new());
let new_page_ptr = new_page.as_mut() as *mut StringPage;
(*self.last_page).next_page = Some(new_page);
self.last_page = new_page_ptr;
}
let remaining = (*self.last_page).remaining;
debug_assert!(data_len <= remaining);
let start = PAGE_SIZE - remaining;
(*self.last_page).buffer[start..start+data_len].copy_from_slice(data.as_bytes());
(*self.last_page).remaining -= data_len;
Ok(std::str::from_utf8_unchecked(&(*self.last_page).buffer[start..start+data_len]))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alloc() {
// Make sure pointers are somewhat correct
let mut alloc = StringAllocator::new();
assert!(alloc.first_page.next_page.is_none());
assert_eq!(alloc.first_page.as_ref() as *const StringPage, alloc.last_page);
// Insert and make page full, should not allocate another page yet
let input = "I am a simple static string";
let filler = " ".repeat(PAGE_SIZE - input.len());
let ref_first = alloc.alloc(input.as_bytes()).expect("alloc first");
let ref_filler = alloc.alloc(filler.as_bytes()).expect("alloc filler");
assert!(alloc.first_page.next_page.is_none());
assert_eq!(alloc.first_page.as_ref() as *const StringPage, alloc.last_page);
let ref_second = alloc.alloc(input.as_bytes()).expect("alloc second");
assert!(alloc.first_page.next_page.is_some());
assert!(alloc.first_page.next_page.as_ref().unwrap().next_page.is_none());
let last_page_ptr = alloc.first_page.next_page.as_ref().unwrap().as_ref() as *const StringPage;
assert_eq!(last_page_ptr, alloc.last_page);
assert_eq!(ref_first, input);
assert_eq!(ref_filler, filler);
assert_eq!(ref_second, input);
}
}
|