231 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS -D_LIBCPP_ENABLE_CXX26_REMOVED_CODECVT10// MSVC warning C4242: '+=': conversion from 'const _Ty' to 'size_t', possible loss of data11// MSVC warning C4244: 'argument': conversion from 'std::streamsize' to 'size_t', possible loss of data12// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4242 /wd424413// UNSUPPORTED: c++0314 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 fstream19// 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 will31// 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 default33// 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(payload_sizes.begin(), payload_sizes.end(), 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(p, std::ios::out | std::ios::binary);59 assert(ofs.is_open());60 ofs.write(data.data(), 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(p, 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(in_buffer.data() + previously_read, 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(p.c_str());100}101 102#ifndef TEST_HAS_NO_WIDE_CHARACTERS103template <class BufferPolicy>104void test_read_codecvt(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {105 std::streamsize total_size = std::accumulate(payload_sizes.begin(), payload_sizes.end(), 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(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));114 ofs.open(p, std::ios::out | std::ios::binary);115 assert(ofs.is_open());116 ofs.write(data.data(), 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(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));130 policy(ifs);131 ifs.open(p, 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(p.c_str());150}151#endif152 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 operations159void test_1_read() {160 // with default library buffer size: 4096b161 for (std::streamsize x : io_sizes_default) {162 test_read(LibraryDefaultBuffer(), {x});163#ifndef TEST_HAS_NO_WIDE_CHARACTERS164 test_read_codecvt(LibraryDefaultBuffer(), {x});165#endif166 }167 168 // with the library-managed buffer of given size169 for (std::streamsize b : buffer_sizes) {170 for (std::streamsize x : io_sizes) {171 test_read(LibraryManagedBuffer(b), {x});172#ifndef TEST_HAS_NO_WIDE_CHARACTERS173 test_read_codecvt(LibraryManagedBuffer(b), {x});174#endif175 }176 }177 178 // with the user-managed buffer of given size179 for (std::streamsize b : buffer_sizes) {180 for (std::streamsize x : io_sizes) {181 test_read(UserManagedBuffer(b), {x});182#ifndef TEST_HAS_NO_WIDE_CHARACTERS183 test_read_codecvt(UserManagedBuffer(b), {x});184#endif185 }186 }187}188 189// Test two read operations190void test_2_reads() {191 // with default library buffer size: 4096b192 for (std::streamsize a : io_sizes_default) {193 for (std::streamsize b : io_sizes_default) {194 test_read(LibraryDefaultBuffer(), {a, b});195#ifndef TEST_HAS_NO_WIDE_CHARACTERS196 test_read_codecvt(LibraryDefaultBuffer(), {a, b});197#endif198 }199 }200 201 // with the library-managed buffer of given size202 for (std::streamsize buf : buffer_sizes) {203 for (std::streamsize a : io_sizes) {204 for (std::streamsize b : io_sizes) {205 test_read(LibraryManagedBuffer(buf), {a, b});206#ifndef TEST_HAS_NO_WIDE_CHARACTERS207 test_read_codecvt(LibraryManagedBuffer(buf), {a, b});208#endif209 }210 }211 }212 213 // with the user-managed buffer of given size214 for (std::streamsize buf : buffer_sizes) {215 for (std::streamsize a : io_sizes) {216 for (std::streamsize b : io_sizes) {217 test_read(UserManagedBuffer(buf), {a, b});218#ifndef TEST_HAS_NO_WIDE_CHARACTERS219 test_read_codecvt(UserManagedBuffer(buf), {a, b});220#endif221 }222 }223 }224}225 226int main(int, char**) {227 test_1_read();228 test_2_reads();229 return 0;230}231