1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS -D_LIBCPP_ENABLE_CXX26_REMOVED_CODECVT
10// MSVC warning C4242: '+=': conversion from 'const _Ty' to 'size_t', possible loss of data
11// MSVC warning C4244: 'argument': conversion from 'std::streamsize' to 'size_t', possible loss of data
12// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4242 /wd4244
13// UNSUPPORTED: c++03
14
15// <fstream>
16
17// This test checks the behavior of reading payloads of different sizes in different patterns.
18// In particular, it was written to exercise code paths that deal with buffering inside the fstream
19// implementation.
20//
21// For each test, we test various behaviors w.r.t. how the buffer is handled:
22// - Provide a user-managed buffer to the library. In this case, we test the following corner-cases:
23// + A 0-sized buffer.
24// + A buffer size greater than and smaller than the payload size, which causes multiple buffer effects.
25// Important values are +/- 1 byte from the payload size.
26// - Let the library manage a buffer of a user-provided size 'n'. In this case, we test the following corner-cases:
27// + A 0-sized buffer.
28// + A buffer size greater than and smaller than the payload size, which causes multiple buffer effects.
29// Important values are +/- 1 or 2 bytes from the payload size.
30// + A buffer size smaller than 8 bytes. If pubsetbuf() is called with less than 8 bytes, the library will
31// use __extbuf_min_ with 8 bytes instead of allocating anything.
32// - Let the library manage a buffer, without specifying any size. In this case, the library will use the default
33// buffer size of 4096 bytes.
34
35#include <cassert>
36#include <codecvt>
37#include <fstream>
38#include <locale>
39#include <numeric>
40#include <string>
41#include <vector>
42
43#include "../types.h"
44#include "assert_macros.h"
45#include "platform_support.h"
46#include "test_macros.h"
47
48template <class BufferPolicy>
49void test_read(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {
50 std::streamsize total_size = std::accumulate(first: payload_sizes.begin(), last: payload_sizes.end(), init: std::streamsize{0});
51 std::vector<char> data(total_size);
52 for (std::size_t i = 0; i < data.size(); ++i) {
53 data[i] = static_cast<char>(i % (1 << 8 * sizeof(char)));
54 }
55 std::string p = get_temp_file_name();
56 {
57 std::ofstream ofs;
58 ofs.open(s: p, mode: std::ios::out | std::ios::binary);
59 assert(ofs.is_open());
60 ofs.write(s: data.data(), n: data.size());
61 assert(!ofs.fail());
62 // test that the user's out_buffer buffer was not modified by write()
63 for (std::streamsize j = 0; j < total_size; ++j) {
64 char exp = j % (1 << 8 * sizeof(char));
65 TEST_REQUIRE(data[j] == exp, [&] {
66 test_eprintf("failed after write() at offset %zu: got=%x, expected=%x\n", j, data[j], exp);
67 });
68 }
69 ofs.close();
70 }
71 {
72 std::ifstream ifs;
73 policy(ifs);
74 ifs.open(s: p, mode: std::ios::ate | std::ios::binary);
75 assert(ifs.is_open());
76 const std::streamsize in_sz = ifs.tellg();
77 TEST_REQUIRE(in_sz == total_size, [&] { test_eprintf("out_sz = %zu, in_sz = %ld\n", total_size, in_sz); });
78 ifs.seekg(0, std::ios::beg);
79 std::size_t previously_read = 0;
80 std::vector<char> in_buffer(total_size);
81 for (const auto& payload_sz : payload_sizes) {
82 ifs.read(s: in_buffer.data() + previously_read, n: payload_sz);
83 assert(ifs);
84 for (std::streamsize j = 0; j < payload_sz; ++j) {
85 char exp = (previously_read + j) % (1 << 8 * sizeof(char));
86 TEST_REQUIRE(in_buffer[previously_read + j] == exp, [&] {
87 test_eprintf(
88 "failed after read() at offset %zu (offset %zu in chunk size %zu): got=%x, expected=%x\n",
89 previously_read + j,
90 j,
91 payload_sz,
92 in_buffer[previously_read + j],
93 exp);
94 });
95 }
96 previously_read += payload_sz;
97 }
98 }
99 std::remove(filename: p.c_str());
100}
101
102#ifndef TEST_HAS_NO_WIDE_CHARACTERS
103template <class BufferPolicy>
104void test_read_codecvt(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {
105 std::streamsize total_size = std::accumulate(first: payload_sizes.begin(), last: payload_sizes.end(), init: std::streamsize{0});
106 std::vector<wchar_t> data(total_size);
107 for (std::size_t i = 0; i < data.size(); ++i) {
108 data[i] = static_cast<wchar_t>(i);
109 }
110 std::string p = get_temp_file_name();
111 {
112 std::wofstream ofs;
113 ofs.imbue(loc: std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));
114 ofs.open(s: p, mode: std::ios::out | std::ios::binary);
115 assert(ofs.is_open());
116 ofs.write(s: data.data(), n: data.size());
117 assert(!ofs.fail());
118 // test that the user's out_buffer buffer was not modified by write()
119 for (std::streamsize j = 0; j < total_size; ++j) {
120 wchar_t exp = static_cast<wchar_t>(j);
121 TEST_REQUIRE(data[j] == exp, [&] {
122 test_eprintf("failed after write() at offset %zu: got=%x, expected=%x\n", j, data[j], exp);
123 });
124 }
125 ofs.close();
126 }
127 {
128 std::wifstream ifs;
129 ifs.imbue(loc: std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));
130 policy(ifs);
131 ifs.open(s: p, mode: std::ios::in | std::ios::binary);
132 assert(ifs.is_open());
133 ifs.seekg(0, std::ios::beg);
134 std::size_t previously_read = 0;
135 std::vector<wchar_t> in_buffer(total_size);
136 for (const auto& payload_sz : payload_sizes) {
137 assert(ifs.read(in_buffer.data() + previously_read, payload_sz));
138 assert(ifs);
139 for (std::streamsize j = 0; j < payload_sz; ++j) {
140 wchar_t exp = static_cast<wchar_t>(j + previously_read);
141 TEST_REQUIRE(in_buffer[j + previously_read] == exp, [&] {
142 test_eprintf(
143 "failed after read() at offset %zu: got=%x, expected=%x\n", j, in_buffer[j + previously_read], exp);
144 });
145 }
146 previously_read += payload_sz;
147 }
148 }
149 std::remove(filename: p.c_str());
150}
151#endif
152
153const std::vector<std::streamsize> buffer_sizes{0L, 3L, 8L, 9L, 11L};
154const std::vector<std::streamsize> io_sizes{0L, 1L, 2L, 3L, 4L, 9L, 10L, 11L, 12L, 13L, 21L, 22L, 23L};
155const std::vector<std::streamsize> io_sizes_default{
156 0L, 1L, 2L, 3L, 4L, 4094L, 4095L, 4096L, 4097L, 4098L, 8190L, 8191L, 8192L, 8193L, 8194L};
157
158// Test single read operations
159void test_1_read() {
160 // with default library buffer size: 4096b
161 for (std::streamsize x : io_sizes_default) {
162 test_read(policy: LibraryDefaultBuffer(), payload_sizes: {x});
163#ifndef TEST_HAS_NO_WIDE_CHARACTERS
164 test_read_codecvt(policy: LibraryDefaultBuffer(), payload_sizes: {x});
165#endif
166 }
167
168 // with the library-managed buffer of given size
169 for (std::streamsize b : buffer_sizes) {
170 for (std::streamsize x : io_sizes) {
171 test_read(policy: LibraryManagedBuffer(b), payload_sizes: {x});
172#ifndef TEST_HAS_NO_WIDE_CHARACTERS
173 test_read_codecvt(policy: LibraryManagedBuffer(b), payload_sizes: {x});
174#endif
175 }
176 }
177
178 // with the user-managed buffer of given size
179 for (std::streamsize b : buffer_sizes) {
180 for (std::streamsize x : io_sizes) {
181 test_read(policy: UserManagedBuffer(b), payload_sizes: {x});
182#ifndef TEST_HAS_NO_WIDE_CHARACTERS
183 test_read_codecvt(policy: UserManagedBuffer(b), payload_sizes: {x});
184#endif
185 }
186 }
187}
188
189// Test two read operations
190void test_2_reads() {
191 // with default library buffer size: 4096b
192 for (std::streamsize a : io_sizes_default) {
193 for (std::streamsize b : io_sizes_default) {
194 test_read(policy: LibraryDefaultBuffer(), payload_sizes: {a, b});
195#ifndef TEST_HAS_NO_WIDE_CHARACTERS
196 test_read_codecvt(policy: LibraryDefaultBuffer(), payload_sizes: {a, b});
197#endif
198 }
199 }
200
201 // with the library-managed buffer of given size
202 for (std::streamsize buf : buffer_sizes) {
203 for (std::streamsize a : io_sizes) {
204 for (std::streamsize b : io_sizes) {
205 test_read(policy: LibraryManagedBuffer(buf), payload_sizes: {a, b});
206#ifndef TEST_HAS_NO_WIDE_CHARACTERS
207 test_read_codecvt(policy: LibraryManagedBuffer(buf), payload_sizes: {a, b});
208#endif
209 }
210 }
211 }
212
213 // with the user-managed buffer of given size
214 for (std::streamsize buf : buffer_sizes) {
215 for (std::streamsize a : io_sizes) {
216 for (std::streamsize b : io_sizes) {
217 test_read(policy: UserManagedBuffer(buf), payload_sizes: {a, b});
218#ifndef TEST_HAS_NO_WIDE_CHARACTERS
219 test_read_codecvt(policy: UserManagedBuffer(buf), payload_sizes: {a, b});
220#endif
221 }
222 }
223 }
224}
225
226int main(int, char**) {
227 test_1_read();
228 test_2_reads();
229 return 0;
230}
231

source code of libcxx/test/std/input.output/file.streams/fstreams/ifstream.members/buffered_reads.pass.cpp