blob: a1e494782ff5b3a92e3a22b79d144f52f00639c6 [file] [log] [blame] [raw]
/*
copyright: Boaz segev, 2016-2017
license: MIT
Feel free to copy, use and enjoy according to the license provided.
*/
#include "http.h"
/**
A faster (yet less localized) alternative to `gmtime_r`.
See the libc `gmtime_r` documentation for details.
Falls back to `gmtime_r` for dates before epoch.
*/
struct tm *http_gmtime(const time_t *timer, struct tm *tmbuf) {
// static char* DAYS[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
// static char * Months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
// "Jul",
// "Aug", "Sep", "Oct", "Nov", "Dec"};
static uint8_t month_len[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, // nonleap year
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // leap year
};
if (*timer < 0)
return gmtime_r(timer, tmbuf);
ssize_t tmp;
tmbuf->tm_gmtoff = 0;
tmbuf->tm_zone = "UTC";
tmbuf->tm_isdst = 0;
tmbuf->tm_year = 70; // tm_year == The number of years since 1900
tmbuf->tm_mon = 0;
// for seconds up to weekdays, we build up, as small values clean up larger
// values.
tmp = ((ssize_t)*timer);
tmbuf->tm_sec = tmp % 60;
tmp = tmp / 60;
tmbuf->tm_min = tmp % 60;
tmp = tmp / 60;
tmbuf->tm_hour = tmp % 24;
tmp = tmp / 24;
// day of epoch was a thursday. Add + 3 so sunday == 0...
tmbuf->tm_wday = (tmp + 3) % 7;
// tmp == number of days since epoch
#define DAYS_PER_400_YEARS ((400 * 365) + 97)
while (tmp >= DAYS_PER_400_YEARS) {
tmbuf->tm_year += 400;
tmp -= DAYS_PER_400_YEARS;
}
#undef DAYS_PER_400_YEARS
#define DAYS_PER_100_YEARS ((100 * 365) + 24)
while (tmp >= DAYS_PER_100_YEARS) {
tmbuf->tm_year += 100;
tmp -= DAYS_PER_100_YEARS;
if (((tmbuf->tm_year / 100) & 3) ==
0) // leap century divisable by 400 => add leap
--tmp;
}
#undef DAYS_PER_100_YEARS
#define DAYS_PER_32_YEARS ((32 * 365) + 8)
while (tmp >= DAYS_PER_32_YEARS) {
tmbuf->tm_year += 32;
tmp -= DAYS_PER_32_YEARS;
}
#undef DAYS_PER_32_YEARS
#define DAYS_PER_8_YEARS ((8 * 365) + 2)
while (tmp >= DAYS_PER_8_YEARS) {
tmbuf->tm_year += 8;
tmp -= DAYS_PER_8_YEARS;
}
#undef DAYS_PER_8_YEARS
#define DAYS_PER_4_YEARS ((4 * 365) + 1)
while (tmp >= DAYS_PER_4_YEARS) {
tmbuf->tm_year += 4;
tmp -= DAYS_PER_4_YEARS;
}
#undef DAYS_PER_4_YEARS
while (tmp >= 365) {
tmbuf->tm_year += 1;
tmp -= 365;
if ((tmbuf->tm_year & 3) == 0) { // leap year
if (tmp > 0) {
--tmp;
continue;
} else {
tmp += 365;
--tmbuf->tm_year;
break;
}
}
}
tmbuf->tm_yday = tmp;
if ((tmbuf->tm_year & 3) == 1) {
// regular year
for (size_t i = 0; i < 12; i++) {
if (tmp < month_len[i])
break;
tmp -= month_len[i];
++tmbuf->tm_mon;
}
} else {
// leap year
for (size_t i = 12; i < 24; i++) {
if (tmp < month_len[i])
break;
tmp -= month_len[i];
++tmbuf->tm_mon;
}
}
tmbuf->tm_mday = tmp;
return tmbuf;
}
static char *DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static char *MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ",
"Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec "};
static const char *GMT_STR = "GMT";
size_t http_date2str(char *target, struct tm *tmbuf) {
char *pos = target;
uint16_t tmp;
*(uint32_t *)pos = *((uint32_t *)DAY_NAMES[tmbuf->tm_wday]);
pos[3] = ',';
pos[4] = ' ';
pos += 5;
if (tmbuf->tm_mday < 10) {
*pos = '0' + tmbuf->tm_mday;
++pos;
} else {
tmp = tmbuf->tm_mday / 10;
pos[0] = '0' + tmp;
pos[1] = '0' + (tmbuf->tm_mday - (tmp * 10));
pos += 2;
}
*(pos++) = ' ';
*(uint32_t *)pos = *((uint32_t *)MONTH_NAMES[tmbuf->tm_mon]);
pos += 4;
// write year.
pos += http_ul2a(pos, tmbuf->tm_year + 1900);
*(pos++) = ' ';
tmp = tmbuf->tm_hour / 10;
pos[0] = '0' + tmp;
pos[1] = '0' + (tmbuf->tm_hour - (tmp * 10));
pos[2] = ':';
tmp = tmbuf->tm_min / 10;
pos[3] = '0' + tmp;
pos[4] = '0' + (tmbuf->tm_min - (tmp * 10));
pos[5] = ':';
tmp = tmbuf->tm_sec / 10;
pos[6] = '0' + tmp;
pos[7] = '0' + (tmbuf->tm_sec - (tmp * 10));
pos += 8;
pos[0] = ' ';
*((uint32_t *)(pos + 1)) = *((uint32_t *)GMT_STR);
pos += 4;
return pos - target;
}
/* Credit to Jonathan Leffler for the idea of a unified conditional */
#define hex_val(c) \
(((c) >= '0' && (c) <= '9') \
? ((c)-48) \
: (((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) \
? (((c) | 32) - 87) \
: ({ \
return -1; \
0; \
}))
ssize_t http_decode_url(char *dest, const char *url_data, size_t length) {
char *pos = dest;
const char *end = url_data + length;
while (url_data < end) {
if (*url_data == '+') {
// decode space
*(pos++) = ' ';
++url_data;
} else if (*url_data == '%') {
// decode hex value
// this is a percent encoded value.
*(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]);
url_data += 3;
} else
*(pos++) = *(url_data++);
}
*pos = 0;
return pos - dest;
}
ssize_t http_decode_url_unsafe(char *dest, const char *url_data) {
char *pos = dest;
while (*url_data) {
if (*url_data == '+') {
// decode space
*(pos++) = ' ';
++url_data;
} else if (*url_data == '%') {
// decode hex value
// this is a percent encoded value.
*(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]);
url_data += 3;
} else
*(pos++) = *(url_data++);
}
*pos = 0;
return pos - dest;
}
#undef hex_val