mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 07:08:48 +00:00
Heavily improve time handling on Windows for time.now() and os.File_Info
This commit is contained in:
@@ -41,9 +41,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
// fi.mode |= file_type_mode(h);
|
// fi.mode |= file_type_mode(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
window_set_file_info_times(&fi, d)
|
||||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
|
||||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
|
||||||
|
|
||||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||||
return
|
return
|
||||||
|
|||||||
+10
-10
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
window_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
|
||||||
|
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||||
|
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||||
|
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||||
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
|
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
|
||||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||||
@@ -235,9 +242,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
|
|||||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||||
|
|
||||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
window_set_file_info_times(&fi, d)
|
||||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
|
||||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
|
||||||
|
|
||||||
fi.fullpath, e = full_path_from_name(name)
|
fi.fullpath, e = full_path_from_name(name)
|
||||||
fi.name = basename(fi.fullpath)
|
fi.name = basename(fi.fullpath)
|
||||||
@@ -252,9 +257,7 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
|
|||||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||||
|
|
||||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
window_set_file_info_times(&fi, d)
|
||||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
|
||||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
|
||||||
|
|
||||||
fi.fullpath, e = full_path_from_name(name)
|
fi.fullpath, e = full_path_from_name(name)
|
||||||
fi.name = basename(fi.fullpath)
|
fi.name = basename(fi.fullpath)
|
||||||
@@ -290,10 +293,7 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
|
|||||||
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
|
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
|
||||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||||
|
|
||||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
window_set_file_info_times(&fi, &d)
|
||||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
|
||||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
|
||||||
|
|
||||||
|
|
||||||
return fi, ERROR_NONE
|
return fi, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,16 @@ foreign kernel32 {
|
|||||||
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
|
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
|
||||||
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
||||||
GetSystemTimePreciseAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
GetSystemTimePreciseAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
||||||
|
FileTimeToSystemTime :: proc(lpFileTime: ^FILETIME, lpSystemTime: ^SYSTEMTIME) -> BOOL ---
|
||||||
|
SystemTimeToTzSpecificLocalTime :: proc(
|
||||||
|
lpTimeZoneInformation: ^TIME_ZONE_INFORMATION,
|
||||||
|
lpUniversalTime: ^SYSTEMTIME,
|
||||||
|
lpLocalTime: ^SYSTEMTIME,
|
||||||
|
) -> BOOL ---
|
||||||
|
SystemTimeToFileTime :: proc(
|
||||||
|
lpSystemTime: ^SYSTEMTIME,
|
||||||
|
lpFileTime: LPFILETIME,
|
||||||
|
) -> BOOL ---
|
||||||
CreateEventW :: proc(
|
CreateEventW :: proc(
|
||||||
lpEventAttributes: LPSECURITY_ATTRIBUTES,
|
lpEventAttributes: LPSECURITY_ATTRIBUTES,
|
||||||
bManualReset: BOOL,
|
bManualReset: BOOL,
|
||||||
|
|||||||
@@ -2267,9 +2267,10 @@ FILETIME :: struct {
|
|||||||
|
|
||||||
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
|
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
|
||||||
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
|
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
|
||||||
return (t - 0x019db1ded53e8000) * 100
|
return (t - 116444736000000000) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OVERLAPPED :: struct {
|
OVERLAPPED :: struct {
|
||||||
Internal: ^c_ulong,
|
Internal: ^c_ulong,
|
||||||
InternalHigh: ^c_ulong,
|
InternalHigh: ^c_ulong,
|
||||||
@@ -2943,6 +2944,16 @@ SYSTEMTIME :: struct {
|
|||||||
milliseconds: WORD,
|
milliseconds: WORD,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TIME_ZONE_INFORMATION :: struct {
|
||||||
|
Bias: LONG,
|
||||||
|
StandardName: [32]WCHAR,
|
||||||
|
StandardDate: SYSTEMTIME,
|
||||||
|
StandardBias: LONG,
|
||||||
|
DaylightName: [32]WCHAR,
|
||||||
|
DaylightDate: SYSTEMTIME,
|
||||||
|
DaylightBias: LONG,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
IMAGE_DOS_HEADER :: struct {
|
IMAGE_DOS_HEADER :: struct {
|
||||||
|
|||||||
+34
-32
@@ -17,7 +17,7 @@ MAX_DURATION :: Duration(1<<63 - 1)
|
|||||||
IS_SUPPORTED :: _IS_SUPPORTED
|
IS_SUPPORTED :: _IS_SUPPORTED
|
||||||
|
|
||||||
Time :: struct {
|
Time :: struct {
|
||||||
_nsec: i64, // zero is 1970-01-01 00:00:00
|
_nsec: i64, // Measured in UNIX nanonseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
Month :: enum int {
|
Month :: enum int {
|
||||||
@@ -59,36 +59,36 @@ sleep :: proc "contextless" (d: Duration) {
|
|||||||
_sleep(d)
|
_sleep(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
|
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||||
if !running {
|
if !running {
|
||||||
_start_time = tick_now()
|
_start_time = tick_now()
|
||||||
running = true
|
running = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch_stop :: proc(using stopwatch: ^Stopwatch) {
|
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||||
if running {
|
if running {
|
||||||
_accumulation += tick_diff(_start_time, tick_now())
|
_accumulation += tick_diff(_start_time, tick_now())
|
||||||
running = false
|
running = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch_reset :: proc(using stopwatch: ^Stopwatch) {
|
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||||
_accumulation = {}
|
_accumulation = {}
|
||||||
running = false
|
running = false
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration {
|
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
|
||||||
if !running { return _accumulation }
|
if !running { return _accumulation }
|
||||||
return _accumulation + tick_diff(_start_time, tick_now())
|
return _accumulation + tick_diff(_start_time, tick_now())
|
||||||
}
|
}
|
||||||
|
|
||||||
diff :: proc(start, end: Time) -> Duration {
|
diff :: proc "contextless" (start, end: Time) -> Duration {
|
||||||
d := end._nsec - start._nsec
|
d := end._nsec - start._nsec
|
||||||
return Duration(d)
|
return Duration(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
since :: proc(start: Time) -> Duration {
|
since :: proc "contextless" (start: Time) -> Duration {
|
||||||
return diff(start, now())
|
return diff(start, now())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +117,8 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
|
|||||||
return f64(hour) + f64(nsec)/(60*60*1e9)
|
return f64(hour) + f64(nsec)/(60*60*1e9)
|
||||||
}
|
}
|
||||||
|
|
||||||
duration_round :: proc(d, m: Duration) -> Duration {
|
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
|
||||||
_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
|
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
|
||||||
return u64(x)+u64(x) < u64(y)
|
return u64(x)+u64(x) < u64(y)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,45 +146,45 @@ duration_round :: proc(d, m: Duration) -> Duration {
|
|||||||
return MAX_DURATION
|
return MAX_DURATION
|
||||||
}
|
}
|
||||||
|
|
||||||
duration_truncate :: proc(d, m: Duration) -> Duration {
|
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
|
||||||
return d if m <= 0 else d - d%m
|
return d if m <= 0 else d - d%m
|
||||||
}
|
}
|
||||||
|
|
||||||
date :: proc(t: Time) -> (year: int, month: Month, day: int) {
|
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
|
||||||
year, month, day, _ = _abs_date(_time_abs(t), true)
|
year, month, day, _ = _abs_date(_time_abs(t), true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
year :: proc(t: Time) -> (year: int) {
|
year :: proc "contextless" (t: Time) -> (year: int) {
|
||||||
year, _, _, _ = _date(t, true)
|
year, _, _, _ = _date(t, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
month :: proc(t: Time) -> (month: Month) {
|
month :: proc "contextless" (t: Time) -> (month: Month) {
|
||||||
_, month, _, _ = _date(t, true)
|
_, month, _, _ = _date(t, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
day :: proc(t: Time) -> (day: int) {
|
day :: proc "contextless" (t: Time) -> (day: int) {
|
||||||
_, _, day, _ = _date(t, true)
|
_, _, day, _ = _date(t, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
|
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
|
||||||
|
|
||||||
clock_from_time :: proc(t: Time) -> (hour, min, sec: int) {
|
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
|
||||||
return clock_from_seconds(_time_abs(t))
|
return clock_from_seconds(_time_abs(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) {
|
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
|
||||||
return clock_from_seconds(u64(d/1e9))
|
return clock_from_seconds(u64(d/1e9))
|
||||||
}
|
}
|
||||||
|
|
||||||
clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) {
|
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
|
||||||
return clock_from_duration(stopwatch_duration(s))
|
return clock_from_duration(stopwatch_duration(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
|
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
|
||||||
sec = int(nsec % SECONDS_PER_DAY)
|
sec = int(nsec % SECONDS_PER_DAY)
|
||||||
hour = sec / SECONDS_PER_HOUR
|
hour = sec / SECONDS_PER_HOUR
|
||||||
sec -= hour * SECONDS_PER_HOUR
|
sec -= hour * SECONDS_PER_HOUR
|
||||||
@@ -193,11 +193,11 @@ clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
read_cycle_counter :: proc() -> u64 {
|
read_cycle_counter :: proc "contextless" () -> u64 {
|
||||||
return u64(intrinsics.read_cycle_counter())
|
return u64(intrinsics.read_cycle_counter())
|
||||||
}
|
}
|
||||||
|
|
||||||
unix :: proc(sec: i64, nsec: i64) -> Time {
|
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
|
||||||
sec, nsec := sec, nsec
|
sec, nsec := sec, nsec
|
||||||
if nsec < 0 || nsec >= 1e9 {
|
if nsec < 0 || nsec >= 1e9 {
|
||||||
n := nsec / 1e9
|
n := nsec / 1e9
|
||||||
@@ -208,20 +208,20 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
|
|||||||
sec -= 1
|
sec -= 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
|
return Time{(sec*1e9 + nsec)}
|
||||||
}
|
}
|
||||||
|
|
||||||
to_unix_seconds :: time_to_unix
|
to_unix_seconds :: time_to_unix
|
||||||
time_to_unix :: proc(t: Time) -> i64 {
|
time_to_unix :: proc "contextless" (t: Time) -> i64 {
|
||||||
return t._nsec/1e9
|
return t._nsec/1e9
|
||||||
}
|
}
|
||||||
|
|
||||||
to_unix_nanoseconds :: time_to_unix_nano
|
to_unix_nanoseconds :: time_to_unix_nano
|
||||||
time_to_unix_nano :: proc(t: Time) -> i64 {
|
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
|
||||||
return t._nsec
|
return t._nsec
|
||||||
}
|
}
|
||||||
|
|
||||||
time_add :: proc(t: Time, d: Duration) -> Time {
|
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
|
||||||
return Time{t._nsec + i64(d)}
|
return Time{t._nsec + i64(d)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ time_add :: proc(t: Time, d: Duration) -> Time {
|
|||||||
// On Windows it depends but is comparable with regular sleep in the worst case.
|
// On Windows it depends but is comparable with regular sleep in the worst case.
|
||||||
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
|
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
|
||||||
// tell Windows to use a more accurate timer for your process.
|
// tell Windows to use a more accurate timer for your process.
|
||||||
accurate_sleep :: proc(d: Duration) {
|
accurate_sleep :: proc "contextless" (d: Duration) {
|
||||||
to_sleep, estimate, mean, m2, count: Duration
|
to_sleep, estimate, mean, m2, count: Duration
|
||||||
|
|
||||||
to_sleep = d
|
to_sleep = d
|
||||||
@@ -279,19 +279,19 @@ ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
|
|||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
_date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||||
year, month, day, yday = _abs_date(_time_abs(t), full)
|
year, month, day, yday = _abs_date(_time_abs(t), full)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_time_abs :: proc(t: Time) -> u64 {
|
_time_abs :: proc "contextless" (t: Time) -> u64 {
|
||||||
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
|
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||||
_is_leap_year :: proc(year: int) -> bool {
|
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,9 +352,11 @@ _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, y
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||||
divmod :: proc(year: int, divisor: int) -> (div: int, mod: int) {
|
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
|
||||||
assert(divisor > 0)
|
if divisor <= 0 {
|
||||||
|
intrinsics.debug_trap()
|
||||||
|
}
|
||||||
div = int(year / divisor)
|
div = int(year / divisor)
|
||||||
mod = year % divisor
|
mod = year % divisor
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -7,9 +7,16 @@ _IS_SUPPORTED :: true
|
|||||||
|
|
||||||
_now :: proc "contextless" () -> Time {
|
_now :: proc "contextless" () -> Time {
|
||||||
file_time: win32.FILETIME
|
file_time: win32.FILETIME
|
||||||
win32.GetSystemTimeAsFileTime(&file_time)
|
|
||||||
ns := win32.FILETIME_as_unix_nanoseconds(file_time)
|
ns: i64
|
||||||
return Time{_nsec=ns}
|
|
||||||
|
// monotonic
|
||||||
|
win32.GetSystemTimePreciseAsFileTime(&file_time)
|
||||||
|
|
||||||
|
dt := u64(transmute(u64le)file_time) // in 100ns units
|
||||||
|
ns = i64((dt - 116444736000000000) * 100) // convert to ns
|
||||||
|
|
||||||
|
return unix(0, ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
_sleep :: proc "contextless" (d: Duration) {
|
_sleep :: proc "contextless" (d: Duration) {
|
||||||
|
|||||||
Reference in New Issue
Block a user