x86-64: remove pushes and pops arounf pointer_dereference and
[ajla.git] / os_com.inc
blob606c251145fe0ffe3f0f7354954da50a07e8c1e3
1 /*
2  * Copyright (C) 2024 Mikulas Patocka
3  *
4  * This file is part of Ajla.
5  *
6  * Ajla is free software: you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free Software
8  * Foundation, either version 3 of the License, or (at your option) any later
9  * version.
10  *
11  * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * Ajla. If not, see <https://www.gnu.org/licenses/>.
17  */
19 #if defined(HAVE_NETWORK) || defined(OS_OS2) || defined(OS_WIN32)
20 #include "os_pos_s.inc"
21 #endif
24 static inline bool os_drives_bitmap(uint32_t mask, char **drives, size_t *drives_l, ajla_error_t *err)
26         unsigned shift = 0;
27         if (unlikely(!array_init_mayfail(char, drives, drives_l, err)))
28                 return false;
29         while (mask) {
30                 char str[4] = " : ";
31                 unsigned bit = low_bit(mask);
32                 mask = mask >> bit >> 1;
33                 str[2] = os_path_separator();
34                 str[0] = bit + shift + 'A';
35                 shift += bit + 1;
36                 if (unlikely(str[0] > 'Z'))
37                         break;
38                 if (unlikely(!array_add_multiple_mayfail(char, drives, drives_l, str, 4, NULL, err)))
39                         return false;
40         }
41         return true;
45 #if defined(HAVE_NETWORK) || defined(OS_OS2) || defined(OS_WIN32)
47 static int translate_flags(int (*x)(int idx, ajla_error_t *err), int flags, ajla_error_t *err)
49         int result = 0;
50         while (flags > 0) {
51                 unsigned lb = 1U << low_bit(flags);
52                 int v = x(lb, err);
53                 if (unlikely(v < 0))
54                         return v;
55                 result |= v;
56                 flags &= ~lb;
57         }
58         return result;
61 static struct sockaddr *os_get_sock_addr(unsigned char *addr, size_t *addr_len, ajla_error_t *err)
63         struct sockaddr *sa;
64         size_t padded_len;
65         int domain;
66         if (unlikely(*addr_len < 2)) {
67                 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
68                 return NULL;
69         }
70         if (unlikely(*addr_len >= SOCKADDR_MAX_LEN)) {
71                 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "too long address");
72                 return NULL;
73         }
74         domain = os_socket_af(addr[0] | (addr[1] << 8), err);
75         if (unlikely(domain == -1))
76                 return NULL;
77         padded_len = maximum(*addr_len, sizeof(struct sockaddr));
78         sa = mem_align_mayfail(struct sockaddr *, padded_len, SOCKADDR_ALIGN, err);
79         if (unlikely(!sa))
80                 return NULL;
81         memset(sa, 0, padded_len);
82         sa->sa_family = domain;
83         memcpy(sa->sa_data, addr + 2, *addr_len - 2);
84         *addr_len = padded_len;
85         return sa;
88 static unsigned char *os_get_ajla_addr(struct sockaddr *sa, socklen_t *addr_len, ajla_error_t *err)
90         unsigned char *addr;
91         int domain;
92         if (unlikely(*addr_len < 2)) {
93                 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SYSTEM_RETURNED_INVALID_DATA), err, "too short address");
94                 return NULL;
95         }
96         if (sa->sa_family == AF_INET)
97                 *addr_len = minimum(*addr_len, 8);
98         domain = os_af_socket(sa->sa_family, err);
99         if (unlikely(domain == -1))
100                 return NULL;
101         addr = mem_alloc_mayfail(unsigned char *, *addr_len, err);
102         if (unlikely(!addr))
103                 return NULL;
104         addr[0] = domain;
105         addr[1] = domain >> 8;
106         memcpy(addr + 2, sa->sa_data, *addr_len - 2);
107         return addr;
111 #endif
114 static const uint8_t month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
116 static bool is_leap_year(int year)
118         return !(year % 4) - !(year % 100) + !(year % 400);
121 #ifndef HAVE_TIMEGM
122 #define add(x) do { t2 = (uintmax_t)t + (x); if (unlikely(t2 < t)) goto ovf; t = t2; } while (0)
123 #define sub(x) do { t2 = (uintmax_t)t - (x); if (unlikely(t2 > t)) goto ovf; t = t2; } while (0)
124 static time_t my_timegm(struct tm *tm)
126         int i;
127         time_t t2;
128         time_t t = 946684800;
129         int year = (unsigned)tm->tm_year - 100;
130         if (unlikely(year > tm->tm_year))
131                 goto ovf;
132         if (year >= 0) {
133                 time_t y400 = year / 400;
134                 year %= 400;
135                 if (unlikely(y400 > signed_maximum(time_t) / 126227808 / 100))
136                         goto ovf;
137                 add(y400 * 126227808 * 100);
138                 for (i = 0; i < year; i++)
139                         add((365 + is_leap_year(i)) * 24 * 3600);
140         }
141         for (i = 0; i < tm->tm_mon; i++) {
142                 add((month_days[i] + (i == 1 && is_leap_year(year))) * 24 * 3600);
143         }
144         add((tm->tm_mday - 1) * 24 * 3600);
145         add(tm->tm_hour * 3600);
146         add(tm->tm_min * 60);
147         add(tm->tm_sec);
148         if (year < 0) {
149                 time_t y400 = (-(unsigned)year / 400);
150                 year = -(-(unsigned)year % 400);
151                 if (unlikely(y400 > signed_maximum(time_t) / 126227808 / 100))
152                         goto ovf;
153                 sub(y400 * 126227808 * 100);
154                 for (i = -1; i >= year; i--)
155                         sub((365 + is_leap_year(400 - i)) * 24 * 3600);
156         }
157         return t;
159 ovf:
160 #ifdef EOVERFLOW
161         errno = EOVERFLOW;
162 #else
163         errno = EINVAL;
164 #endif
165         return (time_t)-1;
167 #undef add
168 #undef sub
169 #else
170 #define my_timegm       timegm
171 #endif
173 #if (!defined(HAVE_GMTIME_R) && !defined(HAVE_LOCALTIME_R)) || defined(THREAD_NONE)
174 #define HAVE_CALENDAR_LOCK
175 static mutex_t calendar_lock;
176 #endif
178 bool os_time_to_calendar(ajla_time_t t, bool local, int *year, int *month, int *day, int *hour, int *min, int *sec, int *usec, int *yday, int *wday, int *is_dst, ajla_error_t *err)
180         ajla_error_t e;
181         struct tm attr_unused xtm;
182         struct tm *tm;
183         ajla_time_t at;
184         time_t tim;
185         if (t >= 0) {
186                 at = t / 1000000;
187         } else {
188                 at = -(-(ajla_utime_t)t / 1000000);
189                 if (t % 1000000)
190                         at--;
191         }
192         tim = at;
193         if (unlikely(tim != at))
194                 goto ovf;
195 #ifdef HAVE_CALENDAR_LOCK
196         mutex_lock(&calendar_lock);
197         tm = local ? localtime(&tim) : gmtime(&tim);
198 #else
199         tm = local ? localtime_r(&tim, &xtm) : gmtime_r(&tim, &xtm);
200 #endif
201         if (unlikely(!tm)) {
202 #ifdef EOVERFLOW
203                 if (errno == EOVERFLOW)
204                         goto ovf;
205 #endif
206                 e = error_from_errno(EC_SYSCALL, errno);
207                 fatal_mayfail(e, err, "can't convert time: %s", error_decode(e));
208 #ifdef HAVE_CALENDAR_LOCK
209                 mutex_unlock(&calendar_lock);
210 #endif
211                 return false;
212         }
213         *year = tm->tm_year + 1900;
214         *month = tm->tm_mon;
215         *day = tm->tm_mday - 1;
216         *hour = tm->tm_hour;
217         *min = tm->tm_min;
218         *sec = tm->tm_sec;
219         *usec = (ajla_utime_t)t - (at * 1000000);
220         *yday = tm->tm_yday;
221         *wday = tm->tm_wday;
222         *is_dst = tm->tm_isdst;
223 #ifdef HAVE_CALENDAR_LOCK
224         mutex_unlock(&calendar_lock);
225 #endif
226         return true;
228 ovf:
229         e = error_ajla(EC_SYNC, AJLA_ERROR_INT_TOO_LARGE);
230         fatal_mayfail(e, err, "can't convert time: %s", error_decode(e));
231         return false;
234 bool os_calendar_to_time(bool local, int year, int month, int day, int hour, int min, int sec, int usec, int is_dst, ajla_time_t *t, ajla_error_t *err)
236         ajla_error_t e;
237         unsigned days;
238         struct tm tm;
239         time_t s;
240         ajla_utime_t at;
241         if (unlikely((int)((unsigned)year - 1900) > year) ||
242             unlikely((unsigned)month >= 12))
243                 goto ovf;
244         days = month_days[month];
245         if (month == 1 && is_leap_year(year))
246                 days++;
247         if (unlikely((unsigned)day >= days) ||
248             unlikely((unsigned)hour >= 24) ||
249             unlikely((unsigned)min >= 60) ||
250             unlikely((unsigned)sec >= 60) ||
251             unlikely((unsigned)usec >= 1000000))
252                 goto ovf;
253         memset(&tm, 0, sizeof(struct tm));
254         tm.tm_year = year - 1900;
255         tm.tm_mon = month;
256         tm.tm_mday = day + 1;
257         tm.tm_hour = hour;
258         tm.tm_min = min;
259         tm.tm_sec = sec;
260         tm.tm_isdst = is_dst;
261         errno = 0;
262         s = local ? mktime(&tm) : my_timegm(&tm);
263         if (unlikely(errno)) {
264 #ifdef EOVERFLOW
265                 if (errno == EOVERFLOW)
266                         goto ovf;
267 #endif
268                 e = error_from_errno(EC_SYSCALL, errno);
269                 fatal_mayfail(e, err, "can't convert time: %s", error_decode(e));
270                 return false;
271         }
272         at = (ajla_utime_t)s * 1000000U;
273         if (unlikely((ajla_time_t)at / 1000000 != (ajla_time_t)s) ||
274             unlikely(at + usec < at)) {
275                 goto ovf;
276         }
277         at += usec;
278         *t = at;
279         return true;
281 ovf:
282         e = error_ajla(EC_SYNC, AJLA_ERROR_INT_TOO_LARGE);
283         fatal_mayfail(e, err, "can't convert time: %s", error_decode(e));
284         return false;
287 static void os_init_calendar_lock(void)
289 #ifdef HAVE_CALENDAR_LOCK
290         mutex_init(&calendar_lock);
291 #endif
294 static void os_done_calendar_lock(void)
296 #ifdef HAVE_CALENDAR_LOCK
297         mutex_done(&calendar_lock);
298 #endif