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: |
24 | explicit Store(std::size_t bytes = 65536) : bytes_{bytes} { |
25 | data_.reset(p: new char[bytes]); |
26 | std::memset(s: &data_[0], c: 0, n: 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>(a: maxBytes, b: bytes_ - at)}; |
44 | std::memcpy(dest: to, src: &data_[at], n: 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(dest: &data_[at], src: from, n: 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(a: chunk, b: 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, most: 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: 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, most: 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: 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 | |