xref: /llvm-project/flang/unittests/Runtime/BufferTest.cpp (revision ffc67bb3602a6a9a4f886af362e1f2d7c9821570)
1 //===-- flang/unittests/Runtime/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:
Store(std::size_t bytes=65536)24   explicit Store(std::size_t bytes = 65536) : bytes_{bytes} {
25     data_.reset(new char[bytes]);
26     std::memset(&data_[0], 0, bytes);
27   }
bytes() const28   std::size_t bytes() const { return bytes_; }
set_enforceSequence(bool yes=true)29   void set_enforceSequence(bool yes = true) { enforceSequence_ = yes; }
set_expect(FileOffset to)30   void set_expect(FileOffset to) { expect_ = to; }
31 
Read(FileOffset at,char * to,std::size_t minBytes,std::size_t maxBytes,IoErrorHandler & handler)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   }
Write(FileOffset at,const char * from,std::size_t bytes,IoErrorHandler & handler)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 
ChunkSize(int j,int most)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 
ValueFor(int at)78 inline int ValueFor(int at) { return (at ^ (at >> 8)) & 0xff; }
79 
80 struct BufferTests : CrashHandlerFixture {};
81 
TEST(BufferTests,TestFrameBufferReadAndWrite)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