229 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 writing 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_write(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {50 std::size_t previously_written = 0;51 std::streamsize total_size = std::accumulate(payload_sizes.begin(), payload_sizes.end(), std::streamsize{0});52 std::vector<char> data(total_size);53 for (std::size_t i = 0; i < data.size(); ++i) {54 data[i] = static_cast<char>(i % (1 << 8 * sizeof(char)));55 }56 std::string p = get_temp_file_name();57 {58 std::ofstream ofs;59 policy(ofs);60 ofs.open(p, std::ios::out | std::ios::binary);61 assert(ofs.is_open());62 for (const auto& payload_sz : payload_sizes) {63 ofs.write(data.data() + previously_written, payload_sz);64 assert(!ofs.fail());65 // test that the user's out_buffer buffer was not modified by write()66 for (std::streamsize j = 0; j < payload_sz; ++j) {67 char exp = (previously_written + j) % (1 << 8 * sizeof(char));68 TEST_REQUIRE(data[previously_written + j] == exp, [&] {69 test_eprintf(70 "failed after write() at offset %zu (offset %zu in chunk size %zu): got=%x, expected=%x\n",71 previously_written + j,72 j,73 payload_sz,74 data[previously_written + j],75 exp);76 });77 }78 previously_written += payload_sz;79 }80 ofs.close();81 }82 { // verify contents after reading the file back83 std::ifstream ifs(p.c_str(), std::ios::ate | std::ios::binary);84 const std::streamsize in_sz = ifs.tellg();85 TEST_REQUIRE(in_sz == total_size, [&] { test_eprintf("out_sz = %zu, in_sz = %ld\n", total_size, in_sz); });86 std::vector<char> in_buffer(total_size);87 ifs.seekg(0, std::ios::beg);88 assert(ifs.read(in_buffer.data(), total_size));89 for (std::size_t i = 0; i < in_buffer.size(); ++i) {90 char exp = i % (1 << 8 * sizeof(char));91 TEST_REQUIRE(in_buffer[i] == exp, [&] {92 test_eprintf("failed after read() at offset %zu: got=%x, expected=%x\n", i, in_buffer[i], exp);93 });94 }95 }96 std::remove(p.c_str());97}98 99#ifndef TEST_HAS_NO_WIDE_CHARACTERS100template <class BufferPolicy>101void test_write_codecvt(BufferPolicy policy, const std::vector<std::streamsize>& payload_sizes) {102 std::size_t previously_written = 0;103 std::streamsize total_size = std::accumulate(payload_sizes.begin(), payload_sizes.end(), std::streamsize{0});104 std::vector<wchar_t> data(total_size);105 for (std::size_t i = 0; i < data.size(); ++i) {106 data[i] = static_cast<wchar_t>(i);107 }108 std::string p = get_temp_file_name();109 {110 std::wofstream ofs;111 ofs.imbue(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));112 policy(ofs);113 ofs.open(p, std::ios::out | std::ios::binary);114 assert(ofs.is_open());115 for (const auto& payload_sz : payload_sizes) {116 ofs.write(data.data() + previously_written, payload_sz);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 < payload_sz; ++j) {120 wchar_t exp = static_cast<wchar_t>(previously_written + j);121 TEST_REQUIRE(data[previously_written + j] == exp, [&] {122 test_eprintf(123 "failed after write() at offset %zu (offset %zu in chunk size %zu): got=%x, expected=%x\n",124 previously_written + j,125 j,126 payload_sz,127 data[previously_written + j],128 exp);129 });130 }131 previously_written += payload_sz;132 }133 ofs.close();134 }135 { // verify contents after reading the file back136 std::wifstream ifs(p.c_str(), std::ios::in | std::ios::binary);137 ifs.imbue(std::locale(std::locale::classic(), new std::codecvt_utf8<wchar_t>));138 std::vector<wchar_t> in_buffer(total_size);139 assert(ifs.read(in_buffer.data(), total_size));140 for (std::size_t i = 0; i < in_buffer.size(); ++i) {141 wchar_t exp = static_cast<wchar_t>(i);142 TEST_REQUIRE(in_buffer[i] == exp, [&] {143 test_eprintf("failed after read() at offset %zu: got=%x, expected=%x\n", i, in_buffer[i], exp);144 });145 }146 }147 std::remove(p.c_str());148}149#endif150 151const std::vector<std::streamsize> buffer_sizes{0L, 3L, 8L, 9L, 11L};152const std::vector<std::streamsize> io_sizes{0L, 1L, 2L, 3L, 4L, 9L, 10L, 11L, 12L, 13L, 21L, 22L, 23L};153const std::vector<std::streamsize> io_sizes_default{154 0L, 1L, 2L, 3L, 4L, 4094L, 4095L, 4096L, 4097L, 4098L, 8190L, 8191L, 8192L, 8193L, 8194L};155 156// Test single write operations157void test_1_write() {158 // with default library buffer size: 4096b159 for (std::streamsize x : io_sizes_default) {160 test_write(LibraryDefaultBuffer(), {x});161#ifndef TEST_HAS_NO_WIDE_CHARACTERS162 test_write_codecvt(LibraryDefaultBuffer(), {x});163#endif164 }165 166 // with the library-managed buffer of given size167 for (std::streamsize b : buffer_sizes) {168 for (std::streamsize x : io_sizes) {169 test_write(LibraryManagedBuffer(b), {x});170#ifndef TEST_HAS_NO_WIDE_CHARACTERS171 test_write_codecvt(LibraryManagedBuffer(b), {x});172#endif173 }174 }175 176 // with the user-managed buffer of given size177 for (std::streamsize b : buffer_sizes) {178 for (std::streamsize x : io_sizes) {179 test_write(UserManagedBuffer(b), {x});180#ifndef TEST_HAS_NO_WIDE_CHARACTERS181 test_write_codecvt(UserManagedBuffer(b), {x});182#endif183 }184 }185}186 187// Test two write operations188void test_2_writes() {189 // with default library buffer size: 4096b190 for (std::streamsize a : io_sizes_default) {191 for (std::streamsize b : io_sizes_default) {192 test_write(LibraryDefaultBuffer(), {a, b});193#ifndef TEST_HAS_NO_WIDE_CHARACTERS194 test_write_codecvt(LibraryDefaultBuffer(), {a, b});195#endif196 }197 }198 199 // with the library-managed buffer of given size200 for (std::streamsize buf : buffer_sizes) {201 for (std::streamsize a : io_sizes) {202 for (std::streamsize b : io_sizes) {203 test_write(LibraryManagedBuffer(buf), {a, b});204#ifndef TEST_HAS_NO_WIDE_CHARACTERS205 test_write_codecvt(LibraryManagedBuffer(buf), {a, b});206#endif207 }208 }209 }210 211 // with the user-managed buffer of given size212 for (std::streamsize buf : buffer_sizes) {213 for (std::streamsize a : io_sizes) {214 for (std::streamsize b : io_sizes) {215 test_write(UserManagedBuffer(buf), {a, b});216#ifndef TEST_HAS_NO_WIDE_CHARACTERS217 test_write_codecvt(UserManagedBuffer(buf), {a, b});218#endif219 }220 }221 }222}223 224int main(int, char**) {225 test_1_write();226 test_2_writes();227 return 0;228}229