tinymux/mux/lib/timeabsolute.cpp
Stephen Dennis 8808b4c375 feat(#2136): flip fargs to const UTF8 * const — and convert every site the compiler surfaced
The flip: FUNCTION/XFUNCTION/FUN::fun/delim_check and the module
interfaces take `const UTF8 * const fargs[]`.  Double-const is
load-bearing: C++ qualification conversion needs const at both pointer
levels, so builder-side `UTF8 *[]` arrays convert implicitly — the
evaluator, the JIT marshaller, and every owner site need zero casts,
and slot reassignment inside bodies becomes a compile error for free.

The conversions: the flip landed first so the compiler enumerated every
violation; this commit is that inventory worked to zero — ~250 sites
across funceval, funceval2, functions, funmath, help, mail, session,
powers, levels, predicates, conf, walkdb, stringutil, timeutil/
date_scan (regenerated, one-line diff), exp3, and mux_main, each
classified per docs/campaign-2136-const-fargs.md's four recipes.

New idioms (functions.h): trim_space_sep_n() — non-destructive trim for
(pointer, length) consumers, so trim-then-scan sites need no copy at
all; FargVec — the argv counterpart of FargCopy for CS_ARGV handlers.
countwords() and DecodeListOfIntegers() rewritten non-destructive.

The flip deleted more than it added: #2157's fun_munge list1 copy, the
engine_com help-topic copy, fun_index's in-place NUL write, and five
const_casts (process_sex x4, sha1_helper).  const_cast budget: zero
added.

Trap recorded in the brief: an old-signature definition doesn't fail
the build — it becomes a C++ overload, and the new-signature symbol
stays undefined until dlopen(RTLD_NOW).  delim_check, the conn_bridge
bridges, the dbt_spike stub, and exp3::Call were all silently shadowed;
muxscript was the only host that noticed, because netmux's own net.cpp
resolved the flat-namespace lookup.  After any signature flip, grep the
old spelling.

Verified: make test EXPECT_CONFIG="jit=yes" (35 passed / 0 failed) and
make test-scenario, including the new tests/scenario/sidefx_fargs.py
that live-probes the class-3 wrappers smoke never touches (pemit/
trigger/link/tel/wipe/destroy).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 14:34:23 -06:00

274 lines
7 KiB
C++

/*! \file timeabsolute.cpp
* \brief CLinearTimeAbsolute module.
*
* CLinearTimeAbsolute deals with civil time from a fixed Epoch.
*
*/
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "core.h"
#include <memory>
#include <string>
#include <algorithm>
int CLinearTimeAbsolute::m_nCount = 0;
UTF8 CLinearTimeAbsolute::m_Buffer[I64BUF_SIZE * 2];
bool operator<(const CLinearTimeAbsolute& lta, const CLinearTimeAbsolute& ltb) noexcept
{
return lta.m_tAbsolute < ltb.m_tAbsolute;
}
bool operator>(const CLinearTimeAbsolute& lta, const CLinearTimeAbsolute& ltb) noexcept
{
return lta.m_tAbsolute > ltb.m_tAbsolute;
}
bool operator==(const CLinearTimeAbsolute& lta, const CLinearTimeAbsolute& ltb) noexcept
{
return lta.m_tAbsolute == ltb.m_tAbsolute;
}
bool operator<=(const CLinearTimeAbsolute& lta, const CLinearTimeAbsolute& ltb) noexcept
{
return lta.m_tAbsolute <= ltb.m_tAbsolute;
}
bool operator>=(const CLinearTimeAbsolute& lta, const CLinearTimeAbsolute& ltb) noexcept
{
return lta.m_tAbsolute >= ltb.m_tAbsolute;
}
CLinearTimeAbsolute operator-(const CLinearTimeAbsolute& lta, const CLinearTimeDelta& ltd) noexcept
{
CLinearTimeAbsolute t;
t.m_tAbsolute = lta.m_tAbsolute - ltd.Return100ns();
return t;
}
CLinearTimeAbsolute::CLinearTimeAbsolute(const CLinearTimeAbsolute& ltaOrigin, const CLinearTimeDelta& ltdOffset) noexcept
{
m_tAbsolute = ltaOrigin.m_tAbsolute + ltdOffset.Return100ns();
}
const UTF8* CLinearTimeAbsolute::ReturnSecondsString(int nFracDigits) const
{
ConvertToSecondsString(m_Buffer, m_tAbsolute - EPOCH_OFFSET, nFracDigits);
return m_Buffer;
}
CLinearTimeAbsolute::CLinearTimeAbsolute(const CLinearTimeAbsolute& lta) noexcept
{
m_tAbsolute = lta.m_tAbsolute;
}
CLinearTimeAbsolute::CLinearTimeAbsolute() noexcept
{
m_tAbsolute = 0;
}
void CLinearTimeAbsolute::Set100ns(UnderlyingTickType arg_t100ns) noexcept
{
m_tAbsolute = arg_t100ns;
}
UnderlyingTickType CLinearTimeAbsolute::Return100ns() const noexcept
{
return m_tAbsolute;
}
void CLinearTimeAbsolute::SetSeconds(UnderlyingTickType arg_tSeconds) noexcept
{
m_tAbsolute = arg_tSeconds;
m_tAbsolute *= FACTOR_100NS_PER_SECOND;
// Epoch difference between (00:00:00 UTC, January 1, 1970) and
// (00:00:00 UTC, January 1, 1601).
//
m_tAbsolute += EPOCH_OFFSET;
}
UnderlyingTickType CLinearTimeAbsolute::ReturnSeconds() const noexcept
{
// UnderlyingTickType is in hundreds of nanoseconds.
// And the Epoch is 0:00 1/1/1601 instead of 0:00 1/1/1970
//
return i64FloorDivision(m_tAbsolute - EPOCH_OFFSET, FACTOR_100NS_PER_SECOND);
}
bool CLinearTimeAbsolute::ReturnFields(FIELDEDTIME* arg_tStruct) const
{
return LinearTimeToFieldedTime(m_tAbsolute, arg_tStruct);
}
bool CLinearTimeAbsolute::SetString(const UTF8* arg_tBuffer)
{
FIELDEDTIME ft;
if (do_convtime(arg_tBuffer, &ft))
{
if (FieldedTimeToLinearTime(&ft, &m_tAbsolute))
{
return true;
}
}
m_tAbsolute = 0;
return false;
}
CLinearTimeAbsolute operator+(const CLinearTimeAbsolute& ltaA, const CLinearTimeDelta& ltdB) noexcept
{
CLinearTimeAbsolute lta;
lta.m_tAbsolute = ltaA.m_tAbsolute + ltdB.Return100ns();
return lta;
}
CLinearTimeAbsolute& CLinearTimeAbsolute::operator=(const CLinearTimeAbsolute& lta) noexcept
{
m_tAbsolute = lta.m_tAbsolute;
return *this;
}
CLinearTimeAbsolute& CLinearTimeAbsolute::operator-=(const CLinearTimeDelta& ltd) noexcept
{
m_tAbsolute -= ltd.Return100ns();
return *this;
}
CLinearTimeAbsolute& CLinearTimeAbsolute::operator+=(const CLinearTimeDelta& ltd) noexcept
{
m_tAbsolute += ltd.Return100ns();
return *this;
}
bool CLinearTimeAbsolute::SetFields(FIELDEDTIME* arg_tStruct)
{
m_tAbsolute = 0;
return FieldedTimeToLinearTime(arg_tStruct, &m_tAbsolute);
}
void CLinearTimeAbsolute::ReturnUniqueString(UTF8* buffer, size_t nBuffer) const
{
FIELDEDTIME ft;
if (LinearTimeToFieldedTime(m_tAbsolute, &ft))
{
mux_sprintf(buffer, nBuffer, T("%04d%02d%02d-%02d%02d%02d"), ft.iYear, ft.iMonth,
ft.iDayOfMonth, ft.iHour, ft.iMinute, ft.iSecond);
}
else
{
mux_sprintf(buffer, nBuffer, T("%03d"), m_nCount++);
}
}
const UTF8* CLinearTimeAbsolute::ReturnDateString(int nFracDigits) const
{
FIELDEDTIME ft;
if (LinearTimeToFieldedTime(m_tAbsolute, &ft))
{
// Sanitize Precision Request.
//
const int maxFracDigits = 7;
const int minFracDigits = 0;
if (nFracDigits < minFracDigits)
{
nFracDigits = minFracDigits;
}
else if (maxFracDigits < nFracDigits)
{
nFracDigits = maxFracDigits;
}
UTF8 buffer[11];
buffer[0] = '\0';
if (0 < nFracDigits
&& (ft.iMillisecond != 0
|| ft.iMicrosecond != 0
|| ft.iNanosecond != 0))
{
mux_sprintf(buffer, sizeof(buffer), T(".%03d%03d%03d"),
ft.iMillisecond, ft.iMicrosecond, ft.iNanosecond);
// Remove trailing zeros.
//
UTF8* p = (buffer + 1) + (nFracDigits - 1);
while (*p == '0')
{
p--;
}
p++;
*p = '\0';
}
mux_sprintf(m_Buffer, sizeof(m_Buffer),
T("%s %s %02d %02d:%02d:%02d%s %04d"),
DayOfWeekString[ft.iDayOfWeek], monthtab[ft.iMonth - 1],
ft.iDayOfMonth, ft.iHour, ft.iMinute, ft.iSecond, buffer,
ft.iYear);
}
else
{
m_Buffer[0] = '\0';
}
return m_Buffer;
}
void CLinearTimeAbsolute::GetUTC()
{
GetUTCLinearTime(&m_tAbsolute);
}
void CLinearTimeAbsolute::GetLocal()
{
GetUTCLinearTime(&m_tAbsolute);
UTC2Local();
}
bool CLinearTimeAbsolute::SetSecondsString(const UTF8* arg_szSeconds)
{
UnderlyingTickType t;
const UnderlyingTickType tEarliest = EARLIEST_VALID_DATE;
const UnderlyingTickType tLatest = LATEST_VALID_DATE;
if (!ParseFractionalSecondsString(t, arg_szSeconds))
{
return false;
}
t += EPOCH_OFFSET;
if (tEarliest <= t && t <= tLatest)
{
m_tAbsolute = t;
return true;
}
return false;
}
void CLinearTimeAbsolute::UTC2Local()
{
bool bDST;
CLinearTimeDelta ltd = TimezoneCache::queryLocalOffsetAtUTC(*this, &bDST);
m_tAbsolute += ltd.Return100ns();
}
void CLinearTimeAbsolute::Local2UTC()
{
bool bDST1, bDST2;
CLinearTimeDelta ltdOffset1 = TimezoneCache::queryLocalOffsetAtUTC(*this, &bDST1);
CLinearTimeAbsolute ltaUTC2 = *this - ltdOffset1;
CLinearTimeDelta ltdOffset2 = TimezoneCache::queryLocalOffsetAtUTC(ltaUTC2, &bDST2);
CLinearTimeAbsolute ltaLocalGuess = ltaUTC2 + ltdOffset2;
if (ltaLocalGuess == *this)
{
// We found an offset, UTC, local time combination that
// works.
//
m_tAbsolute = ltaUTC2.m_tAbsolute;
}
else
{
CLinearTimeAbsolute ltaUTC1 = *this - ltdOffset2;
m_tAbsolute = ltaUTC1.m_tAbsolute;
}
}