Changes in uspace/lib/posix/time.c [9b1503e:3f466c33] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/posix/time.c
r9b1503e r3f466c33 36 36 #define LIBPOSIX_INTERNAL 37 37 38 /* Must be first. */ 39 #include "stdbool.h" 40 38 41 #include "internal/common.h" 39 42 #include "time.h" 40 43 44 #include "ctype.h" 45 #include "errno.h" 46 #include "signal.h" 47 48 #include "libc/malloc.h" 49 #include "libc/task.h" 50 #include "libc/stats.h" 51 #include "libc/sys/time.h" 52 53 // TODO: documentation 54 // TODO: test everything in this file 55 56 /* Helper functions ***********************************************************/ 57 58 #define HOURS_PER_DAY (24) 59 #define MINS_PER_HOUR (60) 60 #define SECS_PER_MIN (60) 61 #define MINS_PER_DAY (MINS_PER_HOUR * HOURS_PER_DAY) 62 #define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR) 63 #define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY) 64 65 static bool _is_leap_year(time_t year) 66 { 67 year += 1900; 68 69 if (year % 400 == 0) 70 return true; 71 if (year % 100 == 0) 72 return false; 73 if (year % 4 == 0) 74 return true; 75 return false; 76 } 77 78 static int _days_in_month(time_t year, time_t mon) 79 { 80 assert(mon >= 0 && mon <= 11); 81 year += 1900; 82 83 static int month_days[] = 84 { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 85 86 if (mon == 1) { 87 /* february */ 88 return _is_leap_year(year) ? 29 : 28; 89 } else { 90 return month_days[mon]; 91 } 92 } 93 94 static int _day_of_year(time_t year, time_t mon, time_t mday) 95 { 96 static int mdays[] = 97 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 98 static int leap_mdays[] = 99 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; 100 101 return (_is_leap_year(year) ? leap_mdays[mon] : mdays[mon]) + mday - 1; 102 } 103 104 /* Integer division that rounds to negative infinity. 105 */ 106 static time_t _floor_div(time_t op1, time_t op2) 107 { 108 if (op1 >= 0 || op1 % op2 == 0) { 109 return op1 / op2; 110 } else { 111 return op1 / op2 - 1; 112 } 113 } 114 115 /* Modulo that rounds to negative infinity. 116 */ 117 static time_t _floor_mod(time_t op1, time_t op2) 118 { 119 int div = _floor_div(op1, op2); 120 121 /* (a / b) * b + a % b == a */ 122 /* thus, a % b == a - (a / b) * b */ 123 124 int result = op1 - div * op2; 125 126 /* Some paranoid checking to ensure I didn't make a mistake here. */ 127 assert(result >= 0); 128 assert(result < op2); 129 assert(div * op2 + result == op1); 130 131 return result; 132 } 133 134 static time_t _days_since_epoch(time_t year, time_t mon, time_t mday) 135 { 136 return (year - 70) * 365 + _floor_div(year - 69, 4) - 137 _floor_div(year - 1, 100) + _floor_div(year + 299, 400) + 138 _day_of_year(year, mon, mday); 139 } 140 141 /* Assumes normalized broken-down time. */ 142 static time_t _secs_since_epoch(const struct posix_tm *tm) 143 { 144 return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) * 145 SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR + 146 tm->tm_min * SECS_PER_MIN + tm->tm_sec; 147 } 148 149 static int _day_of_week(time_t year, time_t mon, time_t mday) 150 { 151 /* 1970-01-01 is Thursday */ 152 return (_days_since_epoch(year, mon, mday) + 4) % 7; 153 } 154 155 struct _long_tm { 156 time_t tm_sec; 157 time_t tm_min; 158 time_t tm_hour; 159 time_t tm_mday; 160 time_t tm_mon; 161 time_t tm_year; 162 int tm_wday; 163 int tm_yday; 164 int tm_isdst; 165 }; 166 167 static void _posix_to_long_tm(struct _long_tm *ltm, struct posix_tm *ptm) 168 { 169 assert(ltm != NULL && ptm != NULL); 170 ltm->tm_sec = ptm->tm_sec; 171 ltm->tm_min = ptm->tm_min; 172 ltm->tm_hour = ptm->tm_hour; 173 ltm->tm_mday = ptm->tm_mday; 174 ltm->tm_mon = ptm->tm_mon; 175 ltm->tm_year = ptm->tm_year; 176 ltm->tm_wday = ptm->tm_wday; 177 ltm->tm_yday = ptm->tm_yday; 178 ltm->tm_isdst = ptm->tm_isdst; 179 } 180 181 static void _long_to_posix_tm(struct posix_tm *ptm, struct _long_tm *ltm) 182 { 183 assert(ltm != NULL && ptm != NULL); 184 // FIXME: the cast should be unnecessary, libarch/common.h brain-damage 185 assert((ltm->tm_year >= (int) INT_MIN) && (ltm->tm_year <= (int) INT_MAX)); 186 187 ptm->tm_sec = ltm->tm_sec; 188 ptm->tm_min = ltm->tm_min; 189 ptm->tm_hour = ltm->tm_hour; 190 ptm->tm_mday = ltm->tm_mday; 191 ptm->tm_mon = ltm->tm_mon; 192 ptm->tm_year = ltm->tm_year; 193 ptm->tm_wday = ltm->tm_wday; 194 ptm->tm_yday = ltm->tm_yday; 195 ptm->tm_isdst = ltm->tm_isdst; 196 } 197 198 static void _normalize_time(struct _long_tm *tm) 199 { 200 // TODO: DST correction 201 202 /* Adjust time. */ 203 tm->tm_min += _floor_div(tm->tm_sec, SECS_PER_MIN); 204 tm->tm_sec = _floor_mod(tm->tm_sec, SECS_PER_MIN); 205 tm->tm_hour += _floor_div(tm->tm_min, MINS_PER_HOUR); 206 tm->tm_min = _floor_mod(tm->tm_min, MINS_PER_HOUR); 207 tm->tm_mday += _floor_div(tm->tm_hour, HOURS_PER_DAY); 208 tm->tm_hour = _floor_mod(tm->tm_hour, HOURS_PER_DAY); 209 210 /* Adjust month. */ 211 tm->tm_year += _floor_div(tm->tm_mon, 12); 212 tm->tm_mon = _floor_mod(tm->tm_mon, 12); 213 214 /* Now the difficult part - days of month. */ 215 /* Slow, but simple. */ 216 // FIXME: do this faster 217 218 while (tm->tm_mday < 1) { 219 tm->tm_mon--; 220 if (tm->tm_mon == -1) { 221 tm->tm_mon = 11; 222 tm->tm_year--; 223 } 224 225 tm->tm_mday += _days_in_month(tm->tm_year, tm->tm_mon); 226 } 227 228 while (tm->tm_mday > _days_in_month(tm->tm_year, tm->tm_mon)) { 229 tm->tm_mday -= _days_in_month(tm->tm_year, tm->tm_mon); 230 231 tm->tm_mon++; 232 if (tm->tm_mon == 12) { 233 tm->tm_mon = 0; 234 tm->tm_year++; 235 } 236 } 237 238 /* Calculate the remaining two fields. */ 239 tm->tm_yday = _day_of_year(tm->tm_year, tm->tm_mon, tm->tm_mday); 240 tm->tm_wday = _day_of_week(tm->tm_year, tm->tm_mon, tm->tm_mday); 241 } 242 243 /* Which day the week-based year starts on relative to the first calendar day. 244 * E.g. if the year starts on December 31st, the return value is -1. 245 */ 246 static int _wbyear_offset(int year) 247 { 248 int start_wday = _day_of_week(year, 0, 1); 249 return _floor_mod(4 - start_wday, 7) - 3; 250 } 251 252 /* Returns week-based year of the specified time. 253 * Assumes normalized broken-down time. 254 */ 255 static int _wbyear(const struct posix_tm *tm) 256 { 257 int day = tm->tm_yday - _wbyear_offset(tm->tm_year); 258 if (day < 0) { 259 /* Last week of previous year. */ 260 return tm->tm_year - 1; 261 } 262 if (day > 364 + _is_leap_year(tm->tm_year)){ 263 /* First week of next year. */ 264 return tm->tm_year + 1; 265 } 266 /* All the other days are in the calendar year. */ 267 return tm->tm_year; 268 } 269 270 /** Week number of the year, assuming weeks start on sunday. 271 * The first Sunday of January is the first day of week 1; 272 * days in the new year before this are in week 0. 273 * 274 * @param tm Normalized broken-down time. 275 * @return The week number (0 - 53). 276 */ 277 static int _sun_week_number(const struct posix_tm *tm) 278 { 279 int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7; 280 return (tm->tm_yday - first_day + 7) / 7; 281 } 282 283 /** Week number of the year, assuming weeks start on monday. 284 * If the week containing January 1st has four or more days in the new year, 285 * then it is considered week 1. Otherwise, it is the last week of the previous 286 * year, and the next week is week 1. Both January 4th and the first Thursday 287 * of January are always in week 1. 288 * 289 * @param tm Normalized broken-down time. 290 * @return The week number (1 - 53). 291 */ 292 static int _iso_week_number(const struct posix_tm *tm) 293 { 294 int day = tm->tm_yday - _wbyear_offset(tm->tm_year); 295 if (day < 0) { 296 /* Last week of previous year. */ 297 return 53; 298 } 299 if (day > 364 + _is_leap_year(tm->tm_year)){ 300 /* First week of next year. */ 301 return 1; 302 } 303 /* All the other days give correct answer. */ 304 return (day / 7 + 1); 305 } 306 307 /** Week number of the year, assuming weeks start on monday. 308 * The first Monday of January is the first day of week 1; 309 * days in the new year before this are in week 0. 310 * 311 * @param tm Normalized broken-down time. 312 * @return The week number (0 - 53). 313 */ 314 static int _mon_week_number(const struct posix_tm *tm) 315 { 316 int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7; 317 return (tm->tm_yday - first_day + 7) / 7; 318 } 319 320 /******************************************************************************/ 321 322 int posix_daylight; 323 long posix_timezone; 324 char *posix_tzname[2]; 325 326 void posix_tzset(void) 327 { 328 // TODO: read environment 329 posix_tzname[0] = (char *) "GMT"; 330 posix_tzname[1] = (char *) "GMT"; 331 posix_daylight = 0; 332 posix_timezone = 0; 333 } 334 335 double posix_difftime(time_t time1, time_t time0) 336 { 337 return (double) (time1 - time0); 338 } 339 340 /** This function first normalizes the provided broken-down time 341 * (moves all values to their proper bounds) and then tries to 342 * calculate the appropriate time_t representation. 343 * 344 * @param timeptr Broken-down time. 345 * @return time_t representation of the time, undefined value on overflow 346 */ 347 time_t posix_mktime(struct posix_tm *tm) 348 { 349 // TODO: take DST flag into account 350 // TODO: detect overflow 351 352 struct _long_tm ltm; 353 _posix_to_long_tm(<m, tm); 354 _normalize_time(<m); 355 _long_to_posix_tm(tm, <m); 356 357 return _secs_since_epoch(tm); 358 } 359 360 struct posix_tm *posix_gmtime(const time_t *timer) 361 { 362 static struct posix_tm result; 363 return posix_gmtime_r(timer, &result); 364 } 365 366 struct posix_tm *posix_gmtime_r(const time_t *restrict timer, 367 struct posix_tm *restrict result) 368 { 369 assert(timer != NULL); 370 assert(result != NULL); 371 372 /* Set epoch and seconds to _long_tm struct and normalize to get 373 * correct values. 374 */ 375 struct _long_tm ltm = { 376 .tm_sec = *timer, 377 .tm_min = 0, 378 .tm_hour = 0, /* 00:00:xx */ 379 .tm_mday = 1, 380 .tm_mon = 0, /* January 1st */ 381 .tm_year = 70, /* 1970 */ 382 }; 383 _normalize_time(<m); 384 385 if (ltm.tm_year < (int) INT_MIN || ltm.tm_year > (int) INT_MAX) { 386 errno = EOVERFLOW; 387 return NULL; 388 } 389 390 _long_to_posix_tm(result, <m); 391 return result; 392 } 393 41 394 /** 42 395 * … … 44 397 * @return 45 398 */ 46 struct posix_tm *posix_localtime(const time_t *timep) 47 { 48 // TODO 49 static struct posix_tm result = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 50 return &result; 399 struct posix_tm *posix_localtime(const time_t *timer) 400 { 401 static struct posix_tm result; 402 return posix_localtime_r(timer, &result); 403 } 404 405 struct posix_tm *posix_localtime_r(const time_t *restrict timer, 406 struct posix_tm *restrict result) 407 { 408 // TODO: deal with timezone 409 // currently assumes system and all times are in GMT 410 return posix_gmtime_r(timer, result); 51 411 } 52 412 … … 56 416 * @return 57 417 */ 58 char *posix_asctime(const struct posix_tm *tm) 59 { 60 // TODO 61 static char result[] = "Sun Jan 01 00:00:00 1900\n"; 62 return result; 418 char *posix_asctime(const struct posix_tm *timeptr) 419 { 420 static char buf[ASCTIME_BUF_LEN]; 421 return posix_asctime_r(timeptr, buf); 422 } 423 424 char *posix_asctime_r(const struct posix_tm *restrict timeptr, 425 char *restrict buf) 426 { 427 assert(timeptr != NULL); 428 assert(buf != NULL); 429 430 static const char *wday[] = { 431 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 432 }; 433 static const char *mon[] = { 434 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 435 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 436 }; 437 438 snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n", 439 wday[timeptr->tm_wday], 440 mon[timeptr->tm_mon], 441 timeptr->tm_mday, timeptr->tm_hour, 442 timeptr->tm_min, timeptr->tm_sec, 443 1900 + timeptr->tm_year); 444 445 return buf; 63 446 } 64 447 … … 68 451 * @return 69 452 */ 70 char *posix_ctime(const time_t *timep) 71 { 72 return posix_asctime(posix_localtime(timep)); 453 char *posix_ctime(const time_t *timer) 454 { 455 struct posix_tm *loctime = posix_localtime(timer); 456 if (loctime == NULL) { 457 return NULL; 458 } 459 return posix_asctime(loctime); 460 } 461 462 char *posix_ctime_r(const time_t *timer, char *buf) 463 { 464 struct posix_tm loctime; 465 if (posix_localtime_r(timer, &loctime) == NULL) { 466 return NULL; 467 } 468 return posix_asctime_r(&loctime, buf); 73 469 } 74 470 … … 81 477 * @return 82 478 */ 83 size_t posix_strftime(char *s, size_t maxsize, const char *format, const struct posix_tm *tm) 479 size_t posix_strftime(char *s, size_t maxsize, 480 const char *format, const struct posix_tm *tm) 481 { 482 // TODO: use locale 483 static const char *wday_abbr[] = { 484 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 485 }; 486 static const char *wday[] = { 487 "Sunday", "Monday", "Tuesday", "Wednesday", 488 "Thursday", "Friday", "Saturday" 489 }; 490 static const char *mon_abbr[] = { 491 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 492 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 493 }; 494 static const char *mon[] = { 495 "January", "February", "March", "April", "May", "June", "July", 496 "August", "September", "October", "November", "December" 497 }; 498 499 if (maxsize < 1) { 500 return 0; 501 } 502 503 char *ptr = s; 504 size_t consumed; 505 size_t remaining = maxsize; 506 507 #define append(...) { \ 508 /* FIXME: this requires POSIX-correct snprintf */ \ 509 /* otherwise it won't work with non-ascii chars */ \ 510 consumed = snprintf(ptr, remaining, __VA_ARGS__); \ 511 if (consumed >= remaining) { \ 512 return 0; \ 513 } \ 514 ptr += consumed; \ 515 remaining -= consumed; \ 516 } 517 518 #define recurse(fmt) { \ 519 consumed = posix_strftime(ptr, remaining, fmt, tm); \ 520 if (consumed == 0) { \ 521 return 0; \ 522 } \ 523 ptr += consumed; \ 524 remaining -= consumed; \ 525 } 526 527 #define TO_12H(hour) (((hour) > 12) ? ((hour) - 12) : \ 528 (((hour) == 0) ? 12 : (hour))) 529 530 while (*format != '\0') { 531 if (*format != '%') { 532 append("%c", *format); 533 format++; 534 continue; 535 } 536 537 format++; 538 if (*format == '0' || *format == '+') { 539 // TODO: padding 540 format++; 541 } 542 while (isdigit(*format)) { 543 // TODO: padding 544 format++; 545 } 546 if (*format == 'O' || *format == 'E') { 547 // TODO: locale's alternative format 548 format++; 549 } 550 551 switch (*format) { 552 case 'a': 553 append("%s", wday_abbr[tm->tm_wday]); break; 554 case 'A': 555 append("%s", wday[tm->tm_wday]); break; 556 case 'b': 557 append("%s", mon_abbr[tm->tm_mon]); break; 558 case 'B': 559 append("%s", mon[tm->tm_mon]); break; 560 case 'c': 561 // TODO: locale-specific datetime format 562 recurse("%Y-%m-%d %H:%M:%S"); break; 563 case 'C': 564 append("%02d", (1900 + tm->tm_year) / 100); break; 565 case 'd': 566 append("%02d", tm->tm_mday); break; 567 case 'D': 568 recurse("%m/%d/%y"); break; 569 case 'e': 570 append("%2d", tm->tm_mday); break; 571 case 'F': 572 recurse("%+4Y-%m-%d"); break; 573 case 'g': 574 append("%02d", _wbyear(tm) % 100); break; 575 case 'G': 576 append("%d", _wbyear(tm)); break; 577 case 'h': 578 recurse("%b"); break; 579 case 'H': 580 append("%02d", tm->tm_hour); break; 581 case 'I': 582 append("%02d", TO_12H(tm->tm_hour)); break; 583 case 'j': 584 append("%03d", tm->tm_yday); break; 585 case 'k': 586 append("%2d", tm->tm_hour); break; 587 case 'l': 588 append("%2d", TO_12H(tm->tm_hour)); break; 589 case 'm': 590 append("%02d", tm->tm_mon); break; 591 case 'M': 592 append("%02d", tm->tm_min); break; 593 case 'n': 594 append("\n"); break; 595 case 'p': 596 append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break; 597 case 'P': 598 append("%s", tm->tm_hour < 12 ? "am" : "PM"); break; 599 case 'r': 600 recurse("%I:%M:%S %p"); break; 601 case 'R': 602 recurse("%H:%M"); break; 603 case 's': 604 append("%ld", _secs_since_epoch(tm)); break; 605 case 'S': 606 append("%02d", tm->tm_sec); break; 607 case 't': 608 append("\t"); break; 609 case 'T': 610 recurse("%H:%M:%S"); break; 611 case 'u': 612 append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); break; 613 case 'U': 614 append("%02d", _sun_week_number(tm)); break; 615 case 'V': 616 append("%02d", _iso_week_number(tm)); break; 617 case 'w': 618 append("%d", tm->tm_wday); break; 619 case 'W': 620 append("%02d", _mon_week_number(tm)); break; 621 case 'x': 622 // TODO: locale-specific date format 623 recurse("%Y-%m-%d"); break; 624 case 'X': 625 // TODO: locale-specific time format 626 recurse("%H:%M:%S"); break; 627 case 'y': 628 append("%02d", tm->tm_year % 100); break; 629 case 'Y': 630 append("%d", 1900 + tm->tm_year); break; 631 case 'z': 632 // TODO: timezone 633 break; 634 case 'Z': 635 // TODO: timezone 636 break; 637 case '%': 638 append("%%"); 639 break; 640 default: 641 /* Invalid specifier, print verbatim. */ 642 while (*format != '%') { 643 format--; 644 } 645 append("%%"); 646 break; 647 } 648 format++; 649 } 650 651 #undef append 652 #undef recurse 653 654 return maxsize - remaining; 655 } 656 657 int posix_clock_getres(posix_clockid_t clock_id, struct posix_timespec *res) 658 { 659 assert(res != NULL); 660 661 switch (clock_id) { 662 case CLOCK_REALTIME: 663 res->tv_sec = 0; 664 res->tv_nsec = 1000; /* Microsecond resolution. */ 665 return 0; 666 default: 667 errno = EINVAL; 668 return -1; 669 } 670 } 671 672 int posix_clock_gettime(posix_clockid_t clock_id, struct posix_timespec *tp) 673 { 674 assert(tp != NULL); 675 676 switch (clock_id) { 677 case CLOCK_REALTIME: 678 ; 679 struct timeval tv; 680 gettimeofday(&tv, NULL); 681 tp->tv_sec = tv.tv_sec; 682 tp->tv_nsec = tv.tv_usec * 1000; 683 return 0; 684 default: 685 errno = EINVAL; 686 return -1; 687 } 688 } 689 690 int posix_clock_settime(posix_clockid_t clock_id, 691 const struct posix_timespec *tp) 692 { 693 assert(tp != NULL); 694 695 switch (clock_id) { 696 case CLOCK_REALTIME: 697 // TODO: setting clock 698 // FIXME: HelenOS doesn't actually support hardware 699 // clock yet 700 errno = EPERM; 701 return -1; 702 default: 703 errno = EINVAL; 704 return -1; 705 } 706 } 707 708 int posix_clock_nanosleep(posix_clockid_t clock_id, int flags, 709 const struct posix_timespec *rqtp, struct posix_timespec *rmtp) 710 { 711 assert(rqtp != NULL); 712 assert(rmtp != NULL); 713 714 switch (clock_id) { 715 case CLOCK_REALTIME: 716 // TODO: interruptible sleep 717 if (rqtp->tv_sec != 0) { 718 sleep(rqtp->tv_sec); 719 } 720 if (rqtp->tv_nsec != 0) { 721 usleep(rqtp->tv_nsec / 1000); 722 } 723 return 0; 724 default: 725 errno = EINVAL; 726 return -1; 727 } 728 } 729 730 #if 0 731 732 struct __posix_timer { 733 posix_clockid_t clockid; 734 struct posix_sigevent evp; 735 }; 736 737 int posix_timer_create(posix_clockid_t clockid, 738 struct posix_sigevent *restrict evp, 739 posix_timer_t *restrict timerid) 84 740 { 85 741 // TODO … … 87 743 } 88 744 745 int posix_timer_delete(posix_timer_t timerid) 746 { 747 // TODO 748 not_implemented(); 749 } 750 751 int posix_timer_getoverrun(posix_timer_t timerid) 752 { 753 // TODO 754 not_implemented(); 755 } 756 757 int posix_timer_gettime(posix_timer_t timerid, 758 struct posix_itimerspec *value) 759 { 760 // TODO 761 not_implemented(); 762 } 763 764 int posix_timer_settime(posix_timer_t timerid, int flags, 765 const struct posix_itimerspec *restrict value, 766 struct posix_itimerspec *restrict ovalue) 767 { 768 // TODO 769 not_implemented(); 770 } 771 772 #endif 773 774 /** 775 * Get CPU time used since the process invocation. 776 * 777 * @return Consumed CPU cycles by this process or -1 if not available. 778 */ 779 posix_clock_t posix_clock(void) 780 { 781 posix_clock_t total_cycles = -1; 782 stats_task_t *task_stats = stats_get_task(task_get_id()); 783 if (task_stats) { 784 total_cycles = (posix_clock_t) (task_stats->kcycles + task_stats->ucycles); 785 } 786 free(task_stats); 787 task_stats = 0; 788 789 return total_cycles; 790 } 791 89 792 /** @} 90 793 */
Note:
See TracChangeset
for help on using the changeset viewer.