stdio: get/put + most of **scanf()

This commit is contained in:
2024-01-10 15:05:44 +02:00
parent 34c360d3e8
commit 37bdd0f7fc
13 changed files with 1003 additions and 55 deletions
+4
View File
@@ -0,0 +1,4 @@
use libyalloc::GlobalAllocator;
#[global_allocator]
pub static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
-1
View File
@@ -1 +0,0 @@
pub mod rust;
-4
View File
@@ -1,4 +0,0 @@
use libyalloc::GlobalAllocator;
#[global_allocator]
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
+6
View File
@@ -126,3 +126,9 @@ impl<T> OptionExt<T> for Option<T> {
}
}
}
pub fn set_errno(e: Errno) {
unsafe {
errno = e;
}
}
+73 -3
View File
@@ -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)
}
-4
View File
@@ -58,10 +58,6 @@ char *tempnam(const char *, const char *);
FILE *tmpfile(void);
char *tmpnam(char *);
*/
macro_rules! locked_op {
-42
View File
@@ -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!()
}
+114
View File
@@ -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<Item = &'a u8>>(
mut it: I,
) -> Result<(I, Self), ScanError> {
let mut maybe_close_bracket = true;
let mut hyphen = false;
let mut start: Option<u8> = 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<u8>) {
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)
}
}
+434
View File
@@ -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<usize>,
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<G: GetChar>(
self,
stream: &mut ScanReader<G>,
spec: ScanSpec,
ap: &mut VaList,
) -> Result<usize, ConversionError> {
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<G: GetChar>(
stream: &mut ScanReader<G>,
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<G: GetChar>(
stream: &mut ScanReader<G>,
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<G: GetChar>(
stream: &mut ScanReader<G>,
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<G: GetChar>(
stream: &mut ScanReader<G>,
radix: ScanRadix,
) -> Result<u64, ConversionError> {
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<G: GetChar>(
stream: &mut ScanReader<G>,
radix: ScanRadix,
) -> Result<i64, ConversionError> {
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 })
}
+213
View File
@@ -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<Errno> for ScanError {
fn from(value: Errno) -> Self {
Self::ReadError(value)
}
}
impl From<Errno> for ConversionError {
fn from(value: Errno) -> Self {
Self::ReadError(value)
}
}
fn scanf_inner<G: GetChar>(
stream: &mut ScanReader<G>,
format: &[u8],
mut ap: VaList,
) -> Result<usize, ScanError> {
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,
}
}
+69
View File
@@ -0,0 +1,69 @@
use crate::{
header::{errno::Errno, stdio::FILE},
io::Read,
};
pub trait GetChar {
fn getc(&mut self) -> Result<Option<u8>, Errno>;
}
pub struct ScanReader<'a, G: GetChar> {
getter: &'a mut G,
buffer: Option<u8>,
consumed_count: usize,
}
impl GetChar for FILE {
fn getc(&mut self) -> Result<Option<u8>, 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<Item = &'a u8>> GetChar for I {
fn getc(&mut self) -> Result<Option<u8>, 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<Option<u8>, 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<Option<u8>, Errno> {
if self.buffer.is_none() {
self.buffer = self.getc()?;
}
Ok(self.buffer)
}
pub fn next(&mut self) -> Result<Option<u8>, Errno> {
match self.buffer.take() {
Some(v) => Ok(Some(v)),
None => self.getc(),
}
}
}
+88 -1
View File
@@ -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);
}
+2
View File
@@ -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};