iodine 0.7.38 → 0.7.43
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +5 -12
- data/.travis.yml +6 -5
- data/CHANGELOG.md +28 -0
- data/README.md +55 -48
- data/Rakefile +3 -1
- data/SPEC-PubSub-Draft.md +89 -47
- data/SPEC-WebSocket-Draft.md +92 -55
- data/examples/async_task.ru +92 -0
- data/exe/iodine +1 -0
- data/ext/iodine/extconf.rb +0 -34
- data/ext/iodine/fio.c +65 -65
- data/ext/iodine/fio_tls_missing.c +6 -0
- data/ext/iodine/fio_tls_openssl.c +64 -28
- data/ext/iodine/http.c +87 -100
- data/ext/iodine/http1.c +6 -15
- data/ext/iodine/http1_parser.h +806 -63
- data/ext/iodine/iodine.c +2 -0
- data/ext/iodine/iodine.h +1 -0
- data/ext/iodine/iodine_caller.c +1 -1
- data/ext/iodine/iodine_connection.c +20 -8
- data/ext/iodine/iodine_defer.c +1 -0
- data/ext/iodine/iodine_http.c +4 -6
- data/ext/iodine/iodine_mustache.c +2 -4
- data/ext/iodine/iodine_tls.c +2 -16
- data/lib/iodine.rb +1 -1
- data/lib/iodine/connection.rb +12 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +6 -0
- metadata +5 -5
- data/ext/iodine/http1_parser.c +0 -616
@@ -33,6 +33,11 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
33
33
|
FIO_LOG_FATAL("No supported SSL/TLS library available."); \
|
34
34
|
exit(-1);
|
35
35
|
#endif
|
36
|
+
|
37
|
+
#ifndef FIO_TLS_TIMEOUT
|
38
|
+
#define FIO_TLS_TIMEOUT 4
|
39
|
+
#endif
|
40
|
+
|
36
41
|
/* STOP deleting after this line */
|
37
42
|
|
38
43
|
/* *****************************************************************************
|
@@ -590,6 +595,7 @@ file_missing:
|
|
590
595
|
*/
|
591
596
|
void FIO_TLS_WEAK fio_tls_accept(intptr_t uuid, fio_tls_s *tls, void *udata) {
|
592
597
|
REQUIRE_LIBRARY();
|
598
|
+
fio_timeout_set(uuid, FIO_TLS_TIMEOUT);
|
593
599
|
fio_tls_attach2uuid(uuid, tls, udata, 1);
|
594
600
|
}
|
595
601
|
|
@@ -29,6 +29,10 @@ The SSL/TLS helper data types (can be left as is)
|
|
29
29
|
#define FIO_FORCE_MALLOC_TMP 1
|
30
30
|
#include <fio.h>
|
31
31
|
|
32
|
+
#ifndef FIO_TLS_TIMEOUT
|
33
|
+
#define FIO_TLS_TIMEOUT 4
|
34
|
+
#endif
|
35
|
+
|
32
36
|
typedef struct {
|
33
37
|
fio_str_s private_key;
|
34
38
|
fio_str_s public_key;
|
@@ -190,8 +194,12 @@ typedef struct {
|
|
190
194
|
|
191
195
|
FIO_FUNC inline void alpn_select___task(void *t_, void *ignr_) {
|
192
196
|
alpn_task_s *t = t_;
|
193
|
-
|
194
|
-
|
197
|
+
if (fio_is_valid(t->uuid)) {
|
198
|
+
fio_timeout_set(t->uuid, 0); // remove TLS timeout
|
199
|
+
t->alpn.on_selected(t->uuid, t->udata_connection, t->alpn.udata_tls);
|
200
|
+
} else {
|
201
|
+
t->alpn.on_selected(-1, t->udata_connection, t->alpn.udata_tls);
|
202
|
+
}
|
195
203
|
fio_free(t);
|
196
204
|
(void)ignr_;
|
197
205
|
}
|
@@ -537,19 +545,21 @@ static ssize_t fio_tls_read(intptr_t uuid, void *udata, void *buf,
|
|
537
545
|
case SSL_ERROR_SSL: /* overflow */
|
538
546
|
case SSL_ERROR_ZERO_RETURN:
|
539
547
|
return 0; /* EOF */
|
548
|
+
case SSL_ERROR_SYSCALL: /* allow errno to inform us */
|
549
|
+
break; /* return -1 */
|
540
550
|
case SSL_ERROR_NONE: /* overflow */
|
541
551
|
case SSL_ERROR_WANT_CONNECT: /* overflow */
|
542
552
|
case SSL_ERROR_WANT_ACCEPT: /* overflow */
|
543
553
|
case SSL_ERROR_WANT_X509_LOOKUP: /* overflow */
|
554
|
+
case SSL_ERROR_WANT_WRITE: /* overflow */
|
555
|
+
case SSL_ERROR_WANT_READ: /* overflow */
|
544
556
|
#ifdef SSL_ERROR_WANT_ASYNC
|
545
557
|
case SSL_ERROR_WANT_ASYNC: /* overflow */
|
546
558
|
#endif
|
547
|
-
case SSL_ERROR_WANT_WRITE: /* overflow */
|
548
|
-
case SSL_ERROR_WANT_READ:
|
549
559
|
default:
|
560
|
+
errno = EWOULDBLOCK;
|
550
561
|
break;
|
551
562
|
}
|
552
|
-
errno = EWOULDBLOCK;
|
553
563
|
return -1;
|
554
564
|
(void)uuid;
|
555
565
|
}
|
@@ -591,19 +601,21 @@ static ssize_t fio_tls_write(intptr_t uuid, void *udata, const void *buf,
|
|
591
601
|
case SSL_ERROR_SSL: /* overflow */
|
592
602
|
case SSL_ERROR_ZERO_RETURN:
|
593
603
|
return 0; /* EOF */
|
604
|
+
case SSL_ERROR_SYSCALL: /* allow errno to inform us */
|
605
|
+
break; /* return -1 */
|
594
606
|
case SSL_ERROR_NONE: /* overflow */
|
595
607
|
case SSL_ERROR_WANT_CONNECT: /* overflow */
|
596
608
|
case SSL_ERROR_WANT_ACCEPT: /* overflow */
|
597
609
|
case SSL_ERROR_WANT_X509_LOOKUP: /* overflow */
|
610
|
+
case SSL_ERROR_WANT_WRITE: /* overflow */
|
611
|
+
case SSL_ERROR_WANT_READ: /* overflow */
|
598
612
|
#ifdef SSL_ERROR_WANT_ASYNC
|
599
613
|
case SSL_ERROR_WANT_ASYNC: /* overflow */
|
600
614
|
#endif
|
601
|
-
case SSL_ERROR_WANT_WRITE: /* overflow */
|
602
|
-
case SSL_ERROR_WANT_READ:
|
603
615
|
default:
|
616
|
+
errno = EWOULDBLOCK;
|
604
617
|
break;
|
605
618
|
}
|
606
|
-
errno = EWOULDBLOCK;
|
607
619
|
return -1;
|
608
620
|
(void)uuid;
|
609
621
|
}
|
@@ -640,13 +652,20 @@ static void fio_tls_cleanup(void *udata) {
|
|
640
652
|
static fio_rw_hook_s FIO_TLS_HOOKS = {
|
641
653
|
.read = fio_tls_read,
|
642
654
|
.write = fio_tls_write,
|
643
|
-
.before_close = fio_tls_before_close,
|
644
655
|
.flush = fio_tls_flush,
|
656
|
+
.before_close = fio_tls_before_close,
|
645
657
|
.cleanup = fio_tls_cleanup,
|
646
658
|
};
|
647
659
|
|
660
|
+
#define FIO_TLS_HANDSHAKE_ERROR 0
|
661
|
+
#define FIO_TLS_HANDSHAKE_OK 1
|
662
|
+
#define FIO_TLS_HANDSHAKE_NEED_READ 2
|
663
|
+
#define FIO_TLS_HANDSHAKE_NEED_WRITE 4
|
664
|
+
|
648
665
|
static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
|
666
|
+
size_t status = FIO_TLS_HANDSHAKE_ERROR;
|
649
667
|
fio_tls_connection_s *c = udata;
|
668
|
+
|
650
669
|
int ri;
|
651
670
|
if (c->is_server) {
|
652
671
|
ri = SSL_accept(c->ssl);
|
@@ -659,26 +678,29 @@ static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
|
|
659
678
|
case SSL_ERROR_NONE:
|
660
679
|
// FIO_LOG_DEBUG("SSL_accept/SSL_connect %p state: SSL_ERROR_NONE",
|
661
680
|
// (void *)uuid);
|
662
|
-
|
681
|
+
status = FIO_TLS_HANDSHAKE_NEED_READ | FIO_TLS_HANDSHAKE_NEED_WRITE;
|
682
|
+
return status;
|
663
683
|
case SSL_ERROR_WANT_WRITE:
|
664
684
|
// FIO_LOG_DEBUG("SSL_accept/SSL_connect %p state: SSL_ERROR_WANT_WRITE",
|
665
685
|
// (void *)uuid);
|
666
|
-
|
667
|
-
return
|
686
|
+
status = FIO_TLS_HANDSHAKE_NEED_WRITE;
|
687
|
+
return status;
|
668
688
|
case SSL_ERROR_WANT_READ:
|
669
689
|
// FIO_LOG_DEBUG("SSL_accept/SSL_connect %p state: SSL_ERROR_WANT_READ",
|
670
690
|
// (void *)uuid);
|
671
|
-
|
672
|
-
return
|
691
|
+
status = FIO_TLS_HANDSHAKE_NEED_READ;
|
692
|
+
return status;
|
673
693
|
case SSL_ERROR_SYSCALL:
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
694
|
+
if (errno) {
|
695
|
+
FIO_LOG_DEBUG(
|
696
|
+
"SSL_accept/SSL_connect %p error: SSL_ERROR_SYSCALL, errno: %s",
|
697
|
+
(void *)uuid, strerror(errno));
|
698
|
+
}
|
699
|
+
break;
|
679
700
|
case SSL_ERROR_SSL:
|
680
|
-
FIO_LOG_DEBUG(
|
681
|
-
|
701
|
+
FIO_LOG_DEBUG(
|
702
|
+
"SSL_accept/SSL_connect %p error: SSL_ERROR_SSL (non SSL attempt?)",
|
703
|
+
(void *)uuid);
|
682
704
|
break;
|
683
705
|
case SSL_ERROR_ZERO_RETURN:
|
684
706
|
FIO_LOG_DEBUG("SSL_accept/SSL_connect %p error: SSL_ERROR_ZERO_RETURN",
|
@@ -715,8 +737,9 @@ static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
|
|
715
737
|
(void *)uuid, ri);
|
716
738
|
break;
|
717
739
|
}
|
740
|
+
fio_rw_hook_replace_unsafe(uuid, &FIO_TLS_HOOKS, udata);
|
718
741
|
fio_defer(fio_tls_delayed_close, (void *)uuid, NULL);
|
719
|
-
return
|
742
|
+
return status;
|
720
743
|
}
|
721
744
|
if (!c->alpn_ok) {
|
722
745
|
c->alpn_ok = 1;
|
@@ -745,7 +768,7 @@ static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
|
|
745
768
|
} else {
|
746
769
|
FIO_LOG_DEBUG("Something went wrong during TLS handshake for %p",
|
747
770
|
(void *)uuid);
|
748
|
-
return
|
771
|
+
return status;
|
749
772
|
}
|
750
773
|
/* make sure the connection is re-added to the reactor */
|
751
774
|
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
@@ -768,14 +791,18 @@ static size_t fio_tls_handshake(intptr_t uuid, void *udata) {
|
|
768
791
|
buff2);
|
769
792
|
}
|
770
793
|
#endif
|
771
|
-
|
794
|
+
status = FIO_TLS_HANDSHAKE_OK;
|
795
|
+
return status;
|
772
796
|
}
|
773
797
|
|
774
798
|
static ssize_t fio_tls_read4handshake(intptr_t uuid, void *udata, void *buf,
|
775
799
|
size_t count) {
|
776
800
|
// FIO_LOG_DEBUG("TLS handshake from read %p", (void *)uuid);
|
777
|
-
|
801
|
+
size_t s = fio_tls_handshake(uuid, udata);
|
802
|
+
if (s == FIO_TLS_HANDSHAKE_OK)
|
778
803
|
return fio_tls_read(uuid, udata, buf, count);
|
804
|
+
if (!s)
|
805
|
+
return 0;
|
779
806
|
errno = EWOULDBLOCK;
|
780
807
|
return -1;
|
781
808
|
}
|
@@ -783,20 +810,27 @@ static ssize_t fio_tls_read4handshake(intptr_t uuid, void *udata, void *buf,
|
|
783
810
|
static ssize_t fio_tls_write4handshake(intptr_t uuid, void *udata,
|
784
811
|
const void *buf, size_t count) {
|
785
812
|
// FIO_LOG_DEBUG("TLS handshake from write %p", (void *)uuid);
|
786
|
-
|
813
|
+
size_t s = fio_tls_handshake(uuid, udata);
|
814
|
+
if (s == FIO_TLS_HANDSHAKE_OK)
|
787
815
|
return fio_tls_write(uuid, udata, buf, count);
|
816
|
+
if (!s)
|
817
|
+
return 0;
|
788
818
|
errno = EWOULDBLOCK;
|
789
819
|
return -1;
|
790
820
|
}
|
791
821
|
|
792
822
|
static ssize_t fio_tls_flush4handshake(intptr_t uuid, void *udata) {
|
793
823
|
// FIO_LOG_DEBUG("TLS handshake from flush %p", (void *)uuid);
|
794
|
-
|
824
|
+
size_t s = fio_tls_handshake(uuid, udata);
|
825
|
+
if (s == FIO_TLS_HANDSHAKE_OK) {
|
795
826
|
return fio_tls_flush(uuid, udata);
|
796
827
|
}
|
828
|
+
if (!s)
|
829
|
+
return 0;
|
797
830
|
errno = 0;
|
798
|
-
return
|
831
|
+
return s | FIO_TLS_HANDSHAKE_NEED_WRITE;
|
799
832
|
}
|
833
|
+
|
800
834
|
static fio_rw_hook_s FIO_TLS_HANDSHAKE_HOOKS = {
|
801
835
|
.read = fio_tls_read4handshake,
|
802
836
|
.write = fio_tls_write4handshake,
|
@@ -967,6 +1001,7 @@ file_missing:
|
|
967
1001
|
*/
|
968
1002
|
void FIO_TLS_WEAK fio_tls_accept(intptr_t uuid, fio_tls_s *tls, void *udata) {
|
969
1003
|
REQUIRE_LIBRARY();
|
1004
|
+
fio_timeout_set(uuid, FIO_TLS_TIMEOUT);
|
970
1005
|
fio_tls_attach2uuid(uuid, tls, udata, 1);
|
971
1006
|
}
|
972
1007
|
|
@@ -1007,6 +1042,7 @@ void FIO_TLS_WEAK fio_tls_destroy(fio_tls_s *tls) {
|
|
1007
1042
|
cert_ary_free(&tls->sni);
|
1008
1043
|
trust_ary_free(&tls->trust);
|
1009
1044
|
free(tls);
|
1045
|
+
FIO_LOG_DEBUG("freed TLS context %p", (void *)tls);
|
1010
1046
|
}
|
1011
1047
|
|
1012
1048
|
#endif /* Library compiler flags */
|
data/ext/iodine/http.c
CHANGED
@@ -874,19 +874,23 @@ static uint8_t fio_http_at_capa = 0;
|
|
874
874
|
|
875
875
|
static void http_on_server_protocol_http1(intptr_t uuid, void *set,
|
876
876
|
void *ignr_) {
|
877
|
-
|
878
|
-
|
879
|
-
if (
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
877
|
+
if ((unsigned int)fio_uuid2fd(uuid) >=
|
878
|
+
((http_settings_s *)set)->max_clients) {
|
879
|
+
if (fio_uuid2fd(uuid) != -1) {
|
880
|
+
if (!fio_http_at_capa)
|
881
|
+
FIO_LOG_WARNING("HTTP server at capacity");
|
882
|
+
fio_http_at_capa = 1;
|
883
|
+
http_send_error2(uuid, 503, set);
|
884
|
+
fio_close(uuid);
|
885
|
+
}
|
884
886
|
return;
|
885
887
|
}
|
886
888
|
fio_http_at_capa = 0;
|
887
889
|
fio_protocol_s *pr = http1_new(uuid, set, NULL, 0);
|
888
890
|
if (!pr)
|
889
891
|
fio_close(uuid);
|
892
|
+
else
|
893
|
+
fio_timeout_set(uuid, ((http_settings_s *)set)->timeout);
|
890
894
|
(void)ignr_;
|
891
895
|
}
|
892
896
|
|
@@ -2089,111 +2093,94 @@ See the libc `gmtime_r` documentation for details.
|
|
2089
2093
|
|
2090
2094
|
Falls back to `gmtime_r` for dates before epoch.
|
2091
2095
|
*/
|
2092
|
-
struct tm *http_gmtime(time_t timer, struct tm *
|
2093
|
-
// static char* DAYS[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
|
2094
|
-
// "Sat"}; static char * Months = { "Jan", "Feb", "Mar", "Apr", "May",
|
2095
|
-
// "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
2096
|
-
static const uint8_t month_len[] = {
|
2097
|
-
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, // nonleap year
|
2098
|
-
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // leap year
|
2099
|
-
};
|
2100
|
-
if (timer < 0)
|
2101
|
-
return gmtime_r(&timer, tmbuf);
|
2096
|
+
struct tm *http_gmtime(time_t timer, struct tm *tm) {
|
2102
2097
|
ssize_t a, b;
|
2103
|
-
#if HAVE_TM_TM_ZONE
|
2104
|
-
*
|
2098
|
+
#if HAVE_TM_TM_ZONE || defined(BSD)
|
2099
|
+
*tm = (struct tm){
|
2105
2100
|
.tm_isdst = 0,
|
2106
|
-
.
|
2107
|
-
.tm_mon = 0,
|
2108
|
-
.tm_zone = "UTC",
|
2101
|
+
.tm_zone = (char *)"UTC",
|
2109
2102
|
};
|
2110
2103
|
#else
|
2111
|
-
*
|
2104
|
+
*tm = (struct tm){
|
2112
2105
|
.tm_isdst = 0,
|
2113
|
-
.tm_year = 70, // tm_year == The number of years since 1900
|
2114
|
-
.tm_mon = 0,
|
2115
2106
|
};
|
2116
2107
|
#endif
|
2117
|
-
|
2118
|
-
//
|
2119
|
-
|
2120
|
-
|
2121
|
-
|
2122
|
-
|
2123
|
-
|
2124
|
-
|
2125
|
-
|
2126
|
-
|
2127
|
-
|
2128
|
-
//
|
2129
|
-
|
2130
|
-
|
2131
|
-
|
2132
|
-
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2136
|
-
|
2137
|
-
|
2138
|
-
b -= DAYS_PER_100_YEARS;
|
2139
|
-
if (((tmbuf->tm_year / 100) & 3) ==
|
2140
|
-
0) // leap century divisable by 400 => add leap
|
2108
|
+
|
2109
|
+
// convert seconds from epoch to days from epoch + extract data
|
2110
|
+
if (timer >= 0) {
|
2111
|
+
// for seconds up to weekdays, we reduce the reminder every step.
|
2112
|
+
a = (ssize_t)timer;
|
2113
|
+
b = a / 60; // b == time in minutes
|
2114
|
+
tm->tm_sec = a - (b * 60);
|
2115
|
+
a = b / 60; // b == time in hours
|
2116
|
+
tm->tm_min = b - (a * 60);
|
2117
|
+
b = a / 24; // b == time in days since epoch
|
2118
|
+
tm->tm_hour = a - (b * 24);
|
2119
|
+
// b == number of days since epoch
|
2120
|
+
// day of epoch was a thursday. Add + 4 so sunday == 0...
|
2121
|
+
tm->tm_wday = (b + 4) % 7;
|
2122
|
+
} else {
|
2123
|
+
// for seconds up to weekdays, we reduce the reminder every step.
|
2124
|
+
a = (ssize_t)timer;
|
2125
|
+
b = a / 60; // b == time in minutes
|
2126
|
+
if (b * 60 != a) {
|
2127
|
+
/* seconds passed */
|
2128
|
+
tm->tm_sec = (a - (b * 60)) + 60;
|
2141
2129
|
--b;
|
2142
|
-
|
2143
|
-
|
2144
|
-
|
2145
|
-
while (b >= DAYS_PER_32_YEARS) {
|
2146
|
-
tmbuf->tm_year += 32;
|
2147
|
-
b -= DAYS_PER_32_YEARS;
|
2148
|
-
}
|
2149
|
-
#undef DAYS_PER_32_YEARS
|
2150
|
-
#define DAYS_PER_8_YEARS ((8 * 365) + 2)
|
2151
|
-
while (b >= DAYS_PER_8_YEARS) {
|
2152
|
-
tmbuf->tm_year += 8;
|
2153
|
-
b -= DAYS_PER_8_YEARS;
|
2154
|
-
}
|
2155
|
-
#undef DAYS_PER_8_YEARS
|
2156
|
-
#define DAYS_PER_4_YEARS ((4 * 365) + 1)
|
2157
|
-
while (b >= DAYS_PER_4_YEARS) {
|
2158
|
-
tmbuf->tm_year += 4;
|
2159
|
-
b -= DAYS_PER_4_YEARS;
|
2160
|
-
}
|
2161
|
-
#undef DAYS_PER_4_YEARS
|
2162
|
-
while (b >= 365) {
|
2163
|
-
tmbuf->tm_year += 1;
|
2164
|
-
b -= 365;
|
2165
|
-
if ((tmbuf->tm_year & 3) == 0) { // leap year
|
2166
|
-
if (b > 0) {
|
2167
|
-
--b;
|
2168
|
-
continue;
|
2169
|
-
} else {
|
2170
|
-
b += 365;
|
2171
|
-
--tmbuf->tm_year;
|
2172
|
-
break;
|
2173
|
-
}
|
2130
|
+
} else {
|
2131
|
+
/* no seconds */
|
2132
|
+
tm->tm_sec = 0;
|
2174
2133
|
}
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
b -= month_len[i];
|
2184
|
-
++tmbuf->tm_mon;
|
2134
|
+
a = b / 60; // b == time in hours
|
2135
|
+
if (a * 60 != b) {
|
2136
|
+
/* minutes passed */
|
2137
|
+
tm->tm_min = (b - (a * 60)) + 60;
|
2138
|
+
--a;
|
2139
|
+
} else {
|
2140
|
+
/* no minutes */
|
2141
|
+
tm->tm_min = 0;
|
2185
2142
|
}
|
2186
|
-
|
2187
|
-
|
2188
|
-
|
2189
|
-
|
2190
|
-
|
2191
|
-
|
2192
|
-
|
2143
|
+
b = a / 24; // b == time in days since epoch?
|
2144
|
+
if (b * 24 != a) {
|
2145
|
+
/* hours passed */
|
2146
|
+
tm->tm_hour = (a - (b * 24)) + 24;
|
2147
|
+
--b;
|
2148
|
+
} else {
|
2149
|
+
/* no hours */
|
2150
|
+
tm->tm_hour = 0;
|
2193
2151
|
}
|
2152
|
+
// day of epoch was a thursday. Add + 4 so sunday == 0...
|
2153
|
+
tm->tm_wday = ((b - 3) % 7);
|
2154
|
+
if (tm->tm_wday)
|
2155
|
+
tm->tm_wday += 7;
|
2156
|
+
/* b == days from epoch */
|
2194
2157
|
}
|
2195
|
-
|
2196
|
-
|
2158
|
+
|
2159
|
+
// at this point we can apply the algorithm described here:
|
2160
|
+
// http://howardhinnant.github.io/date_algorithms.html#civil_from_days
|
2161
|
+
// Credit to Howard Hinnant.
|
2162
|
+
{
|
2163
|
+
b += 719468L; // adjust to March 1st, 2000 (post leap of 400 year era)
|
2164
|
+
// 146,097 = days in era (400 years)
|
2165
|
+
const size_t era = (b >= 0 ? b : b - 146096) / 146097;
|
2166
|
+
const uint32_t doe = (b - (era * 146097)); // day of era
|
2167
|
+
const uint16_t yoe =
|
2168
|
+
(doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // year of era
|
2169
|
+
a = yoe;
|
2170
|
+
a += era * 400; // a == year number, assuming year starts on March 1st...
|
2171
|
+
const uint16_t doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
2172
|
+
const uint16_t mp = (5U * doy + 2) / 153;
|
2173
|
+
const uint16_t d = doy - (153U * mp + 2) / 5 + 1;
|
2174
|
+
const uint8_t m = mp + (mp < 10 ? 2 : -10);
|
2175
|
+
a += (m <= 1);
|
2176
|
+
tm->tm_year = a - 1900; // tm_year == years since 1900
|
2177
|
+
tm->tm_mon = m;
|
2178
|
+
tm->tm_mday = d;
|
2179
|
+
const uint8_t is_leap = (a % 4 == 0 && (a % 100 != 0 || a % 400 == 0));
|
2180
|
+
tm->tm_yday = (doy + (is_leap) + 28 + 31) % (365 + is_leap);
|
2181
|
+
}
|
2182
|
+
|
2183
|
+
return tm;
|
2197
2184
|
}
|
2198
2185
|
|
2199
2186
|
static const char *DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed",
|