codeberg.org/gruf/go-format@v1.0.6/format.go (about)

     1  package format
     2  
     3  import (
     4  	"reflect"
     5  	"strconv"
     6  	"unsafe"
     7  )
     8  
     9  // Formattable defines a type capable of being formatted and appended to a byte buffer.
    10  type Formattable interface {
    11  	AppendFormat([]byte) []byte
    12  }
    13  
    14  // format is the object passed among the append___ formatting functions.
    15  type format struct {
    16  	flags uint8   // 'isKey' and 'verbose' flags
    17  	drefs uint8   // current value deref count
    18  	curd  uint8   // current depth
    19  	maxd  uint8   // maximum depth
    20  	buf   *Buffer // out buffer
    21  }
    22  
    23  const (
    24  	// flag bit constants.
    25  	isKeyBit = uint8(1) << 0 // set to indicate key formatting
    26  	isValBit = uint8(1) << 1 // set to indicate value formatting
    27  	vboseBit = uint8(1) << 2 // set to indicate verbose formatting
    28  	panicBit = uint8(1) << 3 // set after panic to prevent recursion
    29  	rtypeBit = uint8(1) << 4 // set to indicate only print type within .Appendf()
    30  )
    31  
    32  // AtMaxDepth returns whether format is currently at max depth.
    33  func (f format) AtMaxDepth() bool {
    34  	return f.curd > f.maxd
    35  }
    36  
    37  // Derefs returns no. times current value has been dereferenced.
    38  func (f format) Derefs() uint8 {
    39  	return f.drefs
    40  }
    41  
    42  // IsKey returns whether the isKey flag is set.
    43  func (f format) IsKey() bool {
    44  	return (f.flags & isKeyBit) != 0
    45  }
    46  
    47  // IsValue returns whether the isVal flag is set.
    48  func (f format) IsValue() bool {
    49  	return (f.flags & isValBit) != 0
    50  }
    51  
    52  // Verbose returns whether the verbose flag is set.
    53  func (f format) Verbose() bool {
    54  	return (f.flags & vboseBit) != 0
    55  }
    56  
    57  // Panic returns whether the panic flag is set.
    58  func (f format) Panic() bool {
    59  	return (f.flags & panicBit) != 0
    60  }
    61  
    62  // SetIsKey returns format instance with the isKey bit set to value.
    63  func (f format) SetIsKey() format {
    64  	return format{
    65  		flags: f.flags & ^isValBit | isKeyBit,
    66  		curd:  f.curd,
    67  		maxd:  f.maxd,
    68  		buf:   f.buf,
    69  	}
    70  }
    71  
    72  // SetIsValue returns format instance with the isVal bit set to value.
    73  func (f format) SetIsValue() format {
    74  	return format{
    75  		flags: f.flags & ^isKeyBit | isValBit,
    76  		curd:  f.curd,
    77  		maxd:  f.maxd,
    78  		buf:   f.buf,
    79  	}
    80  }
    81  
    82  // SetPanic returns format instance with the panic bit set to value.
    83  func (f format) SetPanic() format {
    84  	return format{
    85  		flags: f.flags | panicBit /* handle panic as value */ | isValBit & ^isKeyBit,
    86  		curd:  f.curd,
    87  		maxd:  f.maxd,
    88  		buf:   f.buf,
    89  	}
    90  }
    91  
    92  // IncrDepth returns format instance with depth incremented.
    93  func (f format) IncrDepth() format {
    94  	return format{
    95  		flags: f.flags,
    96  		curd:  f.curd + 1,
    97  		maxd:  f.maxd,
    98  		buf:   f.buf,
    99  	}
   100  }
   101  
   102  // IncrDerefs returns format instance with dereference count incremented.
   103  func (f format) IncrDerefs() format {
   104  	return format{
   105  		flags: f.flags,
   106  		drefs: f.drefs + 1,
   107  		curd:  f.curd,
   108  		maxd:  f.maxd,
   109  		buf:   f.buf,
   110  	}
   111  }
   112  
   113  // appendType appends a type using supplied type str.
   114  func appendType(fmt format, t string) {
   115  	for i := uint8(0); i < fmt.Derefs(); i++ {
   116  		fmt.buf.AppendByte('*')
   117  	}
   118  	fmt.buf.AppendString(t)
   119  }
   120  
   121  // appendNilType Appends nil to buf, type included if verbose.
   122  func appendNilType(fmt format, t string) {
   123  	if !fmt.Verbose() {
   124  		fmt.buf.AppendString(`nil`)
   125  		return
   126  	}
   127  	fmt.buf.AppendByte('(')
   128  	appendType(fmt, t)
   129  	fmt.buf.AppendString(`)(nil)`)
   130  }
   131  
   132  // appendByte Appends a single byte to buf.
   133  func appendByte(fmt format, b byte) {
   134  	if fmt.IsKey() || fmt.IsValue() || fmt.Verbose() {
   135  		fmt.buf.B = strconv.AppendQuoteRune(fmt.buf.B, rune(b))
   136  	} else {
   137  		fmt.buf.AppendByte(b)
   138  	}
   139  }
   140  
   141  // appendBytes Appends a quoted byte slice to buf.
   142  func appendBytes(fmt format, b []byte) {
   143  	if b == nil {
   144  		// Bytes CAN be nil formatted
   145  		appendNilType(fmt, `[]byte`)
   146  	} else {
   147  		// Append bytes as slice
   148  		fmt.buf.AppendByte('[')
   149  		for i := 0; i < len(b); i++ {
   150  			fmt.buf.AppendByte(b[i])
   151  			fmt.buf.AppendByte(',')
   152  		}
   153  		if len(b) > 0 {
   154  			fmt.buf.Truncate(1)
   155  		}
   156  		fmt.buf.AppendByte(']')
   157  	}
   158  }
   159  
   160  // appendString Appends an escaped, double-quoted string to buf.
   161  func appendString(fmt format, s string) {
   162  	switch {
   163  	// Key in a key-value pair
   164  	case fmt.IsKey():
   165  		if !strconv.CanBackquote(s) {
   166  			// Requires quoting AND escaping
   167  			fmt.buf.B = strconv.AppendQuote(fmt.buf.B, s)
   168  		} else if containsSpaceOrTab(s) {
   169  			// Contains space, needs quotes
   170  			fmt.buf.AppendString(`"` + s + `"`)
   171  		} else {
   172  			// All else write as-is
   173  			fmt.buf.AppendString(s)
   174  		}
   175  
   176  	// Value in a key-value pair (always escape+quote)
   177  	case fmt.IsValue():
   178  		fmt.buf.B = strconv.AppendQuote(fmt.buf.B, s)
   179  
   180  	// Verbose but neither key nor value (always quote)
   181  	case fmt.Verbose():
   182  		fmt.buf.AppendString(`"` + s + `"`)
   183  
   184  	// All else
   185  	default:
   186  		fmt.buf.AppendString(s)
   187  	}
   188  }
   189  
   190  // appendBool Appends a formatted bool to buf.
   191  func appendBool(fmt format, b bool) {
   192  	fmt.buf.B = strconv.AppendBool(fmt.buf.B, b)
   193  }
   194  
   195  // appendInt Appends a formatted int to buf.
   196  func appendInt(fmt format, i int64) {
   197  	fmt.buf.B = strconv.AppendInt(fmt.buf.B, i, 10)
   198  }
   199  
   200  // appendUint Appends a formatted uint to buf.
   201  func appendUint(fmt format, u uint64) {
   202  	fmt.buf.B = strconv.AppendUint(fmt.buf.B, u, 10)
   203  }
   204  
   205  // appendFloat Appends a formatted float to buf.
   206  func appendFloat(fmt format, f float64) {
   207  	fmt.buf.B = strconv.AppendFloat(fmt.buf.B, f, 'f', -1, 64)
   208  }
   209  
   210  // appendComplex Appends a formatted complex128 to buf.
   211  func appendComplex(fmt format, c complex128) {
   212  	appendFloat(fmt, real(c))
   213  	fmt.buf.AppendByte('+')
   214  	appendFloat(fmt, imag(c))
   215  	fmt.buf.AppendByte('i')
   216  }
   217  
   218  // isNil will safely check if 'v' is nil without dealing with weird Go interface nil bullshit.
   219  func isNil(i interface{}) bool {
   220  	return (*(*struct{ _, v unsafe.Pointer })(unsafe.Pointer(&i))).v == nil //nolint
   221  }
   222  
   223  // appendIfaceOrReflectValue will attempt to append as interface, falling back to reflection.
   224  func appendIfaceOrRValue(fmt format, i interface{}) {
   225  	if !appendIface(fmt, i) {
   226  		appendRValue(fmt, reflect.ValueOf(i))
   227  	}
   228  }
   229  
   230  // appendValueNext checks for interface methods before performing appendRValue, checking + incr depth.
   231  func appendRValueOrIfaceNext(fmt format, v reflect.Value) {
   232  	// Check we haven't hit max
   233  	if fmt.AtMaxDepth() {
   234  		fmt.buf.AppendString("...")
   235  		return
   236  	}
   237  
   238  	// Incr the depth
   239  	fmt = fmt.IncrDepth()
   240  
   241  	// Make actual call
   242  	if !v.CanInterface() ||
   243  		!appendIface(fmt, v.Interface()) {
   244  		appendRValue(fmt, v)
   245  	}
   246  }
   247  
   248  // catchPanic is a deferrable panic catcher.
   249  func catchPanic(fmt format) {
   250  	if r := recover(); r != nil {
   251  		// DON'T recurse catchPanic()
   252  		if fmt.Panic() {
   253  			panic(r)
   254  		}
   255  
   256  		// Attempt to decode panic into buf
   257  		fmt.buf.AppendString(`!{PANIC=`)
   258  		appendIfaceOrRValue(fmt.SetPanic(), r)
   259  		fmt.buf.AppendByte('}')
   260  	}
   261  }
   262  
   263  // appendIface parses and Appends a formatted interface value to buf.
   264  func appendIface(fmt format, i interface{}) (ok bool) {
   265  	// Default true
   266  	ok = true
   267  
   268  	switch i := i.(type) {
   269  	// Nil type
   270  	case nil:
   271  		fmt.buf.AppendString(`nil`)
   272  
   273  	// Reflect types
   274  	case reflect.Type:
   275  		switch {
   276  		case isNil(i) /* safer nil check */ :
   277  			appendNilType(fmt, `reflect.Type`)
   278  		case fmt.Verbose():
   279  			appendType(fmt, `reflect.Type`)
   280  			fmt.buf.AppendString(`(` + i.String() + `)`)
   281  		default:
   282  			fmt.buf.AppendString(i.String())
   283  		}
   284  	case reflect.Value:
   285  		appendType(fmt, `reflect.Value`)
   286  		fmt.buf.AppendByte('(')
   287  		fmt.flags |= vboseBit
   288  		appendRValue(fmt, i)
   289  		fmt.buf.AppendByte(')')
   290  
   291  	// Bytes and string types
   292  	case byte /* also uint8 */ :
   293  		appendByte(fmt, i)
   294  	case []byte:
   295  		appendBytes(fmt, i)
   296  	case string:
   297  		appendString(fmt, i)
   298  
   299  	// Int types
   300  	case int:
   301  		appendInt(fmt, int64(i))
   302  	case int8:
   303  		appendInt(fmt, int64(i))
   304  	case int16:
   305  		appendInt(fmt, int64(i))
   306  	case int32 /* also rune */ :
   307  		appendInt(fmt, int64(i))
   308  	case int64:
   309  		appendInt(fmt, i)
   310  
   311  	// Uint types
   312  	case uint:
   313  		appendUint(fmt, uint64(i))
   314  	case uint16:
   315  		appendUint(fmt, uint64(i))
   316  	case uint32:
   317  		appendUint(fmt, uint64(i))
   318  	case uint64:
   319  		appendUint(fmt, i)
   320  
   321  	// Float types
   322  	case float32:
   323  		appendFloat(fmt, float64(i))
   324  	case float64:
   325  		appendFloat(fmt, i)
   326  
   327  	// Bool type
   328  	case bool:
   329  		appendBool(fmt, i)
   330  
   331  	// Complex types
   332  	case complex64:
   333  		appendComplex(fmt, complex128(i))
   334  	case complex128:
   335  		appendComplex(fmt, i)
   336  
   337  	// Method types
   338  	case error:
   339  		switch {
   340  		case fmt.Verbose():
   341  			return false
   342  		case isNil(i) /* use safer nil check */ :
   343  			appendNilType(fmt, reflect.TypeOf(i).String())
   344  		default:
   345  			defer catchPanic(fmt)
   346  			appendString(fmt, i.Error())
   347  		}
   348  	case Formattable:
   349  		switch {
   350  		case fmt.Verbose():
   351  			return false
   352  		case isNil(i) /* use safer nil check */ :
   353  			t := reflect.TypeOf(i)
   354  			appendNilType(fmt, t.String())
   355  		default:
   356  			defer catchPanic(fmt)
   357  			fmt.buf.B = i.AppendFormat(fmt.buf.B)
   358  		}
   359  	case interface{ String() string }:
   360  		switch {
   361  		case fmt.Verbose():
   362  			return false
   363  		case isNil(i) /* use safer nil check */ :
   364  			t := reflect.TypeOf(i)
   365  			appendNilType(fmt, t.String())
   366  		default:
   367  			defer catchPanic(fmt)
   368  			appendString(fmt, i.String())
   369  		}
   370  
   371  	// No quick handler
   372  	default:
   373  		return false
   374  	}
   375  
   376  	return ok
   377  }
   378  
   379  // appendReflectValue will safely append a reflected value.
   380  func appendRValue(fmt format, v reflect.Value) {
   381  	switch v.Kind() {
   382  	// String and byte types
   383  	case reflect.Uint8:
   384  		appendByte(fmt, byte(v.Uint()))
   385  	case reflect.String:
   386  		appendString(fmt, v.String())
   387  
   388  	// Float tpyes
   389  	case reflect.Float32, reflect.Float64:
   390  		appendFloat(fmt, v.Float())
   391  
   392  	// Int types
   393  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   394  		appendInt(fmt, v.Int())
   395  
   396  	// Uint types
   397  	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   398  		appendUint(fmt, v.Uint())
   399  
   400  	// Complex types
   401  	case reflect.Complex64, reflect.Complex128:
   402  		appendComplex(fmt, v.Complex())
   403  
   404  	// Bool type
   405  	case reflect.Bool:
   406  		appendBool(fmt, v.Bool())
   407  
   408  	// Slice and array types
   409  	case reflect.Array:
   410  		appendArrayType(fmt, v)
   411  	case reflect.Slice:
   412  		if v.IsNil() {
   413  			appendNilType(fmt, v.Type().String())
   414  		} else {
   415  			appendArrayType(fmt, v)
   416  		}
   417  
   418  	// Map types
   419  	case reflect.Map:
   420  		if v.IsNil() {
   421  			appendNilType(fmt, v.Type().String())
   422  		} else {
   423  			appendMapType(fmt, v)
   424  		}
   425  
   426  	// Struct types
   427  	case reflect.Struct:
   428  		appendStructType(fmt, v)
   429  
   430  	// Deref'able ptr types
   431  	case reflect.Ptr, reflect.Interface:
   432  		if v.IsNil() {
   433  			appendNilType(fmt, v.Type().String())
   434  		} else {
   435  			appendRValue(fmt.IncrDerefs(), v.Elem())
   436  		}
   437  
   438  	// 'raw' pointer types
   439  	case reflect.UnsafePointer:
   440  		appendType(fmt, `unsafe.Pointer`)
   441  		fmt.buf.AppendByte('(')
   442  		if u := v.Pointer(); u != 0 {
   443  			fmt.buf.AppendString("0x")
   444  			fmt.buf.B = strconv.AppendUint(fmt.buf.B, uint64(u), 16)
   445  		} else {
   446  			fmt.buf.AppendString(`nil`)
   447  		}
   448  		fmt.buf.AppendByte(')')
   449  	case reflect.Uintptr:
   450  		appendType(fmt, `uintptr`)
   451  		fmt.buf.AppendByte('(')
   452  		if u := v.Uint(); u != 0 {
   453  			fmt.buf.AppendString("0x")
   454  			fmt.buf.B = strconv.AppendUint(fmt.buf.B, u, 16)
   455  		} else {
   456  			fmt.buf.AppendString(`nil`)
   457  		}
   458  		fmt.buf.AppendByte(')')
   459  
   460  	// Generic types we don't *exactly* handle
   461  	case reflect.Func, reflect.Chan:
   462  		if v.IsNil() {
   463  			appendNilType(fmt, v.Type().String())
   464  		} else {
   465  			fmt.buf.AppendString(v.String())
   466  		}
   467  
   468  	// Unhandled kind
   469  	default:
   470  		fmt.buf.AppendString(v.String())
   471  	}
   472  }
   473  
   474  // appendArrayType Appends an array of unknown type (parsed by reflection) to buf, unlike appendSliceType does NOT catch nil slice.
   475  func appendArrayType(fmt format, v reflect.Value) {
   476  	// Prepend type if verbose
   477  	if fmt.Verbose() {
   478  		t := v.Type()
   479  		appendType(fmt, t.String())
   480  	}
   481  
   482  	// get no. elements
   483  	n := v.Len()
   484  
   485  	fmt.buf.AppendByte('[')
   486  
   487  	// Append values
   488  	for i := 0; i < n; i++ {
   489  		appendRValueOrIfaceNext(fmt.SetIsValue(), v.Index(i))
   490  		fmt.buf.AppendByte(',')
   491  	}
   492  
   493  	// Drop last comma
   494  	if n > 0 {
   495  		fmt.buf.Truncate(1)
   496  	}
   497  
   498  	fmt.buf.AppendByte(']')
   499  }
   500  
   501  // appendMapType Appends a map of unknown types (parsed by reflection) to buf.
   502  func appendMapType(fmt format, v reflect.Value) {
   503  	// Prepend type if verbose
   504  	if fmt.Verbose() {
   505  		t := v.Type()
   506  		appendType(fmt, t.String())
   507  	}
   508  
   509  	// Get a map iterator
   510  	r := v.MapRange()
   511  	n := v.Len()
   512  
   513  	fmt.buf.AppendByte('{')
   514  
   515  	// Iterate pairs
   516  	for r.Next() {
   517  		appendRValueOrIfaceNext(fmt.SetIsKey(), r.Key())
   518  		fmt.buf.AppendByte('=')
   519  		appendRValueOrIfaceNext(fmt.SetIsValue(), r.Value())
   520  		fmt.buf.AppendByte(' ')
   521  	}
   522  
   523  	// Drop last space
   524  	if n > 0 {
   525  		fmt.buf.Truncate(1)
   526  	}
   527  
   528  	fmt.buf.AppendByte('}')
   529  }
   530  
   531  // appendStructType Appends a struct (as a set of key-value fields) to buf.
   532  func appendStructType(fmt format, v reflect.Value) {
   533  	// Get value type & no. fields
   534  	t := v.Type()
   535  	n := v.NumField()
   536  
   537  	// Prepend type if verbose
   538  	if fmt.Verbose() {
   539  		t := v.Type()
   540  		appendType(fmt, t.String())
   541  	}
   542  
   543  	fmt.buf.AppendByte('{')
   544  
   545  	// Iterate fields
   546  	for i := 0; i < n; i++ {
   547  		vfield := v.Field(i)
   548  		tfield := t.Field(i)
   549  
   550  		// Append field name
   551  		fmt.buf.AppendString(tfield.Name)
   552  		fmt.buf.AppendByte('=')
   553  		appendRValueOrIfaceNext(fmt.SetIsValue(), vfield)
   554  
   555  		// Iter written count
   556  		fmt.buf.AppendByte(' ')
   557  	}
   558  
   559  	// Drop last space
   560  	if n > 0 {
   561  		fmt.buf.Truncate(1)
   562  	}
   563  
   564  	fmt.buf.AppendByte('}')
   565  }
   566  
   567  // containsSpaceOrTab checks if "s" contains space or tabs.
   568  // NOTE: we rely on bytealg.IndexByteString() as it's ASM.
   569  func containsSpaceOrTab(s string) bool {
   570  	if i := bytealg_IndexBytes(s, ' '); i != -1 {
   571  		return true
   572  	} else if i := bytealg_IndexBytes(s, '\t'); i != -1 {
   573  		return true
   574  	}
   575  	return false
   576  }