nativehttp

Simple interface to the native http client
Log | Files | Refs | README | LICENSE

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:
Acore/core.go | 19+++++++++++++++++++
Acore/math.go | 9+++++++++
Acore/strconv.go | 29+++++++++++++++++++++++++++++
Acore/strings.go | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/url.go | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/windows/windows.go | 36++++++++++++++++++++++++++++++++++++
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 +}