1 //===-- flang/unittests/RuntimeGTest/BufferTest.cpp -------------*- C++ -*-===// 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 #include "../../runtime/buffer.h" 10 #include "CrashHandlerFixture.h" 11 #include "gtest/gtest.h" 12 #include <algorithm> 13 #include <cstdint> 14 #include <cstring> 15 #include <memory> 16 17 static constexpr std::size_t tinyBufferSize{32}; 18 using FileOffset = std::int64_t; 19 using namespace Fortran::runtime; 20 using namespace Fortran::runtime::io; 21 22 class Store : public FileFrame<Store, tinyBufferSize> { 23 public: 24 explicit Store(std::size_t bytes = 65536) : bytes_{bytes} { 25 data_.reset(new char[bytes]); 26 std::memset(&data_[0], 0, bytes); 27 } 28 std::size_t bytes() const { return bytes_; } 29 void set_enforceSequence(bool yes = true) { enforceSequence_ = yes; } 30 void set_expect(FileOffset to) { expect_ = to; } 31 32 std::size_t Read(FileOffset at, char *to, std::size_t minBytes, 33 std::size_t maxBytes, IoErrorHandler &handler) { 34 if (enforceSequence_ && at != expect_) { 35 handler.SignalError("Read(%d,%d,%d) not at expected %d", 36 static_cast<int>(at), static_cast<int>(minBytes), 37 static_cast<int>(maxBytes), static_cast<int>(expect_)); 38 } else if (at < 0 || at + minBytes > bytes_) { 39 handler.SignalError("Read(%d,%d,%d) is out of bounds", 40 static_cast<int>(at), static_cast<int>(minBytes), 41 static_cast<int>(maxBytes)); 42 } 43 auto result{std::min<std::size_t>(maxBytes, bytes_ - at)}; 44 std::memcpy(to, &data_[at], result); 45 expect_ = at + result; 46 return result; 47 } 48 std::size_t Write(FileOffset at, const char *from, std::size_t bytes, 49 IoErrorHandler &handler) { 50 if (enforceSequence_ && at != expect_) { 51 handler.SignalError("Write(%d,%d) not at expected %d", 52 static_cast<int>(at), static_cast<int>(bytes), 53 static_cast<int>(expect_)); 54 } else if (at < 0 || at + bytes > bytes_) { 55 handler.SignalError("Write(%d,%d) is out of bounds", static_cast<int>(at), 56 static_cast<int>(bytes)); 57 } 58 std::memcpy(&data_[at], from, bytes); 59 expect_ = at + bytes; 60 return bytes; 61 } 62 63 private: 64 std::size_t bytes_; 65 std::unique_ptr<char[]> data_; 66 bool enforceSequence_{false}; 67 FileOffset expect_{0}; 68 }; 69 70 inline int ChunkSize(int j, int most) { 71 // 31, 1, 29, 3, 27, ... 72 j %= tinyBufferSize; 73 auto chunk{static_cast<int>( 74 ((j % 2) ? j : (tinyBufferSize - 1 - j)) % tinyBufferSize)}; 75 return std::min(chunk, most); 76 } 77 78 inline int ValueFor(int at) { return (at ^ (at >> 8)) & 0xff; } 79 80 struct BufferTests : CrashHandlerFixture {}; 81 82 TEST(BufferTests, TestFrameBufferReadAndWrite) { 83 Terminator terminator{__FILE__, __LINE__}; 84 IoErrorHandler handler{terminator}; 85 Store store; 86 store.set_enforceSequence(true); 87 const auto bytes{static_cast<FileOffset>(store.bytes())}; 88 // Fill with an assortment of chunks 89 int at{0}, j{0}; 90 while (at < bytes) { 91 auto chunk{ChunkSize(j, static_cast<int>(bytes - at))}; 92 store.WriteFrame(at, chunk, handler); 93 char *to{store.Frame()}; 94 for (int k{0}; k < chunk; ++k) { 95 to[k] = ValueFor(at + k); 96 } 97 at += chunk; 98 ++j; 99 } 100 store.Flush(handler); 101 // Validate 102 store.set_expect(0); 103 at = 0; 104 while (at < bytes) { 105 auto chunk{ChunkSize(j, static_cast<int>(bytes - at))}; 106 std::size_t frame{store.ReadFrame(at, chunk, handler)}; 107 ASSERT_GE(frame, static_cast<std::size_t>(chunk)) 108 << "Badly-sized ReadFrame at " << at << ", chunk=" << chunk << ", got " 109 << frame << '\n'; 110 111 const char *from{store.Frame()}; 112 for (int k{0}; k < chunk; ++k) { 113 auto expect{static_cast<char>(ValueFor(at + k))}; 114 ASSERT_EQ(from[k], expect) 115 << "At " << at << '+' << k << '(' << (at + k) << "), read " 116 << (from[k] & 0xff) << ", expected " << static_cast<int>(expect) 117 << '\n'; 118 } 119 at += chunk; 120 ++j; 121 } 122 } 123