diff --git a/libcxx/src/std_stream.h b/libcxx/src/std_stream.h index 4b9d3a34b2441..214acc9e88b70 100644 --- a/libcxx/src/std_stream.h +++ b/libcxx/src/std_stream.h @@ -45,6 +45,7 @@ class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_Ch protected: virtual int_type underflow(); virtual int_type uflow(); + virtual streamsize xsgetn(char_type* __s, streamsize __n); virtual int_type pbackfail(int_type __c = traits_type::eof()); virtual void imbue(const locale& __loc); @@ -199,6 +200,28 @@ typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::__getchar(bool __consu return traits_type::to_int_type(__1buf); } +template +streamsize __stdinbuf<_CharT>::xsgetn(char_type* __s, streamsize __n) { + if constexpr (is_same<_CharT, char>::value) { + if (__always_noconv_) { + streamsize __i = 0; + if (__i < __n && __last_consumed_is_next_) { + __s[__i++] = traits_type::to_char_type(__last_consumed_); + __last_consumed_ = traits_type::eof(); + __last_consumed_is_next_ = false; + } + if (__i < __n) { + size_t __nread = fread(__s + __i, 1, static_cast(__n - __i), __file_); + if (__nread > 0) + __last_consumed_ = traits_type::to_int_type(__s[__i + __nread - 1]); + __i += static_cast(__nread); + } + return __i; + } + } + return basic_streambuf::xsgetn(__s, __n); +} + template typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::pbackfail(int_type __c) { if (traits_type::eq_int_type(__c, traits_type::eof())) { diff --git a/libcxx/test/libcxx/input.output/iostream.objects/cin-read-stdio-sync.sh.cpp b/libcxx/test/libcxx/input.output/iostream.objects/cin-read-stdio-sync.sh.cpp new file mode 100644 index 0000000000000..d3b8f5485abd4 --- /dev/null +++ b/libcxx/test/libcxx/input.output/iostream.objects/cin-read-stdio-sync.sh.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// istream cin; + +// std::cin is backed by __stdinbuf, which reads from the C stdin FILE so that +// C++ and C input can be interleaved (std::ios_base::sync_with_stdio). Its +// xsgetn() takes a bulk fread() fast path for the no-conversion case, while +// single-character operations go through __getchar()/getc(). This test checks +// that the two paths observe the same stream and agree on the putback +// bookkeeping (__last_consumed_). Some of the putback expectations +// (e.g. sungetc() failing after re-consuming a putback character) are +// libc++-specific, which is why this test lives under test/libcxx. + +// RUN: %{build} +// RUN: echo -n ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 > %t.input +// RUN: %{exec} %t.exe < %t.input + +#include +#include +#include +#include + +int main(int, char**) { + typedef std::char_traits Traits; + char buf[16]; + + // C read first: bulk reads must continue where stdio left off. + int c = std::getchar(); + assert(c == 'A'); + + // A character pushed back with ungetc() onto the FILE must be the first + // byte a bulk read delivers. + assert(std::ungetc('a', stdin) == 'a'); + std::cin.read(buf, 4); + assert(std::cin.gcount() == 4); + assert(std::memcmp(buf, "aBCD", 4) == 0); + + // peek() (underflow, which pushes the byte back with ungetc()) followed by + // a bulk read must not lose or duplicate the peeked byte. + assert(std::cin.peek() == 'E'); + std::cin.read(buf, 3); + assert(std::cin.gcount() == 3); + assert(std::memcmp(buf, "EFG", 3) == 0); + + // sungetc() after a bulk read: the last character delivered by the bulk + // read must be the one made available again. + assert(std::cin.rdbuf()->sungetc() == 'G'); + std::cin.read(buf, 2); + assert(std::cin.gcount() == 2); + assert(std::memcmp(buf, "GH", 2) == 0); + + // putback() of an arbitrary character, then a bulk read: the pending + // character comes first, the rest comes from the stream. + assert(std::cin.putback('h').good()); + std::cin.read(buf, 2); + assert(std::cin.gcount() == 2); + assert(std::memcmp(buf, "hI", 2) == 0); + + // A bulk read that delivers ONLY a pending putback character must behave + // like __getchar() re-consuming it: sungetc() afterwards fails. This + // matches the single-character path, which forgets __last_consumed_ when + // returning a putback character. + assert(std::cin.putback('q').good()); + std::cin.read(buf, 1); + assert(std::cin.gcount() == 1); + assert(buf[0] == 'q'); + assert(std::cin.rdbuf()->sungetc() == Traits::eof()); + + // Back to C stdio: it must see the byte right after what C++ consumed. + c = std::getchar(); + assert(c == 'J'); + + // Single-character get() still works after bulk reads. + assert(std::cin.get() == 'K'); + + // Read to EOF: 'L'..'Z' and '0'..'9' remain (25 characters). The first + // read is satisfied in full, the second is short and hits EOF. + std::cin.read(buf, sizeof(buf)); + assert(std::cin.gcount() == 16); + std::cin.read(buf, sizeof(buf)); + assert(std::cin.gcount() == 9); + assert(std::cin.eof()); + + return 0; +} diff --git a/third-party/benchmark/src/sysinfo.cc b/third-party/benchmark/src/sysinfo.cc index 3977772bfede4..c622eeb142dc7 100644 --- a/third-party/benchmark/src/sysinfo.cc +++ b/third-party/benchmark/src/sysinfo.cc @@ -143,7 +143,7 @@ struct ValueUnion { } template - std::array GetAsArray() { + BENCHMARK_MAYBE_UNUSED std::array GetAsArray() { const int arr_size = sizeof(T) * N; BM_CHECK_LE(arr_size, size); std::array arr; @@ -204,7 +204,8 @@ bool GetSysctl(std::string const& name, Tp* out) { } template -bool GetSysctl(std::string const& name, std::array* out) { +BENCHMARK_MAYBE_UNUSED bool GetSysctl(std::string const& name, + std::array* out) { auto buff = GetSysctlImp(name); if (!buff) return false; *out = buff.GetAsArray();