commit af19b49425a7d768a68d26da1e56f698015e6b01
parent f657937e94d32956bec85ee41059939a992f82ce
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Tue, 13 Feb 2024 10:23:13 +0800
core: add core collection
This is an extremely minimal as-needed set of core logic that is mostly
copied from the standard library to avoid large package dependencies.
Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat:
6 files changed, 225 insertions(+), 0 deletions(-)
diff --git a/core/core.go b/core/core.go
@@ -0,0 +1,19 @@
+// Package core offers a minimal set of core functionality like string processing and io.
+//
+// The purpose is to avoid taking a dependency on large standard library packages in order
+// to reduce binary size.
+//
+// Being highly performant is not a goal, being small and simple is.
+// Code is added as needed.
+package core
+
+import "errors"
+
+// Cleanup executes a fallible function if the captured error is not nil.
+func Cleanup(err *error, fn func() error) {
+ if err != nil && *err != nil {
+ if e := fn(); e != nil {
+ *err = errors.Join(*err, e)
+ }
+ }
+}
diff --git a/core/math.go b/core/math.go
@@ -0,0 +1,9 @@
+package core
+
+func Pow(base, exponent uint) (n uint) {
+ n = 1
+ for ii := uint(0); ii < exponent; ii++ {
+ n *= base
+ }
+ return n
+}
diff --git a/core/strconv.go b/core/strconv.go
@@ -0,0 +1,29 @@
+package core
+
+// ParseUint is a specialized helper that parses a sequence of digits into an unsigned integer.
+func ParseUint(s string) (n uint, _ error) {
+ const asciiDigitOffset = 48
+
+ width := uint(len(s))
+
+ for ii := width; ii > 0; ii-- {
+ c := s[ii]
+ switch c {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ d := uint(c) - asciiDigitOffset
+ n += d * Pow(10, width-ii)
+ default:
+ return 0, parseErr{literal: c}
+ }
+ }
+
+ return n, nil
+}
+
+type parseErr struct {
+ literal byte
+}
+
+func (p parseErr) Error() string {
+ return "parse: invalid literal (not a digit) " + string(p.literal)
+}
diff --git a/core/strings.go b/core/strings.go
@@ -0,0 +1,77 @@
+package core
+
+/*
+ copied from "strings"
+*/
+
+// TrimPrefix returns s without the provided leading prefix string.
+// If s doesn't start with prefix, s is returned unchanged.
+func TrimPrefix(s, prefix string) string {
+ if HasPrefix(s, prefix) {
+ return s[len(prefix):]
+ }
+ return s
+}
+
+// HasPrefix tests whether the string s begins with prefix.
+func HasPrefix(s, prefix string) bool {
+ return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
+}
+
+// Cut slices s around the first instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, cut returns s, "", false.
+func Cut(s, sep string) (before, after string, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
+
+// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
+//
+// NOTE: Copied from strings, but without the bytealg calls.
+func Index(s, substr string) int {
+ n := len(substr)
+ switch {
+ case n == 0:
+ return 0
+ case n == 1:
+ return IndexByte(s, substr[0])
+ case n == len(s):
+ if substr == s {
+ return 0
+ }
+ return -1
+ default:
+ c0 := substr[0]
+ c1 := substr[1]
+ i := 0
+ t := len(s) - n + 1
+ for i < t {
+ if s[i] != c0 {
+ o := IndexByte(s[i+1:t], c0)
+ if o < 0 {
+ return -1
+ }
+ i += o + 1
+ }
+ if s[i+1] == c1 && s[i:i+n] == substr {
+ return i
+ }
+ i++
+ }
+ return -1
+ }
+}
+
+// IndexByte returns the byte index of the first instance of the given byte, or -1 if not present.
+func IndexByte(s string, b byte) int {
+ for ii := 0; ii < len(s); ii++ {
+ if s[ii] == b {
+ return ii
+ }
+ }
+ return -1
+}
diff --git a/core/url.go b/core/url.go
@@ -0,0 +1,55 @@
+package core
+
+type URL struct {
+ Domain string
+ Path string
+ Port uint32
+ Encrypted bool
+}
+
+const (
+ DefaultHttpPort = 80
+ DefaultHttpsPort = 443
+)
+
+// ParseURL a url string into a structured [URL].
+func ParseURL(s string) (u URL, _ error) {
+ protocol, body, ok := Cut(s, "://")
+ if !ok {
+ return u, Err{Msg: "missing protocol scheme"}
+ }
+
+ if protocol == "https" {
+ u.Encrypted = true
+ u.Port = DefaultHttpsPort
+ }
+
+ hostname, body, _ := Cut(body, "/")
+ hostname, port, _ := Cut(hostname, ":")
+
+ u.Domain = hostname
+ u.Path = body
+
+ if port != "" {
+ n, err := ParseUint(port)
+ if err != nil {
+ return u, Err{Cause: err, Msg: "parsing port"}
+ }
+ u.Port = uint32(n)
+ }
+
+ return u, nil
+}
+
+type Err struct {
+ Cause error
+ Msg string
+}
+
+func (e Err) Error() string {
+ var cause string
+ if e.Cause != nil {
+ cause += ": " + e.Cause.Error()
+ }
+ return e.Msg + cause
+}
diff --git a/core/windows/windows.go b/core/windows/windows.go
@@ -0,0 +1,36 @@
+//go:build windows
+
+package windows
+
+import "syscall"
+
+// UTF16PtrFromString returns pointer to the UTF-16 encoding of
+// the UTF-8 string s, with a terminating NUL added. If s
+// contains a NUL byte at any location, it returns (nil, syscall.EINVAL).
+func UTF16PtrFromString(s string) (*uint16, error) {
+ a, err := UTF16FromString(s)
+ if err != nil {
+ return nil, err
+ }
+ return &a[0], nil
+}
+
+// UTF16FromString returns the UTF-16 encoding of the UTF-8 string
+// s, with a terminating NUL added. If s contains a NUL byte at any
+// location, it returns (nil, syscall.EINVAL).
+func UTF16FromString(s string) ([]uint16, error) {
+ return syscall.UTF16FromString(s)
+}
+
+var (
+ modkernel32 = syscall.NewLazyDLL("kernel32")
+ procGetLastError = modkernel32.NewProc("GetLastError")
+)
+
+func GetLastError() (lasterr error) {
+ r0, _, _ := procGetLastError.Call()
+ if r0 != 0 {
+ lasterr = syscall.Errno(r0)
+ }
+ return
+}