227 lines
6.5 KiB
Plaintext
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
|