@@ -130,20 +130,94 @@ class IoStatementState {
130130 }
131131
132132 // Vacant after the end of the current record
133- RT_API_ATTRS Fortran::common::optional<char32_t > GetCurrentChar (
133+ RT_API_ATTRS Fortran::common::optional<char32_t > GetCurrentCharSlow (
134134 std::size_t &byteCount);
135135
136+ // For faster formatted input editing, this structure can be built by
137+ // GetUpcomingFastAsciiField() and used to save significant time in
138+ // GetCurrentChar, NextInField() and other input utilities when the input
139+ // is buffered, does not require UTF-8 conversion, and comprises only
140+ // single byte characters.
141+ class FastAsciiField {
142+ public:
143+ RT_API_ATTRS FastAsciiField (ConnectionState &connection)
144+ : connection_{connection} {}
145+ RT_API_ATTRS FastAsciiField (
146+ ConnectionState &connection, const char *start, std::size_t bytes)
147+ : connection_{connection}, at_{start}, limit_{start + bytes} {
148+ CheckForAsterisk ();
149+ }
150+ RT_API_ATTRS ConnectionState &connection () { return connection_; }
151+ RT_API_ATTRS std::size_t got () const { return got_; }
152+
153+ RT_API_ATTRS bool MustUseSlowPath () const { return at_ == nullptr ; }
154+
155+ RT_API_ATTRS Fortran::common::optional<char32_t > Next () const {
156+ if (at_ && at_ < limit_) {
157+ return *at_;
158+ } else {
159+ return std::nullopt ;
160+ }
161+ }
162+ RT_API_ATTRS void NextRecord (IoStatementState &io) {
163+ if (at_) {
164+ if (std::size_t bytes{io.GetNextInputBytes (at_)}) {
165+ limit_ = at_ + bytes;
166+ CheckForAsterisk ();
167+ } else {
168+ at_ = limit_ = nullptr ;
169+ }
170+ }
171+ }
172+ RT_API_ATTRS void Advance (int gotten, std::size_t bytes) {
173+ if (at_ && at_ < limit_) {
174+ ++at_;
175+ got_ += gotten;
176+ }
177+ connection_.HandleRelativePosition (bytes);
178+ }
179+ RT_API_ATTRS bool MightHaveAsterisk () const { return !at_ || hasAsterisk_; }
180+
181+ private:
182+ RT_API_ATTRS void CheckForAsterisk () {
183+ hasAsterisk_ =
184+ at_ && at_ < limit_ && std::memchr (at_, ' *' , limit_ - at_) != nullptr ;
185+ }
186+
187+ ConnectionState &connection_;
188+ const char *at_{nullptr };
189+ const char *limit_{nullptr };
190+ std::size_t got_{0 }; // for READ(..., SIZE=)
191+ bool hasAsterisk_{false };
192+ };
193+
194+ RT_API_ATTRS FastAsciiField GetUpcomingFastAsciiField ();
195+
196+ RT_API_ATTRS Fortran::common::optional<char32_t > GetCurrentChar (
197+ std::size_t &byteCount, FastAsciiField *field = nullptr ) {
198+ if (field) {
199+ if (auto ch{field->Next ()}) {
200+ byteCount = ch ? 1 : 0 ;
201+ return ch;
202+ } else if (!field->MustUseSlowPath ()) {
203+ return std::nullopt ;
204+ }
205+ }
206+ return GetCurrentCharSlow (byteCount);
207+ }
208+
136209 // The result of CueUpInput() and the "remaining" arguments to SkipSpaces()
137210 // and NextInField() are always in units of bytes, not characters; the
138211 // distinction matters for internal input from CHARACTER(KIND=2 and 4).
139212
140213 // For fixed-width fields, return the number of remaining bytes.
141214 // Skip over leading blanks.
142- RT_API_ATTRS Fortran::common::optional<int > CueUpInput (const DataEdit &edit) {
215+ RT_API_ATTRS Fortran::common::optional<int > CueUpInput (
216+ const DataEdit &edit, FastAsciiField *fastField = nullptr ) {
143217 Fortran::common::optional<int > remaining;
144218 if (edit.IsListDirected ()) {
145219 std::size_t byteCount{0 };
146- GetNextNonBlank (byteCount);
220+ GetNextNonBlank (byteCount, fastField );
147221 } else {
148222 if (edit.width .value_or (0 ) > 0 ) {
149223 remaining = *edit.width ;
@@ -152,16 +226,17 @@ class IoStatementState {
152226 *remaining *= bytesPerChar;
153227 }
154228 }
155- SkipSpaces (remaining);
229+ SkipSpaces (remaining, fastField );
156230 }
157231 return remaining;
158232 }
159233
160234 RT_API_ATTRS Fortran::common::optional<char32_t > SkipSpaces (
161- Fortran::common::optional<int > &remaining) {
235+ Fortran::common::optional<int > &remaining,
236+ FastAsciiField *fastField = nullptr ) {
162237 while (!remaining || *remaining > 0 ) {
163238 std::size_t byteCount{0 };
164- if (auto ch{GetCurrentChar (byteCount)}) {
239+ if (auto ch{GetCurrentChar (byteCount, fastField )}) {
165240 if (*ch != ' ' && *ch != ' \t ' ) {
166241 return ch;
167242 }
@@ -172,7 +247,11 @@ class IoStatementState {
172247 GotChar (byteCount);
173248 *remaining -= byteCount;
174249 }
175- HandleRelativePosition (byteCount);
250+ if (fastField) {
251+ fastField->Advance (0 , byteCount);
252+ } else {
253+ HandleRelativePosition (byteCount);
254+ }
176255 } else {
177256 break ;
178257 }
@@ -183,25 +262,35 @@ class IoStatementState {
183262 // Acquires the next input character, respecting any applicable field width
184263 // or separator character.
185264 RT_API_ATTRS Fortran::common::optional<char32_t > NextInField (
186- Fortran::common::optional<int > &remaining, const DataEdit &);
265+ Fortran::common::optional<int > &remaining, const DataEdit &,
266+ FastAsciiField *field = nullptr );
187267
188268 // Detect and signal any end-of-record condition after input.
189269 // Returns true if at EOR and remaining input should be padded with blanks.
190- RT_API_ATTRS bool CheckForEndOfRecord (std::size_t afterReading);
270+ RT_API_ATTRS bool CheckForEndOfRecord (
271+ std::size_t afterReading, const ConnectionState &);
191272
192273 // Skips spaces, advances records, and ignores NAMELIST comments
193274 RT_API_ATTRS Fortran::common::optional<char32_t > GetNextNonBlank (
194- std::size_t &byteCount) {
195- auto ch{GetCurrentChar (byteCount)};
275+ std::size_t &byteCount, FastAsciiField *fastField = nullptr ) {
276+ auto ch{GetCurrentChar (byteCount, fastField )};
196277 bool inNamelist{mutableModes ().inNamelist };
197278 while (!ch || *ch == ' ' || *ch == ' \t ' || *ch == ' \n ' ||
198279 (inNamelist && *ch == ' !' )) {
199280 if (ch && (*ch == ' ' || *ch == ' \t ' || *ch == ' \n ' )) {
200- HandleRelativePosition (byteCount);
201- } else if (!AdvanceRecord ()) {
281+ if (fastField) {
282+ fastField->Advance (0 , byteCount);
283+ } else {
284+ HandleRelativePosition (byteCount);
285+ }
286+ } else if (AdvanceRecord ()) {
287+ if (fastField) {
288+ fastField->NextRecord (*this );
289+ }
290+ } else {
202291 return Fortran::common::nullopt ;
203292 }
204- ch = GetCurrentChar (byteCount);
293+ ch = GetCurrentChar (byteCount, fastField );
205294 }
206295 return ch;
207296 }
0 commit comments