1 //===-- runtime/buffer.h ----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // External file buffering
11 #ifndef FORTRAN_RUNTIME_BUFFER_H_
12 #define FORTRAN_RUNTIME_BUFFER_H_
15 #include "flang/Runtime/memory.h"
20 namespace Fortran::runtime::io
{
22 void LeftShiftBufferCircularly(char *, std::size_t bytes
, std::size_t shift
);
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.
31 // Possible scenario with a tiny 32-byte buffer after a ReadFrame or
32 // WriteFrame with a file offset of 103 to access "DEF":
34 // fileOffset_ 100 --+ +-+ frame of interest (103:105)
35 // file: ............ABCDEFGHIJKLMNOPQRSTUVWXYZ....
36 // buffer: [NOPQRSTUVWXYZ......ABCDEFGHIJKLM] (size_ == 32)
38 // +----- start_ == 19, length_ == 26
40 // The buffer holds length_ == 26 bytes from file offsets 100:125.
41 // Those 26 bytes "wrap around" the end of the circular buffer,
42 // so file offsets 100:112 map to buffer offsets 19:31 ("A..M") and
43 // file offsets 113:125 map to buffer offsets 0:12 ("N..Z")
44 // The 3-byte frame of file offsets 103:105 is contiguous in the buffer
45 // at buffer offset (start_ + frame_) == 22 ("DEF").
47 template <typename STORE
, std::size_t minBuffer
= 65536> class FileFrame
{
49 using FileOffset
= std::int64_t;
51 ~FileFrame() { FreeMemoryAndNullify(buffer_
); }
53 // The valid data in the buffer begins at buffer_[start_] and proceeds
54 // with possible wrap-around for length_ bytes. The current frame
55 // is offset by frame_ bytes into that region and is guaranteed to
56 // be contiguous for at least as many bytes as were requested.
58 FileOffset
FrameAt() const { return fileOffset_
+ frame_
; }
59 char *Frame() const { return buffer_
+ start_
+ frame_
; }
60 std::size_t FrameLength() const {
61 return std::min
<std::size_t>(length_
- frame_
, size_
- (start_
+ frame_
));
63 std::size_t BytesBufferedBeforeFrame() const { return frame_
- start_
; }
65 // Returns a short frame at a non-fatal EOF. Can return a long frame as well.
66 std::size_t ReadFrame(
67 FileOffset at
, std::size_t bytes
, IoErrorHandler
&handler
) {
69 Reallocate(bytes
, handler
);
70 std::int64_t newFrame
{at
- fileOffset_
};
71 if (newFrame
< 0 || newFrame
> length_
) {
76 RUNTIME_CHECK(handler
, at
== fileOffset_
+ frame_
);
77 if (static_cast<std::int64_t>(start_
+ frame_
+ bytes
) > size_
) {
78 DiscardLeadingBytes(frame_
, handler
);
79 MakeDataContiguous(handler
, bytes
);
80 RUNTIME_CHECK(handler
, at
== fileOffset_
+ frame_
);
82 if (FrameLength() < bytes
) {
83 auto next
{start_
+ length_
};
84 RUNTIME_CHECK(handler
, next
< size_
);
85 auto minBytes
{bytes
- FrameLength()};
86 auto maxBytes
{size_
- next
};
87 auto got
{Store().Read(
88 fileOffset_
+ length_
, buffer_
+ next
, minBytes
, maxBytes
, handler
)};
90 RUNTIME_CHECK(handler
, length_
<= size_
);
95 void WriteFrame(FileOffset at
, std::size_t bytes
, IoErrorHandler
&handler
) {
96 Reallocate(bytes
, handler
);
97 std::int64_t newFrame
{at
- fileOffset_
};
98 if (!dirty_
|| newFrame
< 0 || newFrame
> length_
) {
101 } else if (start_
+ newFrame
+ static_cast<std::int64_t>(bytes
) > size_
) {
102 // Flush leading data before "at", retain from "at" onward
103 Flush(handler
, length_
- newFrame
);
104 MakeDataContiguous(handler
, bytes
);
108 RUNTIME_CHECK(handler
, at
== fileOffset_
+ frame_
);
110 length_
= std::max
<std::int64_t>(length_
, frame_
+ bytes
);
113 void Flush(IoErrorHandler
&handler
, std::int64_t keep
= 0) {
115 while (length_
> keep
) {
117 std::min
<std::size_t>(length_
- keep
, size_
- start_
)};
119 Store().Write(fileOffset_
, buffer_
+ start_
, chunk
, handler
)};
120 DiscardLeadingBytes(put
, handler
);
131 void TruncateFrame(std::int64_t at
, IoErrorHandler
&handler
) {
132 RUNTIME_CHECK(handler
, !dirty_
);
133 if (at
<= fileOffset_
) {
135 } else if (at
< fileOffset_
+ length_
) {
136 length_
= at
- fileOffset_
;
141 STORE
&Store() { return static_cast<STORE
&>(*this); }
143 void Reallocate(std::int64_t bytes
, const Terminator
&terminator
) {
147 size_
= std::max
<std::int64_t>(bytes
, size_
+ minBuffer
);
149 reinterpret_cast<char *>(AllocateMemoryOrCrash(terminator
, size_
));
150 auto chunk
{std::min
<std::int64_t>(length_
, oldSize
- start_
)};
151 std::memcpy(buffer_
, old
+ start_
, chunk
);
153 std::memcpy(buffer_
+ chunk
, old
, length_
- chunk
);
158 void Reset(FileOffset at
) {
159 start_
= length_
= frame_
= 0;
164 void DiscardLeadingBytes(std::int64_t n
, const Terminator
&terminator
) {
165 RUNTIME_CHECK(terminator
, length_
>= n
);
171 if (start_
>= size_
) {
183 void MakeDataContiguous(IoErrorHandler
&handler
, std::size_t bytes
) {
184 if (static_cast<std::int64_t>(start_
+ bytes
) > size_
) {
185 // Frame would wrap around; shift current data (if any) to force
187 RUNTIME_CHECK(handler
, length_
< size_
);
188 if (start_
+ length_
<= size_
) {
189 // [......abcde..] -> [abcde........]
190 std::memmove(buffer_
, buffer_
+ start_
, length_
);
192 // [cde........ab] -> [abcde........]
193 auto n
{start_
+ length_
- size_
}; // 3 for cde
194 RUNTIME_CHECK(handler
, length_
>= n
);
195 std::memmove(buffer_
+ n
, buffer_
+ start_
, length_
- n
); // cdeab
196 LeftShiftBufferCircularly(buffer_
, length_
, n
); // abcde
202 char *buffer_
{nullptr};
203 std::int64_t size_
{0}; // current allocated buffer size
204 FileOffset fileOffset_
{0}; // file offset corresponding to buffer valid data
205 std::int64_t start_
{0}; // buffer_[] offset of valid data
206 std::int64_t length_
{0}; // valid data length (can wrap)
207 std::int64_t frame_
{0}; // offset of current frame in valid data
210 } // namespace Fortran::runtime::io
211 #endif // FORTRAN_RUNTIME_BUFFER_H_