@@ -94,6 +94,103 @@ is_dropframe_rate(double rate)
94
94
return std::find (b, e, rate) != e;
95
95
}
96
96
97
+ static bool
98
+ parseFloat (char const * pCurr, char const * pEnd, bool allow_negative, double * result)
99
+ {
100
+ if (pCurr >= pEnd || !pCurr)
101
+ {
102
+ *result = 0.0 ;
103
+ return false ;
104
+ }
105
+
106
+ double ret = 0.0 ;
107
+ double sign = 1.0 ;
108
+
109
+ if (*pCurr == ' +' )
110
+ {
111
+ ++pCurr;
112
+ }
113
+ else if (*pCurr == ' -' )
114
+ {
115
+ if (!allow_negative)
116
+ {
117
+ *result = 0.0 ;
118
+ return false ;
119
+ }
120
+ sign = -1.0 ;
121
+ ++pCurr;
122
+ }
123
+
124
+ // get integer part
125
+ //
126
+ // Note that uint64_t is used because overflow is well defined for
127
+ // unsigned integers, but it is undefined behavior for signed integers,
128
+ // and floating point values are couched in the specification with
129
+ // the caveat that an implementation may be IEEE-754 compliant, or only
130
+ // partially compliant.
131
+ //
132
+ uint64_t uintPart = 0 ;
133
+ while (pCurr < pEnd)
134
+ {
135
+ char c = *pCurr;
136
+ if (c < ' 0' || c > ' 9' )
137
+ {
138
+ break ;
139
+ }
140
+ uint64_t accumulated = uintPart * 10 + c - ' 0' ;
141
+ if (accumulated < uintPart)
142
+ {
143
+ // if there are too many digits, resulting in an overflow, fail
144
+ *result = 0.0 ;
145
+ return false ;
146
+ }
147
+ uintPart = accumulated;
148
+ ++pCurr;
149
+ }
150
+
151
+ ret = static_cast <double >(uintPart);
152
+ if (uintPart != static_cast <uint64_t >(ret))
153
+ {
154
+ // if the double cannot be casted precisely back to uint64_t, fail
155
+ // A double has 15 digits of precision, but a uint64_t can encode more.
156
+ *result = 0.0 ;
157
+ return false ;
158
+ }
159
+
160
+ // check for end of string or delimiter
161
+ if (pCurr == pEnd || *pCurr == ' \0 ' )
162
+ {
163
+ *result = sign * ret;
164
+ return true ;
165
+ }
166
+
167
+ // if the next character is not a decimal point, the string is malformed.
168
+ if (*pCurr != ' .' )
169
+ {
170
+ *result = 0.0 ; // zero consistent with earlier error condition
171
+ return false ;
172
+ }
173
+
174
+ ++pCurr; // skip decimal
175
+
176
+ double position_scale = 0.1 ;
177
+ while (pCurr < pEnd)
178
+ {
179
+ char c = *pCurr;
180
+ if (c < ' 0' || c > ' 9' )
181
+ {
182
+ break ;
183
+ }
184
+ ret = ret + static_cast <double >(c - ' 0' ) * position_scale;
185
+ ++pCurr;
186
+ position_scale *= 0.1 ;
187
+ }
188
+
189
+ *result = sign * ret;
190
+ return true ;
191
+ }
192
+
193
+
97
194
RationalTime
98
195
RationalTime::from_timecode (
99
196
std::string const & timecode, double rate, ErrorStatus* error_status)
@@ -205,55 +302,109 @@ RationalTime::from_timecode(
205
302
return RationalTime{ double (value), rate };
206
303
}
207
304
305
+ static void
306
+ set_error (std::string const & time_string,
307
+ ErrorStatus::Outcome code,
308
+ ErrorStatus* err)
309
+ {
310
+ if (err) {
311
+ *err = ErrorStatus (
312
+ code,
313
+ string_printf (
314
+ " Error: '%s' - %s" ,
315
+ time_string.c_str (),
316
+ ErrorStatus::outcome_to_string (code).c_str ()));
317
+ }
318
+ }
319
+
208
320
RationalTime
209
321
RationalTime::from_time_string (
210
322
std::string const & time_string, double rate, ErrorStatus* error_status)
211
323
{
212
- if (!RationalTime::is_valid_timecode_rate (rate))
324
+ if (!RationalTime::is_valid_timecode_rate (rate))
213
325
{
214
- if (error_status)
215
- {
216
- *error_status = ErrorStatus (ErrorStatus::INVALID_TIMECODE_RATE);
217
- }
326
+ set_error (time_string, ErrorStatus::INVALID_TIMECODE_RATE, error_status);
218
327
return RationalTime::_invalid_time;
219
328
}
220
329
221
- std::vector<std::string> fields (3 , std::string ());
222
-
223
- // split the fields
224
- int last_pos = 0 ;
225
-
226
- for (int i = 0 ; i < 2 ; i++)
330
+ const char * start = time_string.data ();
331
+ const char * end = start + time_string.length ();
332
+ char * current = const_cast <char *>(end);
333
+ char * parse_end = current;
334
+ char * prev_parse_end = current;
335
+
336
+ double power[3 ] = {
337
+ 1.0 , // seconds
338
+ 60.0 , // minutes
339
+ 3600.0 // hours
340
+ };
341
+
342
+ double accumulator = 0.0 ;
343
+ int radix = 0 ;
344
+ while (start <= current)
227
345
{
228
- fields[i] = time_string.substr (last_pos, 2 );
229
- last_pos = last_pos + 3 ;
230
- }
231
-
232
- fields[2 ] = time_string.substr (last_pos, time_string.length ());
233
-
234
- double hours, minutes, seconds;
346
+ if (*current == ' :' )
347
+ {
348
+ parse_end = current + 1 ;
349
+ char c = *parse_end;
350
+ if (c != ' \0 ' && c != ' :' )
351
+ {
352
+ if (c< ' 0' || c > ' 9' )
353
+ {
354
+ set_error (time_string, ErrorStatus::INVALID_TIME_STRING, error_status);
355
+ return RationalTime::_invalid_time;
356
+ }
357
+ double val = 0.0 ;
358
+ if (!parseFloat (parse_end, prev_parse_end + 1 , false , &val))
359
+ {
360
+ set_error (time_string, ErrorStatus::INVALID_TIME_STRING, error_status);
361
+ return RationalTime::_invalid_time;
362
+ }
363
+ prev_parse_end = nullptr ;
364
+ if (radix < 2 && val >= 60.0 )
365
+ {
366
+ set_error (time_string, ErrorStatus::INVALID_TIME_STRING, error_status);
367
+ return RationalTime::_invalid_time;
368
+ }
369
+ accumulator += val * power[radix];
370
+ }
371
+ ++radix;
372
+ if (radix == sizeof (power) / sizeof (power[0 ]))
373
+ {
374
+ set_error (time_string, ErrorStatus::INVALID_TIME_STRING, error_status);
375
+ return RationalTime::_invalid_time;
376
+ }
377
+ }
378
+ else if (current < prev_parse_end &&
379
+ (*current < ' 0' || *current > ' 9' ) &&
380
+ *current != ' .' )
381
+ {
382
+ set_error (time_string, ErrorStatus::INVALID_TIME_STRING, error_status);
383
+ return RationalTime::_invalid_time;
384
+ }
235
385
236
- try
237
- {
238
- hours = std::stod (fields[0 ]);
239
- minutes = std::stod (fields[1 ]);
240
- seconds = std::stod (fields[2 ]);
241
- }
242
- catch (std::exception const & e)
243
- {
244
- if (error_status)
386
+ if (start == current)
245
387
{
246
- *error_status = ErrorStatus (
247
- ErrorStatus::INVALID_TIME_STRING,
248
- string_printf (
249
- " Input time string '%s' is an invalid time string" ,
250
- time_string.c_str ()));
388
+ if (prev_parse_end)
389
+ {
390
+ double val = 0.0 ;
391
+ if (!parseFloat (start, prev_parse_end + 1 , true , &val))
392
+ {
393
+ set_error (time_string, ErrorStatus::INVALID_TIME_STRING, error_status);
394
+ return RationalTime::_invalid_time;
395
+ }
396
+ accumulator += val * power[radix];
397
+ }
398
+ break ;
399
+ }
400
+ --current;
401
+ if (!prev_parse_end)
402
+ {
403
+ prev_parse_end = current;
251
404
}
252
- return RationalTime::_invalid_time;
253
405
}
254
406
255
- return from_seconds (seconds + minutes * 60 + hours * 60 * 60 )
256
- .rescaled_to (rate);
407
+ return from_seconds (accumulator).rescaled_to (rate);
257
408
}
258
409
259
410
std::string
0 commit comments