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