package gobang import ( "crypto/rand" "encoding/hex" "fmt" "io" "log/slog" "math/big" "os" "runtime/debug" "sync" "syscall" "time" ) // FIXME: finish rewriting // // lastV7time is the last time we returned stored as: // // 52 bits of time in milliseconds since epoch // 12 bits of (fractional nanoseconds) >> 8 var lastV7Time int64 var timeMu sync.Mutex // getV7Time returns the time in milliseconds and nanoseconds / 256. // The returned (milli << (12 + seq)) is guaranteed to be greater than // (milli << (12 + seq)) returned by any previous call to getV7Time. // `seq` Sequence number is between 0 and 3906 (nanoPerMilli >> 8) func getV7Time(nano int64) (int64, int64) { const nanoPerMilli = 1000 * 1000 milli := nano / nanoPerMilli seq := (nano - (milli * nanoPerMilli)) >> 8 now := milli << (12 + seq) timeMu.Lock() defer timeMu.Unlock() if now <= lastV7Time { now = lastV7Time + 1 milli = now >> 12 seq = now & 0xfff } lastV7Time = now return milli, seq } const lengthUUID = 16 type UUID struct { bytes [lengthUUID]byte } func NewUUID() UUID { var buf [lengthUUID]byte _, err := io.ReadFull(rand.Reader, buf[7:]) if err != nil { panic(err) } buf[6] = (buf[6] & 0x0f) | 0x40 // Version 4 buf[8] = (buf[8] & 0x3f) | 0x80 // Variant is 10 t, s := getV7Time(time.Now().UnixNano()) buf[0] = byte(t >> 40) buf[1] = byte(t >> 32) buf[2] = byte(t >> 24) buf[3] = byte(t >> 16) buf[4] = byte(t >> 8) buf[5] = byte(t >> 0) buf[6] = 0x70 | (0x0f & byte(s >> 8)) buf[7] = byte(s) return UUID { bytes: buf } } func (uuid UUID) ToString() string { const dashCount = 4 const encodedLength = (lengthUUID * 2) + dashCount dst := [encodedLength]byte { 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, 0, '-', 0, 0, 0, 0, '-', 0, 0, 0, 0, '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } hex.Encode(dst[ 0:8], uuid.bytes[0:4]) hex.Encode(dst[ 9:13], uuid.bytes[4:6]) hex.Encode(dst[14:18], uuid.bytes[6:8]) hex.Encode(dst[19:23], uuid.bytes[8:10]) hex.Encode(dst[24:36], uuid.bytes[10:]) return string(dst[:]) } func Debug(message string, type_ string, args ...any) { slog.Debug( message, append( []any { "id", NewUUID().ToString(), "kind", "log", "type", type_, }, args..., )..., ) } func Info(message string, type_ string, args ...any) { slog.Info( message, append( []any { "id", NewUUID().ToString(), "kind", "log", "type", type_, }, args..., )..., ) } func Warning(message string, type_ string, args ...any) { slog.Warn( message, append( []any { "id", NewUUID().ToString(), "kind", "log", "type", type_, }, args..., )..., ) } func Error(message string, type_ string, args ...any) { slog.Error( message, append( []any { "id", NewUUID().ToString(), "kind", "log", "type", type_, }, args..., )..., ) } func Metric(type_ string, label string, args ...any) { slog.Info( "_", append( []any { "id", NewUUID().ToString(), "kind", "metric", "type", type_, "label", label, }, args..., )..., ) } type Gauge struct { Inc func(...any) Dec func(...any) } var zero = big.NewInt(0) var one = big.NewInt(1) func MakeGauge(label string, staticArgs ...any) Gauge { count := big.NewInt(0) emitGauge := func(dynamicArgs ...any) { if count.Cmp(zero) == -1 { Error( "Gauge went negative", "process-metric", append( []any { "value", count }, append( staticArgs, dynamicArgs..., )..., )..., ) return // avoid wrong metrics being emitted } Metric( "gauge", label, // TODO: we'll have slices.Concat on Go 1.22 append( []any { "value", count }, append( staticArgs, dynamicArgs..., )..., )..., ) } return Gauge { Inc: func(dynamicArgs ...any) { count.Add(count, one) emitGauge(dynamicArgs...) }, Dec: func(dynamicArgs ...any) { count.Sub(count, one) emitGauge(dynamicArgs...) }, } } func MakeCounter(label string) func(...any) { return func(args ...any) { Metric( "counter", label, append([]any { "value", 1 }, args...)..., ) } } func SetLoggerOutput(w io.Writer) { slog.SetDefault(slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions { AddSource: true, })).With( slog.Group( "info", "pid", os.Getpid(), "ppid", os.Getppid(), "puuid", NewUUID().ToString(), ), )) } func SetTraceback() { if os.Getenv("GOTRACEBACK") == "" { debug.SetTraceback("crash") } } func Init() { SetLoggerOutput(os.Stdout) SetTraceback() } func Fatal(err error) { Error( "Fatal error", "fatal-error", "error", err, "stack", string(debug.Stack()), ) syscall.Kill(os.Getpid(), syscall.SIGABRT) os.Exit(3) } func FatalIf(err error) { if err != nil { Fatal(err) } } func Main() { fmt.Println(NewUUID().ToString()) }