Files
phishingclub/backend/vendor/github.com/enetx/g/print_fmt.go
T
RonniSkansing 2028391c3c vendor backend
Signed-off-by: RonniSkansing <rskansing@gmail.com>
2026-09-16 23:17:35 +02:00

1039 lines
19 KiB
Go

package g
import (
"fmt"
"math"
"strconv"
"unicode/utf8"
)
type fmtAlign int
const (
alignNone fmtAlign = iota
alignLeft // '<'
alignRight // '>'
alignCenter // '^'
)
type fmtSign int
const (
signNone fmtSign = iota
signPlus // '+'
signMinus // '-'
signSpace // ' '
)
type fmtSpec struct {
fill rune
align fmtAlign
sign fmtSign
alternate bool
zeroPad bool
width Int
precision Int
verb byte
}
func splitFmtSpec(placeholder String) (String, String) {
if i := findUnparenColon(placeholder); i >= 0 {
return placeholder[:i], placeholder[i+1:]
}
return placeholder, ""
}
func findUnparenColon(placeholder String) Int {
depth := 0
for i := Int(0); i < placeholder.Len(); i++ {
switch placeholder[i] {
case '(':
depth++
case ')':
depth--
case ':':
if depth == 0 {
return i
}
}
}
return -1
}
// maxFmtDigits caps the accumulated width/precision value to avoid huge
// allocations or integer overflow on absurd inputs like {:99999999999999999999}.
// 1<<20 is far larger than any reasonable field width while staying well below
// int range on every platform.
const maxFmtDigits = 1 << 20
func parseDigits(runes []rune, pos int) (int, int, bool) {
n, has := 0, false
for pos < len(runes) && runes[pos] >= '0' && runes[pos] <= '9' {
if n <= maxFmtDigits {
n = min(n*10+int(runes[pos]-'0'), maxFmtDigits)
}
pos++
has = true
}
return n, pos, has
}
func validFmtSpec(spec String) bool {
runes := []rune(spec)
pos := 0
if pos < len(runes) {
if pos+1 < len(runes) && isAlign(runes[pos+1]) {
pos += 2
} else if isAlign(runes[pos]) {
pos++
}
}
if pos < len(runes) && (runes[pos] == '+' || runes[pos] == '-' || runes[pos] == ' ') {
pos++
}
if pos < len(runes) && runes[pos] == '#' {
pos++
}
if pos < len(runes) && runes[pos] == '0' {
pos++
}
_, pos, _ = parseDigits(runes, pos)
if pos < len(runes) && runes[pos] == '.' {
pos++
_, pos, _ = parseDigits(runes, pos)
}
if pos == len(runes) {
return true
}
verb := runes[pos]
pos++
if pos != len(runes) {
return false
}
switch verb {
case 'd', 'c', 'q', 'U', 'x', 'X', 'o', 'b', 'e', 'E', 'T', 'p', '?', 'w', 's', 'f':
return true
default:
return false
}
}
func parseFmtSpec(spec String) fmtSpec {
if isASCIIFmtSpec(spec) {
return parseASCIIFmtSpec(spec)
}
return parseRuneFmtSpec(spec)
}
func isASCIIFmtSpec(spec String) bool {
for i := Int(0); i < spec.Len(); i++ {
if spec[i] >= utf8.RuneSelf {
return false
}
}
return true
}
func parseASCIIDigits(spec String, pos Int) (int, Int, bool) {
n, has := 0, false
for pos < spec.Len() && spec[pos] >= '0' && spec[pos] <= '9' {
if n <= maxFmtDigits {
n = min(n*10+int(spec[pos]-'0'), maxFmtDigits)
}
pos++
has = true
}
return n, pos, has
}
func parseASCIIFmtSpec(spec String) fmtSpec {
fs := fmtSpec{fill: ' ', width: -1, precision: -1}
pos := Int(0)
if pos < spec.Len() {
if pos+1 < spec.Len() && isAlign(rune(spec[pos+1])) {
fs.fill = rune(spec[pos])
fs.align = toAlign(rune(spec[pos+1]))
pos += 2
} else if isAlign(rune(spec[pos])) {
fs.align = toAlign(rune(spec[pos]))
pos++
}
}
if pos < spec.Len() {
switch spec[pos] {
case '+':
fs.sign = signPlus
pos++
case '-':
fs.sign = signMinus
pos++
case ' ':
fs.sign = signSpace
pos++
}
}
if pos < spec.Len() && spec[pos] == '#' {
fs.alternate = true
pos++
}
if pos < spec.Len() && spec[pos] == '0' && fs.align == alignNone {
fs.zeroPad = true
pos++
}
if n, next, ok := parseASCIIDigits(spec, pos); ok {
fs.width, pos = Int(n), next
}
if pos < spec.Len() && spec[pos] == '.' {
pos++
if n, next, ok := parseASCIIDigits(spec, pos); ok {
fs.precision, pos = Int(n), next
} else {
fs.precision = 0
}
}
if pos < spec.Len() {
fs.verb = spec[pos]
}
return fs
}
func parseRuneFmtSpec(spec String) fmtSpec {
fs := fmtSpec{fill: ' ', width: -1, precision: -1}
runes := []rune(spec)
pos := 0
// parse [[fill]align]
if pos < len(runes) {
if pos+1 < len(runes) && isAlign(runes[pos+1]) {
fs.fill = runes[pos]
fs.align = toAlign(runes[pos+1])
pos += 2
} else if isAlign(runes[pos]) {
fs.align = toAlign(runes[pos])
pos++
}
}
// parse [sign]
if pos < len(runes) {
switch runes[pos] {
case '+':
fs.sign = signPlus
pos++
case '-':
fs.sign = signMinus
pos++
case ' ':
fs.sign = signSpace
pos++
}
}
// parse [#]
if pos < len(runes) && runes[pos] == '#' {
fs.alternate = true
pos++
}
// parse [0]
if pos < len(runes) && runes[pos] == '0' && fs.align == alignNone {
fs.zeroPad = true
pos++
}
// parse [width]
if n, np, ok := parseDigits(runes, pos); ok {
fs.width = Int(n)
pos = np
}
// parse [.precision]
if pos < len(runes) && runes[pos] == '.' {
pos++
if n, np, ok := parseDigits(runes, pos); ok {
fs.precision = Int(n)
pos = np
} else {
fs.precision = 0
}
}
// parse [type]
if pos < len(runes) {
fs.verb = byte(runes[pos])
}
return fs
}
func isAlign(r rune) bool { return r == '<' || r == '>' || r == '^' }
func toAlign(r rune) fmtAlign {
switch r {
case '<':
return alignLeft
case '>':
return alignRight
default:
return alignCenter
}
}
func applyFmtSpec(value any, spec fmtSpec) String {
var s String
switch spec.verb {
case 'd':
s = fmtDecimal(value, spec)
case 'c':
s = fmtCharacter(value)
case 'q':
s = fmtQuoted(value)
case 'U':
s = fmtUnicode(value, spec.alternate)
case 'x':
s = fmtIntBase(value, 16, fmtAltPrefix("0x", spec), spec, false)
case 'X':
s = fmtIntBase(value, 16, fmtAltPrefix("0x", spec), spec, true)
case 'o':
s = fmtIntBase(value, 8, fmtAltPrefix("0o", spec), spec, false)
case 'b':
s = fmtIntBase(value, 2, fmtAltPrefix("0b", spec), spec, false)
case 'e':
s = fmtExponential(value, spec, 'e')
case 'E':
s = fmtExponential(value, spec, 'E')
case 'T':
s = String(fmt.Sprintf("%T", value))
case 'p':
s = String(fmt.Sprintf("%p", value))
case '?':
if spec.alternate {
s = fmtPrettyDebug(value)
} else {
s = String(fmt.Sprintf("%#v", value))
}
default:
s = fmtDefault(value, spec)
}
return fmtPad(s, spec, fmtIsNumeric(value))
}
func tryAppendNativeSpec(builder *Builder, value any, spec fmtSpec) bool {
return tryAppendNativeIntSpec(builder, value, spec) ||
tryAppendNativeTextSpec(builder, value, spec)
}
// tryAppendNativeIntSpec handles the common integer representations directly in
// the destination builder. It avoids the temporary strings otherwise produced
// by strconv.Format*, sign/prefix concatenation, and padding.
func tryAppendNativeIntSpec(builder *Builder, value any, spec fmtSpec) bool {
base := 10
prefix := String("")
upper := false
switch spec.verb {
case 'd':
case 'x':
base = 16
if spec.alternate {
prefix = "0x"
}
case 'X':
base, upper = 16, true
if spec.alternate {
prefix = "0x"
}
case 'o':
base = 8
if spec.alternate {
prefix = "0o"
}
case 'b':
base = 2
if spec.alternate {
prefix = "0b"
}
default:
return false
}
var storage [128]byte
digits := storage[:0]
negative := false
if u, ok := fmtToUint64(value); ok {
digits = strconv.AppendUint(digits, u, base)
} else if i, ok := fmtToInt64(value); ok {
negative = i < 0
if negative {
// -(i+1)+1 is safe for MinInt64.
digits = strconv.AppendUint(digits, uint64(-(i+1))+1, base)
} else {
digits = strconv.AppendUint(digits, uint64(i), base)
}
} else {
return false
}
if upper {
for i, ch := range digits {
if ch >= 'a' && ch <= 'f' {
digits[i] = ch - ('a' - 'A')
}
}
}
var sign byte
if negative {
sign = '-'
} else if spec.sign == signPlus {
sign = '+'
} else if spec.sign == signSpace {
sign = ' '
}
contentLen := Int(len(digits)) + prefix.Len()
if sign != 0 {
contentLen++
}
padding := Int(0)
if spec.width > contentLen {
padding = spec.width - contentLen
}
if spec.zeroPad && spec.align == alignNone {
if sign != 0 {
builder.WriteByte(sign)
}
builder.WriteString(prefix)
for range padding {
builder.WriteByte('0')
}
builder.Write(digits)
return true
}
align := spec.align
if align == alignNone {
align = alignRight
}
left, right := Int(0), Int(0)
switch align {
case alignLeft:
right = padding
case alignRight:
left = padding
default:
left = padding / 2
right = padding - left
}
for range left {
builder.WriteRune(spec.fill)
}
if sign != 0 {
builder.WriteByte(sign)
}
builder.WriteString(prefix)
builder.Write(digits)
for range right {
builder.WriteRune(spec.fill)
}
return true
}
// tryAppendNativeTextSpec writes character, quoted, and Unicode representations
// without allocating intermediate strings.
func tryAppendNativeTextSpec(builder *Builder, value any, spec fmtSpec) bool {
var storage [128]byte
content := storage[:0]
switch spec.verb {
case 'c':
r, ok := fmtToRune(value)
if !ok {
return false
}
content = utf8.AppendRune(content, r)
case 'q':
switch v := value.(type) {
case string:
content = strconv.AppendQuote(content, v)
case String:
content = strconv.AppendQuote(content, v.Std())
case []byte:
content = strconv.AppendQuote(content, string(v))
case Bytes:
content = strconv.AppendQuote(content, string(v))
default:
r, ok := fmtToRune(value)
if !ok {
return false
}
content = strconv.AppendQuoteRune(content, r)
}
case 'U':
r, ok := fmtToRune(value)
if !ok || !utf8.ValidRune(r) {
return false
}
content = append(content, 'U', '+')
var digits [8]byte
hex := strconv.AppendInt(digits[:0], int64(r), 16)
for padding := 4 - len(hex); padding > 0; padding-- {
content = append(content, '0')
}
for _, ch := range hex {
if ch >= 'a' && ch <= 'f' {
ch -= 'a' - 'A'
}
content = append(content, ch)
}
if spec.alternate && strconv.IsPrint(r) {
content = append(content, ' ')
content = strconv.AppendQuoteRune(content, r)
}
default:
return false
}
appendNativePadded(builder, content, utf8.RuneCount(content), spec, fmtIsNumeric(value))
return true
}
func fmtToRune(value any) (rune, bool) {
if u, ok := fmtToUint64(value); ok {
return rune(u), true
}
if i, ok := fmtToSignedInt64(value); ok {
return rune(i), true
}
return 0, false
}
func appendNativePadded(builder *Builder, content []byte, runes int, spec fmtSpec, isNum bool) {
padding := spec.width - Int(runes)
if padding <= 0 {
builder.Write(content)
return
}
fill := spec.fill
align := spec.align
if spec.zeroPad && align == alignNone && isNum {
fill, align = '0', alignRight
} else if align == alignNone {
if isNum {
align = alignRight
} else {
align = alignLeft
}
}
left, right := Int(0), Int(0)
switch align {
case alignLeft:
right = padding
case alignRight:
left = padding
default:
left = padding / 2
right = padding - left
}
for range left {
builder.WriteRune(fill)
}
builder.Write(content)
for range right {
builder.WriteRune(fill)
}
}
func fmtDecimal(value any, spec fmtSpec) String {
if u, ok := fmtToUint64(value); ok {
return fmtApplySign(String(strconv.FormatUint(u, 10)), true, spec)
}
if i, ok := fmtToSignedInt64(value); ok {
s := String(strconv.FormatInt(i, 10))
return fmtApplySign(s, i >= 0, spec)
}
return String(fmt.Sprint(value))
}
func fmtCharacter(value any) String {
if u, ok := fmtToUint64(value); ok {
return String(string(rune(u)))
}
if i, ok := fmtToSignedInt64(value); ok {
return String(string(rune(i)))
}
return String(fmt.Sprintf("%c", value))
}
func fmtQuoted(value any) String {
switch v := value.(type) {
case string:
return String(strconv.Quote(v))
case String:
return String(strconv.Quote(v.Std()))
case []byte:
return String(strconv.Quote(string(v)))
case Bytes:
return String(strconv.Quote(string(v)))
}
if u, ok := fmtToUint64(value); ok {
return String(strconv.QuoteRune(rune(u)))
}
if i, ok := fmtToSignedInt64(value); ok {
return String(strconv.QuoteRune(rune(i)))
}
return String(fmt.Sprintf("%q", value))
}
func fmtUnicode(value any, alternate bool) String {
var r rune
if u, ok := fmtToUint64(value); ok {
r = rune(u)
} else if i, ok := fmtToSignedInt64(value); ok {
r = rune(i)
} else {
verb := "%U"
if alternate {
verb = "%#U"
}
return String(fmt.Sprintf(verb, value))
}
if alternate {
return String(fmt.Sprintf("%#U", r))
}
return String(fmt.Sprintf("%U", r))
}
func fmtAltPrefix(prefix String, spec fmtSpec) String {
if spec.alternate {
return prefix
}
return ""
}
func fmtIntBase(value any, base int, prefix String, spec fmtSpec, upper bool) String {
format := func(s String) String {
if upper {
return prefix + s.Upper()
}
return prefix + s
}
if u, ok := fmtToUint64(value); ok {
return format(String(strconv.FormatUint(u, base)))
}
if i, ok := fmtToInt64(value); ok {
s := format(String(strconv.FormatInt(abs64(i), base)))
if i < 0 {
return "-" + s
}
return fmtApplySign(s, true, spec)
}
return String(fmt.Sprint(value))
}
func fmtDefault(value any, spec fmtSpec) String {
prec := -1
if spec.precision >= 0 {
prec = spec.precision.Std()
}
if prec >= 0 || spec.sign != signNone {
if f, ok := fmtToFloat64(value); ok {
return fmtApplySign(
String(strconv.FormatFloat(f, 'f', prec, 64)), f >= 0, spec,
)
}
if spec.sign != signNone {
if i, ok := fmtToInt64(value); ok {
return fmtApplySign(
String(strconv.FormatInt(abs64(i), 10)), i >= 0, spec,
)
}
// uint64 values above MaxInt64 don't fit in int64; format them
// unsigned so the requested sign (e.g. {:+}) is still applied.
if u, ok := fmtToUint64(value); ok {
return fmtApplySign(
String(strconv.FormatUint(u, 10)), true, spec,
)
}
}
}
s := String(fmt.Sprint(value))
if prec >= 0 && s.LenRunes() > spec.precision {
return String([]rune(s)[:spec.precision])
}
return s
}
func fmtExponential(value any, spec fmtSpec, verb byte) String {
prec := 6
if spec.precision >= 0 {
prec = spec.precision.Std()
}
f, ok := fmtToFloat64(value)
if !ok {
var i int64
if i, ok = fmtToInt64(value); ok {
f = float64(i)
}
}
if ok {
return fmtApplySign(
String(strconv.FormatFloat(f, verb, prec, 64)), f >= 0, spec,
)
}
return String(fmt.Sprint(value))
}
func fmtApplySign(s String, positive bool, spec fmtSpec) String {
if !positive {
if s[0] != '-' {
return "-" + s
}
return s
}
switch spec.sign {
case signPlus:
return "+" + s
case signSpace:
return " " + s
}
return s
}
func fmtPad(s String, spec fmtSpec, isNum bool) String {
if spec.width.IsNegative() || s.LenRunes() >= spec.width {
return s
}
// zero-pad: sign-aware
if spec.zeroPad && spec.align == alignNone && isNum {
return fmtZeroPad(s, spec.width)
}
// default alignment: right for numbers, left for strings
align := spec.align
if align == alignNone {
if isNum {
align = alignRight
} else {
align = alignLeft
}
}
return fmtJustify(s, spec.width, spec.fill, align)
}
func fmtJustify(s String, width Int, fill rune, align fmtAlign) String {
remaining := width - s.LenRunes()
left, right := Int(0), Int(0)
switch align {
case alignLeft:
right = remaining
case alignRight:
left = remaining
default:
left = remaining / 2
right = remaining - left
}
var b Builder
b.Grow(s.Len() + remaining*Int(utf8.RuneLen(fill)))
for range left {
b.WriteRune(fill)
}
b.WriteString(s)
for range right {
b.WriteRune(fill)
}
return b.String()
}
func fmtZeroPad(s String, width Int) String {
var sign byte
body := s
if s.Len() > 0 && (s[0] == '+' || s[0] == '-' || s[0] == ' ') {
sign = s[0]
body = s[1:]
}
var prefix String
if body.Len() > 1 && body[0] == '0' && (body[1] == 'x' || body[1] == 'X' || body[1] == 'b' || body[1] == 'o') {
prefix = body[:2]
body = body[2:]
}
padLen := width - body.LenRunes() - prefix.Len()
if sign != 0 {
padLen--
}
var b Builder
b.Grow(width)
if sign != 0 {
b.WriteByte(sign)
}
b.WriteString(prefix)
for range padLen {
b.WriteByte('0')
}
b.WriteString(body)
return b.String()
}
func fmtPrettyDebug(value any) String {
return fmtIndentDebug(String(fmt.Sprintf("%#v", value)))
}
func fmtIndentDebug(s String) String {
var b Builder
b.Grow(s.Len() + 16)
indent := Int(0)
inString := false
escaped := false
pad := String(" ")
for i := Int(0); i < s.Len(); i++ {
ch := s[i]
if inString {
b.WriteByte(ch)
// Track escapes with a running toggle so that a closing quote
// preceded by an escaped backslash (`\\"`) is not mistaken for an
// escaped quote. A single-char lookback would leave inString stuck.
if escaped {
escaped = false
} else if ch == '\\' {
escaped = true
} else if ch == '"' {
inString = false
}
continue
}
if ch == '"' {
inString = true
escaped = false
b.WriteByte(ch)
continue
}
switch ch {
case '{', '[':
indent++
b.WriteByte(ch)
b.WriteByte('\n')
writeIndent(&b, pad, indent)
case '}', ']':
indent--
b.WriteByte('\n')
writeIndent(&b, pad, indent)
b.WriteByte(ch)
case ',':
b.WriteByte(ch)
b.WriteByte('\n')
writeIndent(&b, pad, indent)
default:
b.WriteByte(ch)
}
}
return b.String()
}
func writeIndent(b *Builder, pad String, indent Int) {
for range indent {
b.WriteString(pad)
}
}
func fmtIsNumeric(value any) bool {
switch value.(type) {
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64,
float32, float64,
Int, Float:
return true
}
return false
}
func fmtToInt64(value any) (int64, bool) {
switch v := value.(type) {
case Int:
return int64(v), true
case int:
return int64(v), true
case int8:
return int64(v), true
case int16:
return int64(v), true
case int32:
return int64(v), true
case int64:
return v, true
case uint:
return int64(v), true
case uint8:
return int64(v), true
case uint16:
return int64(v), true
case uint32:
return int64(v), true
case uint64:
if v <= math.MaxInt64 {
return int64(v), true
}
return 0, false
case Float:
return int64(v), true
case float32:
return int64(v), true
case float64:
return int64(v), true
}
return 0, false
}
func fmtToSignedInt64(value any) (int64, bool) {
switch v := value.(type) {
case Int:
return int64(v), true
case int:
return int64(v), true
case int8:
return int64(v), true
case int16:
return int64(v), true
case int32:
return int64(v), true
case int64:
return v, true
}
return 0, false
}
func fmtToUint64(value any) (uint64, bool) {
switch v := value.(type) {
case uint:
return uint64(v), true
case uint8:
return uint64(v), true
case uint16:
return uint64(v), true
case uint32:
return uint64(v), true
case uint64:
return v, true
}
return 0, false
}
func fmtToFloat64(value any) (float64, bool) {
switch v := value.(type) {
case Float:
return float64(v), true
case float32:
return float64(v), true
case float64:
return v, true
}
return 0, false
}
func abs64(i int64) int64 {
if i < 0 {
return -i
}
return i
}