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