Files
2026-10-17 21:57:25 +08:00

227 lines
6.5 KiB
Plaintext

from stdint import *
import stdlib
import string
import viperlib
import win32.win32file as wf
import win32.win32base as wb
# ============================================================
# VLog - output primitives (all human-readable logs go to stderr; stdout keeps JSON only)
#
# Verbosity: module-level Verbose_Enabled (0=quiet default, 1=--verbose). Quiet mode keeps only
# the startup summary, heartbeat notices/failures, warnings, fatal errors and one end-of-round
# line. VerboseStr / VerboseFmt* are siblings of Log*: they return immediately (no formatting,
# no write) when disabled, else forward verbatim. Call sites only call Verbose*, so the switch
# stays in this module. Set via SetVerbose before any logging.
# ============================================================
define LOG_CAP = 4096 # Log formatting buffer
Log_Buffer: bytes = None # Log format buffer (stdlib.malloc, process lifetime)
# Write literal/pre-formatted bytes to stderr verbatim (no newline appended)
def LogStr(S: str):
if S is None:
return
H: wb.HANDLE = wf.GetStdHandle(wf.STD_ERROR_HANDLE)
if H is None or H == wb.INVALID_HANDLE_VALUE:
return
n: sizet = string.strlen(S)
if n == 0:
return
bw: ULONG = 0
wf.WriteFile(H, S, ULONG(n), &bw, None)
def LogInit() -> int:
global Log_Buffer
Log_Buffer = stdlib.malloc(LOG_CAP)
if Log_Buffer is None:
return 0
Log_Buffer[0] = 0
return 1
# Fixed-arity format forwarders (not varargs, avoiding custom variadic risk):
# call viperlib.snprintf then write the result to stderr.
def LogFmt1(Fmt: str, A: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A)
LogStr(str(Log_Buffer))
return 1
def LogFmt2(Fmt: str, A: sizet, B: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B)
LogStr(str(Log_Buffer))
return 1
def LogFmt3(Fmt: str, A: sizet, B: sizet, C: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B, C)
LogStr(str(Log_Buffer))
return 1
# 4-integer progress line (r/dir/file/cand)
def LogFmt4(Fmt: str, A: sizet, B: sizet, C: sizet, D: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B, C, D)
LogStr(str(Log_Buffer))
return 1
def LogFmtS1(Fmt: str, A: str) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A)
LogStr(str(Log_Buffer))
return 1
def LogFmtS2(Fmt: str, A: str, B: str) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B)
LogStr(str(Log_Buffer))
return 1
def LogFmtIS(Fmt: str, A: sizet, B: str) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B)
LogStr(str(Log_Buffer))
return 1
# ============================================================
# Verbose switch and "verbose-only" sibling functions
# * switch centralized in Verbose_Enabled; call sites only call Verbose*, no if.
# * disabled returns 0 without formatting; enabled forwards verbatim to the Log* sibling.
# * return codes match Log* (1=written, 0=not written/failed).
# ============================================================
Verbose_Enabled: int = 0 # 0=quiet default; 1=--verbose
def SetVerbose(V: int):
global Verbose_Enabled
Verbose_Enabled = V
def VerboseOn() -> int:
return Verbose_Enabled
def VerboseStr(S: str) -> int:
if Verbose_Enabled == 0:
return 0
LogStr(S)
return 1
def VerboseFmt1(Fmt: str, A: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt1(Fmt, A)
return 1
def VerboseFmt2(Fmt: str, A: sizet, B: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt2(Fmt, A, B)
return 1
def VerboseFmt3(Fmt: str, A: sizet, B: sizet, C: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt3(Fmt, A, B, C)
return 1
def VerboseFmt4(Fmt: str, A: sizet, B: sizet, C: sizet, D: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt4(Fmt, A, B, C, D)
return 1
def VerboseFmtS1(Fmt: str, A: str) -> int:
if Verbose_Enabled == 0:
return 0
LogFmtS1(Fmt, A)
return 1
def VerboseFmtS2(Fmt: str, A: str, B: str) -> int:
if Verbose_Enabled == 0:
return 0
LogFmtS2(Fmt, A, B)
return 1
def VerboseFmtIS(Fmt: str, A: sizet, B: str) -> int:
if Verbose_Enabled == 0:
return 0
LogFmtIS(Fmt, A, B)
return 1
# Write to stdout verbatim (scan mode outputs JSON only)
def StdoutWrite(S: str):
if S is None:
return
H: wb.HANDLE = wf.GetStdHandle(wf.STD_OUTPUT_HANDLE)
if H is None or H == wb.INVALID_HANDLE_VALUE:
return
n: sizet = string.strlen(S)
if n == 0:
return
bw: ULONG = 0
wf.WriteFile(H, S, ULONG(n), &bw, None)
# ============================================================
# Timestamps (round start / next round start)
# Uses Win32 local time; AddMs adds FILETIME (100ns units) then converts back to SYSTEMTIME.
# FILETIME is 100ns per tick, so the increment is AddMs*10000.
# ============================================================
def TimeStampNow(Out: bytes, Cap: int) -> int:
St: wb.SYSTEMTIME = wb.SYSTEMTIME()
wb.GetLocalTime(&St)
viperlib.snprintf(Out, sizet(Cap), "%04u-%02u-%02u %02u:%02u:%02u",
uint32(St.wYear), uint32(St.wMonth), uint32(St.wDay),
uint32(St.wHour), uint32(St.wMinute), uint32(St.wSecond))
return 1
def TimeStampAfter(Out: bytes, Cap: int, AddMs: sizet) -> int:
St: wb.SYSTEMTIME = wb.SYSTEMTIME()
wb.GetLocalTime(&St)
Ft: wb.FILETIME = wb.FILETIME()
if wb.SystemTimeToFileTime(&St, &Ft) == 0:
return TimeStampNow(Out, Cap)
Ticks: ULONGLONG = (ULONGLONG(Ft.dwHighDateTime) << 32) | ULONGLONG(Ft.dwLowDateTime)
Ticks = Ticks + (ULONGLONG(AddMs) * ULONGLONG(10000))
Ft.dwLowDateTime = DWORD(Ticks & ULONGLONG(0xFFFFFFFF))
Ft.dwHighDateTime = DWORD(Ticks >> 32)
St2: wb.SYSTEMTIME = wb.SYSTEMTIME()
if wb.FileTimeToSystemTime(&Ft, &St2) == 0:
return TimeStampNow(Out, Cap)
viperlib.snprintf(Out, sizet(Cap), "%04u-%02u-%02u %02u:%02u:%02u",
uint32(St2.wYear), uint32(St2.wMonth), uint32(St2.wDay),
uint32(St2.wHour), uint32(St2.wMinute), uint32(St2.wSecond))
return 1