From 37bdd0f7fc2517c62573e54ff7df331c15f7098f Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 10 Jan 2024 15:05:44 +0200 Subject: [PATCH] stdio: get/put + most of **scanf() --- src/allocator.rs | 4 + src/allocator/mod.rs | 1 - src/allocator/rust.rs | 4 - src/error.rs | 6 + src/header/stdio/get_put.rs | 76 ++++- src/header/stdio/mod.rs | 4 - src/header/stdio/scanf.rs | 42 --- src/header/stdio/scanf/char_set.rs | 114 ++++++++ src/header/stdio/scanf/format.rs | 434 +++++++++++++++++++++++++++++ src/header/stdio/scanf/mod.rs | 213 ++++++++++++++ src/header/stdio/scanf/reader.rs | 69 +++++ src/header/stdlib/mod.rs | 89 +++++- src/lib.rs | 2 + 13 files changed, 1003 insertions(+), 55 deletions(-) create mode 100644 src/allocator.rs delete mode 100644 src/allocator/mod.rs delete mode 100644 src/allocator/rust.rs delete mode 100644 src/header/stdio/scanf.rs create mode 100644 src/header/stdio/scanf/char_set.rs create mode 100644 src/header/stdio/scanf/format.rs create mode 100644 src/header/stdio/scanf/mod.rs create mode 100644 src/header/stdio/scanf/reader.rs diff --git a/src/allocator.rs b/src/allocator.rs new file mode 100644 index 0000000..25e0779 --- /dev/null +++ b/src/allocator.rs @@ -0,0 +1,4 @@ +use libyalloc::GlobalAllocator; + +#[global_allocator] +pub static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator; diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs deleted file mode 100644 index 0ad9e7d..0000000 --- a/src/allocator/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod rust; diff --git a/src/allocator/rust.rs b/src/allocator/rust.rs deleted file mode 100644 index 79cb3ab..0000000 --- a/src/allocator/rust.rs +++ /dev/null @@ -1,4 +0,0 @@ -use libyalloc::GlobalAllocator; - -#[global_allocator] -static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator; diff --git a/src/error.rs b/src/error.rs index b227d78..c72e3d8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -126,3 +126,9 @@ impl OptionExt for Option { } } } + +pub fn set_errno(e: Errno) { + unsafe { + errno = e; + } +} diff --git a/src/header/stdio/get_put.rs b/src/header/stdio/get_put.rs index 27278b6..52d228c 100644 --- a/src/header/stdio/get_put.rs +++ b/src/header/stdio/get_put.rs @@ -3,7 +3,10 @@ use core::{ ptr::null_mut, }; -use crate::io::{Read, Write}; +use crate::{ + header::stdlib::realloc, + io::{Read, Write}, +}; use super::{stdin, stdout, EOF, FILE}; @@ -132,6 +135,32 @@ unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { // Line +struct MallocBufferWriter { + buffer: *mut c_char, + position: usize, + capacity: usize, +} + +impl MallocBufferWriter { + fn putc(&mut self, ch: c_int) -> bool { + if self.position == self.capacity { + // Extend the buffer + self.capacity = (self.capacity + 64) & !63; + self.buffer = unsafe { realloc(self.buffer as _, self.capacity) as _ }; + if self.buffer.is_null() { + // ENOMEM is set + return false; + } + } + + unsafe { + *self.buffer.add(self.position) = ch as _; + } + self.position += 1; + true + } +} + #[no_mangle] unsafe extern "C" fn getdelim( lineptr: *mut *mut c_char, @@ -139,10 +168,51 @@ unsafe extern "C" fn getdelim( delim: c_int, stream: *mut FILE, ) -> isize { - todo!() + let lineptr = lineptr.as_mut().unwrap(); + let n = n.as_mut().unwrap(); + let mut capacity = *n; + + if lineptr.is_null() { + capacity = 0; + } + + let mut writer = MallocBufferWriter { + buffer: *lineptr, + position: 0, + capacity, + }; + + loop { + let ch = fgetc(stream); + if ch == EOF { + if writer.position == 0 { + // EOF and no data read + return -1; + } + break; + } + + if !writer.putc(ch) { + return -1; + } + + if ch == delim { + break; + } + } + + if !writer.putc(0) { + return -1; + } + + *lineptr = writer.buffer; + *n = writer.capacity; + + // Minus the '\0' + (writer.position - 1).try_into().unwrap() } #[no_mangle] unsafe extern "C" fn getline(lineptr: *mut *mut c_char, n: *mut usize, stream: *mut FILE) -> isize { - todo!() + getdelim(lineptr, n, b'\n' as _, stream) } diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 70bd238..fb59d08 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -58,10 +58,6 @@ char *tempnam(const char *, const char *); FILE *tmpfile(void); char *tmpnam(char *); - - - - */ macro_rules! locked_op { diff --git a/src/header/stdio/scanf.rs b/src/header/stdio/scanf.rs deleted file mode 100644 index 906ae63..0000000 --- a/src/header/stdio/scanf.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* -int fscanf(FILE *restrict, const char *restrict, ...); -int scanf(const char *restrict, ...); -int sscanf(const char *restrict, const char *restrict, ...); -int vfscanf(FILE *restrict, const char *restrict, va_list); -int vscanf(const char *restrict, va_list); -int vsscanf(const char *restrict, const char *restrict, va_list); - */ - -use core::ffi::{c_char, c_int, VaList}; - -use super::{stdin, FILE}; - -#[no_mangle] -unsafe extern "C" fn fscanf(stream: *mut FILE, format: *const c_char, mut args: ...) -> c_int { - vfscanf(stream, format, args.as_va_list()) -} - -#[no_mangle] -unsafe extern "C" fn scanf(format: *const c_char, mut args: ...) -> c_int { - vfscanf(stdin, format, args.as_va_list()) -} - -#[no_mangle] -unsafe extern "C" fn sscanf(str: *const c_char, format: *const c_char, mut args: ...) -> c_int { - vsscanf(str, format, args.as_va_list()) -} - -#[no_mangle] -unsafe extern "C" fn vfscanf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn vscanf(format: *const c_char, ap: VaList) -> c_int { - vfscanf(stdin, format, ap) -} - -#[no_mangle] -unsafe extern "C" fn vsscanf(str: *const c_char, format: *const c_char, ap: VaList) -> c_int { - todo!() -} diff --git a/src/header/stdio/scanf/char_set.rs b/src/header/stdio/scanf/char_set.rs new file mode 100644 index 0000000..faaba09 --- /dev/null +++ b/src/header/stdio/scanf/char_set.rs @@ -0,0 +1,114 @@ +use core::ops::RangeInclusive; + +use super::ScanError; + +pub struct ScanCharSet { + mask: [u64; 4], + invert: bool, +} + +impl ScanCharSet { + pub fn new() -> Self { + Self { + mask: [0; 4], + invert: false, + } + } + + pub fn from_format_iter<'a, I: Iterator>( + mut it: I, + ) -> Result<(I, Self), ScanError> { + let mut maybe_close_bracket = true; + let mut hyphen = false; + let mut start: Option = None; + let mut set = ScanCharSet::new(); + + while let Some(&ch) = it.next() { + if ch == b'^' && maybe_close_bracket { + // "[^...]" + set.set_invert(); + continue; + } + + if ch == b']' { + if !maybe_close_bracket { + if hyphen { + // "[...-]" + set.insert(b'-'); + } + break; + } else { + debug_assert!(!hyphen); + // "[]...]" or "[^]...]" + maybe_close_bracket = false; + set.insert(b']'); + continue; + } + } + + maybe_close_bracket = false; + + if ch == b'-' { + if hyphen { + // "--"? + return Err(ScanError::InvalidFormat); + } + + hyphen = true; + continue; + } + + // Any other character + if hyphen { + let Some(start) = start.take() else { + // "-x" without start? + return Err(ScanError::InvalidFormat); + }; + + let same_category = (start.is_ascii_digit() && ch.is_ascii_digit()) + || (start.is_ascii_uppercase() && ch.is_ascii_uppercase()) + || (start.is_ascii_lowercase() && ch.is_ascii_lowercase()); + + if same_category && ch > start { + hyphen = false; + set.insert_range(start..=ch); + } else { + return Err(ScanError::InvalidFormat); + } + } else { + if let Some(start) = start.replace(ch) { + // "c" without range + set.insert(ch); + } + } + } + + Ok((it, set)) + } + + pub fn set_invert(&mut self) { + self.invert = true; + } + + pub fn insert(&mut self, ch: u8) { + let index = (ch / 64) as usize; + let bit = 1 << (ch % 64); + self.mask[index] |= bit; + } + + pub fn insert_range(&mut self, range: RangeInclusive) { + for ch in range { + self.insert(ch); + } + } + + pub fn contains(&self, ch: u8) -> bool { + let index = (ch / 64) as usize; + let bit = 1 << (ch % 64); + self.mask[index] & bit == bit + } + + pub fn check(&self, ch: u8) -> bool { + self.invert ^ self.contains(ch) + } +} diff --git a/src/header/stdio/scanf/format.rs b/src/header/stdio/scanf/format.rs new file mode 100644 index 0000000..1ca7a69 --- /dev/null +++ b/src/header/stdio/scanf/format.rs @@ -0,0 +1,434 @@ +use core::ffi::{ + c_char, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort, + c_void, VaList, +}; + +use super::{ + char_set::ScanCharSet, + reader::{GetChar, ScanReader}, + ConversionError, +}; + +pub enum ScanRadix { + Decimal, + Oct, + Hex, + Auto, +} + +pub enum ScanSpec { + // %s, a sequence of non-whitespace characters + Word, + // %c, a sequence of characters whose length is specified by field width (default 1) + Chars(ScanCharSet), + // %[...], nonempty sequence of characters from the specified set + AnyChar, + // %d, %u, %x/%X, %o, %i, deprecated, optionally-signed/unsigned decimals + Signed(ScanRadix), + Unsigned(ScanRadix), + // %n, reports consumed count + ConsumedCount, + // %p + Pointer, + // %% + Percent, +} + +pub enum ScanSize { + ShortShort, + Short, + Normal, + Long, + LongLong, + Size, +} + +pub struct ScanOpt { + pub width: Option, + pub size: ScanSize, + pub discard: bool, +} + +impl ScanSize { + pub fn shorter(self) -> ScanSize { + match self { + Self::Normal => Self::Short, + Self::Short => Self::ShortShort, + _ => self, + } + } + + pub fn longer(self) -> ScanSize { + match self { + Self::Normal => Self::Long, + Self::Long => Self::LongLong, + _ => self, + } + } + + pub unsafe fn write_int(&self, value: i64, ap: &mut VaList) { + let raw = ap.arg::<*mut c_void>(); + + if raw.is_null() { + panic!(); + } + + match self { + Self::ShortShort => *(raw as *mut c_char) = value as _, + Self::Short => *(raw as *mut c_short) = value as _, + Self::Normal => *(raw as *mut c_int) = value as _, + Self::Long => *(raw as *mut c_long) = value as _, + Self::LongLong => *(raw as *mut c_longlong) = value as _, + Self::Size => *(raw as *mut isize) = value as _, + } + } + + pub unsafe fn write_uint(&self, value: u64, ap: &mut VaList) { + let raw = ap.arg::<*mut c_void>(); + + if raw.is_null() { + panic!(); + } + + match self { + Self::ShortShort => *(raw as *mut c_uchar) = value as _, + Self::Short => *(raw as *mut c_ushort) = value as _, + Self::Normal => *(raw as *mut c_uint) = value as _, + Self::Long => *(raw as *mut c_ulong) = value as _, + Self::LongLong => *(raw as *mut c_ulonglong) = value as _, + Self::Size => *(raw as *mut usize) = value as _, + } + } +} + +impl Default for ScanOpt { + fn default() -> Self { + Self { + width: None, + size: ScanSize::Normal, + discard: false, + } + } +} + +impl ScanOpt { + pub fn scan( + self, + stream: &mut ScanReader, + spec: ScanSpec, + ap: &mut VaList, + ) -> Result { + let n = if self.discard { 0 } else { 1 }; + + match (spec, self.size) { + (ScanSpec::Percent, _) => todo!(), + (ScanSpec::Word, ScanSize::Normal) => { + let width = self.width.unwrap_or(usize::MAX); + let dst = if self.discard { + None + } else { + let dst = unsafe { ap.arg::<*mut c_char>() }; + if dst.is_null() { + panic!(); + } + Some(dst) + }; + + scan_word(stream, dst, width)?; + + Ok(n) + } + (ScanSpec::Chars(set), ScanSize::Normal) => { + let width = self.width.unwrap_or(usize::MAX); + let dst = if self.discard { + None + } else { + let dst = unsafe { ap.arg::<*mut c_char>() }; + if dst.is_null() { + panic!(); + } + Some(dst) + }; + + scan_char_set(stream, dst, set, width)?; + + Ok(n) + } + (ScanSpec::AnyChar, ScanSize::Normal) => { + let width = self.width.unwrap_or(1); + let dst = if self.discard { + None + } else { + let dst = unsafe { ap.arg::<*mut c_char>() }; + if dst.is_null() { + panic!(); + } + Some(dst) + }; + + scan_chars(stream, dst, width)?; + + Ok(n) + } + (ScanSpec::Word, _) => todo!("%ls"), + (ScanSpec::Chars(_), _) => todo!("%l[...]"), + (ScanSpec::AnyChar, _) => todo!("%lc"), + (ScanSpec::Signed(radix), size) => { + let value = scan_signed(stream, radix)?; + + if !self.discard { + unsafe { + size.write_int(value, ap); + } + } + + Ok(n) + } + (ScanSpec::Unsigned(radix), size) => { + let value = scan_unsigned(stream, radix)?; + + if !self.discard { + unsafe { + size.write_uint(value, ap); + } + } + + Ok(n) + } + (ScanSpec::Pointer, _) => todo!(), + (ScanSpec::ConsumedCount, size) => { + if !self.discard { + unsafe { + size.write_int(stream.consumed_count().try_into().unwrap(), ap); + } + } + Ok(0) + } + } + } +} + +fn scan_word( + stream: &mut ScanReader, + mut dst: Option<*mut c_char>, + limit: usize, +) -> Result<(), ConversionError> { + // Read until limit reached or whitespace/EOF + let mut matched = 0; + while matched < limit { + let Some(ch) = stream.peek()? else { + break; + }; + + if ch.is_ascii_whitespace() { + break; + } + + if let Some(dst) = dst.as_mut() { + unsafe { + **dst = ch as _; + *dst = dst.add(1); + } + } + matched += 1; + + stream.next()?; + } + + if matched == 0 { + return Err(ConversionError::ConversionFailed); + } + + if let Some(dst) = dst.as_mut() { + unsafe { + **dst = 0; + } + } + Ok(()) +} + +fn scan_chars( + stream: &mut ScanReader, + mut dst: Option<*mut c_char>, + width: usize, +) -> Result<(), ConversionError> { + let mut matched = 0; + + while matched < width { + let Some(ch) = stream.next()? else { + break; + }; + + if let Some(dst) = dst.as_mut() { + unsafe { + **dst = ch as _; + *dst = dst.add(1); + } + } + + matched += 1; + stream.next()?; + } + + if matched == width { + Ok(()) + } else { + Err(ConversionError::ConversionFailed) + } +} + +fn scan_char_set( + stream: &mut ScanReader, + mut dst: Option<*mut c_char>, + set: ScanCharSet, + width: usize, +) -> Result<(), ConversionError> { + let mut matched = 0; + + while matched < width { + let Some(ch) = stream.peek()? else { + break; + }; + + if !set.check(ch) { + break; + } + + if let Some(dst) = dst.as_mut() { + unsafe { + **dst = ch as _; + *dst = dst.add(1); + } + } + + stream.next()?; + matched += 1; + } + + if matched == 0 { + return Err(ConversionError::ConversionFailed); + } + + if let Some(dst) = dst.as_mut() { + unsafe { + **dst = 0; + } + } + + Ok(()) +} + +fn scan_unsigned( + stream: &mut ScanReader, + radix: ScanRadix, +) -> Result { + let mut value = 0; + let mut matched = 0; + + match radix { + ScanRadix::Auto => { + let Some(maybe_0) = stream.peek()? else { + return Err(ConversionError::ConversionFailed); + }; + + if maybe_0 == b'0' { + stream.next()?; + + // Maybe x/X or octal + let Some(maybe_next) = stream.peek()? else { + // At least matched a zero + return Ok(0); + }; + + return match maybe_next { + b'x' | b'X' => { + stream.next()?; + scan_unsigned(stream, ScanRadix::Hex) + } + b'0'..=b'9' => scan_unsigned(stream, ScanRadix::Oct), + _ => Err(ConversionError::ConversionFailed), + }; + } else { + // Anything else is a decimal + return scan_unsigned(stream, ScanRadix::Decimal); + } + } + ScanRadix::Oct => { + while let Some(ch) = stream.peek()? { + let digit = match ch { + b'0'..=b'7' => (ch - b'0') as u64, + _ => break, + }; + + stream.next()?; + + value <<= 3; + value |= digit; + + matched += 1; + } + } + ScanRadix::Decimal => { + while let Some(ch) = stream.peek()? { + let digit = match ch { + b'0'..=b'9' => (ch - b'0') as u64, + _ => break, + }; + + stream.next()?; + + value *= 10; + value += digit; + + matched += 1; + } + } + ScanRadix::Hex => { + while let Some(ch) = stream.peek()? { + let digit = match ch { + b'0'..=b'9' => (ch - b'0') as u64, + b'a'..=b'f' => (ch - b'a') as u64 + 10, + b'A'..=b'F' => (ch - b'A') as u64 + 10, + _ => break, + }; + + stream.next()?; + + value <<= 4; + value |= digit; + + matched += 1; + } + } + } + + if matched != 0 { + Ok(value) + } else { + // No characters matched + Err(ConversionError::ConversionFailed) + } +} + +fn scan_signed( + stream: &mut ScanReader, + radix: ScanRadix, +) -> Result { + let sign = if let Some(ch) = stream.peek()? { + if ch == b'-' { + stream.next()?; + false + } else { + true + } + } else { + // EOF + return Err(ConversionError::ConversionFailed); + }; + + let value: i64 = scan_unsigned(stream, radix)? + .try_into() + .map_err(|_| ConversionError::ConversionFailed)?; + + Ok(if sign { value } else { -value }) +} diff --git a/src/header/stdio/scanf/mod.rs b/src/header/stdio/scanf/mod.rs new file mode 100644 index 0000000..7210b53 --- /dev/null +++ b/src/header/stdio/scanf/mod.rs @@ -0,0 +1,213 @@ +use core::ffi::{c_char, c_int, c_void, CStr, VaList}; + +use crate::header::errno::Errno; + +use self::{ + char_set::ScanCharSet, + format::{ScanOpt, ScanRadix, ScanSize, ScanSpec}, + reader::{GetChar, ScanReader}, +}; + +use super::{stdin, FILE}; + +mod char_set; +mod format; +mod reader; + +pub enum ScanError { + ReadError(Errno), + InvalidFormat, +} + +pub enum ConversionError { + ReadError(Errno), + ConversionFailed, +} + +impl From for ScanError { + fn from(value: Errno) -> Self { + Self::ReadError(value) + } +} + +impl From for ConversionError { + fn from(value: Errno) -> Self { + Self::ReadError(value) + } +} + +fn scanf_inner( + stream: &mut ScanReader, + format: &[u8], + mut ap: VaList, +) -> Result { + let mut it = format.into_iter(); + let mut matched = 0; + let mut skip_space = false; + + while let Some(&ch) = it.next() { + if ch == b' ' { + // Any amount of whitespace + if skip_space { + continue; + } + + // Skip following whitespace in format + skip_space = true; + + // Skip whitespace in stream + while let Some(ic) = stream.peek()? { + if !ic.is_ascii_whitespace() { + break; + } + + stream.next()?; + } + + continue; + } else if ch != b'%' { + // Regular character + skip_space = false; + + let ic = stream.peek()?; + if !ic.map(|ic| ic == ch).unwrap_or(false) { + break; + } + + stream.next()?; + + continue; + } + + skip_space = false; + + // %*123lld + let mut opts = ScanOpt::default(); + let mut cur = it.next(); + + if let Some(&ch) = cur { + if ch == b'*' { + opts.discard = true; + cur = it.next(); + } + } + + while let Some(&ch) = cur { + if ch.is_ascii_digit() { + opts.width + .replace(opts.width.unwrap_or(0) * 10 + (ch - b'0') as usize); + cur = it.next(); + } else { + break; + } + } + + while let Some(&ch) = cur { + match ch { + b'h' => opts.size = opts.size.shorter(), + b'l' => opts.size = opts.size.longer(), + b'z' => opts.size = ScanSize::Size, + // TODO ptrdiff_t + b't' => todo!(), + // TODO intmax_t/uintmax_t + b'j' => todo!(), + // TODO floats + b'L' | b'q' => todo!(), + _ => break, + } + + cur = it.next(); + } + + let spec = if let Some(&ch) = cur { + match ch { + b'i' => ScanSpec::Signed(ScanRadix::Auto), + b'd' => ScanSpec::Signed(ScanRadix::Decimal), + b'x' | b'X' => ScanSpec::Unsigned(ScanRadix::Hex), + b'u' => ScanSpec::Unsigned(ScanRadix::Decimal), + b'o' => ScanSpec::Unsigned(ScanRadix::Oct), + b's' => ScanSpec::Word, + b'[' => { + let (new_it, set) = ScanCharSet::from_format_iter(it)?; + it = new_it; + ScanSpec::Chars(set) + } + b'c' => ScanSpec::AnyChar, + b'n' => ScanSpec::ConsumedCount, + b'p' => ScanSpec::Pointer, + b'%' => ScanSpec::Percent, + b'f' | b'e' | b'g' | b'E' | b'a' => todo!(), + // Unrecognized specifier + _ => return Err(ScanError::InvalidFormat), + } + } else { + // No specifier after % + return Err(ScanError::InvalidFormat); + }; + + matched += match opts.scan(stream, spec, &mut ap) { + Ok(matched) => matched, + Err(ConversionError::ConversionFailed) => break, + Err(ConversionError::ReadError(err)) => return Err(ScanError::ReadError(err)), + }; + } + + // TODO report end of stream before anything is matched + + Ok(matched) +} + +#[no_mangle] +unsafe extern "C" fn fscanf(stream: *mut FILE, format: *const c_char, mut args: ...) -> c_int { + vfscanf(stream, format, args.as_va_list()) +} + +#[no_mangle] +unsafe extern "C" fn scanf(format: *const c_char, mut args: ...) -> c_int { + vfscanf(stdin, format, args.as_va_list()) +} + +#[no_mangle] +unsafe extern "C" fn sscanf(str: *const c_char, format: *const c_char, mut args: ...) -> c_int { + vsscanf(str, format, args.as_va_list()) +} + +#[no_mangle] +unsafe extern "C" fn vfscanf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int { + if format.is_null() { + panic!(); + } + + let stream = stream.as_mut().unwrap(); + let format = CStr::from_ptr(format); + + let mut reader = ScanReader::new(stream); + + match scanf_inner(&mut reader, format.to_bytes(), ap) { + Ok(count) => count.try_into().unwrap(), + Err(_) => -1, + } +} + +#[no_mangle] +unsafe extern "C" fn vscanf(format: *const c_char, ap: VaList) -> c_int { + vfscanf(stdin, format, ap) +} + +#[no_mangle] +unsafe extern "C" fn vsscanf(str: *const c_char, format: *const c_char, ap: VaList) -> c_int { + if str.is_null() || format.is_null() { + panic!(); + } + + let str = CStr::from_ptr(str); + let format = CStr::from_ptr(format); + + let mut str_it = str.to_bytes().into_iter(); + let mut reader = ScanReader::new(&mut str_it); + + match scanf_inner(&mut reader, format.to_bytes(), ap) { + Ok(count) => count.try_into().unwrap(), + Err(_) => -1, + } +} diff --git a/src/header/stdio/scanf/reader.rs b/src/header/stdio/scanf/reader.rs new file mode 100644 index 0000000..75611bf --- /dev/null +++ b/src/header/stdio/scanf/reader.rs @@ -0,0 +1,69 @@ +use crate::{ + header::{errno::Errno, stdio::FILE}, + io::Read, +}; + +pub trait GetChar { + fn getc(&mut self) -> Result, Errno>; +} + +pub struct ScanReader<'a, G: GetChar> { + getter: &'a mut G, + buffer: Option, + consumed_count: usize, +} + +impl GetChar for FILE { + fn getc(&mut self) -> Result, Errno> { + let mut buf = [0]; + match self.read(&mut buf) { + Ok(1) => Ok(Some(buf[0])), + Ok(_) => Ok(None), + Err(err) => Err(err), + } + } +} + +impl<'a, I: Iterator> GetChar for I { + fn getc(&mut self) -> Result, Errno> { + Ok(self.next().copied()) + } +} + +impl<'a, G: GetChar> ScanReader<'a, G> { + pub fn new(getter: &'a mut G) -> Self { + Self { + getter, + buffer: None, + consumed_count: 0, + } + } + + fn getc(&mut self) -> Result, Errno> { + match self.getter.getc() { + Ok(Some(n)) => { + self.consumed_count += 1; + Ok(Some(n)) + } + e => e, + } + } + + pub fn consumed_count(&self) -> usize { + self.consumed_count + } + + pub fn peek(&mut self) -> Result, Errno> { + if self.buffer.is_none() { + self.buffer = self.getc()?; + } + Ok(self.buffer) + } + + pub fn next(&mut self) -> Result, Errno> { + match self.buffer.take() { + Some(v) => Ok(Some(v)), + None => self.getc(), + } + } +} diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 39546e3..27b2b9a 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -1,4 +1,91 @@ -use core::ffi::c_int; +use core::{ + alloc::{GlobalAlloc, Layout}, + ffi::{c_int, c_void}, + intrinsics, + ptr::null_mut, +}; + +use crate::{allocator::GLOBAL_ALLOCATOR, error}; + +use super::errno::ENOMEM; pub const EXIT_SUCCESS: c_int = 0; pub const EXIT_FAILURE: c_int = 1; + +unsafe fn alloc_inner(size: usize, offset: usize, align: usize) -> *mut c_void { + let layout = Layout::from_size_align(size + offset, align).unwrap(); + let ptr = GLOBAL_ALLOCATOR.alloc(layout); + + if ptr.is_null() { + error::set_errno(ENOMEM); + return ptr as *mut c_void; + } + + *(ptr as *mut u64) = (size + offset) as u64; + *(ptr as *mut u64).add(1) = align as u64; + + ptr.add(offset) as *mut c_void +} + +unsafe fn get_allocation(ptr: *mut c_void) -> (*mut u8, Layout) { + let ptr = (ptr as *mut u64).offset(-2); + let size = *ptr as usize; + let align = *ptr.add(1) as usize; + ( + ptr as *mut u8, + Layout::from_size_align(size, align).unwrap(), + ) +} + +#[no_mangle] +pub unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut c_void { + match nmemb.checked_mul(size) { + Some(size) => { + let ptr = malloc(size); + if !ptr.is_null() { + intrinsics::write_bytes(ptr as *mut u8, 0, size); + } + ptr + } + None => { + error::set_errno(ENOMEM); + null_mut() + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn malloc(size: usize) -> *mut c_void { + alloc_inner(size, 16, 8) +} + +#[no_mangle] +pub unsafe extern "C" fn realloc(ptr: *mut c_void, new_size: usize) -> *mut c_void { + if ptr.is_null() { + return malloc(new_size); + } + if new_size == 0 { + free(ptr); + return null_mut(); + } + + // TODO: implement realloc in libyalloc? + let (_, old_layout) = get_allocation(ptr); + let new_ptr = malloc(new_size); + + if !new_ptr.is_null() { + yggdrasil_rt::memcpy(new_ptr as _, ptr as _, old_layout.size()); + free(ptr); + } + + new_ptr +} + +#[no_mangle] +pub unsafe extern "C" fn free(ptr: *mut c_void) { + if ptr.is_null() { + return; + } + let (allocation, layout) = get_allocation(ptr); + GLOBAL_ALLOCATOR.dealloc(allocation, layout); +} diff --git a/src/lib.rs b/src/lib.rs index 5e8f4a6..a1fb37c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,12 @@ c_variadic, try_trait_v2, slice_internals, + core_intrinsics, arbitrary_self_types, new_uninit, maybe_uninit_slice )] +#![allow(internal_features)] #![no_std] use core::ffi::{c_char, c_int};