vinyl-cache/lib/libvinyl/vtim.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Semi-trivial functions to handle HTTP header timestamps according to
31
 * RFC 2616 section 3.3.
32
 *
33
 * We must parse four different formats:
34
 *       000000000011111111112222222222
35
 *       012345678901234567890123456789
36
 *       ------------------------------
37
 *      "Sun, 06 Nov 1994 08:49:37 GMT"         RFC822 & RFC1123
38
 *      "Sunday, 06-Nov-94 08:49:37 GMT"        RFC850
39
 *      "Sun Nov  6 08:49:37 1994"              ANSI-C asctime()
40
 *      "1994-11-06T08:49:37"                   ISO 8601
41
 *
42
 * And always output the RFC1123 format.
43
 *
44
 * So why are these functions hand-built ?
45
 *
46
 * Because the people behind POSIX were short-sighted morons who didn't think
47
 * anybody would ever need to deal with timestamps in multiple different
48
 * timezones at the same time -- for that matter, convert timestamps to
49
 * broken down UTC/GMT time.
50
 *
51
 * We could, and used to, get by by smashing our TZ variable to "UTC" but
52
 * that ruins the LOCALE for VMODs.
53
 *
54
 */
55
56
#include "config.h"
57
58
#include <time.h>
59
#include <sys/time.h>
60
61
#include <math.h>
62
#include <stdint.h>
63
#include <stdio.h>
64
#include <stdlib.h>
65
#include <string.h>
66
#ifdef __MACH__
67
#include <mach/mach_time.h>
68
#endif
69
70
#include "vdef.h"
71
72
#include "vas.h"
73
#include "vct.h"
74
#include "vtim.h"
75
76
/* relax vtim parsing */
77
unsigned VTIM_postel = 0;
78
79
static const char * const weekday_name[] = {
80
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
81
};
82
83
static const char * const more_weekday[] = {
84
        "day", "day", "sday", "nesday", "rsday", "day", "urday"
85
};
86
87
static const char * const month_name[] = {
88
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
89
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
90
};
91
92
static const int days_in_month[] = {
93
        31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
94
};
95
96
static const int days_before_month[] = {
97
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
98
};
99
100
static const unsigned days_before_month_leap_year[] = {
101
        0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
102
};
103
104
#ifdef __MACH__
105
// http://stackoverflow.com/a/21352348
106
static uint64_t mt_base;
107
static double   mt_scale;
108
109
static void
110
mach_time_init(void)
111
{
112
        mach_timebase_info_data_t timebase;
113
114
        mt_base = mach_absolute_time();
115
116
        AZ(mach_timebase_info(&timebase));
117
        mt_scale = (double)timebase.numer / (double)timebase.denom * 1e-9;
118
}
119
120
static __attribute__((constructor)) void
121
init(void)
122
{
123
        mach_time_init();
124
}
125
#endif
126
127
/*
128
 * On older Solaris-incarnations, gethrtime() was faster than
129
 * clock_gettime(CLOCK_MONOTONIC). Our configure script prefers
130
 * clock_gettime if it is consistently at least twice as fast as
131
 * gethrtime(), which is the case on modern Solaris descendents.
132
 */
133
134
vtim_mono
135 17652440
VTIM_mono(void)
136
{
137
#if defined(HAVE_GETHRTIME) && defined(USE_GETHRTIME)
138
        return (gethrtime() * 1e-9);
139
#else
140
        struct timespec ts;
141
142 17652440
        AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
143 17652440
        return (ts_vtim(ts));
144
#endif
145
}
146
147
vtim_real
148 7194970
VTIM_real(void)
149
{
150
#ifdef HAVE_CLOCK_GETTIME
151
        struct timespec ts;
152
153 7194970
        AZ(clock_gettime(CLOCK_REALTIME, &ts));
154 7194970
        return (ts_vtim(ts));
155
#else
156
        struct timeval tv;
157
158
        AZ(gettimeofday(&tv, NULL));
159
        return (tv_vtim(tv));
160
#endif
161
}
162
163
/*
164
 * The tzcode project calls a mutex for gmtime_r(), to see if the
165
 * definition of UTC time has changed.
166
 *
167
 * They probably have their reasons.
168
 *
169
 * We roll our own.
170
 *
171
 */
