@@ -130,20 +130,95 @@ 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 IsActive () 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 void SkipRestOfRecord () { at_ = limit_; }
180+ RT_API_ATTRS bool MightHaveAsterisk () const { return !at_ || hasAsterisk_; }
181+
182+ private:
183+ RT_API_ATTRS void CheckForAsterisk () {
184+ hasAsterisk_ =
185+ at_ && at_ < limit_ && std::memchr (at_, ' *' , limit_ - at_) != nullptr ;
186+ }
187+
188+ ConnectionState &connection_;
189+ const char *at_{nullptr };
190+ const char *limit_{nullptr };
191+ std::size_t got_{0 }; // for READ(..., SIZE=)
192+ bool hasAsterisk_{false };
193+ };
194+
195+ RT_API_ATTRS FastAsciiField GetUpcomingFastAsciiField ();
196+
197+ RT_API_ATTRS Fortran::common::optional<char32_t > GetCurrentChar (
198+ std::size_t &byteCount, FastAsciiField *field = nullptr ) {
199+ if (field) {
200+ if (auto ch{field->Next ()}) {
201+ byteCount = ch ? 1 : 0 ;
202+ return ch;
203+ } else if (field->IsActive ()) {
204+ return std::nullopt ;
205+ }
206+ }
207+ return GetCurrentCharSlow (byteCount);
208+ }
209+
136210 // The result of CueUpInput() and the "remaining" arguments to SkipSpaces()
137211 // and NextInField() are always in units of bytes, not characters; the
138212 // distinction matters for internal input from CHARACTER(KIND=2 and 4).
139213
140214 // For fixed-width fields, return the number of remaining bytes.
141215 // Skip over leading blanks.
142- RT_API_ATTRS Fortran::common::optional<int > CueUpInput (const DataEdit &edit) {
216+ RT_API_ATTRS Fortran::common::optional<int > CueUpInput (
217+ const DataEdit &edit, FastAsciiField *fastField = nullptr ) {
143218 Fortran::common::optional<int > remaining;
144219 if (edit.IsListDirected ()) {
145220 std::size_t byteCount{0 };
146- GetNextNonBlank (byteCount);
221+ GetNextNonBlank (byteCount, fastField );
147222 } else {
148223 if (edit.width .value_or (0 ) > 0 ) {
149224 remaining = *edit.width ;
@@ -152,16 +227,17 @@ class IoStatementState {
152227 *remaining *= bytesPerChar;
153228 }
154229 }
155- SkipSpaces (remaining);
230+ SkipSpaces (remaining, fastField );
156231 }
157232 return remaining;
158233 }
159234
160235 RT_API_ATTRS Fortran::common::optional<char32_t > SkipSpaces (
161- Fortran::common::optional<int > &remaining) {
236+ Fortran::common::optional<int > &remaining,
237+ FastAsciiField *fastField = nullptr ) {
162238 while (!remaining || *remaining > 0 ) {
163239 std::size_t byteCount{0 };
164- if (auto ch{GetCurrentChar (byteCount)}) {
240+ if (auto ch{GetCurrentChar (byteCount, fastField )}) {
165241 if (*ch != ' ' && *ch != ' \t ' ) {
166242 return ch;
167243 }
@@ -172,7 +248,11 @@ class IoStatementState {
172248 GotChar (byteCount);
173249 *remaining -= byteCount;
174250 }
175- HandleRelativePosition (byteCount);
251+ if (fastField) {
252+ fastField->Advance (0 , byteCount);
253+ } else {
254+ HandleRelativePosition (byteCount);
255+ }
176256 } else {
177257 break ;
178258 }
@@ -183,25 +263,35 @@ class IoStatementState {
183263 // Acquires the next input character, respecting any applicable field width
184264 // or separator character.
185265 RT_API_ATTRS Fortran::common::optional<char32_t > NextInField (
186- Fortran::common::optional<int > &remaining, const DataEdit &);
266+ Fortran::common::optional<int > &remaining, const DataEdit &,
267+ FastAsciiField *field = nullptr );
187268
188269 // Detect and signal any end-of-record condition after input.
189270 // Returns true if at EOR and remaining input should be padded with blanks.
190- RT_API_ATTRS bool CheckForEndOfRecord (std::size_t afterReading);
271+ RT_API_ATTRS bool CheckForEndOfRecord (
272+ std::size_t afterReading, const ConnectionState &);
191273
192274 // Skips spaces, advances records, and ignores NAMELIST comments
193275 RT_API_ATTRS Fortran::common::optional<char32_t > GetNextNonBlank (
194- std::size_t &byteCount) {
195- auto ch{GetCurrentChar (byteCount)};
276+ std::size_t &byteCount, FastAsciiField *fastField = nullptr ) {
277+ auto ch{GetCurrentChar (byteCount, fastField )};
196278 bool inNamelist{mutableModes ().inNamelist };
197279 while (!ch || *ch == ' ' || *ch == ' \t ' || *ch == ' \n ' ||
198280 (inNamelist && *ch == ' !' )) {
199281 if (ch && (*ch == ' ' || *ch == ' \t ' || *ch == ' \n ' )) {
200- HandleRelativePosition (byteCount);
201- } else if (!AdvanceRecord ()) {
282+ if (fastField) {
283+ fastField->Advance (0 , byteCount);
284+ } else {
285+ HandleRelativePosition (byteCount);
286+ }
287+ } else if (AdvanceRecord ()) {
288+ if (fastField) {
289+ fastField->NextRecord (*this );
290+ }
291+ } else {
202292 return Fortran::common::nullopt ;
203293 }
204- ch = GetCurrentChar (byteCount);
294+ ch = GetCurrentChar (byteCount, fastField );
205295 }
206296 return ch;
207297 }
0 commit comments