@@ -130,20 +130,94 @@ class IoStatementState {
130
130
}
131
131
132
132
// 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 (
134
134
std::size_t &byteCount);
135
135
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
+
136
209
// The result of CueUpInput() and the "remaining" arguments to SkipSpaces()
137
210
// and NextInField() are always in units of bytes, not characters; the
138
211
// distinction matters for internal input from CHARACTER(KIND=2 and 4).
139
212
140
213
// For fixed-width fields, return the number of remaining bytes.
141
214
// 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 ) {
143
217
Fortran::common::optional<int > remaining;
144
218
if (edit.IsListDirected ()) {
145
219
std::size_t byteCount{0 };
146
- GetNextNonBlank (byteCount);
220
+ GetNextNonBlank (byteCount, fastField );
147
221
} else {
148
222
if (edit.width .value_or (0 ) > 0 ) {
149
223
remaining = *edit.width ;
@@ -152,16 +226,17 @@ class IoStatementState {
152
226
*remaining *= bytesPerChar;
153
227
}
154
228
}
155
- SkipSpaces (remaining);
229
+ SkipSpaces (remaining, fastField );
156
230
}
157
231
return remaining;
158
232
}
159
233
160
234
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 ) {
162
237
while (!remaining || *remaining > 0 ) {
163
238
std::size_t byteCount{0 };
164
- if (auto ch{GetCurrentChar (byteCount)}) {
239
+ if (auto ch{GetCurrentChar (byteCount, fastField )}) {
165
240
if (*ch != ' ' && *ch != ' \t ' ) {
166
241
return ch;
167
242
}
@@ -172,7 +247,11 @@ class IoStatementState {
172
247
GotChar (byteCount);
173
248
*remaining -= byteCount;
174
249
}
175
- HandleRelativePosition (byteCount);
250
+ if (fastField) {
251
+ fastField->Advance (0 , byteCount);
252
+ } else {
253
+ HandleRelativePosition (byteCount);
254
+ }
176
255
} else {
177
256
break ;
178
257
}
@@ -183,25 +262,35 @@ class IoStatementState {
183
262
// Acquires the next input character, respecting any applicable field width
184
263
// or separator character.
185
264
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 );
187
267
188
268
// Detect and signal any end-of-record condition after input.
189
269
// 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 &);
191
272
192
273
// Skips spaces, advances records, and ignores NAMELIST comments
193
274
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 )};
196
277
bool inNamelist{mutableModes ().inNamelist };
197
278
while (!ch || *ch == ' ' || *ch == ' \t ' || *ch == ' \n ' ||
198
279
(inNamelist && *ch == ' !' )) {
199
280
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 {
202
291
return Fortran::common::nullopt;
203
292
}
204
- ch = GetCurrentChar (byteCount);
293
+ ch = GetCurrentChar (byteCount, fastField );
205
294
}
206
295
return ch;
207
296
}
0 commit comments