diff --git a/c/time/legacy_functions_other.go b/c/time/legacy_functions_other.go new file mode 100644 index 00000000..71d97797 --- /dev/null +++ b/c/time/legacy_functions_other.go @@ -0,0 +1,35 @@ +//go:build !windows && !((darwin && arm64 && !ios) || (linux && (amd64 || arm64) && !android)) + +package time + +import ( + "github.com/goplus/lib/c" + _ "unsafe" +) + +//go:linkname Time C.time +func Time(timer *TimeT) TimeT + +//go:linkname Mktime C.mktime +func Mktime(timer *Tm) TimeT + +//go:linkname Difftime C.difftime +func Difftime(end, start TimeT) float64 + +//go:linkname Gmtime C.gmtime +func Gmtime(timer *TimeT) *Tm + +//go:linkname Localtime C.localtime +func Localtime(timer *TimeT) *Tm + +//go:linkname Clock C.clock +func Clock() ClockT + +//go:linkname ClockGettime C.clock_gettime +func ClockGettime(clkId ClockidT, tp *Timespec) c.Int + +//go:linkname ClockSettime C.clock_settime +func ClockSettime(clkId ClockidT, tp *Timespec) c.Int + +//go:linkname ClockGetres C.clock_getres +func ClockGetres(clkId ClockidT, res *Timespec) c.Int diff --git a/c/time/libc_darwin_arm64.go b/c/time/libc_darwin_arm64.go new file mode 100644 index 00000000..d4080368 --- /dev/null +++ b/c/time/libc_darwin_arm64.go @@ -0,0 +1,120 @@ +//go:build darwin && arm64 && !ios + +// Code generated by llcppg; DO NOT EDIT. +// Classified from the common libc binding. + +package time + +import ( + "github.com/goplus/lib/c" + _ "unsafe" +) + +// ISO/IEC 9899:201x 7.27.2.5 The timespec_get function +const TIME_UTC = 1 + +//go:linkname Tzname C.tzname +var Tzname **c.Char + +//go:linkname GetdateErr C.getdate_err +var GetdateErr c.Int + +//go:linkname Daylight C.daylight +var Daylight c.Int + +// llgo:link (*Tm).Asctime C.asctime +func (self *Tm) Asctime() *c.Char { + return nil +} + +//go:linkname Clock C.clock +func Clock() ClockT + +//go:linkname NativeCtime C.ctime +func NativeCtime(_llcppg_param1 *TimeT) *c.Char + +//go:linkname Difftime C.difftime +func Difftime(_llcppg_param1 TimeT, _llcppg_param2 TimeT) c.Double + +//go:linkname Getdate C.getdate +func Getdate(_llcppg_param1 *c.Char) *Tm + +//go:linkname Gmtime C.gmtime +func Gmtime(_llcppg_param1 *TimeT) *Tm + +//go:linkname Localtime C.localtime +func Localtime(_llcppg_param1 *TimeT) *Tm + +//go:linkname Mktime C.mktime +func Mktime(self *Tm) TimeT + +//go:linkname NativeStrftime C.strftime +func NativeStrftime(_llcppg_param1 *c.Char, _llcppg_param2 c.SizeT, _llcppg_param3 *c.Char, _llcppg_param4 *Tm) c.SizeT + +//go:linkname Strptime C.strptime +func Strptime(_llcppg_param1 *c.Char, _llcppg_param2 *c.Char, _llcppg_param3 *Tm) *c.Char + +//go:linkname Time C.time +func Time(_llcppg_param1 *TimeT) TimeT + +//go:linkname Tzset C.tzset +func Tzset() + +// [TSF] Thread safe functions +// +// llgo:link (*Tm).AsctimeR C.asctime_r +func (self *Tm) AsctimeR(_llcppg_param2 *c.Char) *c.Char { + return nil +} + +//go:linkname CtimeR C.ctime_r +func CtimeR(_llcppg_param1 *TimeT, _llcppg_param2 *c.Char) *c.Char + +//go:linkname GmtimeR C.gmtime_r +func GmtimeR(_llcppg_param1 *TimeT, _llcppg_param2 *Tm) *Tm + +//go:linkname LocaltimeR C.localtime_r +func LocaltimeR(_llcppg_param1 *TimeT, _llcppg_param2 *Tm) *Tm + +//go:linkname Posix2time C.posix2time +func Posix2time(_llcppg_param1 TimeT) TimeT + +//go:linkname Tzsetwall C.tzsetwall +func Tzsetwall() + +//go:linkname Time2posix C.time2posix +func Time2posix(_llcppg_param1 TimeT) TimeT + +// llgo:link (*Tm).Timelocal C.timelocal +func (self *Tm) Timelocal() TimeT { + return 0 +} + +// llgo:link (*Tm).Timegm C.timegm +func (self *Tm) Timegm() TimeT { + return 0 +} + +// llgo:link (*Timespec).Nanosleep C.nanosleep +func (self *Timespec) Nanosleep(__rmtp *Timespec) c.Int { + return 0 +} + +//go:linkname ClockGetres C.clock_getres +func ClockGetres(self ClockidT, __res *Timespec) c.Int + +//go:linkname ClockGettime C.clock_gettime +func ClockGettime(self ClockidT, __tp *Timespec) c.Int + +// llgo:link ClockidT.ClockGettimeNsecNp C.clock_gettime_nsec_np +func (self ClockidT) ClockGettimeNsecNp() c.UlongLong { + return 0 +} + +//go:linkname ClockSettime C.clock_settime +func ClockSettime(self ClockidT, __tp *Timespec) c.Int + +// llgo:link (*Timespec).Get C.timespec_get +func (self *Timespec) Get(base c.Int) c.Int { + return 0 +} diff --git a/c/time/libc_linux_amd64.go b/c/time/libc_linux_amd64.go new file mode 100644 index 00000000..3d171738 --- /dev/null +++ b/c/time/libc_linux_amd64.go @@ -0,0 +1,219 @@ +//go:build linux && amd64 && !android + +// Code generated by llcppg; DO NOT EDIT. +// Classified from the common libc binding. + +package time + +import ( + "github.com/goplus/lib/c" + _ "unsafe" +) + +// Timer ID returned by `timer_create'. +type X__TimerT uintptr + +// Time base values for timespec_get. +const TIME_UTC = 1 + +// POSIX.1b structure for timer start values and intervals. +type Itimerspec struct { + ItInterval Timespec + ItValue Timespec +} + +// Timer ID returned by `timer_create'. +type TimerT = X__TimerT + +// Time used by the program so far (user time + system time). +// The result / CLOCKS_PER_SECOND is program time in seconds. +// +//go:linkname Clock C.clock +func Clock() ClockT + +// Return the current time and put it in *TIMER if TIMER is not NULL. +// +//go:linkname Time C.time +func Time(__timer *TimeT) TimeT + +// Return the difference between TIME1 and TIME0. +// +//go:linkname Difftime C.difftime +func Difftime(__time1 TimeT, __time0 TimeT) c.Double + +// Return the `time_t' representation of TP and normalize TP. +// +//go:linkname Mktime C.mktime +func Mktime(self *Tm) TimeT + +// Format TP into S according to FORMAT. +// Write no more than MAXSIZE characters and return the number +// of characters written, or 0 if it would exceed MAXSIZE. +// +//go:linkname NativeStrftime C.strftime +func NativeStrftime(__s *c.Char, __maxsize c.SizeT, __format *c.Char, __tp *Tm) c.SizeT + +//go:linkname StrftimeL C.strftime_l +func StrftimeL(__s *c.Char, __maxsize c.SizeT, __format *c.Char, __tp *Tm, __loc c.X__LocaleT) c.SizeT + +// Return the `struct tm' representation of *TIMER +// in Universal Coordinated Time (aka Greenwich Mean Time). +// +//go:linkname Gmtime C.gmtime +func Gmtime(__timer *TimeT) *Tm + +// Return the `struct tm' representation +// of *TIMER in the local timezone. +// +//go:linkname Localtime C.localtime +func Localtime(__timer *TimeT) *Tm + +// Return the `struct tm' representation of *TIMER in UTC, +// using *TP to store the result. +// +//go:linkname GmtimeR C.gmtime_r +func GmtimeR(__timer *TimeT, __tp *Tm) *Tm + +// Return the `struct tm' representation of *TIMER in local time, +// using *TP to store the result. +// +//go:linkname LocaltimeR C.localtime_r +func LocaltimeR(__timer *TimeT, __tp *Tm) *Tm + +// Return a string of the form "Day Mon dd hh:mm:ss yyyy\n" +// that is the representation of TP in this format. +// +// llgo:link (*Tm).Asctime C.asctime +func (self *Tm) Asctime() *c.Char { + return nil +} + +// Equivalent to `asctime (localtime (timer))'. +// +//go:linkname NativeCtime C.ctime +func NativeCtime(__timer *TimeT) *c.Char + +// Return in BUF a string of the form "Day Mon dd hh:mm:ss yyyy\n" +// that is the representation of TP in this format. +// +// llgo:link (*Tm).AsctimeR C.asctime_r +func (self *Tm) AsctimeR(__buf *c.Char) *c.Char { + return nil +} + +// Equivalent to `asctime_r (localtime_r (timer, *TMP*), buf)'. +// +//go:linkname CtimeR C.ctime_r +func CtimeR(__timer *TimeT, __buf *c.Char) *c.Char + +// Same as above. +// +//go:linkname Tzname C.tzname +var Tzname [2]*c.Char + +// Set time conversion information from the TZ environment variable. +// If TZ is not defined, a locale-dependent default is used. +// +//go:linkname Tzset C.tzset +func Tzset() + +//go:linkname Daylight C.daylight +var Daylight c.Int + +// Set the system time to *WHEN. +// This call is restricted to the superuser. +// +//go:linkname Stime C.stime +func Stime(__when *TimeT) c.Int + +// Like `mktime', but for TP represents Universal Time, not local time. +// +// llgo:link (*Tm).Timegm C.timegm +func (self *Tm) Timegm() TimeT { + return 0 +} + +// Another name for `mktime'. +// +// llgo:link (*Tm).Timelocal C.timelocal +func (self *Tm) Timelocal() TimeT { + return 0 +} + +// Return the number of days in YEAR. +// +//go:linkname Dysize C.dysize +func Dysize(__year c.Int) c.Int + +// Pause execution for a number of nanoseconds. +// +// This function is a cancellation point and therefore not marked with +// __THROW. +// +// llgo:link (*Timespec).Nanosleep C.nanosleep +func (self *Timespec) Nanosleep(__remaining *Timespec) c.Int { + return 0 +} + +// Get resolution of clock CLOCK_ID. +// +//go:linkname ClockGetres C.clock_getres +func ClockGetres(__clock_id ClockidT, __res *Timespec) c.Int + +// Get current value of clock CLOCK_ID and store it in TP. +// +//go:linkname ClockGettime C.clock_gettime +func ClockGettime(__clock_id ClockidT, __tp *Timespec) c.Int + +// Set clock CLOCK_ID to value TP. +// +//go:linkname ClockSettime C.clock_settime +func ClockSettime(__clock_id ClockidT, __tp *Timespec) c.Int + +// High-resolution sleep with the specified clock. +// +// This function is a cancellation point and therefore not marked with +// __THROW. +// +//go:linkname ClockNanosleep C.clock_nanosleep +func ClockNanosleep(__clock_id ClockidT, __flags c.Int, __req *Timespec, __rem *Timespec) c.Int + +// Return clock ID for CPU-time clock. +// +//go:linkname ClockGetcpuclockid C.clock_getcpuclockid +func ClockGetcpuclockid(__pid c.Int, __clock_id *ClockidT) c.Int + +// Delete timer TIMERID. +// +// llgo:link TimerT.TimerDelete C.timer_delete +func (self TimerT) TimerDelete() c.Int { + return 0 +} + +// Set timer TIMERID to VALUE, returning old value in OVALUE. +// +// llgo:link TimerT.TimerSettime C.timer_settime +func (self TimerT) TimerSettime(__flags c.Int, __value *Itimerspec, __ovalue *Itimerspec) c.Int { + return 0 +} + +// Get current value of timer TIMERID and store it in VALUE. +// +// llgo:link TimerT.TimerGettime C.timer_gettime +func (self TimerT) TimerGettime(__value *Itimerspec) c.Int { + return 0 +} + +// Get expiration overrun for timer TIMERID. +// +// llgo:link TimerT.TimerGetoverrun C.timer_getoverrun +func (self TimerT) TimerGetoverrun() c.Int { + return 0 +} + +// Set TS to calendar time based in time base BASE. +// +// llgo:link (*Timespec).Get C.timespec_get +func (self *Timespec) Get(__base c.Int) c.Int { + return 0 +} diff --git a/c/time/libc_linux_arm64.go b/c/time/libc_linux_arm64.go new file mode 100644 index 00000000..e7ab4ae1 --- /dev/null +++ b/c/time/libc_linux_arm64.go @@ -0,0 +1,219 @@ +//go:build linux && arm64 && !android + +// Code generated by llcppg; DO NOT EDIT. +// Classified from the common libc binding. + +package time + +import ( + "github.com/goplus/lib/c" + _ "unsafe" +) + +// Timer ID returned by `timer_create'. +type X__TimerT uintptr + +// Time base values for timespec_get. +const TIME_UTC = 1 + +// POSIX.1b structure for timer start values and intervals. +type Itimerspec struct { + ItInterval Timespec + ItValue Timespec +} + +// Timer ID returned by `timer_create'. +type TimerT = X__TimerT + +// Time used by the program so far (user time + system time). +// The result / CLOCKS_PER_SECOND is program time in seconds. +// +//go:linkname Clock C.clock +func Clock() ClockT + +// Return the current time and put it in *TIMER if TIMER is not NULL. +// +//go:linkname Time C.time +func Time(__timer *TimeT) TimeT + +// Return the difference between TIME1 and TIME0. +// +//go:linkname Difftime C.difftime +func Difftime(__time1 TimeT, __time0 TimeT) c.Double + +// Return the `time_t' representation of TP and normalize TP. +// +//go:linkname Mktime C.mktime +func Mktime(self *Tm) TimeT + +// Format TP into S according to FORMAT. +// Write no more than MAXSIZE characters and return the number +// of characters written, or 0 if it would exceed MAXSIZE. +// +//go:linkname NativeStrftime C.strftime +func NativeStrftime(__s *uint8, __maxsize c.SizeT, __format *uint8, __tp *Tm) c.SizeT + +//go:linkname StrftimeL C.strftime_l +func StrftimeL(__s *uint8, __maxsize c.SizeT, __format *uint8, __tp *Tm, __loc c.X__LocaleT) c.SizeT + +// Return the `struct tm' representation of *TIMER +// in Universal Coordinated Time (aka Greenwich Mean Time). +// +//go:linkname Gmtime C.gmtime +func Gmtime(__timer *TimeT) *Tm + +// Return the `struct tm' representation +// of *TIMER in the local timezone. +// +//go:linkname Localtime C.localtime +func Localtime(__timer *TimeT) *Tm + +// Return the `struct tm' representation of *TIMER in UTC, +// using *TP to store the result. +// +//go:linkname GmtimeR C.gmtime_r +func GmtimeR(__timer *TimeT, __tp *Tm) *Tm + +// Return the `struct tm' representation of *TIMER in local time, +// using *TP to store the result. +// +//go:linkname LocaltimeR C.localtime_r +func LocaltimeR(__timer *TimeT, __tp *Tm) *Tm + +// Return a string of the form "Day Mon dd hh:mm:ss yyyy\n" +// that is the representation of TP in this format. +// +// llgo:link (*Tm).Asctime C.asctime +func (self *Tm) Asctime() *uint8 { + return nil +} + +// Equivalent to `asctime (localtime (timer))'. +// +//go:linkname NativeCtime C.ctime +func NativeCtime(__timer *TimeT) *uint8 + +// Return in BUF a string of the form "Day Mon dd hh:mm:ss yyyy\n" +// that is the representation of TP in this format. +// +// llgo:link (*Tm).AsctimeR C.asctime_r +func (self *Tm) AsctimeR(__buf *uint8) *uint8 { + return nil +} + +// Equivalent to `asctime_r (localtime_r (timer, *TMP*), buf)'. +// +//go:linkname CtimeR C.ctime_r +func CtimeR(__timer *TimeT, __buf *uint8) *uint8 + +// Same as above. +// +//go:linkname Tzname C.tzname +var Tzname [2]*uint8 + +// Set time conversion information from the TZ environment variable. +// If TZ is not defined, a locale-dependent default is used. +// +//go:linkname Tzset C.tzset +func Tzset() + +//go:linkname Daylight C.daylight +var Daylight c.Int + +// Set the system time to *WHEN. +// This call is restricted to the superuser. +// +//go:linkname Stime C.stime +func Stime(__when *TimeT) c.Int + +// Like `mktime', but for TP represents Universal Time, not local time. +// +// llgo:link (*Tm).Timegm C.timegm +func (self *Tm) Timegm() TimeT { + return 0 +} + +// Another name for `mktime'. +// +// llgo:link (*Tm).Timelocal C.timelocal +func (self *Tm) Timelocal() TimeT { + return 0 +} + +// Return the number of days in YEAR. +// +//go:linkname Dysize C.dysize +func Dysize(__year c.Int) c.Int + +// Pause execution for a number of nanoseconds. +// +// This function is a cancellation point and therefore not marked with +// __THROW. +// +// llgo:link (*Timespec).Nanosleep C.nanosleep +func (self *Timespec) Nanosleep(__remaining *Timespec) c.Int { + return 0 +} + +// Get resolution of clock CLOCK_ID. +// +//go:linkname ClockGetres C.clock_getres +func ClockGetres(__clock_id ClockidT, __res *Timespec) c.Int + +// Get current value of clock CLOCK_ID and store it in TP. +// +//go:linkname ClockGettime C.clock_gettime +func ClockGettime(__clock_id ClockidT, __tp *Timespec) c.Int + +// Set clock CLOCK_ID to value TP. +// +//go:linkname ClockSettime C.clock_settime +func ClockSettime(__clock_id ClockidT, __tp *Timespec) c.Int + +// High-resolution sleep with the specified clock. +// +// This function is a cancellation point and therefore not marked with +// __THROW. +// +//go:linkname ClockNanosleep C.clock_nanosleep +func ClockNanosleep(__clock_id ClockidT, __flags c.Int, __req *Timespec, __rem *Timespec) c.Int + +// Return clock ID for CPU-time clock. +// +//go:linkname ClockGetcpuclockid C.clock_getcpuclockid +func ClockGetcpuclockid(__pid c.Int, __clock_id *ClockidT) c.Int + +// Delete timer TIMERID. +// +// llgo:link TimerT.TimerDelete C.timer_delete +func (self TimerT) TimerDelete() c.Int { + return 0 +} + +// Set timer TIMERID to VALUE, returning old value in OVALUE. +// +// llgo:link TimerT.TimerSettime C.timer_settime +func (self TimerT) TimerSettime(__flags c.Int, __value *Itimerspec, __ovalue *Itimerspec) c.Int { + return 0 +} + +// Get current value of timer TIMERID and store it in VALUE. +// +// llgo:link TimerT.TimerGettime C.timer_gettime +func (self TimerT) TimerGettime(__value *Itimerspec) c.Int { + return 0 +} + +// Get expiration overrun for timer TIMERID. +// +// llgo:link TimerT.TimerGetoverrun C.timer_getoverrun +func (self TimerT) TimerGetoverrun() c.Int { + return 0 +} + +// Set TS to calendar time based in time base BASE. +// +// llgo:link (*Timespec).Get C.timespec_get +func (self *Timespec) Get(__base c.Int) c.Int { + return 0 +} diff --git a/c/time/llcppg.pub b/c/time/llcppg.pub index 4f30163d..ef77fae6 100644 --- a/c/time/llcppg.pub +++ b/c/time/llcppg.pub @@ -1,5 +1,8 @@ -T time_t TimeT -T tm Tm +T __timer_t X__TimerT T clock_t ClockT T clockid_t ClockidT +T itimerspec Itimerspec +T time_t TimeT +T timer_t TimerT T timespec Timespec +T tm Tm diff --git a/c/time/native_char_signed_llgo_test.go b/c/time/native_char_signed_llgo_test.go new file mode 100644 index 00000000..2bc0955d --- /dev/null +++ b/c/time/native_char_signed_llgo_test.go @@ -0,0 +1,7 @@ +//go:build llgo && ((darwin && arm64 && !ios) || (linux && amd64 && !android)) + +package time_test + +import "github.com/goplus/lib/c" + +type nativeTimeChar = c.Char diff --git a/c/time/native_char_unsigned_llgo_test.go b/c/time/native_char_unsigned_llgo_test.go new file mode 100644 index 00000000..a17b08ff --- /dev/null +++ b/c/time/native_char_unsigned_llgo_test.go @@ -0,0 +1,5 @@ +//go:build llgo && linux && arm64 && !android + +package time_test + +type nativeTimeChar = uint8 diff --git a/c/time/native_linux_llgo_test.go b/c/time/native_linux_llgo_test.go new file mode 100644 index 00000000..7883a184 --- /dev/null +++ b/c/time/native_linux_llgo_test.go @@ -0,0 +1,35 @@ +//go:build llgo && linux && (amd64 || arm64) && !android + +package time_test + +import ( + "testing" + "unsafe" + + lltime "github.com/goplus/lib/c/time" +) + +func TestNativeLinuxTimerLayouts(t *testing.T) { + var timer lltime.TimerT + var setting lltime.Itimerspec + if unsafe.Sizeof(timer) != 8 || unsafe.Alignof(timer) != 8 { + t.Fatalf("timer_t size/alignment = %d/%d, want 8/8", unsafe.Sizeof(timer), unsafe.Alignof(timer)) + } + if unsafe.Sizeof(setting) != 32 || unsafe.Alignof(setting) != 8 || unsafe.Offsetof(setting.ItInterval) != 0 || unsafe.Offsetof(setting.ItValue) != 16 { + t.Fatalf("itimerspec layout: size=%d align=%d interval=%d value=%d", unsafe.Sizeof(setting), unsafe.Alignof(setting), unsafe.Offsetof(setting.ItInterval), unsafe.Offsetof(setting.ItValue)) + } +} + +func TestNativeLinuxProcessClock(t *testing.T) { + var clock lltime.ClockidT + if result := lltime.ClockGetcpuclockid(0, &clock); result != 0 { + t.Fatalf("clock_getcpuclockid = %d", result) + } + var value lltime.Timespec + if result := lltime.ClockGettime(clock, &value); result != 0 { + t.Fatalf("process clock_gettime = %d", result) + } + if value.Sec < 0 || value.Nsec < 0 || value.Nsec >= 1_000_000_000 { + t.Fatalf("invalid process CPU clock value: %+v", value) + } +} diff --git a/c/time/native_llgo_test.go b/c/time/native_llgo_test.go new file mode 100644 index 00000000..ba0ffd0a --- /dev/null +++ b/c/time/native_llgo_test.go @@ -0,0 +1,130 @@ +//go:build llgo && ((darwin && arm64 && !ios) || (linux && (amd64 || arm64) && !android)) + +// Copyright (c) 2026 The XGo Authors. Licensed under the Apache License 2.0. + +package time_test + +import ( + "testing" + "unsafe" + + "github.com/goplus/lib/c" + lltime "github.com/goplus/lib/c/time" +) + +func TestNativeTimeLayouts(t *testing.T) { + var stamp lltime.TimeT + var clock lltime.ClockT + var clockID lltime.ClockidT + var calendar lltime.Tm + var timespec lltime.Timespec + cases := []struct { + name string + size, align, wantSize, wantAlign uintptr + }{ + {"time_t", unsafe.Sizeof(stamp), unsafe.Alignof(stamp), 8, 8}, + {"clock_t", unsafe.Sizeof(clock), unsafe.Alignof(clock), 8, 8}, + {"clockid_t", unsafe.Sizeof(clockID), unsafe.Alignof(clockID), 4, 4}, + {"tm", unsafe.Sizeof(calendar), unsafe.Alignof(calendar), 56, 8}, + {"timespec", unsafe.Sizeof(timespec), unsafe.Alignof(timespec), 16, 8}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.size != tc.wantSize || tc.align != tc.wantAlign { + t.Fatalf("size/alignment = %d/%d, want %d/%d", tc.size, tc.align, tc.wantSize, tc.wantAlign) + } + }) + } + if unsafe.Offsetof(calendar.Sec) != 0 || unsafe.Offsetof(calendar.Year) != 20 || unsafe.Offsetof(calendar.Gmtoff) != 40 || unsafe.Offsetof(calendar.Zone) != 48 { + t.Fatalf("tm offsets: sec=%d year=%d gmtoff=%d zone=%d", unsafe.Offsetof(calendar.Sec), unsafe.Offsetof(calendar.Year), unsafe.Offsetof(calendar.Gmtoff), unsafe.Offsetof(calendar.Zone)) + } + if unsafe.Offsetof(timespec.Sec) != 0 || unsafe.Offsetof(timespec.Nsec) != 8 { + t.Fatalf("timespec offsets = %d/%d, want 0/8", unsafe.Offsetof(timespec.Sec), unsafe.Offsetof(timespec.Nsec)) + } +} + +func TestNativeCalendarConversions(t *testing.T) { + stamp := lltime.TimeT(946782245) // 2000-01-02 03:04:05 UTC. + var utc lltime.Tm + if result := lltime.GmtimeR(&stamp, &utc); result != &utc { + t.Fatalf("gmtime_r returned %p, want caller buffer %p", result, &utc) + } + if utc.Year != 100 || utc.Mon != 0 || utc.Mday != 2 || utc.Hour != 3 || utc.Min != 4 || utc.Sec != 5 || utc.Wday != 0 || utc.Yday != 1 || utc.Isdst != 0 { + t.Fatalf("gmtime_r fields = %+v", utc) + } + if got := utc.Timegm(); got != stamp { + t.Fatalf("timegm = %d, want %d", got, stamp) + } + var local lltime.Tm + if result := lltime.LocaltimeR(&stamp, &local); result != &local { + t.Fatalf("localtime_r returned %p, want caller buffer %p", result, &local) + } + if got := local.Timelocal(); got != stamp { + t.Fatalf("timelocal(localtime_r(t)) = %d, want %d", got, stamp) + } +} + +func TestNativeTimeOutputBuffers(t *testing.T) { + stamp := lltime.TimeT(946782245) + var utc lltime.Tm + if lltime.GmtimeR(&stamp, &utc) != &utc { + t.Fatal("gmtime_r did not return the caller buffer") + } + var buffer [64]byte + for i := range buffer { + buffer[i] = 0xa5 + } + pointer := (*nativeTimeChar)(unsafe.Pointer(&buffer[0])) + if result := utc.AsctimeR(pointer); result != pointer { + t.Fatalf("asctime_r returned %p, want %p", result, pointer) + } + if got := c.GoString((*c.Char)(unsafe.Pointer(pointer))); got != "Sun Jan 2 03:04:05 2000\n" { + t.Fatalf("asctime_r = %q", got) + } + if buffer[26] != 0xa5 { + t.Fatalf("asctime_r changed the byte after its 26-byte output: %x", buffer[26]) + } + expected := lltime.Ctime(&stamp) + if expected == nil { + t.Fatal("ctime returned nil") + } + want := c.GoString(expected) + for i := range buffer { + buffer[i] = 0xa5 + } + if result := lltime.CtimeR(&stamp, pointer); result != pointer { + t.Fatalf("ctime_r returned %p, want %p", result, pointer) + } + if got := c.GoString((*c.Char)(unsafe.Pointer(pointer))); got != want { + t.Fatalf("ctime_r = %q, want %q", got, want) + } + if buffer[26] != 0xa5 { + t.Fatalf("ctime_r changed the byte after its 26-byte output: %x", buffer[26]) + } +} + +func TestNativeClockQueries(t *testing.T) { + var before, after, resolution lltime.Timespec + if result := lltime.ClockGettime(lltime.CLOCK_MONOTONIC, &before); result != 0 { + t.Fatalf("clock_gettime = %d", result) + } + if result := lltime.ClockGettime(lltime.CLOCK_MONOTONIC, &after); result != 0 { + t.Fatalf("clock_gettime = %d", result) + } + if before.Sec < 0 || before.Nsec < 0 || before.Nsec >= 1_000_000_000 || after.Sec < before.Sec || (after.Sec == before.Sec && after.Nsec < before.Nsec) || after.Nsec < 0 || after.Nsec >= 1_000_000_000 { + t.Fatalf("invalid monotonic clock samples: before=%+v after=%+v", before, after) + } + if result := lltime.ClockGetres(lltime.CLOCK_MONOTONIC, &resolution); result != 0 { + t.Fatalf("clock_getres = %d", result) + } + if resolution.Sec < 0 || resolution.Nsec < 0 || resolution.Nsec >= 1_000_000_000 || (resolution.Sec == 0 && resolution.Nsec == 0) { + t.Fatalf("invalid clock resolution: %+v", resolution) + } + var realtime lltime.Timespec + if result := realtime.Get(lltime.TIME_UTC); result != lltime.TIME_UTC { + t.Fatalf("timespec_get = %d, want TIME_UTC", result) + } + if realtime.Sec <= 0 || realtime.Nsec < 0 || realtime.Nsec >= 1_000_000_000 { + t.Fatalf("invalid timespec_get output: %+v", realtime) + } +} diff --git a/c/time/time.go b/c/time/time.go index e49088fa..7a6d7272 100644 --- a/c/time/time.go +++ b/c/time/time.go @@ -35,18 +35,9 @@ const ( type TimeT C.time_t -//go:linkname Time C.time -func Time(timer *TimeT) TimeT - -//go:linkname Mktime C.mktime -func Mktime(timer *Tm) TimeT - //go:linkname Ctime C.ctime func Ctime(timer *TimeT) *c.Char -//go:linkname Difftime C.difftime -func Difftime(end, start TimeT) float64 - // ----------------------------------------------------------------------------- type Tm struct { @@ -63,12 +54,6 @@ type Tm struct { Zone *c.Char } -//go:linkname Gmtime C.gmtime -func Gmtime(timer *TimeT) *Tm - -//go:linkname Localtime C.localtime -func Localtime(timer *TimeT) *Tm - //go:linkname Strftime C.strftime func Strftime(buf *c.Char, bufSize uintptr, format *c.Char, timeptr *Tm) uintptr @@ -76,9 +61,6 @@ func Strftime(buf *c.Char, bufSize uintptr, format *c.Char, timeptr *Tm) uintptr type ClockT C.clock_t -//go:linkname Clock C.clock -func Clock() ClockT - // ----------------------------------------------------------------------------- type ClockidT C.clockid_t @@ -122,13 +104,4 @@ type Timespec struct { Nsec c.Long // and nanoseconds } -//go:linkname ClockGettime C.clock_gettime -func ClockGettime(clkId ClockidT, tp *Timespec) c.Int - -//go:linkname ClockSettime C.clock_settime -func ClockSettime(clkId ClockidT, tp *Timespec) c.Int - -//go:linkname ClockGetres C.clock_getres -func ClockGetres(clkId ClockidT, res *Timespec) c.Int - // -----------------------------------------------------------------------------