Преглед на файлове

LibC: Implement ungetwc()

Ali Mohammad Pur преди 3 години
родител
ревизия
e717ca32d1

+ 7 - 2
Userland/Libraries/LibC/bits/stdio_file_implementation.h

@@ -4,9 +4,11 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <AK/Array.h>
 #include <AK/Types.h>
 #include <LibC/bits/FILE.h>
 #include <LibC/bits/pthread_integration.h>
+#include <LibC/bits/wchar.h>
 #include <sys/types.h>
 
 #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<u8, unget_buffer_size> 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.

+ 10 - 0
Userland/Libraries/LibC/bits/wchar.h

@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#define MB_CUR_MAX 4
+#define MB_LEN_MAX 16

+ 1 - 2
Userland/Libraries/LibC/limits.h

@@ -7,6 +7,7 @@
 #pragma once
 
 #include <bits/stdint.h>
+#include <bits/wchar.h>
 
 #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

+ 24 - 13
Userland/Libraries/LibC/stdio.cpp

@@ -5,6 +5,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <AK/BuiltinWrappers.h>
 #include <AK/Format.h>
 #include <AK/PrintfImplementation.h>
 #include <AK/ScopedValueRollback.h>
@@ -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<size_t>(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<u32>(~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;
 }
 

+ 1 - 1
Userland/Libraries/LibC/stdlib.h

@@ -6,6 +6,7 @@
 
 #pragma once
 
+#include <bits/wchar.h>
 #include <stddef.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
@@ -14,7 +15,6 @@ __BEGIN_DECLS
 
 #define EXIT_SUCCESS 0
 #define EXIT_FAILURE 1
-#define MB_CUR_MAX 4
 
 __attribute__((noreturn)) void _abort();
 

+ 1 - 0
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, ...);

+ 19 - 0
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<u32>(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<u8*>(bytes.data()), ok_bytes);
+            return WEOF;
+        }
+        ++ok_bytes;
+    }
+    return wc;
+}
+
 int wprintf(wchar_t const* __restrict format, ...)
 {
     va_list ap;