xref: /llvm-project/flang/runtime/buffer.h (revision 3b337242ee165554f0017b00671381ec5b1ba855)
1f7be2518Speter klausler //===-- runtime/buffer.h ----------------------------------------*- C++ -*-===//
2f7be2518Speter klausler //
3f7be2518Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f7be2518Speter klausler // See https://llvm.org/LICENSE.txt for license information.
5f7be2518Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f7be2518Speter klausler //
7f7be2518Speter klausler //===----------------------------------------------------------------------===//
8f7be2518Speter klausler 
9f7be2518Speter klausler // External file buffering
10f7be2518Speter klausler 
11f7be2518Speter klausler #ifndef FORTRAN_RUNTIME_BUFFER_H_
12f7be2518Speter klausler #define FORTRAN_RUNTIME_BUFFER_H_
13f7be2518Speter klausler 
14f7be2518Speter klausler #include "io-error.h"
15*3b337242SSlava Zakharin #include "flang/Runtime/freestanding-tools.h"
16830c0b90SPeter Klausler #include "flang/Runtime/memory.h"
17f7be2518Speter klausler #include <algorithm>
18f7be2518Speter klausler #include <cinttypes>
19f7be2518Speter klausler #include <cstring>
20f7be2518Speter klausler 
21f7be2518Speter klausler namespace Fortran::runtime::io {
22f7be2518Speter klausler 
238ebf7411SSlava Zakharin RT_API_ATTRS void LeftShiftBufferCircularly(
248ebf7411SSlava Zakharin     char *, std::size_t bytes, std::size_t shift);
25f7be2518Speter klausler 
26f7be2518Speter klausler // Maintains a view of a contiguous region of a file in a memory buffer.
27f7be2518Speter klausler // The valid data in the buffer may be circular, but any active frame
28f7be2518Speter klausler // will also be contiguous in memory.  The requirement stems from the need to
29f7be2518Speter klausler // preserve read data that may be reused by means of Tn/TLn edit descriptors
30f7be2518Speter klausler // without needing to position the file (which may not always be possible,
31f7be2518Speter klausler // e.g. a socket) and a general desire to reduce system call counts.
325da55bfcSpeter klausler //
335da55bfcSpeter klausler // Possible scenario with a tiny 32-byte buffer after a ReadFrame or
345da55bfcSpeter klausler // WriteFrame with a file offset of 103 to access "DEF":
355da55bfcSpeter klausler //
365da55bfcSpeter klausler //    fileOffset_ 100 --+  +-+ frame of interest (103:105)
375da55bfcSpeter klausler //   file:  ............ABCDEFGHIJKLMNOPQRSTUVWXYZ....
385da55bfcSpeter klausler // buffer: [NOPQRSTUVWXYZ......ABCDEFGHIJKLM]   (size_ == 32)
395da55bfcSpeter klausler //                             |  +-- frame_ == 3
405da55bfcSpeter klausler //                             +----- start_ == 19, length_ == 26
415da55bfcSpeter klausler //
425da55bfcSpeter klausler // The buffer holds length_ == 26 bytes from file offsets 100:125.
435da55bfcSpeter klausler // Those 26 bytes "wrap around" the end of the circular buffer,
445da55bfcSpeter klausler // so file offsets 100:112 map to buffer offsets 19:31 ("A..M") and
455da55bfcSpeter klausler //    file offsets 113:125 map to buffer offsets  0:12 ("N..Z")
465da55bfcSpeter klausler // The 3-byte frame of file offsets 103:105 is contiguous in the buffer
475da55bfcSpeter klausler // at buffer offset (start_ + frame_) == 22 ("DEF").
485da55bfcSpeter klausler 
495da55bfcSpeter klausler template <typename STORE, std::size_t minBuffer = 65536> class FileFrame {
50f7be2518Speter klausler public:
51f7be2518Speter klausler   using FileOffset = std::int64_t;
52f7be2518Speter klausler 
~FileFrame()538ebf7411SSlava Zakharin   RT_API_ATTRS ~FileFrame() { FreeMemoryAndNullify(buffer_); }
54f7be2518Speter klausler 
55f7be2518Speter klausler   // The valid data in the buffer begins at buffer_[start_] and proceeds
56f7be2518Speter klausler   // with possible wrap-around for length_ bytes.  The current frame
57f7be2518Speter klausler   // is offset by frame_ bytes into that region and is guaranteed to
58f7be2518Speter klausler   // be contiguous for at least as many bytes as were requested.
59f7be2518Speter klausler 
FrameAt()608ebf7411SSlava Zakharin   RT_API_ATTRS FileOffset FrameAt() const { return fileOffset_ + frame_; }
Frame()618ebf7411SSlava Zakharin   RT_API_ATTRS char *Frame() const { return buffer_ + start_ + frame_; }
FrameLength()628ebf7411SSlava Zakharin   RT_API_ATTRS std::size_t FrameLength() const {
636256fbe2STim Keith     return std::min<std::size_t>(length_ - frame_, size_ - (start_ + frame_));
64f7be2518Speter klausler   }
BytesBufferedBeforeFrame()658ebf7411SSlava Zakharin   RT_API_ATTRS std::size_t BytesBufferedBeforeFrame() const {
668ebf7411SSlava Zakharin     return frame_ - start_;
678ebf7411SSlava Zakharin   }
68f7be2518Speter klausler 
69f7be2518Speter klausler   // Returns a short frame at a non-fatal EOF.  Can return a long frame as well.
ReadFrame(FileOffset at,std::size_t bytes,IoErrorHandler & handler)708ebf7411SSlava Zakharin   RT_API_ATTRS std::size_t ReadFrame(
71f7be2518Speter klausler       FileOffset at, std::size_t bytes, IoErrorHandler &handler) {
72f7be2518Speter klausler     Flush(handler);
73f7be2518Speter klausler     Reallocate(bytes, handler);
745da55bfcSpeter klausler     std::int64_t newFrame{at - fileOffset_};
755da55bfcSpeter klausler     if (newFrame < 0 || newFrame > length_) {
76f7be2518Speter klausler       Reset(at);
775da55bfcSpeter klausler     } else {
785da55bfcSpeter klausler       frame_ = newFrame;
79f7be2518Speter klausler     }
805da55bfcSpeter klausler     RUNTIME_CHECK(handler, at == fileOffset_ + frame_);
813b635714Speter klausler     if (static_cast<std::int64_t>(start_ + frame_ + bytes) > size_) {
82f7be2518Speter klausler       DiscardLeadingBytes(frame_, handler);
835da55bfcSpeter klausler       MakeDataContiguous(handler, bytes);
845da55bfcSpeter klausler       RUNTIME_CHECK(handler, at == fileOffset_ + frame_);
85f7be2518Speter klausler     }
8617e2f236Speter klausler     if (FrameLength() < bytes) {
87f7be2518Speter klausler       auto next{start_ + length_};
88f7be2518Speter klausler       RUNTIME_CHECK(handler, next < size_);
89f7be2518Speter klausler       auto minBytes{bytes - FrameLength()};
90f7be2518Speter klausler       auto maxBytes{size_ - next};
91f7be2518Speter klausler       auto got{Store().Read(
92f7be2518Speter klausler           fileOffset_ + length_, buffer_ + next, minBytes, maxBytes, handler)};
93f7be2518Speter klausler       length_ += got;
945da55bfcSpeter klausler       RUNTIME_CHECK(handler, length_ <= size_);
95f7be2518Speter klausler     }
96f7be2518Speter klausler     return FrameLength();
97f7be2518Speter klausler   }
98f7be2518Speter klausler 
WriteFrame(FileOffset at,std::size_t bytes,IoErrorHandler & handler)998ebf7411SSlava Zakharin   RT_API_ATTRS void WriteFrame(
1008ebf7411SSlava Zakharin       FileOffset at, std::size_t bytes, IoErrorHandler &handler) {
1015da55bfcSpeter klausler     Reallocate(bytes, handler);
1025da55bfcSpeter klausler     std::int64_t newFrame{at - fileOffset_};
1035da55bfcSpeter klausler     if (!dirty_ || newFrame < 0 || newFrame > length_) {
104f7be2518Speter klausler       Flush(handler);
10561687f3aSpeter klausler       Reset(at);
1065da55bfcSpeter klausler     } else if (start_ + newFrame + static_cast<std::int64_t>(bytes) > size_) {
1075da55bfcSpeter klausler       // Flush leading data before "at", retain from "at" onward
1085da55bfcSpeter klausler       Flush(handler, length_ - newFrame);
1095da55bfcSpeter klausler       MakeDataContiguous(handler, bytes);
1105da55bfcSpeter klausler     } else {
1115da55bfcSpeter klausler       frame_ = newFrame;
112f7be2518Speter klausler     }
1135da55bfcSpeter klausler     RUNTIME_CHECK(handler, at == fileOffset_ + frame_);
114f7be2518Speter klausler     dirty_ = true;
11595696d56Speter klausler     length_ = std::max<std::int64_t>(length_, frame_ + bytes);
116f7be2518Speter klausler   }
117f7be2518Speter klausler 
1188ebf7411SSlava Zakharin   RT_API_ATTRS void Flush(IoErrorHandler &handler, std::int64_t keep = 0) {
119f7be2518Speter klausler     if (dirty_) {
1205da55bfcSpeter klausler       while (length_ > keep) {
1215da55bfcSpeter klausler         std::size_t chunk{
1225da55bfcSpeter klausler             std::min<std::size_t>(length_ - keep, size_ - start_)};
123f7be2518Speter klausler         std::size_t put{
124f7be2518Speter klausler             Store().Write(fileOffset_, buffer_ + start_, chunk, handler)};
1255da55bfcSpeter klausler         DiscardLeadingBytes(put, handler);
126f7be2518Speter klausler         if (put < chunk) {
127f7be2518Speter klausler           break;
128f7be2518Speter klausler         }
129f7be2518Speter klausler       }
1305da55bfcSpeter klausler       if (length_ == 0) {
131f7be2518Speter klausler         Reset(fileOffset_);
132f7be2518Speter klausler       }
133f7be2518Speter klausler     }
1345da55bfcSpeter klausler   }
135f7be2518Speter klausler 
TruncateFrame(std::int64_t at,IoErrorHandler & handler)1368ebf7411SSlava Zakharin   RT_API_ATTRS void TruncateFrame(std::int64_t at, IoErrorHandler &handler) {
1371a65d09dSPeter Klausler     RUNTIME_CHECK(handler, !dirty_);
1381a65d09dSPeter Klausler     if (at <= fileOffset_) {
1391a65d09dSPeter Klausler       Reset(at);
1401a65d09dSPeter Klausler     } else if (at < fileOffset_ + length_) {
1411a65d09dSPeter Klausler       length_ = at - fileOffset_;
1421a65d09dSPeter Klausler     }
1431a65d09dSPeter Klausler   }
1441a65d09dSPeter Klausler 
145f7be2518Speter klausler private:
Store()1468ebf7411SSlava Zakharin   RT_API_ATTRS STORE &Store() { return static_cast<STORE &>(*this); }
147f7be2518Speter klausler 
Reallocate(std::int64_t bytes,const Terminator & terminator)1488ebf7411SSlava Zakharin   RT_API_ATTRS void Reallocate(
1498ebf7411SSlava Zakharin       std::int64_t bytes, const Terminator &terminator) {
150f7be2518Speter klausler     if (bytes > size_) {
151f7be2518Speter klausler       char *old{buffer_};
152f7be2518Speter klausler       auto oldSize{size_};
153702c0cfaSPeter Klausler       size_ = std::max<std::int64_t>(bytes, size_ + minBuffer);
154f7be2518Speter klausler       buffer_ =
155f7be2518Speter klausler           reinterpret_cast<char *>(AllocateMemoryOrCrash(terminator, size_));
15695696d56Speter klausler       auto chunk{std::min<std::int64_t>(length_, oldSize - start_)};
157871086bfSKrzysztof Parzyszek       // "memcpy" in glibc has a "nonnull" attribute on the source pointer.
158871086bfSKrzysztof Parzyszek       // Avoid passing a null pointer, since it would result in an undefined
159871086bfSKrzysztof Parzyszek       // behavior.
160871086bfSKrzysztof Parzyszek       if (old != nullptr) {
161f7be2518Speter klausler         std::memcpy(buffer_, old + start_, chunk);
162f7be2518Speter klausler         std::memcpy(buffer_ + chunk, old, length_ - chunk);
163f7be2518Speter klausler         FreeMemory(old);
164f7be2518Speter klausler       }
165871086bfSKrzysztof Parzyszek       start_ = 0;
166871086bfSKrzysztof Parzyszek     }
167f7be2518Speter klausler   }
168f7be2518Speter klausler 
Reset(FileOffset at)1698ebf7411SSlava Zakharin   RT_API_ATTRS void Reset(FileOffset at) {
170f7be2518Speter klausler     start_ = length_ = frame_ = 0;
171f7be2518Speter klausler     fileOffset_ = at;
172f7be2518Speter klausler     dirty_ = false;
173f7be2518Speter klausler   }
174f7be2518Speter klausler 
DiscardLeadingBytes(std::int64_t n,const Terminator & terminator)1758ebf7411SSlava Zakharin   RT_API_ATTRS void DiscardLeadingBytes(
1768ebf7411SSlava Zakharin       std::int64_t n, const Terminator &terminator) {
177f7be2518Speter klausler     RUNTIME_CHECK(terminator, length_ >= n);
178f7be2518Speter klausler     length_ -= n;
179f7be2518Speter klausler     if (length_ == 0) {
180f7be2518Speter klausler       start_ = 0;
181f7be2518Speter klausler     } else {
182f7be2518Speter klausler       start_ += n;
183f7be2518Speter klausler       if (start_ >= size_) {
184f7be2518Speter klausler         start_ -= size_;
185f7be2518Speter klausler       }
186f7be2518Speter klausler     }
187f7be2518Speter klausler     if (frame_ >= n) {
188f7be2518Speter klausler       frame_ -= n;
189f7be2518Speter klausler     } else {
190f7be2518Speter klausler       frame_ = 0;
191f7be2518Speter klausler     }
192f7be2518Speter klausler     fileOffset_ += n;
193f7be2518Speter klausler   }
194f7be2518Speter klausler 
MakeDataContiguous(IoErrorHandler & handler,std::size_t bytes)1958ebf7411SSlava Zakharin   RT_API_ATTRS void MakeDataContiguous(
1968ebf7411SSlava Zakharin       IoErrorHandler &handler, std::size_t bytes) {
1975da55bfcSpeter klausler     if (static_cast<std::int64_t>(start_ + bytes) > size_) {
1985da55bfcSpeter klausler       // Frame would wrap around; shift current data (if any) to force
1995da55bfcSpeter klausler       // contiguity.
2005da55bfcSpeter klausler       RUNTIME_CHECK(handler, length_ < size_);
2015da55bfcSpeter klausler       if (start_ + length_ <= size_) {
2025da55bfcSpeter klausler         // [......abcde..] -> [abcde........]
2038ebf7411SSlava Zakharin         runtime::memmove(buffer_, buffer_ + start_, length_);
2045da55bfcSpeter klausler       } else {
2055da55bfcSpeter klausler         // [cde........ab] -> [abcde........]
2065da55bfcSpeter klausler         auto n{start_ + length_ - size_}; // 3 for cde
2075da55bfcSpeter klausler         RUNTIME_CHECK(handler, length_ >= n);
2088ebf7411SSlava Zakharin         runtime::memmove(buffer_ + n, buffer_ + start_, length_ - n); // cdeab
2095da55bfcSpeter klausler         LeftShiftBufferCircularly(buffer_, length_, n); // abcde
2105da55bfcSpeter klausler       }
2115da55bfcSpeter klausler       start_ = 0;
2125da55bfcSpeter klausler     }
2135da55bfcSpeter klausler   }
214f7be2518Speter klausler 
215f7be2518Speter klausler   char *buffer_{nullptr};
2163b635714Speter klausler   std::int64_t size_{0}; // current allocated buffer size
217f7be2518Speter klausler   FileOffset fileOffset_{0}; // file offset corresponding to buffer valid data
218f7be2518Speter klausler   std::int64_t start_{0}; // buffer_[] offset of valid data
219f7be2518Speter klausler   std::int64_t length_{0}; // valid data length (can wrap)
220f7be2518Speter klausler   std::int64_t frame_{0}; // offset of current frame in valid data
221f7be2518Speter klausler   bool dirty_{false};
222f7be2518Speter klausler };
2231f879005STim Keith } // namespace Fortran::runtime::io
224f7be2518Speter klausler #endif // FORTRAN_RUNTIME_BUFFER_H_
225