172
173
#define T1600 (-11676096000.)           // time_t at 1600-01-01T00:00:00Z
174
#define ND400 (365 * 400 + 3 * 24 + 25) // Days in the 400 year period
175
#define ND100 (365 * 100 + 24)          // Days in "normal" century
176
#define ND4Y  (365 * 4 + 1)             // Days in 4-year period
177
178
void
179 129720327
VTIM_format(vtim_real t, char p[VTIM_FORMAT_SIZE])
180
{
181
        int r;
182
183 129720327
        AN(p);
184 129720327
        *p = '\0';
185
186 129720327
        if (t < (vtim_real)INTMAX_MIN || t > (vtim_real)INTMAX_MAX)
187 21
                return;
188
189 129720306
        if (t < T1600)
190 0
                return;
191
192 129720306
        int is_leap = 0;
193 129720306
        unsigned day_in_year = 0;
194
195 129720306
        uint64_t days = (uint64_t)((t - T1600)) / 86400;
196 129720306
        unsigned seconds = (uint64_t)((t - T1600)) % 86400;
197
198 129720306
        unsigned year = 1600 + 400 * (days / ND400);
199 129720306
        unsigned day_in_4_century = days % ND400;
200
201 129720306
        if (day_in_4_century < 366) {
202
                /* Years divisible by 400 are leap years */
203 369054
                is_leap = 1;
204 369054
                day_in_year = day_in_4_century;
205 369054
        } else {
206 129351252
                unsigned century = (day_in_4_century - 1) / ND100;
207 129351252
                unsigned day_in_century = (day_in_4_century - 1) % ND100;
208 129351252
                year += century * 100;
209
210 129351252
                if (day_in_century < 365) {
211
                        /* Years divisible by 100 are not leap years */
212 1104033
                        is_leap = 0;
213 1104033
                        day_in_year = day_in_century;
214 1104033
                } else {
215 128247219
                        unsigned n_4_year = (day_in_century + 1) / ND4Y;
216 128247219
                        unsigned day_in_4_year = (day_in_century + 1) % ND4Y;
217 128247219
                        year += n_4_year * 4;
218
219 128247219
                        if (day_in_4_year < 366) {
220
                                /* Years divisible by 4 are leap years */
221 30998583
                                is_leap = 1;
222 30998583
                                day_in_year = day_in_4_year;
223 30998583
                        } else {
224 97248636
                                is_leap = 0;
225 97248636
                                year += (day_in_4_year - 1) / 365;
226 97248636
                                day_in_year = (day_in_4_year - 1) % 365;
227
                        }
228
                }
229
        }
230
231 129720306
        if (!is_leap && day_in_year >= 59)
232 82469088
                day_in_year += 1;
233
234 129720306
        unsigned month = day_in_year / 31;
235 129720306
        if (month < 11 && day_in_year >= days_before_month_leap_year[month + 1])
236 13571428
                month += 1;
237
238 129720306
        unsigned day = day_in_year - days_before_month_leap_year[month];
239 129720306
        unsigned wday = (days + 6) % 7;
240
241 129720306
        unsigned hour = seconds / 3600;
242 129720306
        unsigned minute = (seconds / 60) % 60;
243 129720306
        unsigned second = seconds % 60;
244
245 259440612
        r = snprintf(p, VTIM_FORMAT_SIZE,
246
            "%s, %02d %s %4d %02d:%02d:%02d GMT",
247 129720306
            weekday_name[wday],
248 129720306
            day + 1,
249 129720306
            month_name[month],
250 129720306
            year,
251 129720306
            hour,
252 129720306
            minute,
253 129720306
            second
254
        );
255
256 129720306
        assert(r == VTIM_FORMAT_SIZE - 1);
257 129720327
}
258
259
#ifdef TEST_DRIVER
260
#define FAIL()  \
261
        do { printf("\nFAIL <<%d>>\n", __LINE__); return (0); } while (0)
