v1.16.9
ADDED: * netx.IsPub * encodingx/hexx Rest are mostly small corrections and docs
This commit is contained in:
@@ -67,8 +67,9 @@ for f in $(find . -type f -iname "README.adoc"); do
|
|||||||
newf="${pfx}.md"
|
newf="${pfx}.md"
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
#asciidoctor -a ROOTDIR="${orig}/" -b docbook -o - "${f}" | pandoc -f docbook -t markdown_strict -o "${newf}"
|
# --wrap=preserve or --wrap=none is required to avoid it reflowing the section titles to newlines.
|
||||||
asciidoctor -a ROOTDIR="${orig}/" -b html -o - "${f}" | pandoc -f html -t gfm -o "${newf}"
|
#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 ];
|
if [ $? -eq 0 ];
|
||||||
then
|
then
|
||||||
echo "Generated ${newf} from ${f}"
|
echo "Generated ${newf} from ${f}"
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
- validx: validator functions for https://pkg.go.dev/github.com/go-playground/validator/v10
|
- validx: validator functions for https://pkg.go.dev/github.com/go-playground/validator/v10
|
||||||
|
|
||||||
|
- hashx?
|
||||||
|
|||||||
+30
-39
@@ -11,11 +11,11 @@ import (
|
|||||||
type MaskBit uint
|
type MaskBit uint
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NewMaskBit is a convenience function.
|
NewMaskBit is a convenience function.
|
||||||
It will return a MaskBit with a (referenced) value of 0, so set your consts up accordingly.
|
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!)
|
It is highly recommended to set this default as a "None" flag (separate from your iotas!)
|
||||||
as shown in the example.
|
as shown in the example.
|
||||||
*/
|
*/
|
||||||
func NewMaskBit() (m *MaskBit) {
|
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.
|
See the "Composite (OR'd) Flags" section in this module's
|
||||||
|
documentation for important caveats.
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
func (m *MaskBit) HasFlag(flag MaskBit) (r bool) {
|
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]
|
HasOneOf is like a "looser" form of [MaskBit.HasFlag]
|
||||||
in that it allows for testing composite membership.
|
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.
|
If composite is *not* an OR'd MaskBit (i.e.
|
||||||
it falls directly on a boundary -- 0, 1, 2, 4, 8, 16, etc.),
|
it falls directly on a boundary -- 0, 1, 2, 4, 8, 16, etc.),
|
||||||
then IsOneOf will behave exactly like HasFlag.
|
then HasOneOf will behave exactly like [MaskBit.HasFlag].
|
||||||
|
|
||||||
If m is a composite MaskBit (it usually is) and composite is ALSO a composite MaskBit,
|
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.
|
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
|
var b MaskBit = *m
|
||||||
|
|
||||||
@@ -89,6 +74,13 @@ func (m *MaskBit) IsOneOf(composite MaskBit) (r bool) {
|
|||||||
return
|
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.
|
// AddFlag adds MaskBit flag to m.
|
||||||
func (m *MaskBit) AddFlag(flag MaskBit) {
|
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,
|
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
|
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.
|
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 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) {
|
func (m *MaskBit) Bytes(trim bool) (b []byte) {
|
||||||
|
|
||||||
|
|||||||
+117
-44
@@ -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!"
|
"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.
|
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)
|
One instance of OptStruct takes up *8 bytes* (*64 bits*).
|
||||||
or 18446744073709551615 bytes (16 EiB - yes, that's [exbibytes]) of RAM for 32-bit/64-bit platforms respectively.
|
|
||||||
|
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!"
|
"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
|
# Usage
|
||||||
|
|
||||||
@@ -34,6 +97,8 @@ To use this library, set constants like thus:
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"r00t2.io/goutils/bitmask"
|
"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() {
|
func main() {
|
||||||
MyMask = bitmask.NewMaskBit()
|
|
||||||
|
|
||||||
MyMask.AddFlag(OPT1)
|
MyMask.AddFlag(OPT1)
|
||||||
MyMask.AddFlag(OPT3)
|
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
|
# 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
|
For example, if you are checking a permissions set for a user that has the value, say, 6
|
||||||
|
|
||||||
var userPerms uint = 6 // 0x0000000000000006
|
var userPerms uint = 6 // 0x0000000000000006
|
||||||
@@ -88,7 +151,7 @@ and your library has the following permission bits defined:
|
|||||||
PermsAdmin // 16
|
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)
|
userPermMask = bitmask.NewMaskBitExplicit(userPerms)
|
||||||
if userPermMask.HasFlag(PermsRead) {
|
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:
|
NOT:
|
||||||
|
|
||||||
userPermMask = bitmask.NewMaskBitExplicit(PermsRead)
|
userPermMask = bitmask.NewMaskBitExplicit(PermsRead)
|
||||||
// Nor:
|
userPermMask.HasFlag(bitmask.MaskBit(userPerms))
|
||||||
// userPermMask = PermsRead
|
|
||||||
if userPermMask.HasFlag(userPerms) {
|
NOR:
|
||||||
|
userPermMask = PermsRead
|
||||||
|
if userPermMask.HasFlag(bitmask.MaskBit(userPerms)) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
This will be terribly, horribly wrong, cause incredibly unexpected results,
|
This will be terribly, horribly wrong, cause incredibly unexpected results,
|
||||||
and quite possibly cause massive security issues. Don't do it.
|
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,
|
Composite Flags
|
||||||
your inclination would be to bitwise-OR them together:
|
|
||||||
|
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 (
|
const (
|
||||||
flagA bitmask.MaskBit = 1 << iota // 1
|
flagA bitmask.MaskBit = 1 << iota // 1
|
||||||
flagB // 2
|
flagB // 2
|
||||||
|
flagC // 4
|
||||||
|
// ...
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
flagAB bitmask.MaskBit = flagA | flagB // 3
|
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()
|
var myMask *bitmask.MaskBit = bitmask.NewMaskBit()
|
||||||
|
|
||||||
myMask.AddFlag(flagA)
|
myMask.AddFlag(flagA)
|
||||||
|
|
||||||
You may expect this call to [MaskBit.HasFlag]:
|
you may expect this call to [MaskBit.HasFlag]:
|
||||||
|
|
||||||
myMask.HasFlag(flagAB)
|
myMask.HasFlag(flagAB)
|
||||||
|
|
||||||
to be true, since flagA is "in" flagAB.
|
to be true, since flagA is "in" flagAB.
|
||||||
|
|
||||||
It will return false - HasFlag does strict comparisons.
|
It will return false - HasFlag does strict comparisons.
|
||||||
It will only return true if you then ALSO do:
|
|
||||||
|
|
||||||
// This would require setting flagA first.
|
It would only return true if you did:
|
||||||
// The order of setting flagA/flagB doesn't matter,
|
|
||||||
// but you must have both set for HasFlag(flagAB) to return true.
|
|
||||||
myMask.AddFlag(flagB)
|
|
||||||
|
|
||||||
or if you do:
|
// ...
|
||||||
|
|
||||||
// This can be done with or without additionally setting flagA.
|
|
||||||
myMask.AddFlag(flagAB)
|
myMask.AddFlag(flagAB)
|
||||||
|
|
||||||
Instead, if you want to see if a mask has membership within a composite flag,
|
or:
|
||||||
you can use [MaskBit.IsOneOf].
|
|
||||||
|
// ...
|
||||||
|
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
|
# Other Options
|
||||||
|
|
||||||
@@ -157,6 +230,6 @@ you may be interested in one of the following libraries:
|
|||||||
* [github.com/abice/go-enum]
|
* [github.com/abice/go-enum]
|
||||||
* [github.com/jeffreyrichter/enum/enum]
|
* [github.com/jeffreyrichter/enum/enum]
|
||||||
|
|
||||||
[exbibytes]: https://simple.wikipedia.org/wiki/Exbibyte
|
[dmflags]: https://doomwiki.org/wiki/DMFlags
|
||||||
*/
|
*/
|
||||||
package bitmask
|
package bitmask
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
Package bytesx aims to extend functionality of the stdlib [bytes] module.
|
||||||
|
|
||||||
|
TODO.
|
||||||
|
*/
|
||||||
|
package bytesx
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
Package binaryx aims to extend functionality of the stdlib [encoding/binary] module.
|
||||||
|
*/
|
||||||
|
package binaryx
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
Package encodingx aims to extend functionality of the stdlib [encoding] module.
|
||||||
|
*/
|
||||||
|
package encodingx
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
Package hexx aims to extend [encoding/hex] functionality.
|
||||||
|
*/
|
||||||
|
package hexx
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,35 +1,47 @@
|
|||||||
module r00t2.io/goutils
|
module r00t2.io/goutils
|
||||||
|
|
||||||
go 1.25
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0
|
github.com/Masterminds/sprig/v3 v3.3.0
|
||||||
github.com/coreos/go-systemd/v22 v22.7.0
|
github.com/coreos/go-systemd/v22 v22.7.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/google/uuid v1.6.0
|
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
|
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
|
r00t2.io/sysutils v1.16.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.2 // indirect
|
dario.cat/mergo v1.0.2 // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // 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/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/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/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/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // 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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/spf13/cast v1.10.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/go-sysconf v0.4.0 // indirect
|
||||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
github.com/tklauser/numcpus v0.12.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/crypto v0.47.0 // indirect
|
golang.org/x/crypto v0.53.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.21.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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/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 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
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 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
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 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
|
||||||
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
|
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=
|
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/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 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
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 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
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.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 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
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 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/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 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-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 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
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 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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 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/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 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
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 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
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/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 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
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 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
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 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
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 h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
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 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
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 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
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-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-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.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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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=
|
r00t2.io/sysutils v1.16.2/go.mod h1:iXK+ALOwIdRKjAJIE5USlkZ669SVDHBNNuYhunsznH8=
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ func (c *CtxIO) GetChunkLen() (size uint) {
|
|||||||
return c.l.GetChunkLen()
|
return c.l.GetChunkLen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CtxIO) Len() (unread int) {
|
||||||
|
return c.buf.Len()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CtxIO) Read(p []byte) (n int, err error) {
|
func (c *CtxIO) Read(p []byte) (n int, err error) {
|
||||||
|
|
||||||
var nr int64
|
var nr int64
|
||||||
|
|||||||
@@ -147,6 +147,17 @@ type (
|
|||||||
ContextReader
|
ContextReader
|
||||||
ContextWriter
|
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 (
|
type (
|
||||||
@@ -172,6 +183,7 @@ type (
|
|||||||
* [ChunkReadWriter]
|
* [ChunkReadWriter]
|
||||||
* [ContextReadWriter]
|
* [ContextReadWriter]
|
||||||
* [SizedCopyBufferer]
|
* [SizedCopyBufferer]
|
||||||
|
* [LenReader]
|
||||||
|
|
||||||
Unlike [XIO], it must be non-nil (see [NewCtxIO]) since it maintains state
|
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
|
(though technically, one does not need to call [NewCtxIO] if they call
|
||||||
|
|||||||
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
|
- ScopedLogger, take an io.Writer for each log level
|
||||||
|
|
||||||
- logging probably needs mutexes
|
- logging probably needs mutexes
|
||||||
|
|
||||||
- macOS support beyond the legacy NIX stuff. it apparently uses something called "ULS", "Unified Logging System".
|
- 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.
|
-- 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.
|
-- 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
|
- make configurable via OR bitmask
|
||||||
|
|
||||||
- Suport remote loggers? (eventlog, syslog, journald)
|
- Suport remote loggers? (eventlog, syslog, journald)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
)
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
Package netx includes extensions to the stdlib [net] module.
|
||||||
|
*/
|
||||||
|
package netx
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/*
|
|
||||||
Package netx includes extensions to the stdlib `net` module.
|
|
||||||
*/
|
|
||||||
package netx
|
|
||||||
@@ -455,6 +455,82 @@ func IsPrefixNet(s string) (isNet bool) {
|
|||||||
return
|
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.
|
Mask4ToCidr converts an IPv4 netmask *in bitmask form* to a CIDR prefix size/bit size/bit length.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
This is mostly just for generating some reference. It's not really intended for public consumption.
|
||||||
@@ -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
|
||||||
|
)
|
||||||
@@ -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=
|
||||||
@@ -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())
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
----
|
||||||
@@ -2,4 +2,14 @@
|
|||||||
-- draw border around multiline s
|
-- draw border around multiline s
|
||||||
-- i have a version in python somewhere that does this, should dig that up
|
-- 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?
|
- create bytesx package that duplicates the functions here?
|
||||||
|
|||||||
@@ -10,6 +10,53 @@ import (
|
|||||||
`unicode`
|
`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.
|
IsAscii returns true if all characters in string s are ASCII.
|
||||||
|
|
||||||
|
|||||||
+846
-679
File diff suppressed because it is too large
Load Diff
+1299
-476
File diff suppressed because it is too large
Load Diff
+2072
-1205
File diff suppressed because it is too large
Load Diff
+3
-13
@@ -1,14 +1,4 @@
|
|||||||
- osReadFileBytes
|
- base64, base16, base32, hex, pem encoding/decoding tpl funcs
|
||||||
- osReadFileStr
|
-- template hashing functions?
|
||||||
- osReadDir
|
|
||||||
|
|
||||||
- `dns*` funcs (net)
|
- os env vars
|
||||||
- `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.
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[source,subs="+attributes,+post_replacements",opts="novalidate"]
|
||||||
|
.Function Signature
|
||||||
|
----
|
||||||
|
func {func}{sig}
|
||||||
|
----
|
||||||
+56
-13
@@ -21,6 +21,8 @@ import (
|
|||||||
`github.com/shirou/gopsutil/v4/sensors`
|
`github.com/shirou/gopsutil/v4/sensors`
|
||||||
`go4.org/netipx`
|
`go4.org/netipx`
|
||||||
`r00t2.io/goutils/netx`
|
`r00t2.io/goutils/netx`
|
||||||
|
`r00t2.io/goutils/netx/dnsx`
|
||||||
|
`r00t2.io/goutils/stringsx`
|
||||||
`r00t2.io/goutils/timex`
|
`r00t2.io/goutils/timex`
|
||||||
`r00t2.io/sysutils`
|
`r00t2.io/sysutils`
|
||||||
)
|
)
|
||||||
@@ -71,18 +73,44 @@ var (
|
|||||||
/*
|
/*
|
||||||
Networking (r00t.io/goutils/netx)
|
Networking (r00t.io/goutils/netx)
|
||||||
*/
|
*/
|
||||||
"netxAddrRfc": netx.AddrRfc,
|
"netxAddrRfc": netx.AddrRfc,
|
||||||
"netxCidr4IpMask": netx.Cidr4ToIPMask,
|
"netxCidr4IpMask": netx.Cidr4ToIPMask,
|
||||||
"netxCidr4Mask": netx.Cidr4ToMask,
|
"netxCidr4Mask": netx.Cidr4ToMask,
|
||||||
"netxCidr4Str": netx.Cidr4ToStr,
|
"netxCidr4Str": netx.Cidr4ToStr,
|
||||||
"netxFamilyVer": netx.FamilyToVer,
|
"netxFamilyVer": netx.FamilyToVer,
|
||||||
"netxGetAddrFam": netx.GetAddrFamily,
|
"netxGetAddrFam": netx.GetAddrFamily,
|
||||||
"netxGetIpFam": netx.GetIpFamily,
|
"netxGetIpFam": netx.GetIpFamily,
|
||||||
"netxIpRfc": netx.IpRfc,
|
"netxIpRfc": netx.IpRfc,
|
||||||
"netxIpRfcStr": netx.IpRfcStr,
|
"netxIpRfcStr": netx.IpRfcStr,
|
||||||
"netxIpStripRfc": netx.IpStripRfcStr,
|
"netxIpStripRfc": netx.IpStripRfcStr,
|
||||||
"netxIp4MaskCidr": netx.IPMask4ToCidr,
|
"netxIp4MaskCidr": netx.IPMask4ToCidr,
|
||||||
"netxIp4MaskMask": netx.IPMask4ToMask,
|
"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
|
Numbers/Math
|
||||||
*/
|
*/
|
||||||
@@ -171,9 +199,24 @@ var (
|
|||||||
// .../sensors
|
// .../sensors
|
||||||
"psSensorTemps": sensors.SensorsTemperatures,
|
"psSensorTemps": sensors.SensorsTemperatures,
|
||||||
/*
|
/*
|
||||||
Strings
|
Strings (Standalone)
|
||||||
*/
|
*/
|
||||||
"extIndent": extIndent, // PR in: https://github.com/Masterminds/sprig/pull/468
|
"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
|
System/Platform
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user