diff --git a/Userland/Libraries/LibC/bits/stdio_file_implementation.h b/Userland/Libraries/LibC/bits/stdio_file_implementation.h index 46e9a8cec5..34d3172866 100644 --- a/Userland/Libraries/LibC/bits/stdio_file_implementation.h +++ b/Userland/Libraries/LibC/bits/stdio_file_implementation.h @@ -4,9 +4,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include +#include #include #pragma once @@ -85,6 +87,9 @@ private: bool enqueue_front(u8 byte); private: + constexpr static auto unget_buffer_size = MB_CUR_MAX; + constexpr static u32 ungotten_mask = ((u32)0xffffffff) >> (sizeof(u32) * 8 - unget_buffer_size); + // Note: the fields here are arranged this way // to make sizeof(Buffer) smaller. u8* m_data { nullptr }; @@ -93,8 +98,8 @@ private: size_t m_end { 0 }; int m_mode { -1 }; - u8 m_unget_buffer { 0 }; - bool m_ungotten : 1 { false }; + Array m_unget_buffer { 0 }; + u32 m_ungotten : unget_buffer_size { 0 }; bool m_data_is_malloced : 1 { false }; // When m_begin == m_end, we want to distinguish whether // the buffer is full or empty. diff --git a/Userland/Libraries/LibC/bits/wchar.h b/Userland/Libraries/LibC/bits/wchar.h new file mode 100644 index 0000000000..224dbc477c --- /dev/null +++ b/Userland/Libraries/LibC/bits/wchar.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2021, Ali Mohammad Pur + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#define MB_CUR_MAX 4 +#define MB_LEN_MAX 16 diff --git a/Userland/Libraries/LibC/limits.h b/Userland/Libraries/LibC/limits.h index bfb7fc23f0..4fde490201 100644 --- a/Userland/Libraries/LibC/limits.h +++ b/Userland/Libraries/LibC/limits.h @@ -7,6 +7,7 @@ #pragma once #include +#include #ifndef PAGE_SIZE # define PAGE_SIZE 4096 @@ -73,8 +74,6 @@ #define LLONG_WIDTH 64 #define ULLONG_WIDTH 64 -#define MB_LEN_MAX 16 - #define ARG_MAX 65536 #define PTHREAD_STACK_MIN 65536 diff --git a/Userland/Libraries/LibC/stdio.cpp b/Userland/Libraries/LibC/stdio.cpp index 84017cf5b5..a5ce5a5528 100644 --- a/Userland/Libraries/LibC/stdio.cpp +++ b/Userland/Libraries/LibC/stdio.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -351,7 +352,7 @@ FILE::Buffer::~Buffer() bool FILE::Buffer::may_use() const { - return m_ungotten || m_mode != _IONBF; + return m_ungotten != 0u || m_mode != _IONBF; } void FILE::Buffer::realize(int fd) @@ -384,7 +385,7 @@ void FILE::Buffer::drop() } m_begin = m_end = 0; m_empty = true; - m_ungotten = false; + m_ungotten = 0u; } size_t FILE::Buffer::buffered_size() const @@ -402,9 +403,10 @@ size_t FILE::Buffer::buffered_size() const const u8* FILE::Buffer::begin_dequeue(size_t& available_size) const { - if (m_ungotten) { - available_size = 1; - return &m_unget_buffer; + if (m_ungotten != 0u) { + auto available_bytes = count_trailing_zeroes(m_ungotten) + 1; + available_size = available_bytes; + return &m_unget_buffer[unget_buffer_size - available_bytes]; } if (m_empty) { @@ -424,9 +426,10 @@ void FILE::Buffer::did_dequeue(size_t actual_size) { VERIFY(actual_size > 0); - if (m_ungotten) { - VERIFY(actual_size == 1); - m_ungotten = false; + if (m_ungotten != 0u) { + VERIFY(actual_size <= static_cast(popcount(m_ungotten & ungotten_mask))); + auto available_bytes = count_trailing_zeroes(m_ungotten); + m_ungotten &= (0xffffffffu << (actual_size + available_bytes)); return; } @@ -476,13 +479,21 @@ void FILE::Buffer::did_enqueue(size_t actual_size) bool FILE::Buffer::enqueue_front(u8 byte) { - if (m_ungotten) { - // Sorry, the place is already taken! - return false; + size_t placement_index; + if (m_ungotten == 0u) { + placement_index = 3u; + m_ungotten = 1u; + } else { + auto first_zero_index = count_trailing_zeroes(bit_cast(~m_ungotten)); // Thanks C. + if (first_zero_index >= unget_buffer_size) { + // Sorry, the place is already taken! + return false; + } + placement_index = unget_buffer_size - first_zero_index - 1; + m_ungotten |= (1 << first_zero_index); } - m_ungotten = true; - m_unget_buffer = byte; + m_unget_buffer[placement_index] = byte; return true; } diff --git a/Userland/Libraries/LibC/stdlib.h b/Userland/Libraries/LibC/stdlib.h index 5fe5fd2a8a..68caf7a3cb 100644 --- a/Userland/Libraries/LibC/stdlib.h +++ b/Userland/Libraries/LibC/stdlib.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -14,7 +15,6 @@ __BEGIN_DECLS #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 -#define MB_CUR_MAX 4 __attribute__((noreturn)) void _abort(); diff --git a/Userland/Libraries/LibC/wchar.h b/Userland/Libraries/LibC/wchar.h index 9d244daffa..1848204609 100644 --- a/Userland/Libraries/LibC/wchar.h +++ b/Userland/Libraries/LibC/wchar.h @@ -78,6 +78,7 @@ wint_t putwc(wchar_t wc, FILE* stream); wint_t putwchar(wchar_t wc); wchar_t* fgetws(wchar_t* __restrict ws, int n, FILE* __restrict stream); int fputws(const wchar_t* __restrict ws, FILE* __restrict stream); +wint_t ungetwc(wint_t wc, FILE* stream); int fwide(FILE* stream, int mode); int wprintf(const wchar_t* __restrict format, ...); diff --git a/Userland/Libraries/LibC/wstdio.cpp b/Userland/Libraries/LibC/wstdio.cpp index 931f71da3d..7e872491d9 100644 --- a/Userland/Libraries/LibC/wstdio.cpp +++ b/Userland/Libraries/LibC/wstdio.cpp @@ -120,6 +120,25 @@ int fputws(wchar_t const* __restrict ws, FILE* __restrict stream) return size; } +wint_t ungetwc(wint_t wc, FILE* stream) +{ + VERIFY(stream); + ScopedFileLock lock(stream); + StringBuilder sb; + sb.append_code_point(static_cast(wc)); + auto bytes = sb.string_view().bytes(); + size_t ok_bytes = 0; + for (auto byte : bytes) { + if (!stream->ungetc(byte)) { + // Discard the half-ungotten bytes. + stream->read(const_cast(bytes.data()), ok_bytes); + return WEOF; + } + ++ok_bytes; + } + return wc; +} + int wprintf(wchar_t const* __restrict format, ...) { va_list ap;