262
#else
263
#define FAIL()  \
264
        do { return (0); } while (0)
265
#endif
266
267
#define DIGIT(mult, fld)                                        \
268
        do {                                                    \
269
                if (*p < '0' || *p > '9')                       \
270
                        FAIL();                                 \
271
                tm->tm_##fld += (*p - '0') * mult;              \
272
                p++;                                            \
273
        } while(0)
274
275
#define MUSTBE(chr)                                             \
276
        do {                                                    \
277
                if (*p != chr)                                  \
278
                        FAIL();                                 \
279
                p++;                                            \
280
        } while(0)
281
282
#define WEEKDAY()                                               \
283
        do {                                                    \
284
                int i;                                          \
285
                for (i = 0; i < 7; i++) {                       \
286
                        if (!vstrncmp(p, weekday_name[i], 3)) { \
287
                                tm->tm_wday = i;                \
288
                                break;                          \
289
                        }                                       \
290
                }                                               \
291
                if (i == 7)                                     \
292
                        FAIL();                                 \
293
                p += 3;                                         \
294
        } while(0)
295
296
297
#define MONTH()                                                 \
298
        do {                                                    \
299
                int i;                                          \
300
                for (i = 0; i < 12; i++) {                      \
301
                        if (!vstrncmp(p, month_name[i], 3)) {   \
302
                                tm->tm_mon = i + 1;             \
303
                                break;                          \
304
                        }                                       \
305
                }                                               \
306
                if (i == 12)                                    \
307
                        FAIL();                                 \
308
                p += 3;                                         \
309
        } while(0)
310
311
#define TIMESTAMP()                                             \
312
        do {                                                    \
313
                DIGIT(10, hour);                                \
314
                DIGIT(1, hour);                                 \
315
                MUSTBE(':');                                    \
316
                DIGIT(10, min);                                 \
317
                DIGIT(1, min);                                  \
318
                MUSTBE(':');                                    \
319
                DIGIT(10, sec);                                 \
320
                DIGIT(1, sec);                                  \
321
        } while(0)
