Compare commits

..
1 Commits
Author SHA1 Message Date
brent saner 7ec35990a9 v1.17.6
ADDED:
* mapsx.(ListSortedKeys|ListSortedValues|SortedKeys|SortedValues)
2026-10-10 01:32:35 -04:00
16 changed files with 960 additions and 15 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
I need to test this still; I can't recall if it's complete or not
+10
View File
@@ -0,0 +1,10 @@
package binarystr
const (
/*
bufSz is the size of the buffer/chunk for encoders/decords to use when unspecified by the method caller.
The below is 64 KiB, which should be relatively reasonable.
*/
bufSz int = 65536
)
+44
View File
@@ -0,0 +1,44 @@
/*
Package binarystr parses and renders string (and other) representations of binary data.
To distinguish this package from [encoding/binary], this package refers to this string encoding format
as "bit encoding" or "bitstrings".
For example, the original data:
[]byte("test")
// Or:
// []byte{0x74, 0x65, 0x73, 0x74}
can be encoded to a bitstring via e.g. [BytesToStr]:
01110100011001010111001101110100
And even formatted via [FmtBinStr]:
// For example, FmtBinStr("01110100011001010111001101110100", "", "", " "):
01110100 01100101 01110011 01110100
// Or FmtBinStr("01110100011001010111001101110100", "", " ", "\n"):
0111 0100
0110 0101
0111 0011
0111 0100
Or can be converted to a series of "binary booleans" via e.g. [BytesToBool]:
[]bool{
false, true, true, true,
false, true, false, false,
false, true, true, false,
false, true, false, true,
false, true, true, true,
false, false, true, true,
false, true, true, true,
false, true, false, false,
}
*/
package binarystr
+28
View File
@@ -0,0 +1,28 @@
package binarystr
import (
`errors`
)
var (
ErrBadNumOct error = errors.New("bad number of bits for even octet split")
ErrNoChunk error = errors.New("empty/nil chunk")
)
type (
BadBinCharErr struct {
posByte uint64
posRune uint64
r rune
}
BadNumOctErr struct {
posByte uint64
posRune uint64
rem string
}
BadReadErr struct {
numBytes uint64
}
)
+408
View File
@@ -0,0 +1,408 @@
package binarystr
import (
`bytes`
`errors`
`io`
`strconv`
`strings`
`unicode`
)
/*
BoolToBytes provides the inverse of [BytesToBool].
Each boolean in bools indicates a 0 (false) or 1 (true).
e.g.:
[]bool{
false, true, true, true,
false, true, false, false,
false, true, true, false,
false, true, false, true,
false, true, true, true,
false, false, true, true,
false, true, true, true,
false, true, false, false,
}
to:
[]byte{
0x74, 0x65, 0x73, 0x74,
}
If bools is nil, then b, rem, and err will also be nil and this function will no-op.
If len(bools) % 8 != 0, then err will be [ErrBadNumOct].
The remaining booleans can be found in rem.
*/
func BoolToBytes(bools []bool) (b []byte, rem []bool, err error) {
var idx int
var loc []bool
var sb *strings.Builder = new(strings.Builder)
if bools == nil {
return
}
if len(bools) == 0 {
b = make([]byte, 0)
return
}
loc = make([]bool, len(bools))
copy(loc, bools)
for idx = 0; idx < len(loc); idx += 8 {
if len(loc) < 8 {
break
}
writeBoolChunk(loc[idx:idx+8], sb)
loc = loc[idx+8:]
}
if b, err = StrToBytes(sb.String()); err != nil {
return
}
if len(loc) > 1 {
rem = append(rem, loc...)
err = ErrBadNumOct
}
return
}
/*
BytesToStr converts bytes to a bitstring.
e.g.:
[]byte{
0x74, 0x65, 0x73, 0x74,
}
to:
"01110100011001010111001101110100"
For the inverse, see [StrToBytes].
To format the bitstring, see [FmtBinStr].
*/
func BytesToStr(b []byte) (binStr string) {
var cb byte
var sb *strings.Builder
if b == nil {
return
}
sb = new(strings.Builder)
for _, cb = range b {
// This will never actually return an error.
_, _ = sb.WriteString(
strconv.FormatUint(
uint64(uint8(cb)),
2,
),
)
}
binStr = sb.String()
return
}
/*
BytesToBool returns a slice of "binary booleans".
For each bit of data in b, a 0 is false and a 1 is true (as one would expect).
For the inverse of this, see [BoolToBytes].
*/
func BytesToBool(b []byte) (bools []bool) {
var err error
var r rune
var rSz int
var idx int
var s *strings.Reader
if len(b) == 0 {
return
}
// s will always be a valid bitstring.
s = strings.NewReader(BytesToStr(b))
bools = make([]bool, s.Len())
for idx = 0; idx < s.Len(); idx++ {
r, rSz, err = s.ReadRune()
if rSz == 0 {
break
}
if err != nil {
// It'll only be an io.EOF if anything at all.
err = nil
break
}
switch r {
case '0':
bools[idx] = false
case '1':
bools[idx] = true
}
}
return
}
/*
FmtBinStr formats bitstring s to more comfortable representations.
bitSep is a string to separate each bit boundary.
nybSep is a string to separate each "nibble"/"nybble" (4-bit "word") boundary.
bytSep is a string to separate each byte boundary.
Note that if you only explicitly use whitespace separators, then the output
can still be parsed back to native []byte{} data via [StrToBytes].
Note that NO checking is done against the validity of s being a bitstring;
the only content-checking done in s is if a given rune is a whitespace character or not
(whitespace present in s is skipped over, which lets you run FmtBinStr to reformat a bitstring).
*/
func FmtBinStr(s, bitSep, nybSep, bytSep string) (out string) {
var idx int
var lastIdx int
var sb *strings.Builder
if strings.TrimSpace(s) == "" {
return
}
sb = new(strings.Builder)
lastIdx = len(s) - 1
for idx = 0; idx < len(s); idx++ {
_ = sb.WriteByte(s[idx])
if idx != lastIdx {
if (idx+1)%8 == 0 {
if bytSep != "" {
_, _ = sb.WriteString(bytSep)
}
continue
} else if (idx+1)%4 == 0 {
if nybSep != "" {
_, _ = sb.WriteString(nybSep)
}
continue
}
}
if bitSep != "" {
_, _ = sb.WriteString(bitSep)
}
}
out = sb.String()
return
}
/*
NewDecoder returns a [Decoder] from [io.Reader] r.
Data from r is expected to be BITSTRING data (strings);
doing read operations from a [Decoder] returns PLAIN data.
*/
func NewDecoder(r io.Reader) (e *Decoder) {
e = &Decoder{
r: r,
}
return
}
/*
NewEncoder returns an [Encoder] from [io.Reader] r.
Data from r is expected to be PLAIN data;
doing read operations from an [Encoder] returns BITSTRING data (strings).
*/
func NewEncoder(r io.Reader) (e *Encoder) {
e = &Encoder{
r: r,
}
return
}
/*
StrToBytes converts a bitstring to bytes.
Whitespace is ignored.
e.g.:
"01110100011001010111001101110100"
to:
[]byte("test")
or:
[]byte{0x74, 0x65, 0x73, 0x74}
For the inverse, see [BytesToStr].
If the number of characters in binStr is not modulo 8 (`len() % 8 != 0`),
then err will be of type [BadNumOctErr].
If there are any characters encountered in binStr that are not "0" or "1"
(other than whitespace), then err will be of type [BadBinCharErr].
*/
func StrToBytes(binStr string) (b []byte, err error) {
var r rune
var cb byte
var rSz int
var pos uint64
var rErr error
var posR uint64
var chunk []rune
var buf *strings.Reader
var bytBuf *bytes.Buffer = new(bytes.Buffer)
defer func() {
b = bytBuf.Bytes()
}()
if strings.TrimSpace(binStr) == "" {
return
}
buf = strings.NewReader(binStr)
chunk = make([]rune, 0, 8)
for {
r, rSz, rErr = buf.ReadRune()
if rSz == 0 {
break
}
pos += uint64(rSz)
if rSz != 0 {
posR++
}
if len(chunk) == 8 {
if cb, err = chunkToByte(chunk); err != nil {
return
}
if err = bytBuf.WriteByte(cb); err != nil {
return
}
chunk = make([]rune, 0, 8)
}
if unicode.IsSpace(r) {
continue
}
if rSz != 0 {
switch r {
case '0', '1':
chunk = append(chunk, r)
default:
err = &BadBinCharErr{
posByte: pos,
posRune: posR,
r: r,
}
return
}
}
if rErr != nil {
if errors.Is(rErr, io.EOF) {
break
} else {
err = rErr
return
}
}
if rSz == 0 {
break
}
}
if len(chunk) != 0 {
if len(chunk) == 8 {
if cb, err = chunkToByte(chunk); err != nil {
return
}
if err = bytBuf.WriteByte(cb); err != nil {
return
}
} else {
err = &BadNumOctErr{
posByte: pos,
posRune: posR,
rem: string(chunk),
}
return
}
}
return
}
func chunkToByte(chunk []rune) (b byte, err error) {
var u8 uint64
if len(chunk) == 0 {
err = ErrNoChunk
return
}
if u8, err = strconv.ParseUint(string(chunk), 2, 8); err != nil {
return
}
b = byte(uint8(u8))
return
}
func writeBoolChunk(chunk []bool, sb io.StringWriter) {
var b bool
if len(chunk) == 0 {
return
}
for _, b = range chunk {
switch b {
case false:
sb.WriteString("0")
case true:
sb.WriteString("1")
}
}
}
@@ -0,0 +1,26 @@
package binarystr
import "fmt"
// BytePos returns the byte position index immediately leading up to the error.
func (b *BadBinCharErr) BytePos() (pos uint64) { return b.posByte }
// Rune returns the offending rune that caused the error.
func (b *BadBinCharErr) Rune() (r rune) { return b.r }
// RunePos returns the rune (character) position index immediately leading up to the error.
func (b *BadBinCharErr) RunePos() (pos uint64) { return b.posRune }
// Error returns the error message for a BadBinCharError. It conforms to an [error] interface.
func (b *BadBinCharErr) Error() (err string) {
err = fmt.Sprintf(
"rune/character after position index #%d (%#v) (byte #%d) is not \"0\" or \"1\"",
b.posRune, b.r, b.posByte,
)
return
}
// String is an alias for the [BadBinCharErr.Error] method to conform explicitly to a [fmt.Stringer] interface.
func (b *BadBinCharErr) String() (s string) { return b.Error() }
+32
View File
@@ -0,0 +1,32 @@
package binarystr
import "fmt"
// BytePos returns the byte position index immediately leading up to the error.
func (b *BadNumOctErr) BytePos() (pos uint64) { return b.posByte }
// Error returns the error message for a BadNumOctError. It conforms to an [error] interface.
func (b *BadNumOctErr) Error() (err string) {
err = fmt.Sprintf(
"only able to read %d complete octets/bytes (%#d runes/characters); "+
"%d remaining characters are less than 8 bits",
b.posByte, b.posRune,
len(b.rem),
)
return
}
/*
Rem returns the remaining unparsed string of characters; this should ALWAYS be a length of 0 <= len(rem) < 8.
(It will only be an empty string if [Decoder.Read] encountered the end of its source reader before the buffer was filled,
but the available data cleanly broke into a last octet.)
*/
func (b *BadNumOctErr) Rem() (rem string) { return b.rem }
// RunePos returns the rune (character) position index immediately leading up to the error.
func (b *BadNumOctErr) RunePos() (pos uint64) { return b.posRune }
// String is an alias for the [BadNumOctErr.Error] method to conform explicitly to a [fmt.Stringer] interface.
func (b *BadNumOctErr) String() (s string) { return b.Error() }
+16
View File
@@ -0,0 +1,16 @@
package binarystr
import (
`fmt`
)
// Error conforms a [BadReadErr] to an [error].
func (b *BadReadErr) Error() (err string) {
err = fmt.Sprintf("only read %d bytes from internal read source before encountering a read error", b.numBytes)
return
}
// NumBytesRead returns the number of bytes read (BEFORE mutation/encoding/decoding) before the error was hit.
func (b *BadReadErr) NumBytesRead() (u64 uint64) { return b.numBytes }
+116
View File
@@ -0,0 +1,116 @@
package binarystr
import (
`errors`
`io`
`strings`
)
/*
Read conforms to an [io.Reader].
It returns plain data decoded from bitstring encoding in e's read source.
IMPORTANT: Read will return a [BadNumOctErr] if the amount of data in e's read source is not at least len(b) * 8!
ONE BYTE in b equals EIGHT BYTES from e's read source!
Likewise, n will ALWAYS be len(b), but the number of bytes actually read from e's internal reader
will ALWAYS be n*8!
In this case, b will still be populated with the available decoded data, and err ([BadNumOctErr])'s
[BadNumOctErr.Rem] method will contain the remaining bitstring (if there is any left over) after decoding
the octets that were returned (if any).
IMPORTANT: Read will return a [BadBinCharErr] if it encounters any character in e's read source that is not '0' or '1'!
THIS INCLUDES WHITESPACE due to the buffer size calculation (unlike [StrToBytes]); it is NOT skipped!
*/
func (e *Decoder) Read(b []byte) (n int, err error) {
var inN int
var s string
var endIdx int
var rem string
var buf []byte
defer func() {
if b != nil && buf != nil {
s = BytesToStr(buf)
copy(b, []byte(s))
}
}()
if len(b) == 0 {
return
}
buf = make([]byte, len(b)*8)
if inN, err = e.r.Read(buf); err != nil {
// This is caught/handled below.
if errors.Is(err, io.EOF) {
err = nil
} else {
return
}
}
if inN < len(buf) {
}
if inN != len(buf) {
err = &BadNumOctErr{
posByte: uint64(inN),
posRune: uint64(inN),
rem: rem, // We hit the end too "early" so there is no "remaining".
}
return
}
return
}
/*
ReadString returns a bitstring-encoded [string] from the entirety of the data
in the source reader of e.
IMPORTANT: If e's source reader is a non-terminating stream (e.g. a [math/rand.Rand]),
this function will NEVER RETURN (and will likely consume all memory).
YOU HAVE BEEN WARNED.
n is the number of bytes read from e's source reader.
*/
func (e *Decoder) ReadString() (s string, n uint64, err error) {
var inN int
var buf []byte
var rErr error
var sb *strings.Builder = new(strings.Builder)
defer func() {
if sb != nil {
s = sb.String()
}
}()
for {
buf = make([]byte, bufSz)
inN, rErr = e.r.Read(buf)
n += uint64(inN)
if inN != 0 {
if inN < bufSz {
buf = buf[:inN]
}
sb.WriteString(BytesToStr(buf))
} else {
if errors.Is(rErr, io.EOF) {
rErr = nil
}
break
}
if rErr != nil {
err = rErr
return
}
}
return
}
+105
View File
@@ -0,0 +1,105 @@
package binarystr
import (
`errors`
`io`
`strings`
)
/*
Read conforms to an [io.Reader].
It returns bitstring encoding of the data in e's read source.
(To conform to [io.Reader], this is returned as bytes - e.g. []byte(string()).)
IMPORTANT: Read will return [ErrBadNumOct] if len(b) % 8 != 0! EIGHT BYTES in b equal ONE BYTE
from e's read source! Keep this in mind and size b accordingly for your desired decoded data size!
Likewise, n will ALWAYS be len(b), but the number of bytes actually read from e's internal reader
will ALWAYS be n/8! (If for some bizarre case it isn't, err will be [BadReadErr].)
*/
func (e *Encoder) Read(b []byte) (n int, err error) {
var inN int
var s string
var buf []byte
defer func() {
if b != nil && buf != nil {
s = BytesToStr(buf)
copy(b, []byte(s))
}
}()
if len(b) == 0 {
return
}
if len(b)%8 != 0 {
err = ErrBadNumOct
return
}
buf = make([]byte, len(b)/8)
if inN, err = e.r.Read(buf); err != nil {
return
}
if inN != len(buf) {
if len(buf) > inN {
buf = buf[:inN]
}
err = &BadReadErr{
numBytes: uint64(inN),
}
return
}
return
}
/*
ReadString returns a bitstring-encoded [string] from the entirety of the data
in the source reader of e.
IMPORTANT: If e's source reader is a non-terminating stream (e.g. a [math/rand.Rand]),
this function will NEVER RETURN (and will likely consume all memory).
YOU HAVE BEEN WARNED.
n is the number of bytes read from e's source reader.
*/
func (e *Encoder) ReadString() (s string, n uint64, err error) {
var inN int
var buf []byte
var rErr error
var sb *strings.Builder = new(strings.Builder)
defer func() {
if sb != nil {
s = sb.String()
}
}()
for {
buf = make([]byte, bufSz)
inN, rErr = e.r.Read(buf)
n += uint64(inN)
if inN != 0 {
if inN < bufSz {
buf = buf[:inN]
}
sb.WriteString(BytesToStr(buf))
} else {
if errors.Is(rErr, io.EOF) {
rErr = nil
}
break
}
if rErr != nil {
err = rErr
return
}
}
return
}
+17
View File
@@ -0,0 +1,17 @@
package binarystr
import (
`io`
)
type (
Encoder struct {
r io.Reader
cur uint64
}
Decoder struct {
r io.Reader
cur uint64
}
)
+23 -12
View File
@@ -1,6 +1,6 @@
module r00t2.io/goutils
go 1.26
go 1.27
require (
github.com/Masterminds/sprig/v3 v3.3.0
@@ -8,10 +8,12 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.9.0
github.com/google/uuid v1.6.0
github.com/miekg/dns v1.1.73
github.com/olekukonko/tablewriter v1.1.4
github.com/shirou/gopsutil/v4 v4.26.6
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/sys v0.47.0
github.com/shirou/gopsutil/v4 v4.26.9
go4.org/netipx v0.0.0-20260823151212-3075585bcbeb
golang.org/x/sys v0.49.0
r00t2.io/subnetter v0.3.1
r00t2.io/sysutils v1.16.5
)
@@ -23,12 +25,19 @@ require (
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/djherbis/times v1.6.0 // indirect
github.com/ebitengine/purego v0.10.2 // indirect
github.com/ebitengine/purego v0.11.1 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.15 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.2 // indirect
github.com/go-playground/universal-translator v0.18.2 // indirect
github.com/go-playground/validator/v10 v10.30.5 // indirect
github.com/go-resty/resty/v2 v2.17.2 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/lufia/plan9stats v0.0.0-20260627054121-477a66015f15 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/huandu/xstrings v1.6.2 // indirect
github.com/leodido/go-urn v1.5.0 // indirect
github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/mattn/go-runewidth v0.0.24 // indirect
@@ -37,13 +46,15 @@ require (
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.3.0 // indirect
github.com/olekukonko/ll v0.1.8 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0 // indirect
github.com/shopspring/decimal v1.5.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.17.9 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/sync v0.22.0 // indirect
go.mongodb.org/mongo-driver v1.17.10 // indirect
golang.org/x/crypto v0.58.0 // indirect
golang.org/x/net v0.61.0 // indirect
golang.org/x/sync v0.24.0 // indirect
golang.org/x/text v0.43.0 // indirect
)
+45
View File
@@ -30,25 +30,43 @@ github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE=
github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.11.1 h1:2zpWRSQNVKN4eKsKO9eM1ILDgWfYMY9GwqRmK6XeQ/0=
github.com/ebitengine/purego v0.11.1/go.mod h1:DCHPP08djqhNSoTfImcnHYQRZmd0qhakvrozqaEYhGQ=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/locales v0.14.2 h1:d8UmcrM6Nip0hfGZKLGpAvZH37XB4TS0xzK9B56YNCY=
github.com/go-playground/locales v0.14.2/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.2 h1:LCsMLC9RzmbUMNUPVYD15dmcjwYAJhmX8mPZRW4rAVU=
github.com/go-playground/universal-translator v0.18.2/go.mod h1:67VZIMp5lQpDWlnStOct22q1bkdJGJqHghbOtmkawxk=
github.com/go-playground/validator/v10 v10.30.5 h1:YyCXvVShZbs2Sm3Mb53eNOlhRXctSOzW5QJAouCTZL4=
github.com/go-playground/validator/v10 v10.30.5/go.mod h1:wEqiaov48pXX1kjhc3Da8y0M0Dtg/BK7gurFBLgwFrQ=
github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk=
github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.6.2 h1:+X5X6N46b40cmDw7FFJFU6Eoq0yJS8lbYigT2EFau4c=
github.com/huandu/xstrings v1.6.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM=
@@ -57,12 +75,16 @@ github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRt
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lufia/plan9stats v0.0.0-20260627054121-477a66015f15 h1:YkjVPl/YH5XlJ+/NiwzJtPYXXKRcyjmEUhsDci6YK3c=
github.com/lufia/plan9stats v0.0.0-20260627054121-477a66015f15/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 h1:eveIIGn4BGM3qknO74omf6HYr30/exH+eVUTuAgwjZ0=
github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE=
github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
@@ -79,6 +101,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0 h1:XA01Vk/wv9YikCi1V51yRzIHPMT5of9+cMpZoZvDn/M=
github.com/power-devops/perfstat v0.0.0-20260916203055-22a1a467d9f0/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
@@ -89,12 +113,17 @@ github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9R
github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs=
github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shirou/gopsutil/v4 v4.26.9 h1:CaBo/hBFqvlJoLvAQEKdHdf5GD97/MN2ACfyv63+2Ig=
github.com/shirou/gopsutil/v4 v4.26.9/go.mod h1:nKH+8wX2zxr/mDbsR+AAy6Qd2z4ZOQ7wfQaEp+yrYmg=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shopspring/decimal v1.5.0 h1:HxSWk31RmJv0EyY4oLiGQB9u7WPludUwPBdvQZnYFPc=
github.com/shopspring/decimal v1.5.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU=
@@ -107,8 +136,12 @@ github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
go.mongodb.org/mongo-driver v1.17.10 h1:kdAgQvu8TROXZpSkJQd5wzfaNCCrMbpZyKFtQ6qkPCE=
go.mongodb.org/mongo-driver v1.17.10/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
go4.org/netipx v0.0.0-20260823151212-3075585bcbeb h1:XBM4hvfwGAttkkiTIFfeigdfcL1xIfdKXqFdgiHGtDs=
go4.org/netipx v0.0.0-20260823151212-3075585bcbeb/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
@@ -117,12 +150,18 @@ golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54=
golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w=
golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -135,8 +174,14 @@ golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
r00t2.io/subnetter v0.3.1 h1:I/Uoit7Id+QaFK2W2GwUdkr6WopcagFWxCur1gUnTdc=
r00t2.io/subnetter v0.3.1/go.mod h1:nyOpA8S1IU91gcaP8DMaq/VBUHJ28luGp+ulQFsv6YM=
r00t2.io/sysutils v1.16.2 h1:wI01UwZ/bXn/lzBiCpqDmzZCOWiK87kz04SB4xRw+W0=
r00t2.io/sysutils v1.16.2/go.mod h1:iXK+ALOwIdRKjAJIE5USlkZ669SVDHBNNuYhunsznH8=
r00t2.io/sysutils v1.16.5 h1:0v/HWTLOl8UN8B6koXM3e+TWN1N3eAoUM+xGK+itwq0=
+84
View File
@@ -1,5 +1,11 @@
package mapsx
import (
"cmp"
"iter"
"slices"
)
/*
Get mimics Python's [dict.get()] behavior, returning value `v` if key `k`
is not found in map `m`.
@@ -34,6 +40,7 @@ if `m` is non-nil but empty.
The ordering of `mapKeys` is unpredictable as maps are unsorted
and no explicit sorting is performed.
See [SortedKeys] and [ListSortedKeys] if sorting is desired.
*/
func ListKeys[Map ~map[K]V, K comparable, V any](m Map) (mapKeys []K) {
@@ -51,6 +58,36 @@ func ListKeys[Map ~map[K]V, K comparable, V any](m Map) (mapKeys []K) {
return
}
/*
ListSortedKeys is like [ListKeys] but returns the keys in a sorted slice.
The keys in map `m` MUST conform to a [cmp.Ordered]!
*/
func ListSortedKeys[Map ~map[K]V, K cmp.Ordered, V any](m Map) (mapKeys []K) {
// All cmp.Ordered types are comparable, but not all comparable types are cmp.Ordered.
mapKeys = ListKeys(m)
slices.Sort(mapKeys)
return
}
/*
ListSortedValues is like [ListValues] but returns the values in a sorted slice.
The values in map `m` MUST conform to a [cmp.Ordered]!
*/
func ListSortedValues[Map ~map[K]V, K comparable, V cmp.Ordered](m Map) (mapVals []V) {
// All cmp.Ordered types are comparable, but not all comparable types are cmp.Ordered.
mapVals = ListValues(m)
slices.Sort(mapVals)
return
}
/*
ListValues returns map `m`'s values as a slice (unlike [maps.Values],
which returns an iterator via [iter.Seq]).
@@ -60,6 +97,7 @@ if `m` is non-nil but empty.
The ordering of `mapVals` is unpredictable as maps are unsorted
and no explicit sorting is performed.
See [SortedValues] and [ListSortedValues] if sorting is desired.
*/
func ListValues[Map ~map[K]V, K comparable, V any](m Map) (mapVals []V) {
@@ -77,6 +115,52 @@ func ListValues[Map ~map[K]V, K comparable, V any](m Map) (mapVals []V) {
return
}
/*
SortedKeys is like [maps.Keys] but will sort the keys in the returned [iter.Seq].
(There is a small cost to this.)
The keys in map `m` MUST conform to a [cmp.Ordered]!
*/
func SortedKeys[Map ~map[K]V, K cmp.Ordered, V any](m Map) (mapKeys iter.Seq[K]) {
var k K
// All cmp.Ordered types are comparable, but not all comparable types are cmp.Ordered.
var keys []K = ListKeys(m)
slices.Sort(keys)
return func(yield func(K) bool) {
for _, k = range keys {
if !yield(k) {
return
}
}
}
}
/*
SortedValues is like [maps.Values] but will sort the values in the returned [iter.Seq].
(There is a small cost to this.)
The values in map `m` MUST conform to a [cmp.Ordered]!
*/
func SortedValues[Map ~map[K]V, K comparable, V cmp.Ordered](m Map) (mapVals iter.Seq[V]) {
var v V
// All cmp.Ordered types are comparable, but not all comparable types are cmp.Ordered.
var vals []V = ListValues(m)
slices.Sort(vals)
return func(yield func(V) bool) {
for _, v = range vals {
if !yield(v) {
return
}
}
}
}
/*
Must, unlike [Get] or [GetOk], requires that `k` be in map `m`.
+1 -1
View File
@@ -636,7 +636,7 @@ pre.rouge .gs {
<div class="details">
<span id="author" class="author">Brent Saner</span><br>
<span id="email" class="email"><a href="mailto:bts@square-r00t.net">bts@square-r00t.net</a></span><br>
<span id="revdate">Last rendered 2026-09-11 22:12:00 -0400</span>
<span id="revdate">Last rendered 2026-10-10 01:32:29 -0400</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
+4 -2
View File
@@ -6,7 +6,7 @@
<span id="author" class="author">Brent Saner</span>\
<span id="email" class="email"><bts@square-r00t.net></span>\
<span id="revdate">Last rendered 2026-09-11 22:12:05 -0400</span>
<span id="revdate">Last rendered 2026-10-10 01:32:33 -0400</span>
</div>
@@ -1019,7 +1019,9 @@ This function fills in the gap that <a href="https://pkg.go.dev/text/template#Is
<div class="content">
\[id="fn_net_all"\] === Networking These template functions use capabilities from:
   \[id="fn_net_all"\]\
=== Networking\
These template functions use capabilities from:
</div>