diff options
author | EuAndreh <eu@euandre.org> | 2024-05-16 11:58:57 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2024-05-16 14:17:41 -0300 |
commit | 6291643ff88e50579bdbd0823b2b1846c9fd6d09 (patch) | |
tree | 90e7a2a516f70d1c30c80e2a4bcf80f17c684d4b | |
parent | Initial empty commit (diff) | |
download | gobang-6291643ff88e50579bdbd0823b2b1846c9fd6d09.tar.gz gobang-6291643ff88e50579bdbd0823b2b1846c9fd6d09.tar.xz |
Initial implementation: include code from papod
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 104 | ||||
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | src/cmd/main.go | 7 | ||||
-rw-r--r-- | src/export_test.go | 3 | ||||
-rw-r--r-- | src/lib.go | 283 | ||||
-rwxr-xr-x | tests/cli-opts.sh | 4 | ||||
-rw-r--r-- | tests/lib_test.go | 53 |
8 files changed, 459 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85a298a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/*.bin +/tests/*.bin diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fdf1423 --- /dev/null +++ b/Makefile @@ -0,0 +1,104 @@ +.POSIX: +DATE = 1970-01-01 +VERSION = 0.1.0 +NAME = gobang +NAME_UC = $(NAME) +LANGUAGES = en +## Installation prefix. Defaults to "/usr". +PREFIX = /usr +BINDIR = $(PREFIX)/bin +LIBDIR = $(PREFIX)/lib +INCLUDEDIR = $(PREFIX)/include +LIBDDIR = $(PREFIX)/lib/$(NAME) +SRCDIR = $(PREFIX)/src/$(NAME) +SHAREDIR = $(PREFIX)/share +LOCALEDIR = $(SHAREDIR)/locale +MANDIR = $(SHAREDIR)/man +EXEC = ./ +## Where to store the installation. Empty by default. +DESTDIR = +LDLIBS = + + + +.SUFFIXES: +.SUFFIXES: .go .bin + + + +sources = \ + src/lib.go \ + src/cmd/main.go \ + + +derived-assets = \ + $(NAME).bin \ + tests/lib_test.bin \ + +side-assets = \ + + + +## Default target. Builds all artifacts required for testing +## and installation. +all: $(derived-assets) + + +$(NAME).bin: src/lib.go src/cmd/main.go Makefile + go build -o $@ -v src/cmd/main.go + +tests/lib_test.bin: src/lib.go tests/lib_test.go Makefile + go test -c -o $@ -v $*.go + + + +check-unit: tests/lib_test.bin + ./tests/lib_test.bin + + +integration-tests = \ + tests/cli-opts.sh \ + +$(integration-tests): $(NAME).bin ALWAYS + sh $@ $(EXEC)$(NAME).bin + +check-integration: $(integration-tests) + + +## Run all tests. Each test suite is isolated, so that a parallel +## build can run tests at the same time. The required artifacts +## are created if missing. +check: check-unit check-integration + + + +## Remove *all* derived artifacts produced during the build. +## A dedicated test asserts that this is always true. +clean: + rm -rf $(derived-assets) $(side-assets) + + +## Installs into $(DESTDIR)$(PREFIX). Its dependency target +## ensures that all installable artifacts are crafted beforehand. +install: all + mkdir -p \ + '$(DESTDIR)$(BINDIR)' \ + + cp $(NAME).bin '$(DESTDIR)$(BINDIR)'/$(NAME) + for f in $(sources); do \ + dir='$(DESTDIR)$(SRCDIR)'/"`dirname "$${f#src/}"`"; \ + mkdir -p "$$dir"; \ + cp -P "$$f" "$$dir"; \ + done + +## Uninstalls from $(DESTDIR)$(PREFIX). This is a perfect mirror +## of the "install" target, and removes *all* that was installed. +## A dedicated test asserts that this is always true. +uninstall: + rm -rf \ + '$(DESTDIR)$(BINDIR)'/$(NAME) \ + '$(DESTDIR)$(LIBDDIR)' \ + '$(DESTDIR)$(SRCDIR)' \ + + +ALWAYS: @@ -0,0 +1,3 @@ +module euandre.org/gobang + +go 1.21.5 diff --git a/src/cmd/main.go b/src/cmd/main.go new file mode 100644 index 0000000..d5f1346 --- /dev/null +++ b/src/cmd/main.go @@ -0,0 +1,7 @@ +package main + +import "euandre.org/gobang/src" + +func main() { + gobang.Main() +} diff --git a/src/export_test.go b/src/export_test.go new file mode 100644 index 0000000..6bb0416 --- /dev/null +++ b/src/export_test.go @@ -0,0 +1,3 @@ +package gobang + +var Mmain = mmain diff --git a/src/lib.go b/src/lib.go new file mode 100644 index 0000000..8b60853 --- /dev/null +++ b/src/lib.go @@ -0,0 +1,283 @@ +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()) +} + +func mmain() { + fmt.Println(NewUUID().ToString()) +} diff --git a/tests/cli-opts.sh b/tests/cli-opts.sh new file mode 100755 index 0000000..f06f8a6 --- /dev/null +++ b/tests/cli-opts.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu + +"$1" | grep -qE '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' diff --git a/tests/lib_test.go b/tests/lib_test.go new file mode 100644 index 0000000..797bb0f --- /dev/null +++ b/tests/lib_test.go @@ -0,0 +1,53 @@ +package gobang_test + +import ( + "bytes" + "encoding/json" + "fmt" + "log/slog" + "reflect" + "testing" + + "euandre.org/gobang/src" +) + + +func errorIf(t *testing.T, err error) { + if err != nil { + t.Errorf("Unexpected error: %#v\n", err) + } +} + +func assertEqual(t *testing.T, given any, expected any) { + if !reflect.DeepEqual(given, expected) { + t.Errorf("given != expected") + t.Errorf("given: %#v\n", given) + t.Errorf("expected: %#v\n", expected) + } +} + +func TestSetLoggerOutput(t *testing.T) { + return + type entry struct { + msg string `json:"msg"` + aKey string `json:"a-key"` + } + var e entry + var buf bytes.Buffer + slog.Error("the message", "a-key", "a-value") + + s := buf.String() + // fmt.Println(s) + // fmt.Println(e) + err := json.Unmarshal([]byte(s), &e) + if err != nil { + t.Fail() + // gobang.Mmain() + gobang.Main() + } + if e.msg != "the message" { + t.Fail() + } + fmt.Println(1) + // fmt.Println(e) +} |