322
323
static unsigned
324 407298651
vtim_parse_http(struct tm *tm, const char **pp)
325
{
326
        const char *p;
327
328 407298651
        AN(pp);
329 407298651
        p = *pp;
330 407298651
        if (*p == '\0')
331 21
                FAIL();
332
333 407298630
        if (*p >= '0' && *p <= '9') {
334
                /* ISO8601 -- "1994-11-06T08:49:37" */
335 129593961
                DIGIT(1000, year);
336 129593961
                DIGIT(100, year);
337 129593961
                DIGIT(10, year);
338 129593961
                DIGIT(1, year);
339 129593961
                MUSTBE('-');
340 129593667
                DIGIT(10, mon);
341 129593667
                DIGIT(1, mon);
342 129593667
                MUSTBE('-');
343 129593667
                DIGIT(10, mday);
344 129593667
                DIGIT(1, mday);
345 129593667
                MUSTBE('T');
346 129593667
                TIMESTAMP();
347 129593667
        } else {
348 1110634879
                WEEKDAY();
349 277704585
                assert(tm->tm_wday >= 0 && tm->tm_wday <= 6);
350 277704585
                if (*p == ',') {
351
                        /* RFC822 & RFC1123 - "Sun, 06 Nov 1994 08:49:37 GMT" */
352 129650156
                        p++;
353 129650156
                        MUSTBE(' ');
354 129650156
                        if (VTIM_postel && *p && p[1] == ' ')
355 84
                                DIGIT(1, mday);
356
                        else {
357 129650072
                                DIGIT(10, mday);
358 129650072
                                DIGIT(1, mday);
359
                        }
360 129650156
                        MUSTBE(' ');
361 845750755
                        MONTH();
362 129650135
                        MUSTBE(' ');
363 129650135
                        DIGIT(1000, year);
364 129650135
                        DIGIT(100, year);
365 129650135
                        DIGIT(10, year);
366 129650135
                        DIGIT(1, year);
367 129650135
                        MUSTBE(' ');
368 129650135
                        TIMESTAMP();
369 129650135
                        MUSTBE(' ');
370 129650135
                        MUSTBE('G');
371 129650114
                        MUSTBE('M');
372 129650114
                        MUSTBE('T');
373 277704543
                } else if (*p == ' ') {
374
                        /* ANSI-C asctime() -- "Sun Nov  6 08:49:37 1994" */
375 129617018
                        p++;
376 845440081
                        MONTH();
377 129616934
                        MUSTBE(' ');
378 129616934
                        if (*p != ' ')
379 91271922
                                DIGIT(10, mday);
380
                        else
381 38345012
                                p++;
382 129616934
                        DIGIT(1, mday);
383 129616934
                        MUSTBE(' ');
384 129616934
                        TIMESTAMP();
385 129616934
                        MUSTBE(' ');
386 129616934
                        DIGIT(1000, year);
387 129616934
                        DIGIT(100, year);
388 129616934
                        DIGIT(10, year);
389 129616934
                        DIGIT(1, year);
390 148054345
                } else if (!vstrncmp(p, more_weekday[tm->tm_wday],
391
                    vstrlen(more_weekday[tm->tm_wday]))) {
392
                        /* RFC850 -- "Sunday, 06-Nov-94 08:49:37 GMT" */
393 18437327
                        p += vstrlen(more_weekday[tm->tm_wday]);
394 18437327
                        MUSTBE(',');
395 18437327
                        MUSTBE(' ');
396 18437327
                        DIGIT(10, mday);
397 18437327
                        DIGIT(1, mday);
398 18437327
                        MUSTBE('-');
399 120370498
                        MONTH();
400 18437327
                        MUSTBE('-');
401 18437327
                        DIGIT(10, year);
402 18437327
                        DIGIT(1, year);
403 18437327
                        tm->tm_year += 1900;
404 18437327
                        if (tm->tm_year < 1969)
405 12706092
                                tm->tm_year += 100;
406 18437327
                        MUSTBE(' ');
407 18437327
                        TIMESTAMP();
408 18437327
                        MUSTBE(' ');
409 18437327
                        MUSTBE('G');
410 18437327
                        MUSTBE('M');
411 18437327
                        MUSTBE('T');
412 18437327
                } else
413 84
                        FAIL();
414
        }
415
416 407298042
        *pp = p;
417 407298042
        return (1);
418 407298651
}
419
420
static vtim_real
421 407297958
vtim_calc(struct tm *tm)
422
{
423
        vtim_real t;
424
        int d, leap;
425
426 407297958
        if (tm->tm_sec < 0 || tm->tm_sec > 60)  /* Leapseconds! */
427 84
                FAIL();
428 407297874
        if (tm->tm_min < 0 || tm->tm_min > 59)
429 84
                FAIL();
430 407297790
        if (tm->tm_hour < 0 || tm->tm_hour > 23)
431 84
                FAIL();
432 407297706
        if (tm->tm_mon < 1 || tm->tm_mon > 12)
433 84
                FAIL();
434 407297622
        if (tm->tm_mday < 1 || tm->tm_mday > days_in_month[tm->tm_mon - 1])
435 84
                FAIL();
436 407297538
        if (tm->tm_year < 1899)
437 0
                FAIL();
438
439 509327088
        leap = ((tm->tm_year) % 4) == 0 &&
440 102029550
            (((tm->tm_year) % 100) != 0 || ((tm->tm_year) % 400) == 0);
441
442 407297538
        if (tm->tm_mon == 2 && tm->tm_mday > 28 && !leap)
443 84
                FAIL();
444
445 407297454
        if (tm->tm_sec == 60)                   /* Ignore Leapseconds */
446 84
                tm->tm_sec--;
447
448 407297454
        t = ((tm->tm_hour * 60.) + tm->tm_min) * 60. + tm->tm_sec;
449
450 407297454
        d = (tm->tm_mday - 1) + days_before_month[tm->tm_mon - 1];
451
452 407297454
        if (tm->tm_mon > 2 && leap)
453 82534494
                d++;
454
455 407297454
        d += (tm->tm_year % 100) * 365; /* There are 365 days in a year */
456
457 407297454
        if ((tm->tm_year % 100) > 0)    /* And a leap day every four years */
458 402693666
                d += (((tm->tm_year % 100) - 1) / 4);
459
460 407297454
        d += ((tm->tm_year / 100) - 20) *       /* Days relative to y2000 */
461
            (100 * 365 + 24);           /* 24 leapdays per year in a century */
462
463 407297454
        d += ((tm->tm_year - 1) / 400) - 4;     /* One more every 400 years */
464
465
        /*
466
         * Now check weekday, if we have one.
467
         * 6 is because 2000-01-01 was a saturday.
468
         * 10000 is to make sure the modulus argument is always positive
469
         */
470 407297454
        if (tm->tm_wday != -1 && (d + 6 + 7 * 10000) % 7 != tm->tm_wday)
471 168
                FAIL();
472
473 407297286
        t += d * 86400.;
474
475 407297286
        t += 10957. * 86400.;   /* 10957 days frm UNIX epoch to y2000 */
476
477 407297286
        return (t);
478 407297958
}
479
480
vtim_real
481 407298651
VTIM_parse(const char *p)
482
{
483 407298651
        struct tm tm[1] = {{.tm_wday = -1 }};
484
485 407298651
        if (p == NULL)
486 0
                FAIL();
487
488 407298693
        while (vct_isows(*p))
489 42
                p++;
490
491 407298651
        if (!vtim_parse_http(tm, &p))
492 609
                FAIL();
493
494 407298126
        while (vct_isows(*p))
495 84
                p++;
496
497 407298042
        if (*p != '\0')
498 84
                FAIL();
499
500 407297958
        return (vtim_calc(tm));
501 407298651
}
502
503
void
504 3490575
VTIM_sleep(vtim_dur t)
505
{
506
        struct timespec ts;
507
508 3490575
        ts = VTIM_timespec(t);
509
510 3490575
        (void)nanosleep(&ts, NULL);
511 3490575
}
512
513
/*
514
 * VTIM_timeval and VTIM_timespec may need variants with different signatures
515
 * when vtim_real / vtim_mono typedefs are changed
516
 */
