ADDED:
* netx.IsPub
* encodingx/hexx

Rest are mostly small corrections and docs
This commit is contained in:
brent saner
2026-06-22 18:51:13 -04:00
parent c6fc692f5e
commit 58556d7281
35 changed files with 5492 additions and 2486 deletions
+3 -2
View File
@@ -67,8 +67,9 @@ for f in $(find . -type f -iname "README.adoc"); do
newf="${pfx}.md"
set +e
#asciidoctor -a ROOTDIR="${orig}/" -b docbook -o - "${f}" | pandoc -f docbook -t markdown_strict -o "${newf}"
asciidoctor -a ROOTDIR="${orig}/" -b html -o - "${f}" | pandoc -f html -t gfm -o "${newf}"
# --wrap=preserve or --wrap=none is required to avoid it reflowing the section titles to newlines.
#asciidoctor -a ROOTDIR="${orig}/" -b docbook -o - "${f}" | pandoc -f docbook -t markdown_strict -o "${newf}" --wrap=none
asciidoctor -a ROOTDIR="${orig}/" -b html -o - "${f}" | pandoc -f html -t gfm -o "${newf}" --wrap=none
if [ $? -eq 0 ];
then
echo "Generated ${newf} from ${f}"
+2
View File
@@ -1 +1,3 @@
- validx: validator functions for https://pkg.go.dev/github.com/go-playground/validator/v10
- hashx?
+30 -39
View File
@@ -11,11 +11,11 @@ import (
type MaskBit uint
/*
NewMaskBit is a convenience function.
It will return a MaskBit with a (referenced) value of 0, so set your consts up accordingly.
NewMaskBit is a convenience function.
It will return a MaskBit with a (referenced) value of 0, so set your consts up accordingly.
It is highly recommended to set this default as a "None" flag (separate from your iotas!)
as shown in the example.
It is highly recommended to set this default as a "None" flag (separate from your iotas!)
as shown in the example.
*/
func NewMaskBit() (m *MaskBit) {
@@ -35,26 +35,10 @@ func NewMaskBitExplicit(value uint) (m *MaskBit) {
}
/*
HasFlag is true if m has MaskBit flag set/enabled.
HasFlag is true if m has MaskBit flag set/enabled.
THIS WILL RETURN FALSE FOR OR'd FLAGS.
For example:
flagA MaskBit = 0x01
flagB MaskBit = 0x02
flagComposite = flagA | flagB
m *MaskBit = NewMaskBitExplicit(uint(flagA))
m.HasFlag(flagComposite) will return false even though flagComposite is an OR
that contains flagA.
Use [MaskBit.IsOneOf] instead if you do not desire this behavior,
and instead want to test composite flag *membership*.
(MaskBit.IsOneOf will also return true for non-composite equality.)
To be more clear, if MaskBit flag is a composite MaskBit (e.g. flagComposite above),
HasFlag will only return true of ALL bits in flag are also set in MaskBit m.
See the "Composite (OR'd) Flags" section in this module's
documentation for important caveats.
*/
func (m *MaskBit) HasFlag(flag MaskBit) (r bool) {
@@ -67,19 +51,20 @@ func (m *MaskBit) HasFlag(flag MaskBit) (r bool) {
}
/*
IsOneOf is like a "looser" form of [MaskBit.HasFlag]
in that it allows for testing composite membership.
HasOneOf is like a "looser" form of [MaskBit.HasFlag]
in that it allows for testing composite membership.
See [MaskBit.HasFlag] for more information.
See [MaskBit.HasFlag] and the "Composite (OR'd) Flags"
section in this module's documentation for more details.
If composite is *not* an OR'd MaskBit (i.e.
it falls directly on a boundary -- 0, 1, 2, 4, 8, 16, etc.),
then IsOneOf will behave exactly like HasFlag.
If composite is *not* an OR'd MaskBit (i.e.
it falls directly on a boundary -- 0, 1, 2, 4, 8, 16, etc.),
then HasOneOf will behave exactly like [MaskBit.HasFlag].
If m is a composite MaskBit (it usually is) and composite is ALSO a composite MaskBit,
IsOneOf will return true if ANY of the flags set in m is set in composite.
If m is a composite [MaskBit] (it usually is) and composite is ALSO a composite MaskBit,
HasOneOf will return true if ANY of the flags set in composite is set in m.
*/
func (m *MaskBit) IsOneOf(composite MaskBit) (r bool) {
func (m *MaskBit) HasOneOf(composite MaskBit) (r bool) {
var b MaskBit = *m
@@ -89,6 +74,13 @@ func (m *MaskBit) IsOneOf(composite MaskBit) (r bool) {
return
}
/*
IsOneOf is the old name for [MaskBit.HasOneOf].
DEPRECATED: This method will be removed sometime in the future. Use [MaskBit.HasOneOf] instead.
*/
func (m *MaskBit) IsOneOf(composite MaskBit) (r bool) { return m.HasOneOf(composite) }
// AddFlag adds MaskBit flag to m.
func (m *MaskBit) AddFlag(flag MaskBit) {
@@ -114,15 +106,14 @@ func (m *MaskBit) ToggleFlag(flag MaskBit) {
}
/*
Bytes returns the current value of a MasBit as a byte slice (big-endian).
Bytes returns the current value of a MasBit as a byte slice (big-endian).
If trim is false, b will (probably) be 4 bytes long if you're on a 32-bit size system,
and b will (probably) be 8 bytes long if you're on a 64-bit size system. You can determine
the size of the resulting slice via (math/)bits.UintSize / 8.
If trim is true, it will trim leading null bytes (if any). This will lead to an unpredictable
byte slice length in b, but is most likely preferred for byte operations.
If trim is false, b will (probably) be 4 bytes long if you're on a 32-bit size system,
and b will (probably) be 8 bytes long if you're on a 64-bit size system. You can determine
the size of the resulting slice via (math/)bits.UintSize / 8.
If trim is true, it will trim leading null bytes (if any). This will lead to an unpredictable
byte slice length in b, but is most likely preferred for byte operations.
*/
func (m *MaskBit) Bytes(trim bool) (b []byte) {
+117 -44
View File
@@ -11,21 +11,84 @@ However, a [bitmask.MaskBit] is backed by a uint which (depending on your platfo
"But wait, that takes up more memory though!"
Yep, but bitmasking lets you store a "boolean" AT EACH BIT - it operates on
Yep - compared to a *single boolean*. But bitmasking lets you store a "boolean" AT EACH BIT boundary - it operates on
whether a bit in a byte/set of bytes at a given position is 0 or 1.
Which means on 32-bit platforms, a [MaskBit] can have up to 4294967295 "booleans" in a single value (0 to (2^32)-1).
In other words:
On 64-bit platforms, a [MaskBit] can have up to 18446744073709551615 "booleans" in a single value (0 to (2^64)-1).
type (
OptStruct struct {
A bool
B bool
C bool
D bool
E bool
F bool
G bool
H bool
}
)
If you tried to do that with Go bool values, that'd take up 4294967295 bytes (4 GiB)
or 18446744073709551615 bytes (16 EiB - yes, that's [exbibytes]) of RAM for 32-bit/64-bit platforms respectively.
One instance of OptStruct takes up *8 bytes* (*64 bits*).
As a uint8 bitmask, however, it takes up exactly *one byte/8 bits*:
type (
Opt uint8
)
const OptNone Opt = 0
const (
A Opt = 1 << iota // 1
B // 2
C // 4
D // 8
E // 16
F // 32
G // 64
H // 128
// Would overflow to 0:
// I
)
As shown, the size of an unsigned integer determines the number of bit-boundaried flags for that bitmask.
Which means on 32-bit platforms, a [MaskBit] can have up to 32 different flags but only occupies 4 bytes of memory.
On 64-bit platforms, a [MaskBit] can have up to 64 different flags but only occupies 8 bytes of memory.
If you tried to do that with a struct of booleans instead, that'd occupy 32 *bytes* and 64 *bytes* respectively.
In summary, a bitmask set occupies 1/8th the amount of memory as a set of equal number of booleans in Go.
You can, of course, extend this even further by defining meaningful "composites",
or OR'd-combination of flags (see the "Composite Flags" section below):
type Perms uint8
const NoPerms Perms = 0
const (
PermsRead Perms = 1 << iota
PermsList
PermsUpdate
PermsCreate
PermsDelete
PermsUpdateBulk
PermsCreateBulk
PermsDeleteBulk
// This makes it easy to add multiple permissions at once.
PermsCRUD Perms = PermsCreate | PermsRead | PermsUpdate | PermsDelete
PermsCRUDL Perms = PermsCRUD | PermsList
// And so forth.
)
"But that has to be so slow to unpack that!"
Nope. It's not using compression or anything, the CPU is just comparing bit "A" vs. bit "B" 32/64 times. That's super easy work for a CPU.
Nope. It's not using compression or anything, the CPU is just comparing bit "A" vs. bit "B" 32/64 times.
That's super easy work for a CPU.
There's a reason Doom used bitmasking for the "dmflags" value in its server configs.
There's a reason ZDoom used bitmasking for the [dmflags] value in its server configs,
and why *NIX platforms uses them for permission/type modes.
# Usage
@@ -34,6 +97,8 @@ To use this library, set constants like thus:
package main
import (
"fmt"
"r00t2.io/goutils/bitmask"
)
@@ -45,34 +110,32 @@ To use this library, set constants like thus:
// ...
)
var MyMask *bitmask.MaskBit
var (
MyMask *bitmask.MaskBit = bitmask.NewMaskBit()
)
func main() {
MyMask = bitmask.NewMaskBit()
MyMask.AddFlag(OPT1)
MyMask.AddFlag(OPT3)
_ = MyMask
// This would print true.
fmt.Println(MyMask.HasFlag(OPT1))
// As would this:
fmt.Println(MyMask.HasFlag(OPT3))
// But this would print false:
fmt.Println(MyMask.HasFlag(OPT2))
}
This would return true:
MyMask.HasFlag(OPT1)
As would this:
MyMask.HasFlag(OPT3)
But this would return false:
MyMask.HasFlag(OPT2)
# Technical Caveats
TARGETING
Targeting
When implementing, you should always set a "source" mask (e.g. MyMask, from Usage section above)
as the actual value.
When implementing, you should always set MyMask (from Usage section above) as the actual value.
For example, if you are checking a permissions set for a user that has the value, say, 6
var userPerms uint = 6 // 0x0000000000000006
@@ -88,7 +151,7 @@ and your library has the following permission bits defined:
PermsAdmin // 16
)
And you want to see if the user has the PermsRead flag set, you would do:
and you want to see if the user has the PermsRead flag set, you would do:
userPermMask = bitmask.NewMaskBitExplicit(userPerms)
if userPermMask.HasFlag(PermsRead) {
@@ -98,55 +161,65 @@ And you want to see if the user has the PermsRead flag set, you would do:
NOT:
userPermMask = bitmask.NewMaskBitExplicit(PermsRead)
// Nor:
// userPermMask = PermsRead
if userPermMask.HasFlag(userPerms) {
userPermMask.HasFlag(bitmask.MaskBit(userPerms))
NOR:
userPermMask = PermsRead
if userPermMask.HasFlag(bitmask.MaskBit(userPerms)) {
// ...
}
This will be terribly, horribly wrong, cause incredibly unexpected results,
and quite possibly cause massive security issues. Don't do it.
COMPOSITES
Remember, [MaskBit.HasFlag] (and other methods) are for comparing/modifying an
*authoritative/concrete value* with one or more *attributes*, not the other way around.
If you want to define a set of flags that are a combination of other flags,
your inclination would be to bitwise-OR them together:
Composite Flags
If you want to define a set of flags that are a combination
(a "composite", or OR'd set) of other flags, your inclination
would be to bitwise-OR them together:
const (
flagA bitmask.MaskBit = 1 << iota // 1
flagB // 2
flagC // 4
// ...
)
const (
flagAB bitmask.MaskBit = flagA | flagB // 3
)
Which is fine and dandy. But if you then have:
Which is fine, and the correct approach.
But if you then have:
var myMask *bitmask.MaskBit = bitmask.NewMaskBit()
myMask.AddFlag(flagA)
You may expect this call to [MaskBit.HasFlag]:
you may expect this call to [MaskBit.HasFlag]:
myMask.HasFlag(flagAB)
to be true, since flagA is "in" flagAB.
It will return false - HasFlag does strict comparisons.
It will only return true if you then ALSO do:
// This would require setting flagA first.
// The order of setting flagA/flagB doesn't matter,
// but you must have both set for HasFlag(flagAB) to return true.
myMask.AddFlag(flagB)
It would only return true if you did:
or if you do:
// This can be done with or without additionally setting flagA.
// ...
myMask.AddFlag(flagAB)
Instead, if you want to see if a mask has membership within a composite flag,
you can use [MaskBit.IsOneOf].
or:
// ...
myMask.AddFlag(flagA)
myMask.AddFlag(flagB)
Instead, if you want to see if a mask has *at least one flag of* a composite flag,
you can use [MaskBit.HasOneOf].
# Other Options
@@ -157,6 +230,6 @@ you may be interested in one of the following libraries:
* [github.com/abice/go-enum]
* [github.com/jeffreyrichter/enum/enum]
[exbibytes]: https://simple.wikipedia.org/wiki/Exbibyte
[dmflags]: https://doomwiki.org/wiki/DMFlags
*/
package bitmask
+6
View File
@@ -0,0 +1,6 @@
/*
Package bytesx aims to extend functionality of the stdlib [bytes] module.
TODO.
*/
package bytesx
+4
View File
@@ -0,0 +1,4 @@
/*
Package binaryx aims to extend functionality of the stdlib [encoding/binary] module.
*/
package binaryx
+122
View File
@@ -0,0 +1,122 @@
package binaryx
import (
"bytes"
"encoding"
"encoding/binary"
)
/*
Marshal provides a Golang convention marshaling function entry point
(e.g. like [encoding/json.Marshal], [encoding/xml.Marshal]) for arbitrary
binary data.
It simply wraps [OrderedMarshal] with [encoding/binary.NativeEndian]
(which can differ from platform to platform) as the ord parameter to [OrderedMarshal].
If you need to use a different or explicit [encoding/binary.ByteOrder],
use [OrderedMarshal] directly instead.
*/
func Marshal(v any) (data []byte, err error) {
if data, err = OrderedMarshal(v, binary.NativeEndian); err != nil {
return
}
return
}
/*
OrderedMarshal provides a slightly more flexible Golang convention marshaling
function entry point (e.g. similar to [encoding/json.MarshalIndent],
[encoding/xml.MarshalIndent]) for arbitrary binary data.
If v conforms to [encoding.BinaryMarshaler], then the marshal method will be called.
Otherwise, it wraps [encoding/binary.Write] with an internal buffer using ord
as the [encoding/binary.ByteOrder] (endianness), so refer to that function
for all caveats and other details.
Note that [encoding/binary.Write] is *much* more strict than marshaling,
and requires very basic typing.
If you have no need for a specific byte order/endianness or want to explicitly use this
system's native byte order/endianness,
use [Marshal] instead.
*/
func OrderedMarshal(v any, ord binary.ByteOrder) (data []byte, err error) {
var ok bool
var b binary.Marshaler
var buf *bytes.Buffer = new(bytes.Buffer)
if b, ok = v.(encoding.BinaryMarshaler); ok {
if data, err = b.MarshalBinary(); err != nil {
return
}
return
}
if err = binary.Write(buf, ord, v); err != nil {
return
}
data = buf.Bytes()
return
}
/*
Unmarshal provides a Golang convention unmarshaling function entry point
(e.g. like [encoding/json.Unmarshal], [encoding/xml.Unmarshal]) for arbitrary
binary data.
It simply wraps [OrderedUnmarshal] with [encoding/binary.NativeEndian]
(which can differ from platform to platform) as the ord parameter to [OrderedUnmarshal].
If you need to use a different or explicit [encoding/binary.ByteOrder],
use [OrderedUnmarshal] directly instead.
*/
func Unmarshal(data []byte, v any) (err error) {
if err = OrderedUnmarshal(data, v, binary.NativeEndian); err != nil {
return
}
return
}
/*
OrderedUnmarshal provides a slightly more flexible Golang convention unmarshaling
for arbitrary binary data.
If v conforms to [encoding.BinaryUnmarshaler], then the unmarshal method will be called.
Otherwise, it wraps [encoding/binary.Read] with an internal buffer using ord
as the [encoding/binary.ByteOrder] (endianness), so refer to that function
for all caveats and other details.
Note that [encoding/binary.Read] is *much* more strict than unmarshaling,
and requires very basic typing.
If you have no need for a specific byte order/endianness or want to explicitly use this
system's native byte order/endianness,
use [Unmarshal] instead.
*/
func OrderedUnmarshal(data []byte, v any, ord binary.ByteOrder) (err error) {
var ok bool
var b encoding.BinaryUnmarshaler
var buf *bytes.Reader = bytes.NewReader(data)
if b, ok = v.(encoding.BinaryUnmarshaler); ok {
if err = b.UnmarshalBinary(data); err != nil {
return
}
return
}
if err = binary.Read(buf, ord, v); err != nil {
return
}
return
}
+4
View File
@@ -0,0 +1,4 @@
/*
Package encodingx aims to extend functionality of the stdlib [encoding] module.
*/
package encodingx
+4
View File
@@ -0,0 +1,4 @@
/*
Package hexx aims to extend [encoding/hex] functionality.
*/
package hexx
+30
View File
@@ -0,0 +1,30 @@
package hexx
import (
`io`
)
/*
HexString returns a custom-formatted hex representation of b.
*/
func HexString(b []byte, opts ...hexStrFmtrOpt) (out string) {
// TODO
return
}
/*
HexStringStream encodes a stream read from r and written into w.
HexStringStream will return cleanly if r returns no more bytes or if an [io.EOF] is encountered (the [io.EOF] will not be returned).
Any other error will immediately halt reading/writing and will b returned in err.
If w is of a fixed capacity, it must be at least 2x the size of r's capacity.
*/
func HexStringStream(r io.Reader, w io.Writer, opts ...hexStrFmtrOpt) (read, wrtn uint64, err error) {
// TODO
return
}
+67
View File
@@ -0,0 +1,67 @@
package hexx
/*
HexStrWithPrefix sets whether a prefix ("0x" by default, see [HexStrWithPrefixStr])
*/
func HexStrWithPrefix(pfx bool) (f hexStrFmtrOpt) {
f = func(h *hexStrFmtr) {
if h.pfxStr == "" {
h.pfxStr = "0x"
}
h.pfx = pfx
}
return
}
/*
HexStrWithPrefixStr sets the prefix used if a prefix is to be included (see [HexStrWithPrefix]).
The default is "0x".
*/
func HexStrWithPrefixStr(pfx string) (f hexStrFmtrOpt) { return func(h *hexStrFmtr) { h.pfxStr = pfx } }
/*
HexStrWithLower uses lowercase or uppercase hex character representation. The default is lowercase.
To use uppercase, specify this option with lower == false.
*/
func HexStrWithLower(lower bool) (f hexStrFmtrOpt) { return func(h *hexStrFmtr) { h.lower = lower } }
/*
HexStrWithLeftPad specifies if left zero-padding should be used.
The default is true.
*/
func HexStrWithLeftPad(leftPad bool) (f hexStrFmtrOpt) {
return func(h *hexStrFmtr) { h.noLeftPad = !leftPad }
}
/*
HexStrWithSegLeftPad specifies if left zero-padding should be used for each segment (see [HexStrWithSplit]).
The default is true.
*/
func HexStrWithSegLeftPad(segLeftPad bool) (f hexStrFmtrOpt) {
return func(h *hexStrFmtr) { h.noSegmentLeftPad = !segLeftPad }
}
/*
HexStrWithSplit will split the hex string into "chunks" of numBytes bytes.
For example:
numBytes == 0:
0x0123456789abcdef
numBytes == 1:
0x01 0x23 0x45 0x67 0x89 0xab 0xcd 0xef
numBytes == 2:
0x0123 0x4567 0x89ab 0xcdef
*/
func HexStrWithSplit(numBytes uint) (f hexStrFmtrOpt) {
return func(h *hexStrFmtr) { h.segmentBytes = numBytes }
}
// TODO
+16
View File
@@ -0,0 +1,16 @@
package hexx
type (
hexStrFmtrOpt func(h *hexStrFmtr)
hexStrFmtr struct {
pfx bool
pfxStr string
lower bool
noLeftPad bool
noSegmentLeftPad bool
segmentBytes uint
segmentSep string
newlineBytes uint
}
)
+22 -10
View File
@@ -1,35 +1,47 @@
module r00t2.io/goutils
go 1.25
go 1.25.0
require (
github.com/Masterminds/sprig/v3 v3.3.0
github.com/coreos/go-systemd/v22 v22.7.0
github.com/davecgh/go-spew v1.1.1
github.com/google/uuid v1.6.0
github.com/shirou/gopsutil/v4 v4.25.12
github.com/olekukonko/tablewriter v1.1.4
github.com/shirou/gopsutil/v4 v4.26.5
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/sys v0.40.0
golang.org/x/sys v0.46.0
r00t2.io/sysutils v1.16.2
)
require (
dario.cat/mergo v1.0.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/semver/v3 v3.5.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
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.9.1 // indirect
github.com/ebitengine/purego v0.10.1 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/go-ole/go-ole v1.3.0 // 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-20251013123823-9fd1530e3ec3 // indirect
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // 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
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
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/spf13/cast v1.10.0 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.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
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/sync v0.21.0 // indirect
)
+53
View File
@@ -4,8 +4,16 @@ github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJ
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -14,11 +22,19 @@ github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
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/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/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/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=
@@ -31,10 +47,28 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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=
github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak=
github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/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/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=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.3.0 h1:teJvgLGUEqMzBUms+Dj3/3szNqCG/Jdw9iDbum8fR6U=
github.com/olekukonko/errors v1.3.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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=
@@ -43,6 +77,10 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
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=
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM=
github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
@@ -51,22 +89,37 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
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=
github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4=
github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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=
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=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
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/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=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
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/sysutils v1.16.2 h1:wI01UwZ/bXn/lzBiCpqDmzZCOWiK87kz04SB4xRw+W0=
r00t2.io/sysutils v1.16.2/go.mod h1:iXK+ALOwIdRKjAJIE5USlkZ669SVDHBNNuYhunsznH8=
+4
View File
@@ -26,6 +26,10 @@ func (c *CtxIO) GetChunkLen() (size uint) {
return c.l.GetChunkLen()
}
func (c *CtxIO) Len() (unread int) {
return c.buf.Len()
}
func (c *CtxIO) Read(p []byte) (n int, err error) {
var nr int64
+12
View File
@@ -147,6 +147,17 @@ type (
ContextReader
ContextWriter
}
/*
LenReader allows one to read bytes (conforming to [io.Reader]),
and also provides a Len() method which returns an int of remaining unread bytes.
[bytes.Buffer] and [bytes.Reader] conforms to this, but see [CtxIO] for
a package-provided type.
*/
LenReader interface {
io.Reader
Len() (unread int)
}
)
type (
@@ -172,6 +183,7 @@ type (
* [ChunkReadWriter]
* [ContextReadWriter]
* [SizedCopyBufferer]
* [LenReader]
Unlike [XIO], it must be non-nil (see [NewCtxIO]) since it maintains state
(though technically, one does not need to call [NewCtxIO] if they call
+3 -1
View File
@@ -1,3 +1,5 @@
- ScopedLogger, take an io.Writer for each log level
- logging probably needs mutexes
- macOS support beyond the legacy NIX stuff. it apparently uses something called "ULS", "Unified Logging System".
@@ -16,7 +18,7 @@
-- log.LlongFile and log.Lshortfile flags don't currently work properly for StdLogger/FileLogger; they refer to the file in logging package rather than the caller.
-- ZeroLog seems to be able to do it, take a peek there.
- StdLogger2; where stdout and stderr are both logged to depending on severity level.
- StdLvlLogger; where stdout and stderr are both logged to depending on severity level.
- make configurable via OR bitmask
- Suport remote loggers? (eventlog, syslog, journald)
+10
View File
@@ -0,0 +1,10 @@
package netx
import (
`net/netip`
)
var (
ip4In6Legacy netip.Prefix = netip.MustParsePrefix("::/96")
ip4In6Modern netip.Prefix = netip.MustParsePrefix("::ffff:0:0/96")
)
+4
View File
@@ -0,0 +1,4 @@
/*
Package netx includes extensions to the stdlib [net] module.
*/
package netx
-4
View File
@@ -1,4 +0,0 @@
/*
Package netx includes extensions to the stdlib `net` module.
*/
package netx
+76
View File
@@ -455,6 +455,82 @@ func IsPrefixNet(s string) (isNet bool) {
return
}
/*
IsPublic returns true if ALL of the following conditions are *true* for ip:
* [net/netip.Addr.IsGlobalUnicast]
* [net/netip.Addr.IsValid]
* (IPv4) Is not in 192.0.2.0/24, 198.51.100.0/24, or 203.0.113.0/24 ([RFC 5737])
* (IPv6) Is not in 2001:db8::/32 or 3fff::/20 ([RFC 3849], [RFC 9637])
AND ALL of the following conditions are *false* for ip:
* [net/netip.Addr.IsLinkLocalMulticast]
* [net/netip.Addr.IsInterfaceLocalMulticast]
* [net/netip.Addr.IsLinkLocalUnicast]
* [net/netip.Addr.IsMulticast]
* [net/netip.Addr.IsLoopback]
* [net/netip.Addr.IsPrivate]
* [net/netip.Addr.IsUnspecified]
4-in-6 addresses (::0:0/96, "IPv4-Compatible IPv6 Address" [RFC 4291 § 2.5.5.1] (Legacy/Obsolete);
::ffff:0:0/96, "IPv4-Mapped IPv6" [RFC 4291 § 2.5.5.2]) will be internally
"unwrapped" back to native IPv4 before performing conditional checks.
[RFC 5737]: https://datatracker.ietf.org/doc/html/rfc5737
[RFC 4291 § 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
[RFC 4291 § 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
[RFC 3849]: https://datatracker.ietf.org/doc/html/rfc3849
[RFC 9637]: https://datatracker.ietf.org/doc/html/rfc9637
*/
func IsPublic(ip netip.Addr) (isPub bool) {
var err error
var v bool
var addr netip.Addr
// Clone to avoid modification to the passed in value.
if addr, err = netip.ParseAddr(ip.String()); err != nil {
return
}
addr = addr.Unmap()
// Short-circuit on invalid first to avoid any possible logic oddness.
if !addr.IsValid() {
return
}
// Following must be FALSE
for _, v = range []bool{
addr.IsLinkLocalMulticast(),
addr.IsInterfaceLocalMulticast(),
addr.IsLinkLocalUnicast(),
addr.IsMulticast(),
addr.IsLoopback(),
addr.IsPrivate(),
addr.IsUnspecified(),
// these are in the FALSE to keep syntax pattern but it's cleaner to doc as if it's a !<cond> in TRUE.
ip4In6Legacy.Contains(ip),
ip4In6Modern.Contains(ip),
} {
if v {
return
}
}
// Following must be TRUE
for _, v = range []bool{
addr.IsGlobalUnicast(),
} {
if !v {
return
}
}
// All conditions pass
isPub = true
return
}
/*
Mask4ToCidr converts an IPv4 netmask *in bitmask form* to a CIDR prefix size/bit size/bit length.
+1
View File
@@ -0,0 +1 @@
This is mostly just for generating some reference. It's not really intended for public consumption.
+20
View File
@@ -0,0 +1,20 @@
module r00t2.io/goutils/netx/internal
go 1.26.4
require github.com/olekukonko/tablewriter v1.1.4
require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/goccy/go-json v0.10.6 // 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
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
golang.org/x/sys v0.46.0 // indirect
)
+45
View File
@@ -0,0 +1,45 @@
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
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/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/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
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/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/errors v1.3.0 h1:teJvgLGUEqMzBUms+Dj3/3szNqCG/Jdw9iDbum8fR6U=
github.com/olekukonko/errors v1.3.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+295
View File
@@ -0,0 +1,295 @@
package main
import (
"bytes"
"fmt"
"log"
"net/netip"
"strconv"
"strings"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/tw"
)
const (
sectBordChar string = "#"
sectBordWdth int = 2
sectSpcPad int = 1
sectSidePad int = sectBordWdth + sectSpcPad
sectBothPad int = sectSidePad * 2
)
type (
IpInfo struct {
// Desc string
Index int
// IP netip.Addr
Is4 bool
Is4In6 bool
Is6 bool
IsGlobalUnicast bool
IsLinkLocalUnicast bool
IsInterfaceLocalMulticast bool
IsLinkLocalMulticast bool
IsMulticast bool
IsLoopback bool
IsPrivate bool
IsUnspecified bool
IsValid bool
}
)
var (
datHdrsFunc []string = []string{
"-",
".Is4", ".Is4In6()", ".Is6()",
".IsGlobalUnicast()", ".IsLinkLocalUnicast()",
".IsInterfaceLocalMulticast()", ".IsLinkLocalMulticast()", ".IsMulticast()",
".IsLoopback()",
".IsPrivate()",
".IsUnspecified()", ".IsValid()",
}
datHdrsKey []string = []string{
"Address Index (See Fig. 3)",
"IPv4", "4-in-6", "IPv6",
"Global Unicast", "Link-Local Unicast",
"Interface Local Multicast", "Link-Local Multicast", "Multicast",
"Loopback",
"Private/LAN",
"\"Unspecified\" Address", "Valid Address",
}
datHdrsShort = []string{
"IDX",
"4", "6(4)", "6",
"GU", "LLU",
"ILM", "LLM", "M",
"LO",
"P",
"U", "V",
}
exampleAddrs [][2]string = [][2]string{
[2]string{"IPv4", "203.0.113.10"}, // RFC 5737 address (https://datatracker.ietf.org/doc/html/rfc5737)
[2]string{"IPv4 Unspecified", "0.0.0.0"}, // Generally used to represent all of IPv4 address space
[2]string{"IPv4 Global Unicast", "173.230.132.76"}, // r00t2.io
[2]string{"IPv4 Multicast (All) (RFC 1112 § 4)", "224.0.0.1"}, // Should encompass all the multicast below. 224.0.0.0 is reserved.
[2]string{"IPv4 Multicast (Reserved) (RFC 1112 § 4)", "224.0.0.0"}, // Reserved per RFC but it should still report multicast.
[2]string{"IPv4 Multicast (Link-Local Multicast/Local Network Control Block) (RFC 5771 § 4)", "224.0.0.18"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-4 (VRRP multicast addr)
[2]string{"IPv4 Multicast (Internetwork Control Block) (RFC 5771 § 5)", "224.0.1.68"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-5 (mdhcpdiscover, RFC 2730)
[2]string{"IPv4 Multicast (AD-HOC I) (RFC 5771 § 6)", "224.0.2.10"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-6
[2]string{"IPv4 Multicast (SDP/SAP) (RFC 5771 § 7)", "224.2.0.10"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-7
[2]string{"IPv4 Multicast (AD-HOC II) (RFC 5771 § 6)", "224.3.0.10"}, // (above)
[2]string{"IPv4 Multicast (Source-Specific) (RFC 5771 § 8)", "232.0.0.10"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-8
[2]string{"IPv4 Multicast (GLOP) (RFC 5771 § 9)", "233.0.0.10"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-9
[2]string{"IPv4 Multicast (AD-HOC III) (RFC 5771 § 6)", "233.252.0.10"}, // (above)
[2]string{"IPv4 Multicast (Administrative) (RFC 5771 § 10)", "239.0.0.10"}, // https://datatracker.ietf.org/doc/html/rfc5771#section-10
[2]string{"IPv4 Link-Local Unicast (RFC 3927 § 2.1)", "169.254.1.10"}, // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1
[2]string{"IPv4 Loopback", "127.0.1.10"}, // It's actually 127/8. Cannot believe how many people do not know this.
[2]string{"IPv4 Private (RFC 1918)", "10.0.0.10"}, // https://datatracker.ietf.org/doc/html/rfc1918
[2]string{"4-in-6 (RFC 4291 § 2.5.5.1)", "::203.0.113.10"}, // https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
[2]string{"4-in-6 (RFC 4291 § 2.5.5.1) (Native)", "::cb00:710a"}, // ""
[2]string{"4-in-6 (RFC 4291 § 2.5.5.2)", "::ffff:203.0.113.10"}, // https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
[2]string{"4-in-6 (RFC 4291 § 2.5.5.2) (Native)", "::ffff:cb00:710a"}, // ""
[2]string{"IPv6", "2001:db8::cb00:710a"}, // RFC 3849 (https://datatracker.ietf.org/doc/html/rfc3849) / RFC 9637 (https://datatracker.ietf.org/doc/html/rfc9637) address
[2]string{"IPv6 Unspecified", "::"}, // Generally used to represent all of IPv6 address space
[2]string{"IPv6 Global Unicast", "2600:3c02::f03c:91ff:fe93:c0a7"}, // r00t2.io
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (Reserved Net)", "ff00::"}, // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (Reserved)", "ff00::1"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Nodes) (Interface-Local)", "ff01::1"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Nodes) (Link-Local)", "ff02::1"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Interface-Local)", "ff01::2"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Link-Local)", "ff02::2"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Admin-Local)", "ff04::2"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Site-Local)", "ff05::2"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Org-Local)", "ff08::2"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Internet/Global)", "ff0e::2"}, // ""
[2]string{"IPv6 Multicast (RFC 4291 § 2.7.1) (Solicited Node)", "ff02::1:ff00:10"}, // ""
[2]string{"IPv6 Link-Local Unicast (RFC 4291 § 2.5.6)", "fe80::cb00:710a"}, // https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.6
[2]string{"IPv6 Loopback", "::1"}, // It's explicitly always a /128 with the address ::1 per RFC 4291 § 2.5.3.
[2]string{"IPv6 Private (Unique-Local Addresses) (RFC 4193) (Reserved)", "fc00::10"}, // https://datatracker.ietf.org/doc/html/rfc4193
[2]string{"IPv6 Private (Unique-Local Addresses) (RFC 4193) (Valid)", "fd00::10"}, // ""
}
descs []string = make([]string, len(exampleAddrs))
ips []netip.Addr = make([]netip.Addr, len(exampleAddrs))
sectSep string = "\n" + strings.Repeat("-", 80) + "\n"
)
func genHdr(sectNm string) (s string) {
// Wish I finished stringsx.Banner at time of writing this...
var fillLen int = sectBothPad + len(sectNm)
s = fmt.Sprintf(
"%s\n"+ // top border
// begin title line
"%-*s"+ // left-justify/right-pad
"%s"+ // sectNm text
"%[2]*[3]s\n"+ // one-indexed; repeat pad num and pad str from left border as right border
// end title line
"%[1]s\n", // bottom border
strings.Repeat(sectBordChar, fillLen), // top and bottom (bottom uses index)
sectSidePad, strings.Repeat(sectBordChar, sectBordWdth), // title left and right borders (right uses index)
sectNm, // title text
)
return
}
func main() {
var err error
var idx int
var s string
var desc string
var pair [2]string
var buf *bytes.Buffer = new(bytes.Buffer)
var tbl *tablewriter.Table = tablewriter.NewTable(
buf,
tablewriter.WithConfig(
tablewriter.Config{
Header: tw.CellConfig{
Formatting: tw.CellFormatting{
AutoFormat: tw.Off,
},
},
},
),
// requires .Batch(), and the autoheader forces all caps.
// https://github.com/olekukonko/tablewriter/issues/143
// https://github.com/olekukonko/tablewriter/issues/190
/*
tablewriter.WithBehavior(
tw.Behavior{
Structs: tw.Struct{
AutoHeader: tw.On,
},
},
),
*/
tablewriter.WithRendition(
tw.Rendition{
Settings: tw.Settings{
Separators: tw.Separators{BetweenRows: tw.On},
},
},
),
)
defer func() {
var tErr error
if tbl != nil {
if tErr = tbl.Close(); tErr != nil {
log.Printf("Error closing table: %v", tErr)
}
}
}()
buf.WriteString(genHdr("Fig. 1: Address Evaluations"))
buf.WriteString("(See Fig. 2 for a key of header symbols to names)\n\n")
tbl.Header(datHdrsShort)
for idx, pair = range exampleAddrs {
desc, s = pair[0], pair[1]
descs[idx] = desc
if ips[idx], err = netip.ParseAddr(s); err != nil {
log.Panicln(err)
}
// Currently no way to skip cols etc. https://github.com/olekukonko/tablewriter/issues/317
// rows[idx] = IpInfo{
if err = tbl.Append(
IpInfo{
// Desc: desc,
Index: idx,
// IP: ip,
Is4: ips[idx].Is4(),
Is4In6: ips[idx].Is4In6(),
Is6: ips[idx].Is6(),
IsGlobalUnicast: ips[idx].IsGlobalUnicast(),
IsLinkLocalUnicast: ips[idx].IsLinkLocalUnicast(),
IsInterfaceLocalMulticast: ips[idx].IsInterfaceLocalMulticast(),
IsLinkLocalMulticast: ips[idx].IsLinkLocalMulticast(),
IsMulticast: ips[idx].IsMulticast(),
IsLoopback: ips[idx].IsLoopback(),
IsPrivate: ips[idx].IsPrivate(),
IsUnspecified: ips[idx].IsUnspecified(),
IsValid: ips[idx].IsValid(),
},
); err != nil {
log.Panicln(err)
}
}
/*
if err = tbl.Bulk(rows); err != nil {
log.Panicln(err)
}
*/
if err = tbl.Render(); err != nil {
log.Panicln(err)
}
buf.WriteString(sectSep)
buf.WriteString(genHdr("Fig. 2: Headers Key for Fig. 1"))
tbl.Reset()
tbl.Header("Symbol", "Description", "net/netip.Addr Method")
for idx = range datHdrsKey {
if err = tbl.Append(
[]string{
datHdrsShort[idx],
datHdrsKey[idx],
datHdrsFunc[idx],
},
); err != nil {
log.Panicln(err)
}
}
if err = tbl.Render(); err != nil {
log.Panicln(err)
}
buf.WriteString(sectSep)
buf.WriteString(genHdr("Fig. 3: Test/Example IP Address Reference/Lookup for Fig. 1"))
buf.WriteString("(See Fig. 4 for Descriptions/Detailed Information)\n\n")
tbl.Reset()
tbl.Header("Index", "Address (Raw)", "Address (Parsed)")
for idx = range exampleAddrs {
if err = tbl.Append(
[]string{
strconv.Itoa(idx),
exampleAddrs[idx][1],
ips[idx].String(),
},
); err != nil {
log.Panicln(err)
}
}
if err = tbl.Render(); err != nil {
log.Panicln(err)
}
buf.WriteString(sectSep)
buf.WriteString(genHdr("Fig. 4: Extended Information for Fig. 3"))
tbl.Reset()
tbl.Header("Index", "Description")
for idx = range exampleAddrs {
if err = tbl.Append(
[]string{
strconv.Itoa(idx),
descs[idx],
},
); err != nil {
log.Panicln(err)
}
}
if err = tbl.Render(); err != nil {
log.Panicln(err)
}
fmt.Println(buf.String())
}
+147
View File
@@ -0,0 +1,147 @@
package main
import (
"log"
"maps"
"net/netip"
`os`
"slices"
"github.com/olekukonko/tablewriter"
`github.com/olekukonko/tablewriter/tw`
)
type (
IpInfo struct {
Desc string `r:"-"`
IP netip.Addr `r:"-"`
Is4 bool
Is4In6 bool
Is6 bool
IsGlobalUnicast bool
IsInterfaceLocalMulticast bool
IsLinkLocalMulticast bool
IsLinkLocalUnicast bool
IsLoopback bool
IsMulticast bool
IsPrivate bool
IsUnspecified bool
IsValid bool
}
)
func main() {
var err error
var s string
var idx int
var desc string
var ip netip.Addr
var rows []IpInfo
/*
var typ reflect.Type
var val reflect.Value
var fieldVal reflect.Value
var field reflect.StructField
*/
var exampleAddrs map[string]string
var tbl *tablewriter.Table = tablewriter.NewTable(
os.Stdout,
tablewriter.WithBehavior(
tw.Behavior{
Structs: tw.Struct{
AutoHeader: tw.On,
},
},
),
tablewriter.WithRendition(
tw.Rendition{
Settings: tw.Settings{
Separators: tw.Separators{BetweenRows: tw.On},
},
},
),
)
defer func() {
var tErr error
if tbl != nil {
if tErr = tbl.Close(); tErr != nil {
log.Printf("Error closing table: %v", tErr)
}
}
}()
exampleAddrs = map[string]string{
"IPv4": "203.0.113.10", // RFC 5737 address (https://datatracker.ietf.org/doc/html/rfc5737)
"IPv4 Unspecified": "0.0.0.0",
"IPv4 Global Unicast": "173.230.132.76", // r00t2.io
"IPv4 Multicast (All) (RFC 1112 § 4)": "224.0.0.1", // Should encompass all the multicast below. 224.0.0.0 is reserved.
"IPv4 Multicast (Reserved) (RFC 1112 § 4)": "224.0.0.0", // Reserved per RFC but it should still report multicast.
"IPv4 Multicast (Link-Local Multicast/Local Network Control Block) (RFC 5771 § 4)": "224.0.0.18", // https://datatracker.ietf.org/doc/html/rfc5771#section-4 (VRRP multicast addr)
"IPv4 Multicast (Internetwork Control Block) (RFC 5771 § 5)": "224.0.1.68", // https://datatracker.ietf.org/doc/html/rfc5771#section-5 (mdhcpdiscover, RFC 2730)
"IPv4 Multicast (AD-HOC I) (RFC 5771 § 6)": "224.0.2.10", // https://datatracker.ietf.org/doc/html/rfc5771#section-6
"IPv4 Multicast (SDP/SAP) (RFC 5771 § 7)": "224.2.0.10", // https://datatracker.ietf.org/doc/html/rfc5771#section-7
"IPv4 Multicast (AD-HOC II) (RFC 5771 § 6)": "224.3.0.10", // (above)
"IPv4 Multicast (Source-Specific) (RFC 5771 § 8)": "232.0.0.10", // https://datatracker.ietf.org/doc/html/rfc5771#section-8
"IPv4 Multicast (GLOP) (RFC 5771 § 9)": "233.0.0.10", // https://datatracker.ietf.org/doc/html/rfc5771#section-9
"IPv4 Multicast (AD-HOC III) (RFC 5771 § 6)": "233.252.0.10", // (above)
"IPv4 Multicast (Administrative) (RFC 5771 § 10)": "239.0.0.10", // https://datatracker.ietf.org/doc/html/rfc5771#section-10
"IPv4 Link-Local Unicast (RFC 3927 § 2.1)": "169.254.1.10", // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1
"IPv4 Loopback": "127.0.1.10", // It's actually 127/8. Cannot believe how many people do not know this.
"IPv4 Private (RFC 1918)": "10.0.0.10", // https://datatracker.ietf.org/doc/html/rfc1918
"4-in-6 (RFC 4291 § 2.5.5.1)": "::203.0.113.10", // https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1
"4-in-6 (RFC 4291 § 2.5.5.1) (Native)": "::cb00:710a", // ""
"4-in-6 (RFC 4291 § 2.5.5.2)": "::ffff:203.0.113.10", // https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
"4-in-6 (RFC 4291 § 2.5.5.2) (Native)": "::ffff:cb00:710a", // ""
"IPv6": "2001:db8::cb00:710a", // RFC 3849 (https://datatracker.ietf.org/doc/html/rfc3849) / RFC 9637 (https://datatracker.ietf.org/doc/html/rfc9637) address
"IPv6 Unspecified": "::",
"IPv6 Global Unicast": "2600:3c02::f03c:91ff:fe93:c0a7", // r00t2.io
"IPv6 Multicast (RFC 4291 § 2.7.1) (Reserved Net)": "ff00::", // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
"IPv6 Multicast (RFC 4291 § 2.7.1) (Reserved)": "ff00::1", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Nodes) (Interface-Local)": "ff01::1", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Nodes) (Link-Local)": "ff02::1", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Interface-Local)": "ff01::2", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Link-Local)": "ff02::2", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Admin-Local)": "ff04::2", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Site-Local)": "ff05::2", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Org-Local)": "ff08::2", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (All Routers) (Internet/Global)": "ff0e::2", // ""
"IPv6 Multicast (RFC 4291 § 2.7.1) (Solicited Node)": "ff02::1:ff00:10", // ""
"IPv6 Link-Local Unicast (RFC 4291 § 2.5.6)": "fe80::cb00:710a", // https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.6
"IPv6 Loopback": "::1", // It's explicitly always a /128 with the address ::1 per RFC 4291 § 2.5.3.
"IPv6 Private (Unique-Local Addresses) (RFC 4193) (Reserved)": "fc00::10", // https://datatracker.ietf.org/doc/html/rfc4193
"IPv6 Private (Unique-Local Addresses) (RFC 4193) (Valid)": "fd00::10", // ""
}
rows = make([]IpInfo, len(exampleAddrs))
for idx, desc = range slices.Sorted(maps.Keys(exampleAddrs)) {
s = exampleAddrs[desc]
if ip, err = netip.ParseAddr(s); err != nil {
log.Panicln(err)
}
// Currently no way to skip cols etc. https://github.com/olekukonko/tablewriter/issues/317
rows[idx] = IpInfo{
Desc: desc,
IP: ip,
Is4: ip.Is4(),
Is4In6: ip.Is4In6(),
Is6: ip.Is6(),
IsGlobalUnicast: ip.IsGlobalUnicast(),
IsInterfaceLocalMulticast: ip.IsInterfaceLocalMulticast(),
IsLinkLocalMulticast: ip.IsLinkLocalMulticast(),
IsLinkLocalUnicast: ip.IsLinkLocalUnicast(),
IsLoopback: ip.IsLoopback(),
IsMulticast: ip.IsMulticast(),
IsPrivate: ip.IsPrivate(),
IsUnspecified: ip.IsUnspecified(),
IsValid: ip.IsValid(),
}
}
if err = tbl.Bulk(rows); err != nil {
log.Panicln(err)
}
if err = tbl.Render(); err != nil {
log.Panicln(err)
}
}
+57
View File
@@ -0,0 +1,57 @@
- write a flag parser/converter for github.com/scorpionknifes/go-pcre (and github.com/GRbit/go-pcre) :
----
// i CASELESS
// m MULTILINE
// s DOTALL
// x EXTENDED
// U UNGREEDY
// 8 UTF8 (non-standard flag letter; standard PCRE uses (*UTF8) outside (?...) syntax)
//
// Flags after a '-' inside group disable associated flag and are ignored
// (applies inside the regex engine once prefix is stripped).
// pattern, flags := ParsePrefixFlags(`(?im)^hello`)
// re := pcre.MustCompile(pattern, flags)
func ParsePrefixFlags(pattern string) (string, int) {
if !strings.HasPrefix(pattern, "(?") {
return pattern, 0
}
end := strings.IndexByte(pattern, ')')
if end < 0 {
return pattern, 0
}
inner := pattern[2:end] // everything between "(?" and ")"
for _, ch := range inner {
if !strings.ContainsRune("imsxU8-", ch) {
return pattern, 0
}
}
positive := inner
if dash := strings.IndexByte(inner, '-'); dash >= 0 {
positive = inner[:dash]
}
var flags int
for _, ch := range positive {
switch ch {
case 'i':
flags |= pcre.CASELESS
case 'm':
flags |= pcre.MULTILINE
case 's':
flags |= pcre.DOTALL
case 'x':
flags |= pcre.EXTENDED
case 'U':
flags |= pcre.UNGREEDY
case '8':
flags |= pcre.UTF8
}
}
return pattern[end+1:], flags
}
----
+10
View File
@@ -2,4 +2,14 @@
-- draw border around multiline s
-- i have a version in python somewhere that does this, should dig that up
- Tokenize() (new function)
-- PosixFilename() (new function) -- https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282
-- strings.ToLower()
-- compact consecutive:
--- whitespace
--- .
--- ,
-- set custom replacement string (defaults to "_")
-- replace whitespace (after/during compact) with _ (customizable?)
- create bytesx package that duplicates the functions here?
+47
View File
@@ -10,6 +10,53 @@ import (
`unicode`
)
/*
HasBookend returns true if string s both begins AND ends with sym.
It is more strict than [HasBoundary] (which only requires
that s has sym at the beginning OR the end.)
Examples:
HasBookend("|foo|", "|") → true
HasBookend("|foo", "|") → false
HasBookend("foo|", "|") → false
HasBookend("fo|o", "|") → false
HasBookend("|foo| ", "|") → false // Whitespace prevents match
HasBookend(" |foo| ", "|") → false
sym may be a multi-rune string.
If sym is empty, HasBookend will *always* return true.
*/
func HasBookend(s, sym string) (bounded bool) {
bounded = strings.HasPrefix(s, sym) && strings.HasSuffix(s, sym)
return
}
/*
HasBoundary returns true if string s starts OR ends with symbol sym.
Examples:
HasBoundary("|foo|", "|") → true
HasBoundary("|foo", "|") → true
HasBoundary("foo|", "|") → true
HasBoundary("fo|o", "|") → false
HasBoundary("|foo| ", "|") → true
HasBoundary(" |foo| ", "|") → false // Whitespace prevents match
sym may be a multi-rune string.
If sym is empty, HasBoundary will *always* return true.
If you instead require string s to be *enclosed* by sym, see [HasBookend].
*/
func HasBoundary(s, sym string) (bounded bool) {
bounded = strings.HasPrefix(s, sym) || strings.HasSuffix(s, sym)
return
}
/*
IsAscii returns true if all characters in string s are ASCII.
+846 -679
View File
File diff suppressed because it is too large Load Diff
+1299 -476
View File
File diff suppressed because it is too large Load Diff
+2072 -1205
View File
File diff suppressed because it is too large Load Diff
+3 -13
View File
@@ -1,14 +1,4 @@
- osReadFileBytes
- osReadFileStr
- osReadDir
- base64, base16, base32, hex, pem encoding/decoding tpl funcs
-- template hashing functions?
- `dns*` funcs (net)
- `url*` funcs (net/url)
- `uuid*` funcs (github.com/google/uuid and r00t2.io/goutils/uuidx)
- `http*` funcs:
-- `httpReq`: returns a net/http.Request
-- `http<Method>`: performs <Method> (? seems redundant if exposing httpReq)
-- also have `resty*` funcs?
- i should probably explicitly provide a "safe" set vs. "full" set. can just mod the map func getters to accept a "safeOnly" bool param.
- os env vars
+5
View File
@@ -0,0 +1,5 @@
[source,subs="+attributes,+post_replacements",opts="novalidate"]
.Function Signature
----
func {func}{sig}
----
+56 -13
View File
@@ -21,6 +21,8 @@ import (
`github.com/shirou/gopsutil/v4/sensors`
`go4.org/netipx`
`r00t2.io/goutils/netx`
`r00t2.io/goutils/netx/dnsx`
`r00t2.io/goutils/stringsx`
`r00t2.io/goutils/timex`
`r00t2.io/sysutils`
)
@@ -71,18 +73,44 @@ var (
/*
Networking (r00t.io/goutils/netx)
*/
"netxAddrRfc": netx.AddrRfc,
"netxCidr4IpMask": netx.Cidr4ToIPMask,
"netxCidr4Mask": netx.Cidr4ToMask,
"netxCidr4Str": netx.Cidr4ToStr,
"netxFamilyVer": netx.FamilyToVer,
"netxGetAddrFam": netx.GetAddrFamily,
"netxGetIpFam": netx.GetIpFamily,
"netxIpRfc": netx.IpRfc,
"netxIpRfcStr": netx.IpRfcStr,
"netxIpStripRfc": netx.IpStripRfcStr,
"netxIp4MaskCidr": netx.IPMask4ToCidr,
"netxIp4MaskMask": netx.IPMask4ToMask,
"netxAddrRfc": netx.AddrRfc,
"netxCidr4IpMask": netx.Cidr4ToIPMask,
"netxCidr4Mask": netx.Cidr4ToMask,
"netxCidr4Str": netx.Cidr4ToStr,
"netxFamilyVer": netx.FamilyToVer,
"netxGetAddrFam": netx.GetAddrFamily,
"netxGetIpFam": netx.GetIpFamily,
"netxIpRfc": netx.IpRfc,
"netxIpRfcStr": netx.IpRfcStr,
"netxIpStripRfc": netx.IpStripRfcStr,
"netxIp4MaskCidr": netx.IPMask4ToCidr,
"netxIp4MaskMask": netx.IPMask4ToMask,
"netxIp4MaskStr": netx.IPMask4ToStr,
"netxIpVerStr": netx.IpVerStr,
"netxIsBrktd6": netx.IsBracketedIp6,
"netxIsIp": netx.IsIpAddr,
"netxIsPfx": netx.IsPrefixNet,
"netxMask4Cidr": netx.Mask4ToCidr,
"netxMask4StrCidr": netx.Mask4StrToCidr,
"netxMask4StrIpMask": netx.Mask4StrToIPMask,
"netxMask4StrMask": netx.Mask4StrToMask,
"netxVerFamily": netx.VerToFamily,
/*
Networking (r00t.io/goutils/netx/dnsx)
*/
"dnsxPtrAddr": dnsx.AddrFromPtr,
"dnsxAddrPtr": dnsx.AddrToPtr,
"dnsxStrWire": dnsx.DnsStrToWire,
"dnsxWireStr": dnsx.DnsWireToStr,
"dnsxPtrIp": dnsx.IpFromPtr,
"dnsxIpPtr": dnsx.IpToPtr,
"dnsxIsFqdn": dnsx.IsFqdn,
"dnsxIsTxt": dnsx.IsFqdnDefinedTxt,
"dnsxIsNsec3": dnsx.IsFqdnNsec3,
"dnsxIsSrv": dnsx.IsFqdnSrv,
"dnsxIsWild": dnsx.IsFqdnWildcard,
"dnsxIsLbl": dnsx.IsLabel,
"dnsxIsPtr": dnsx.IsPtr,
/*
Numbers/Math
*/
@@ -171,9 +199,24 @@ var (
// .../sensors
"psSensorTemps": sensors.SensorsTemperatures,
/*
Strings
Strings (Standalone)
*/
"extIndent": extIndent, // PR in: https://github.com/Masterminds/sprig/pull/468
/*
Strings (r00t.io/goutils/stringsx)
*/
"strsxIsAscii": stringsx.IsAscii,
"strsxIsAsciiBuf": stringsx.IsAsciiBuf,
"strsxIsAsciiSpcl": stringsx.IsAsciiSpecial,
"strsxIsAsciiBufSpcl": stringsx.IsAsciiBufSpecial,
"strsxLenSpl": stringsx.LenSplit,
"strsxLenSplStr": stringsx.LenSplitStr,
"strsxPad": stringsx.Pad,
"strsxRedact": stringsx.Redact,
"strsxRev": stringsx.Reverse,
"strsxTrimLns": stringsx.TrimLines,
"strsxTrimSpcLft": stringsx.TrimSpaceLeft,
"strsxTrimSpcRt": stringsx.TrimSpaceRight,
/*
System/Platform
*/