|
10 | 10 |
|
11 | 11 | #include "time-intrinsic.h"
|
12 | 12 |
|
| 13 | +#include "descriptor.h" |
| 14 | +#include "terminator.h" |
| 15 | +#include "tools.h" |
| 16 | +#include <algorithm> |
| 17 | +#include <cstdint> |
| 18 | +#include <cstdio> |
| 19 | +#include <cstdlib> |
| 20 | +#include <cstring> |
13 | 21 | #include <ctime>
|
| 22 | +#ifndef _WIN32 |
| 23 | +#include <sys/time.h> // gettimeofday |
| 24 | +#endif |
14 | 25 |
|
15 | 26 | // CPU_TIME (Fortran 2018 16.9.57)
|
16 | 27 | // SYSTEM_CLOCK (Fortran 2018 16.9.168)
|
@@ -163,23 +174,204 @@ count_t GetSystemClockCountMax(preferred_implementation,
|
163 | 174 | count_t max_secs{std::numeric_limits<count_t>::max() / NSECS_PER_SEC};
|
164 | 175 | return max_secs * NSECS_PER_SEC - 1;
|
165 | 176 | }
|
| 177 | + |
| 178 | +// DATE_AND_TIME (Fortran 2018 16.9.59) |
| 179 | + |
| 180 | +// Helper to store integer value in result[at]. |
| 181 | +template <int KIND> struct StoreIntegerAt { |
| 182 | + void operator()(const Fortran::runtime::Descriptor &result, std::size_t at, |
| 183 | + std::int64_t value) const { |
| 184 | + *result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor< |
| 185 | + Fortran::common::TypeCategory::Integer, KIND>>(at) = value; |
| 186 | + } |
| 187 | +}; |
| 188 | + |
| 189 | +// Helper to set an integer value to -HUGE |
| 190 | +template <int KIND> struct StoreNegativeHugeAt { |
| 191 | + void operator()( |
| 192 | + const Fortran::runtime::Descriptor &result, std::size_t at) const { |
| 193 | + *result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor< |
| 194 | + Fortran::common::TypeCategory::Integer, KIND>>(at) = |
| 195 | + -std::numeric_limits<Fortran::runtime::CppTypeFor< |
| 196 | + Fortran::common::TypeCategory::Integer, KIND>>::max(); |
| 197 | + } |
| 198 | +}; |
| 199 | + |
| 200 | +// Default implementation when date and time information is not available (set |
| 201 | +// strings to blanks and values to -HUGE as defined by the standard). |
| 202 | +void DateAndTimeUnavailable(Fortran::runtime::Terminator &terminator, |
| 203 | + char *date, std::size_t dateChars, char *time, std::size_t timeChars, |
| 204 | + char *zone, std::size_t zoneChars, |
| 205 | + const Fortran::runtime::Descriptor *values) { |
| 206 | + if (date) { |
| 207 | + std::memset(date, static_cast<int>(' '), dateChars); |
| 208 | + } |
| 209 | + if (time) { |
| 210 | + std::memset(time, static_cast<int>(' '), timeChars); |
| 211 | + } |
| 212 | + if (zone) { |
| 213 | + std::memset(zone, static_cast<int>(' '), zoneChars); |
| 214 | + } |
| 215 | + if (values) { |
| 216 | + auto typeCode{values->type().GetCategoryAndKind()}; |
| 217 | + RUNTIME_CHECK(terminator, |
| 218 | + values->rank() == 1 && values->GetDimension(0).Extent() >= 8 && |
| 219 | + typeCode && |
| 220 | + typeCode->first == Fortran::common::TypeCategory::Integer); |
| 221 | + // DATE_AND_TIME values argument must have decimal range > 4. Do not accept |
| 222 | + // KIND 1 here. |
| 223 | + int kind{typeCode->second}; |
| 224 | + RUNTIME_CHECK(terminator, kind != 1); |
| 225 | + for (std::size_t i = 0; i < 8; ++i) { |
| 226 | + Fortran::runtime::ApplyIntegerKind<StoreNegativeHugeAt, void>( |
| 227 | + kind, terminator, *values, i); |
| 228 | + } |
| 229 | + } |
| 230 | +} |
| 231 | + |
| 232 | +#ifndef _WIN32 |
| 233 | + |
| 234 | +// SFINAE helper to return the struct tm.tm_gmtoff which is not a POSIX standard |
| 235 | +// field. |
| 236 | +template <int KIND, typename TM = struct tm> |
| 237 | +Fortran::runtime::CppTypeFor<Fortran::common::TypeCategory::Integer, KIND> |
| 238 | +GetGmtOffset(const TM &tm, preferred_implementation, |
| 239 | + decltype(tm.tm_gmtoff) *Enabled = nullptr) { |
| 240 | + // Returns the GMT offset in minutes. |
| 241 | + return tm.tm_gmtoff / 60; |
| 242 | +} |
| 243 | +template <int KIND, typename TM = struct tm> |
| 244 | +Fortran::runtime::CppTypeFor<Fortran::common::TypeCategory::Integer, KIND> |
| 245 | +GetGmtOffset(const TM &tm, fallback_implementation) { |
| 246 | + // tm.tm_gmtoff is not available, there may be platform dependent alternatives |
| 247 | + // (such as using timezone from <time.h> when available), but so far just |
| 248 | + // return -HUGE to report that this information is not available. |
| 249 | + return -std::numeric_limits<Fortran::runtime::CppTypeFor< |
| 250 | + Fortran::common::TypeCategory::Integer, KIND>>::max(); |
| 251 | +} |
| 252 | +template <typename TM = struct tm> struct GmtOffsetHelper { |
| 253 | + template <int KIND> struct StoreGmtOffset { |
| 254 | + void operator()(const Fortran::runtime::Descriptor &result, std::size_t at, |
| 255 | + TM &tm) const { |
| 256 | + *result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor< |
| 257 | + Fortran::common::TypeCategory::Integer, KIND>>(at) = |
| 258 | + GetGmtOffset<KIND>(tm, 0); |
| 259 | + } |
| 260 | + }; |
| 261 | +}; |
| 262 | + |
| 263 | +// Dispatch to posix implemetation when gettimeofday and localtime_r are |
| 264 | +// available. |
| 265 | +void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date, |
| 266 | + std::size_t dateChars, char *time, std::size_t timeChars, char *zone, |
| 267 | + std::size_t zoneChars, const Fortran::runtime::Descriptor *values) { |
| 268 | + |
| 269 | + timeval t; |
| 270 | + if (gettimeofday(&t, nullptr) != 0) { |
| 271 | + DateAndTimeUnavailable( |
| 272 | + terminator, date, dateChars, time, timeChars, zone, zoneChars, values); |
| 273 | + return; |
| 274 | + } |
| 275 | + time_t timer{t.tv_sec}; |
| 276 | + tm localTime; |
| 277 | + localtime_r(&timer, &localTime); |
| 278 | + std::intmax_t ms{t.tv_usec / 1000}; |
| 279 | + |
| 280 | + static constexpr std::size_t buffSize{16}; |
| 281 | + char buffer[buffSize]; |
| 282 | + auto copyBufferAndPad{ |
| 283 | + [&](char *dest, std::size_t destChars, std::size_t len) { |
| 284 | + auto copyLen{std::min(len, destChars)}; |
| 285 | + std::memcpy(dest, buffer, copyLen); |
| 286 | + for (auto i{copyLen}; i < destChars; ++i) { |
| 287 | + dest[i] = ' '; |
| 288 | + } |
| 289 | + }}; |
| 290 | + if (date) { |
| 291 | + auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime); |
| 292 | + copyBufferAndPad(date, dateChars, len); |
| 293 | + } |
| 294 | + if (time) { |
| 295 | + auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd", |
| 296 | + localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)}; |
| 297 | + copyBufferAndPad(time, timeChars, len); |
| 298 | + } |
| 299 | + if (zone) { |
| 300 | + // Note: this may leave the buffer empty on many platforms. Classic flang |
| 301 | + // has a much more complex way of doing this (see __io_timezone in classic |
| 302 | + // flang). |
| 303 | + auto len{std::strftime(buffer, buffSize, "%z", &localTime)}; |
| 304 | + copyBufferAndPad(zone, zoneChars, len); |
| 305 | + } |
| 306 | + if (values) { |
| 307 | + auto typeCode{values->type().GetCategoryAndKind()}; |
| 308 | + RUNTIME_CHECK(terminator, |
| 309 | + values->rank() == 1 && values->GetDimension(0).Extent() >= 8 && |
| 310 | + typeCode && |
| 311 | + typeCode->first == Fortran::common::TypeCategory::Integer); |
| 312 | + // DATE_AND_TIME values argument must have decimal range > 4. Do not accept |
| 313 | + // KIND 1 here. |
| 314 | + int kind{typeCode->second}; |
| 315 | + RUNTIME_CHECK(terminator, kind != 1); |
| 316 | + auto storeIntegerAt = [&](std::size_t atIndex, std::int64_t value) { |
| 317 | + Fortran::runtime::ApplyIntegerKind<StoreIntegerAt, void>( |
| 318 | + kind, terminator, *values, atIndex, value); |
| 319 | + }; |
| 320 | + storeIntegerAt(0, localTime.tm_year + 1900); |
| 321 | + storeIntegerAt(1, localTime.tm_mon + 1); |
| 322 | + storeIntegerAt(2, localTime.tm_mday); |
| 323 | + Fortran::runtime::ApplyIntegerKind< |
| 324 | + GmtOffsetHelper<struct tm>::StoreGmtOffset, void>( |
| 325 | + kind, terminator, *values, 3, localTime); |
| 326 | + storeIntegerAt(4, localTime.tm_hour); |
| 327 | + storeIntegerAt(5, localTime.tm_min); |
| 328 | + storeIntegerAt(6, localTime.tm_sec); |
| 329 | + storeIntegerAt(7, ms); |
| 330 | + } |
| 331 | +} |
| 332 | + |
| 333 | +#else |
| 334 | +// Fallback implementation when gettimeofday or localtime_r is not available |
| 335 | +// (e.g. windows). |
| 336 | +void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date, |
| 337 | + std::size_t dateChars, char *time, std::size_t timeChars, char *zone, |
| 338 | + std::size_t zoneChars, const Fortran::runtime::Descriptor *values) { |
| 339 | + // TODO: An actual implementation for non Posix system should be added. |
| 340 | + // So far, implement as if the date and time is not available on those |
| 341 | + // platforms. |
| 342 | + DateAndTimeUnavailable( |
| 343 | + terminator, date, dateChars, time, timeChars, zone, zoneChars, values); |
| 344 | +} |
| 345 | +#endif |
166 | 346 | } // anonymous namespace
|
167 | 347 |
|
168 | 348 | namespace Fortran::runtime {
|
169 | 349 | extern "C" {
|
170 | 350 |
|
171 | 351 | double RTNAME(CpuTime)() { return GetCpuTime(0); }
|
172 | 352 |
|
173 |
| -CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCount)() { |
| 353 | +CppTypeFor<Fortran::common::TypeCategory::Integer, 8> RTNAME( |
| 354 | + SystemClockCount)() { |
174 | 355 | return GetSystemClockCount(0);
|
175 | 356 | }
|
176 | 357 |
|
177 |
| -CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountRate)() { |
| 358 | +CppTypeFor<Fortran::common::TypeCategory::Integer, 8> RTNAME( |
| 359 | + SystemClockCountRate)() { |
178 | 360 | return GetSystemClockCountRate(0);
|
179 | 361 | }
|
180 | 362 |
|
181 |
| -CppTypeFor<TypeCategory::Integer, 8> RTNAME(SystemClockCountMax)() { |
| 363 | +CppTypeFor<Fortran::common::TypeCategory::Integer, 8> RTNAME( |
| 364 | + SystemClockCountMax)() { |
182 | 365 | return GetSystemClockCountMax(0);
|
183 | 366 | }
|
| 367 | + |
| 368 | +void RTNAME(DateAndTime)(char *date, std::size_t dateChars, char *time, |
| 369 | + std::size_t timeChars, char *zone, std::size_t zoneChars, |
| 370 | + const char *source, int line, const Descriptor *values) { |
| 371 | + Fortran::runtime::Terminator terminator{source, line}; |
| 372 | + return GetDateAndTime( |
| 373 | + terminator, date, dateChars, time, timeChars, zone, zoneChars, values); |
| 374 | +} |
| 375 | + |
184 | 376 | } // extern "C"
|
185 | 377 | } // namespace Fortran::runtime
|
0 commit comments