517
518
struct timeval
519 292803
VTIM_timeval(vtim_dur t)
520
{
521
        struct timeval tv;
522
523 292803
        AZ(isnan(t));
524 292803
        tv.tv_sec = (time_t)trunc(t);
525 292803
        tv.tv_usec = (int)(1e6 * (t - tv.tv_sec));
526 292803
        return (tv);
527
}
528
529
struct timespec
530 4005431
VTIM_timespec(vtim_dur t)
531
{
532
        struct timespec tv;
533
534 4005431
        AZ(isnan(t));
535 4005431
        tv.tv_sec = (time_t)trunc(t);
536 4005431
        tv.tv_nsec = (int)(1e9 * (t - tv.tv_sec));
537 4005431
        return (tv);
538
}
539
540
struct timeval
541 292803
VTIM_timeval_sock(vtim_dur t)
542
{
543
544 292803
        return (VTIM_timeval(isinf(t) ? 0. : vmax(t, 1e-3)));
545
}
546
547
int
548 1244455
VTIM_poll_tmo(vtim_dur tmo)
549
{
550
551 1244455
        if (isinf(tmo))
552 63
                return (-1);
553 1244392
        assert(!isnan(tmo));
554 1244392
        return (vmax_t(int, 0, ((int)(tmo * 1e3))));
555 1244455
}
556
557
#ifdef TEST_DRIVER
558
559
#pragma GCC diagnostic ignored "-Wformat-y2k"
560
561
#include <stdint.h>
562
563
static void
564 84
tst(const char *s, time_t good)
565
{
566
        time_t t;
567
        char buf[BUFSIZ];
568
569 84
        t = VTIM_parse(s);
570 84
        VTIM_format(t, buf);
571 84
        printf("%-30s -> %12jd -> %s\n", s, (intmax_t)t, buf);
572 84
        if (t != good) {
573 0
                printf("Parse error! Got: %jd should have %jd diff %jd\n",
574 0
                    (intmax_t)t, (intmax_t)good, (intmax_t)(t - good));
575 0
                exit(4);
576
        }
577 84
}
578
579
static void
580 63
tstf(time_t t, const char *s)
581
{
582
        char buf[VTIM_FORMAT_SIZE];
583
584 63
        VTIM_format(t, buf);
585 63
        if (strcmp(s, buf)) {
586 0
                printf("VTIM_format(%jd) error\nwant:\t%s\ngot:\t%s\n",
587 0
                    (intmax_t)t, s, buf);
588 0
                exit(4);
589
        }
590 63
}
591
592
/* XXX keep as double for the time being */
593
static int
594 42
tst_delta_check(const char *name, double begin, double end, vtim_dur ref)
595
{
596
#ifdef __MACH__
597
        const double tol_max = 2;
598
#else
599 42
        const double tol_max = 1.1;
600
#endif
601 42
        const double tol_min = 1;
602
603 42
        printf("%s delta for %fs sleep: %f\n", name, ref, (end - begin));
604
605 42
        if ((end - begin) > tol_max * ref) {
606 0
                printf("%s delta above tolerance: ((%f - %f) = %f) > %f\n",
607 0
                    name, end, begin, (end - begin), tol_max);
608 0
                return (1);
609 42
        } else if ((end - begin) < tol_min * ref) {
610 0
                printf("%s delta below tolerance: ((%f - %f) = %f) < %f\n",
611 0
                    name, end, begin, (end - begin), tol_min);
612 0
                return (1);
613
        }
614 42
        return (0);
615 42
}
616
617
static void
618 21
tst_delta(void)
619
{
620
        vtim_mono m_begin, m_end;
621
        vtim_real r_begin, r_end;
622 21
        const vtim_dur ref = 1;
623 21
        int err = 0;
624
625 21
        r_begin = VTIM_real();
626 21
        m_begin = VTIM_mono();
627 21
        VTIM_sleep(ref);
628 21
        r_end = VTIM_real();
629 21
        m_end = VTIM_mono();
630
631 21
        err += tst_delta_check("VTIM_mono", m_begin, m_end, ref);
632 21
        err += tst_delta_check("VTIM_real", r_begin, r_end, ref);
633
634 21
        if (err) {
635 0
                printf("%d time delta test errors\n", err);
636 0
                exit(4);
637
        }
638 21
}
639
640
static void
641 21
bench(void)
642
{
643
        vtim_mono s, e;
644
        vtim_mono t_m;
645
        vtim_real t_r;
646
        unsigned long t_i;
647
        int i;
648
        char buf[64];
649
650 21
        t_m = 0;
651 21
        t_r = 0;
652 21
        s = VTIM_mono();
653 2100021
        for (i=0; i<100000; i++)
654 2100000
                t_r += VTIM_real();
655 21
        e = VTIM_mono();
656 21
        printf("real: %fs / %d = %fns - tst val %f\n",
657 21
            e - s, i, 1e9 * (e - s) / i, t_r);
658
659 21
        t_i = 0;
660 21
        s = VTIM_mono();
661 2100021
        for (i=0; i<100000; i++)
662 2100000
                t_m += VTIM_mono();
663 21
        e = VTIM_mono();
664 21
        printf("mono: %fs / %d = %fns - tst val %f\n",
665 21
            e - s, i, 1e9 * (e - s) / i, t_m);
666
667 21
        t_i = 0;
668 21
        s = VTIM_mono();
669 2100021
        for (i=0; i<100000; i++) {
670 2100000
                snprintf(buf, sizeof(buf), "%.6f", s);
671 2100000
                t_i += buf[4];
672 2100000
        }
673 21
        e = VTIM_mono();
674 21
        printf("printf %%.6f: %fs / %d = %fns - tst val %lu %s\n",
675 21
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
676
677 21
        t_i = 0;
678 21
        s = VTIM_mono();
679 2100021
        for (i=0; i<100000; i++) {
680 4200000
                snprintf(buf, sizeof(buf), "%ju.%06ju",
681 2100000
                    (uintmax_t)floor(s),
682 2100000
                    (uintmax_t)floor((s * 1e6)) % 1000000UL);
683 2100000
                t_i += buf[4];
684 2100000
        }
685 21
        e = VTIM_mono();
686 21
        printf("printf %%ju.%%06ju: %fs / %d = %fns - tst val %lu %s\n",
687 21
            e - s, i, 1e9 * (e - s) / i, t_i, buf);
688 21
}
689
690
static void
691 407194137
parse_check(time_t t, const char *s)
692
{
693
        vtim_real tt;
694
        char buf[BUFSIZ];
695
696 407194137
        tt = VTIM_parse(s);
697 407194137
        if (tt != t) {
698 0
                VTIM_format(tt, buf);
699 0
                printf("  fm: %12jd <%s>\n", (intmax_t)t, s);
700 0
                printf("  to: %12.0f <%s>\n", tt, buf);
701 0
                exit(2);
702
        }
703 407194137
}
704
705
#define TTEST_MIN (sizeof(time_t) >= 8 ? -2209852800LL : INT32_MIN)
706
#define TTEST_MAX (sizeof(time_t) >= 8 ? 20000000000LL : INT32_MAX)
707
708
int
709 21
main(int argc, char **argv)
710
{
711
        time_t t;
712
        intmax_t iter;
713
        struct tm tm;
714
        char buf[BUFSIZ];
715
        char buf1[BUFSIZ];
716
717 21
        (void)argc;
718 21
        (void)argv;
719
720 21
        AZ(setenv("TZ", "UTC", 1));
721
722 21
        bench();
723
724
        /* Brute force test against libc version */
725 129593499
        for (iter = TTEST_MIN; iter < TTEST_MAX; iter += 3599) {
726 129593478
                t = (time_t)iter;
727 129593478
                gmtime_r(&t, &tm);
728 129593478
                strftime(buf1, sizeof buf1, "%a, %d %b %Y %T GMT", &tm);
729 129593478
                VTIM_format(t, buf);
730 129593478
                if (vstrcmp(buf, buf1)) {
731 0
                        printf("libc: <%s> Vtim <%s> %jd\n",
732 0
                            buf1, buf, (intmax_t)t);
733 0
                        exit(2);
734
                }
735 129593478
                parse_check(t, buf1);
736
737 129593478
                strftime(buf1, sizeof buf1, "%a %b %e %T %Y", &tm);
738 129593478
                parse_check(t, buf1);
739
740 129593478
                strftime(buf1, sizeof buf1, "%Y-%m-%dT%T", &tm);
741 129593478
                parse_check(t, buf1);
742
743 129593478
                if (tm.tm_year >= 69 && tm.tm_year < 169) {
744 18413703
                        strftime(buf1, sizeof buf1, "%A, %d-%b-%y %T GMT", &tm);
745 18413703
                        parse_check(t, buf1);
746 18413703
                }
747 129593478
        }
748
749
        /* Examples from RFC2616 section 3.3.1 */
750 21
        tst("Sun, 06 Nov 1994 08:49:37 GMT", 784111777);
751 21
        tst("Sunday, 06-Nov-94 08:49:37 GMT", 784111777);
752 21
        tst("Sun Nov  6 08:49:37 1994", 784111777);
753
754 21
        tst("1994-11-06T08:49:37", 784111777);
755
756 21
        tst_delta();
757
758
        /* test cases from glibc tst-gmtime.c */
759 21
        tstf(0, "Thu, 01 Jan 1970 00:00:00 GMT");
760 21
        tstf(0x7fffffff, "Tue, 19 Jan 2038 03:14:07 GMT");
761
        if (sizeof(time_t) >= 8)
762 21
                tstf(0x80000000ULL, "Tue, 19 Jan 2038 03:14:08 GMT");
763
764 21
        return (0);
765
}
766
767
#endif