Skip to content
Snippets Groups Projects
Commit dc9a5773 authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Merge branch 'mktime-timegm-refactor' into 'master'

Refactor mktime, timegm and fix wcpncpy test

See merge request redox-os/relibc!487
parents 455d7323 17076f37
No related branches found
No related tags found
No related merge requests found
...@@ -12,6 +12,10 @@ pub use self::constants::*; ...@@ -12,6 +12,10 @@ pub use self::constants::*;
pub mod constants; pub mod constants;
mod strftime; mod strftime;
const YEARS_PER_ERA: time_t = 400;
const DAYS_PER_ERA: time_t = 146097;
const SECS_PER_DAY: time_t = 24 * 60 * 60;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
pub struct timespec { pub struct timespec {
...@@ -311,9 +315,6 @@ pub unsafe extern "C" fn gmtime_r(clock: *const time_t, result: *mut tm) -> *mut ...@@ -311,9 +315,6 @@ pub unsafe extern "C" fn gmtime_r(clock: *const time_t, result: *mut tm) -> *mut
* Note that we need 0-based months here, though. * Note that we need 0-based months here, though.
* Overall, this implementation should generate correct results as * Overall, this implementation should generate correct results as
* long as the tm_year value will fit in a c_int. */ * long as the tm_year value will fit in a c_int. */
const SECS_PER_DAY: time_t = 24 * 60 * 60;
const DAYS_PER_ERA: time_t = 146097;
let unix_secs = *clock; let unix_secs = *clock;
/* Day number here is possibly negative, remainder will always be /* Day number here is possibly negative, remainder will always be
...@@ -414,44 +415,68 @@ pub unsafe extern "C" fn localtime_r(clock: *const time_t, t: *mut tm) -> *mut t ...@@ -414,44 +415,68 @@ pub unsafe extern "C" fn localtime_r(clock: *const time_t, t: *mut tm) -> *mut t
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn mktime(t: *mut tm) -> time_t { pub unsafe extern "C" fn mktime(timeptr: *mut tm) -> time_t {
let mut year = (*t).tm_year + 1900; /* For the details of the algorithm used here, see
let mut month = (*t).tm_mon; * https://howardhinnant.github.io/date_algorithms.html#days_from_civil
let mut day = (*t).tm_mday as i64 - 1; */
let leap = if leap_year(year) { 1 } else { 0 }; fn inner(timeptr_mut: &mut tm) -> Option<time_t> {
let year = time_t::try_from(timeptr_mut.tm_year)
if year < 1970 { .ok()
day = MONTH_DAYS[if leap_year(year) { 1 } else { 0 }][(*t).tm_mon as usize] as i64 - day; .and_then(|tm_year| tm_year.checked_add(1900))?;
let month = time_t::try_from(timeptr_mut.tm_mon).ok()?;
while year < 1969 { let mday = time_t::try_from(timeptr_mut.tm_mday).ok()?;
year += 1;
day += if leap_year(year) { 366 } else { 365 }; let hour = time_t::try_from(timeptr_mut.tm_hour).ok()?;
} let min = time_t::try_from(timeptr_mut.tm_min).ok()?;
let sec = time_t::try_from(timeptr_mut.tm_sec).ok()?;
while month < 11 {
month += 1; // TODO: handle tm_isdst
day += MONTH_DAYS[leap][month as usize] as i64;
} let year_transformed = if month < 2 { year - 1 } else { year };
(-(day * (60 * 60 * 24) let era = year_transformed.div_euclid(YEARS_PER_ERA);
- (((*t).tm_hour as i64) * (60 * 60) + ((*t).tm_min as i64) * 60 + (*t).tm_sec as i64))) let year_of_era = year_transformed.rem_euclid(YEARS_PER_ERA);
as time_t
} else { let day_of_year =
while year > 1970 { (153 * (if month > 1 { month - 2 } else { month + 10 }) + 2) / 5 + mday - 1; // adapted for zero-based months
year -= 1;
day += if leap_year(year) { 366 } else { 365 }; let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
let unix_days = era * DAYS_PER_ERA + day_of_era - 719468;
let secs_of_day = hour * (60 * 60) + min * 60 + sec;
let unix_secs = unix_days
.checked_mul(SECS_PER_DAY)
.and_then(|day_secs| day_secs.checked_add(secs_of_day))?;
// Normalize input struct with values from their standard ranges
let mut normalized_tm = tm {
tm_sec: 0,
tm_min: 0,
tm_hour: 0,
tm_mday: 0,
tm_mon: 0,
tm_year: 0,
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
tm_gmtoff: 0,
tm_zone: UTC,
};
if unsafe { gmtime_r(&unix_secs, &mut normalized_tm).is_null() } {
None
} else {
*timeptr_mut = normalized_tm;
Some(unix_secs)
} }
}
while month > 0 { match inner(&mut *timeptr) {
month -= 1; Some(unix_secs) => unix_secs,
day += MONTH_DAYS[leap][month as usize] as i64; None => {
platform::ERRNO.set(EOVERFLOW);
-1 as time_t
} }
(day * (60 * 60 * 24)
+ ((*t).tm_hour as i64) * (60 * 60)
+ ((*t).tm_min as i64) * 60
+ (*t).tm_sec as i64) as time_t
} }
} }
...@@ -502,20 +527,7 @@ pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t { ...@@ -502,20 +527,7 @@ pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t { pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t {
let mut y = (*tm).tm_year as time_t + 1900; mktime(tm)
let mut m = (*tm).tm_mon as time_t + 1;
if m <= 2 {
y -= 1;
m += 12;
}
let d = (*tm).tm_mday as time_t;
let h = (*tm).tm_hour as time_t;
let mi = (*tm).tm_min as time_t;
let s = (*tm).tm_sec as time_t;
(365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400
+ 3600 * h
+ 60 * mi
+ s
} }
// #[no_mangle] // #[no_mangle]
......
...@@ -4,12 +4,34 @@ ...@@ -4,12 +4,34 @@
int main() { int main() {
wchar_t src[] = L"Привет мир"; wchar_t src[] = L"Привет мир";
wchar_t dest[10];
wchar_t* result = wcpncpy(dest, src, 5);
assert(wcsncmp(dest, src, 5) == 0); size_t n_input = 10;
assert(*result == L'\0'); size_t n_short = 6;
assert(result == dest + 5); size_t n_long = 15;
// Initialize with sentinel values to detect exactly how much is overwritten
wchar_t dest_short[] = L"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x90\x12\x34\x56\x78";
wchar_t dest_long[] = L"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x90\x12\x34\x56\x78";
wchar_t expected_short[] = L"Привет\x34\x56\x78\x90\x12\x34\x56\x78";
// The "short" test should copy exactly n_short characters without terminating null
wchar_t* result_short = wcpncpy(dest_short, src, n_short);
assert(wcsncmp(dest_short, src, n_short) == 0);
assert(result_short == dest_short + n_short);
for (size_t i = n_short; i < n_long; i++) {
// Check that the sentinel characters have not been overwritten
assert(dest_short[i] == expected_short[i]);
}
// The "long" test should write the input string, with nulls appended up to n_long
wchar_t* result_long = wcpncpy(dest_long, src, n_long);
assert(wcsncmp(dest_long, src, n_long) == 0);
assert(result_long == dest_long + n_input);
for (size_t i = n_input; i < n_long; i++) {
// Check that nulls are written up to n_long
assert(dest_long[i] == L'\0');
}
return 0; return 0;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment