FLANG

buffer.h
1 //===-- runtime/buffer.h ----------------------------------------*- 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 // External file buffering
10
11 #ifndef FORTRAN_RUNTIME_BUFFER_H_
12 #define FORTRAN_RUNTIME_BUFFER_H_
13
14 #include "io-error.h"
15 #include "memory.h"
16 #include <algorithm>
17 #include <cinttypes>
18 #include <cstring>
19
20 namespace Fortran::runtime::io {
21
22 void LeftShiftBufferCircularly(char *, std::size_t bytes, std::size_t shift);
23
24 // Maintains a view of a contiguous region of a file in a memory buffer.
25 // The valid data in the buffer may be circular, but any active frame
26 // will also be contiguous in memory. The requirement stems from the need to
27 // preserve read data that may be reused by means of Tn/TLn edit descriptors
28 // without needing to position the file (which may not always be possible,
29 // e.g. a socket) and a general desire to reduce system call counts.
30 template <typename STORE> class FileFrame {
31 public:
32 using FileOffset = std::int64_t;
33
34 ~FileFrame() { FreeMemoryAndNullify(buffer_); }
35
36 // The valid data in the buffer begins at buffer_[start_] and proceeds
37 // with possible wrap-around for length_ bytes. The current frame
38 // is offset by frame_ bytes into that region and is guaranteed to
39 // be contiguous for at least as many bytes as were requested.
40
41 FileOffset FrameAt() const { return fileOffset_ + frame_; }
42 char *Frame() const { return buffer_ + start_ + frame_; }
43 std::size_t FrameLength() const {
44 return std::min<std::size_t>(length_ - frame_, size_ - (start_ + frame_));
45 }
46 std::size_t BytesBufferedBeforeFrame() const { return frame_ - start_; }
47
48 // Returns a short frame at a non-fatal EOF. Can return a long frame as well.
49 std::size_t ReadFrame(
50 FileOffset at, std::size_t bytes, IoErrorHandler &handler) {
51 Flush(handler);
52 Reallocate(bytes, handler);
53 if (at < fileOffset_ || at > fileOffset_ + length_) {
54 Reset(at);
55 }
56 frame_ = at - fileOffset_;
57 if (static_cast<std::int64_t>(start_ + frame_ + bytes) > size_) {
58 DiscardLeadingBytes(frame_, handler);
59 if (static_cast<std::int64_t>(start_ + bytes) > size_) {
60 // Frame would wrap around; shift current data (if any) to force
61 // contiguity.
62 RUNTIME_CHECK(handler, length_ < size_);
63 if (start_ + length_ <= size_) {
64 // [......abcde..] -> [abcde........]
65 std::memmove(buffer_, buffer_ + start_, length_);
66 } else {
67 // [cde........ab] -> [abcde........]
68 auto n{start_ + length_ - size_}; // 3 for cde
69 RUNTIME_CHECK(handler, length_ >= n);
70 std::memmove(buffer_ + n, buffer_ + start_, length_ - n); // cdeab
71 LeftShiftBufferCircularly(buffer_, length_, n); // abcde
72 }
73 start_ = 0;
74 }
75 }
76 while (FrameLength() < bytes) {
77 auto next{start_ + length_};
78 RUNTIME_CHECK(handler, next < size_);
79 auto minBytes{bytes - FrameLength()};
80 auto maxBytes{size_ - next};
81 auto got{Store().Read(
82 fileOffset_ + length_, buffer_ + next, minBytes, maxBytes, handler)};
83 length_ += got;
84 RUNTIME_CHECK(handler, length_ < size_);
85 if (got < minBytes) {
86 break; // error or EOF & program can handle it
87 }
88 }
89 return FrameLength();
90 }
91
92 void WriteFrame(FileOffset at, std::size_t bytes, IoErrorHandler &handler) {
93 if (!dirty_ || at < fileOffset_ || at > fileOffset_ + length_ ||
94 start_ + (at - fileOffset_) + static_cast<std::int64_t>(bytes) >
95 size_) {
96 Flush(handler);
97 fileOffset_ = at;
98 Reallocate(bytes, handler);
99 }
100 dirty_ = true;
101 frame_ = at - fileOffset_;
102 length_ = std::max<std::int64_t>(length_, frame_ + bytes);
103 }
104
105 void Flush(IoErrorHandler &handler) {
106 if (dirty_) {
107 while (length_ > 0) {
108 std::size_t chunk{std::min<std::size_t>(length_, size_ - start_)};
109 std::size_t put{
110 Store().Write(fileOffset_, buffer_ + start_, chunk, handler)};
111 length_ -= put;
112 start_ += put;
113 fileOffset_ += put;
114 if (put < chunk) {
115 break;
116 }
117 }
118 Reset(fileOffset_);
119 }
120 }
121
122 private:
123 STORE &Store() { return static_cast<STORE &>(*this); }
124
125 void Reallocate(std::int64_t bytes, const Terminator &terminator) {
126 if (bytes > size_) {
127 char *old{buffer_};
128 auto oldSize{size_};
129 size_ = std::max<std::int64_t>(bytes, minBuffer);
130 buffer_ =
131 reinterpret_cast<char *>(AllocateMemoryOrCrash(terminator, size_));
132 auto chunk{std::min<std::int64_t>(length_, oldSize - start_)};
133 std::memcpy(buffer_, old + start_, chunk);
134 start_ = 0;
135 std::memcpy(buffer_ + chunk, old, length_ - chunk);
136 FreeMemory(old);
137 }
138 }
139
140 void Reset(FileOffset at) {
141 start_ = length_ = frame_ = 0;
142 fileOffset_ = at;
143 dirty_ = false;
144 }
145
146 void DiscardLeadingBytes(std::int64_t n, const Terminator &terminator) {
147 RUNTIME_CHECK(terminator, length_ >= n);
148 length_ -= n;
149 if (length_ == 0) {
150 start_ = 0;
151 } else {
152 start_ += n;
153 if (start_ >= size_) {
154 start_ -= size_;
155 }
156 }
157 if (frame_ >= n) {
158 frame_ -= n;
159 } else {
160 frame_ = 0;
161 }
162 fileOffset_ += n;
163 }
164
165 static constexpr std::size_t minBuffer{64 << 10};
166
167 char *buffer_{nullptr};
168 std::int64_t size_{0}; // current allocated buffer size
169 FileOffset fileOffset_{0}; // file offset corresponding to buffer valid data
170 std::int64_t start_{0}; // buffer_[] offset of valid data
171 std::int64_t length_{0}; // valid data length (can wrap)
172 std::int64_t frame_{0}; // offset of current frame in valid data
173 bool dirty_{false};
174 };
175 } // namespace Fortran::runtime::io
176 #endif // FORTRAN_RUNTIME_BUFFER_H_
Fortran::runtime::Terminator
Definition: terminator.h:21
Fortran::runtime::io::IoErrorHandler
Definition: io-error.h:26
Fortran::runtime::io::FileFrame
Definition: buffer.h:30