// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io package utils import ( "fmt" "math" "reflect" "strconv" "strings" "time" "github.com/gofiber/utils/v2/internal/unsafeconv" ) // UnsafeString returns a string pointer without allocation func UnsafeString(b []byte) string { return unsafeconv.UnsafeString(b) } // UnsafeBytes returns a byte pointer without allocation. func UnsafeBytes(s string) []byte { return unsafeconv.UnsafeBytes(s) } // CopyString copies a string to make it immutable func CopyString(s string) string { // #nosec G103 return string(UnsafeBytes(s)) } // #nosec G103 // CopyBytes copies a slice to make it immutable func CopyBytes(b []byte) []byte { tmp := make([]byte, len(b)) copy(tmp, b) return tmp } const ( uByte = 1 << (10 * iota) uKilobyte uMegabyte uGigabyte uTerabyte uPetabyte uExabyte ) // ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. // The unit that results in the smallest number greater than or equal to 1 is always chosen. // Maximum supported input is math.MaxUint64 / 10 (≈ 1844674407370955161). func ByteSize(bytes uint64) string { const maxSafe = math.MaxUint64 / 10 unit := "" div := uint64(1) switch { case bytes >= uExabyte: unit = "EB" div = uExabyte case bytes >= uPetabyte: unit = "PB" div = uPetabyte case bytes >= uTerabyte: unit = "TB" div = uTerabyte case bytes >= uGigabyte: unit = "GB" div = uGigabyte case bytes >= uMegabyte: unit = "MB" div = uMegabyte case bytes >= uKilobyte: unit = "KB" div = uKilobyte case bytes >= uByte: unit = "B" default: return "0B" } buf := make([]byte, 0, 16) if div == 1 { buf = AppendUint(buf, bytes) buf = append(buf, unit...) return UnsafeString(buf) } // Fix: cap bytes to maxSafe for overflow, but format as fractional if bytes > maxSafe { bytes = maxSafe } scaled := (bytes/div)*10 + ((bytes%div)*10+div/2)/div integer := scaled / 10 fractional := scaled % 10 buf = AppendUint(buf, integer) if fractional > 0 { buf = append(buf, '.') buf = AppendUint(buf, fractional) } buf = append(buf, unit...) return UnsafeString(buf) } // ToString Change arg to string func ToString(arg any, timeFormat ...string) string { switch v := arg.(type) { case int: return FormatInt(int64(v)) case int8: return FormatInt8(v) case int16: return FormatInt16(v) case int32: return FormatInt32(v) case int64: return FormatInt(v) case uint: return FormatUint(uint64(v)) case uint8: return FormatUint8(v) case uint16: return FormatUint16(v) case uint32: return FormatUint32(v) case uint64: return FormatUint(v) case string: return v case []byte: return string(v) case bool: return strconv.FormatBool(v) case float32: return strconv.FormatFloat(float64(v), 'f', -1, 32) case float64: return strconv.FormatFloat(v, 'f', -1, 64) case time.Time: if len(timeFormat) > 0 { return v.Format(timeFormat[0]) } return v.Format("2006-01-02 15:04:05") case reflect.Value: return ToString(v.Interface(), timeFormat...) case fmt.Stringer: return v.String() // Handle common pointer types directly to avoid reflection case *string: if v != nil { return *v } return "" case *int: if v != nil { return FormatInt(int64(*v)) } return "0" case *int64: if v != nil { return FormatInt(*v) } return "0" case *uint64: if v != nil { return FormatUint(*v) } return "0" case *float64: if v != nil { return strconv.FormatFloat(*v, 'f', -1, 64) } return "0" case *bool: if v != nil { return strconv.FormatBool(*v) } return "false" // Handle common slice types directly to avoid reflection case []string: if len(v) == 0 { return "[]" } var buf strings.Builder buf.Grow(len(v) * 8) // Pre-allocate approximate size buf.WriteByte('[') for i, s := range v { if i > 0 { buf.WriteByte(' ') } buf.WriteString(s) } buf.WriteByte(']') return buf.String() case []int: if len(v) == 0 { return "[]" } var buf strings.Builder buf.Grow(len(v) * 4) // Pre-allocate approximate size buf.WriteByte('[') for i, n := range v { if i > 0 { buf.WriteByte(' ') } buf.WriteString(FormatInt(int64(n))) } buf.WriteByte(']') return buf.String() default: // Check if the type is a pointer by using reflection rv := reflect.ValueOf(arg) kind := rv.Kind() if kind == reflect.Ptr && !rv.IsNil() { // Dereference the pointer and recursively call ToString return ToString(rv.Elem().Interface(), timeFormat...) } else if kind == reflect.Slice || kind == reflect.Array { // handle slices n := rv.Len() if n == 0 { return "[]" } var buf strings.Builder buf.Grow(n * 8) // Pre-allocate approximate size buf.WriteByte('[') for i := range n { if i > 0 { buf.WriteByte(' ') } buf.WriteString(ToString(rv.Index(i).Interface())) } buf.WriteByte(']') return buf.String() } // For types not explicitly handled, use fmt.Sprint to generate a string representation return fmt.Sprint(arg) } }