go-io/sigil/sigils.go
Virgil 50bb356c7c refactor(ax): align remaining AX naming surfaces
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-31 13:35:21 +00:00

336 lines
8 KiB
Go

package sigil
import (
"bytes"
"compress/gzip"
"crypto"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
goio "io"
"io/fs"
core "dappco.re/go/core"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/md4"
"golang.org/x/crypto/ripemd160"
"golang.org/x/crypto/sha3"
)
// Example: reverseSigil, _ := sigil.NewSigil("reverse")
type ReverseSigil struct{}
func (sigil *ReverseSigil) In(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
reversed := make([]byte, len(data))
for i, j := 0, len(data)-1; i < len(data); i, j = i+1, j-1 {
reversed[i] = data[j]
}
return reversed, nil
}
func (sigil *ReverseSigil) Out(data []byte) ([]byte, error) {
return sigil.In(data)
}
// Example: hexSigil, _ := sigil.NewSigil("hex")
type HexSigil struct{}
func (sigil *HexSigil) In(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
encodedBytes := make([]byte, hex.EncodedLen(len(data)))
hex.Encode(encodedBytes, data)
return encodedBytes, nil
}
func (sigil *HexSigil) Out(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
decodedBytes := make([]byte, hex.DecodedLen(len(data)))
_, err := hex.Decode(decodedBytes, data)
return decodedBytes, err
}
// Example: base64Sigil, _ := sigil.NewSigil("base64")
type Base64Sigil struct{}
func (sigil *Base64Sigil) In(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(encodedBytes, data)
return encodedBytes, nil
}
func (sigil *Base64Sigil) Out(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
decodedBytes := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
decodedCount, err := base64.StdEncoding.Decode(decodedBytes, data)
return decodedBytes[:decodedCount], err
}
// Example: gzipSigil, _ := sigil.NewSigil("gzip")
type GzipSigil struct {
outputWriter goio.Writer
}
func (sigil *GzipSigil) In(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
var buffer bytes.Buffer
outputWriter := sigil.outputWriter
if outputWriter == nil {
outputWriter = &buffer
}
gzipWriter := gzip.NewWriter(outputWriter)
if _, err := gzipWriter.Write(data); err != nil {
return nil, core.E("sigil.GzipSigil.In", "write gzip payload", err)
}
if err := gzipWriter.Close(); err != nil {
return nil, core.E("sigil.GzipSigil.In", "close gzip writer", err)
}
return buffer.Bytes(), nil
}
func (sigil *GzipSigil) Out(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
gzipReader, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return nil, core.E("sigil.GzipSigil.Out", "open gzip reader", err)
}
defer gzipReader.Close()
out, err := goio.ReadAll(gzipReader)
if err != nil {
return nil, core.E("sigil.GzipSigil.Out", "read gzip payload", err)
}
return out, nil
}
// Example: jsonSigil := &sigil.JSONSigil{Indent: true}
type JSONSigil struct{ Indent bool }
func (sigil *JSONSigil) In(data []byte) ([]byte, error) {
if data == nil {
return nil, nil
}
var decoded any
result := core.JSONUnmarshal(data, &decoded)
if !result.OK {
if err, ok := result.Value.(error); ok {
return nil, core.E("sigil.JSONSigil.In", "decode json", err)
}
return nil, core.E("sigil.JSONSigil.In", "decode json", fs.ErrInvalid)
}
compact := core.JSONMarshalString(decoded)
if sigil.Indent {
return []byte(indentJSON(compact)), nil
}
return []byte(compact), nil
}
func (sigil *JSONSigil) Out(data []byte) ([]byte, error) {
return data, nil
}
// Example: hashSigil := sigil.NewHashSigil(crypto.SHA256)
type HashSigil struct {
Hash crypto.Hash
}
// Example: hashSigil := sigil.NewHashSigil(crypto.SHA256)
// Example: digest, _ := hashSigil.In([]byte("payload"))
func NewHashSigil(hashAlgorithm crypto.Hash) *HashSigil {
return &HashSigil{Hash: hashAlgorithm}
}
func (sigil *HashSigil) In(data []byte) ([]byte, error) {
var hasher goio.Writer
switch sigil.Hash {
case crypto.MD4:
hasher = md4.New()
case crypto.MD5:
hasher = md5.New()
case crypto.SHA1:
hasher = sha1.New()
case crypto.SHA224:
hasher = sha256.New224()
case crypto.SHA256:
hasher = sha256.New()
case crypto.SHA384:
hasher = sha512.New384()
case crypto.SHA512:
hasher = sha512.New()
case crypto.RIPEMD160:
hasher = ripemd160.New()
case crypto.SHA3_224:
hasher = sha3.New224()
case crypto.SHA3_256:
hasher = sha3.New256()
case crypto.SHA3_384:
hasher = sha3.New384()
case crypto.SHA3_512:
hasher = sha3.New512()
case crypto.SHA512_224:
hasher = sha512.New512_224()
case crypto.SHA512_256:
hasher = sha512.New512_256()
case crypto.BLAKE2s_256:
hasher, _ = blake2s.New256(nil)
case crypto.BLAKE2b_256:
hasher, _ = blake2b.New256(nil)
case crypto.BLAKE2b_384:
hasher, _ = blake2b.New384(nil)
case crypto.BLAKE2b_512:
hasher, _ = blake2b.New512(nil)
default:
return nil, core.E("sigil.HashSigil.In", "hash algorithm not available", fs.ErrInvalid)
}
hasher.Write(data)
return hasher.(interface{ Sum([]byte) []byte }).Sum(nil), nil
}
func (sigil *HashSigil) Out(data []byte) ([]byte, error) {
return data, nil
}
// Example: hexSigil, _ := sigil.NewSigil("hex")
// Example: gzipSigil, _ := sigil.NewSigil("gzip")
// Example: transformed, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil})
func NewSigil(sigilName string) (Sigil, error) {
switch sigilName {
case "reverse":
return &ReverseSigil{}, nil
case "hex":
return &HexSigil{}, nil
case "base64":
return &Base64Sigil{}, nil
case "gzip":
return &GzipSigil{}, nil
case "json":
return &JSONSigil{Indent: false}, nil
case "json-indent":
return &JSONSigil{Indent: true}, nil
case "md4":
return NewHashSigil(crypto.MD4), nil
case "md5":
return NewHashSigil(crypto.MD5), nil
case "sha1":
return NewHashSigil(crypto.SHA1), nil
case "sha224":
return NewHashSigil(crypto.SHA224), nil
case "sha256":
return NewHashSigil(crypto.SHA256), nil
case "sha384":
return NewHashSigil(crypto.SHA384), nil
case "sha512":
return NewHashSigil(crypto.SHA512), nil
case "ripemd160":
return NewHashSigil(crypto.RIPEMD160), nil
case "sha3-224":
return NewHashSigil(crypto.SHA3_224), nil
case "sha3-256":
return NewHashSigil(crypto.SHA3_256), nil
case "sha3-384":
return NewHashSigil(crypto.SHA3_384), nil
case "sha3-512":
return NewHashSigil(crypto.SHA3_512), nil
case "sha512-224":
return NewHashSigil(crypto.SHA512_224), nil
case "sha512-256":
return NewHashSigil(crypto.SHA512_256), nil
case "blake2s-256":
return NewHashSigil(crypto.BLAKE2s_256), nil
case "blake2b-256":
return NewHashSigil(crypto.BLAKE2b_256), nil
case "blake2b-384":
return NewHashSigil(crypto.BLAKE2b_384), nil
case "blake2b-512":
return NewHashSigil(crypto.BLAKE2b_512), nil
default:
return nil, core.E("sigil.NewSigil", core.Concat("unknown sigil name: ", sigilName), fs.ErrInvalid)
}
}
func indentJSON(compact string) string {
if compact == "" {
return ""
}
builder := core.NewBuilder()
indent := 0
inString := false
escaped := false
writeIndent := func(level int) {
for i := 0; i < level; i++ {
builder.WriteString(" ")
}
}
for i := 0; i < len(compact); i++ {
ch := compact[i]
if inString {
builder.WriteByte(ch)
if escaped {
escaped = false
continue
}
if ch == '\\' {
escaped = true
continue
}
if ch == '"' {
inString = false
}
continue
}
switch ch {
case '"':
inString = true
builder.WriteByte(ch)
case '{', '[':
builder.WriteByte(ch)
if i+1 < len(compact) && compact[i+1] != '}' && compact[i+1] != ']' {
indent++
builder.WriteByte('\n')
writeIndent(indent)
}
case '}', ']':
if i > 0 && compact[i-1] != '{' && compact[i-1] != '[' {
indent--
builder.WriteByte('\n')
writeIndent(indent)
}
builder.WriteByte(ch)
case ',':
builder.WriteByte(ch)
builder.WriteByte('\n')
writeIndent(indent)
case ':':
builder.WriteString(": ")
default:
builder.WriteByte(ch)
}
}
return builder.String()
}