Golang fmt格式化源码分析

277 阅读3分钟

格式化输出示例

func main() {
   err1 := errors.New("这是err1")
   err2 := fmt.Errorf("这是err2:%w", err1)
   fmt.Printf("err2.Type:%T, err2:%v\n", err2, err2)
}

fmt包源码分析

State接口

// State represents the printer state passed to custom formatters.
// It provides access to the io.Writer interface plus information about
// the flags and options for the operand's format specifier.
type State interface {
   // Write is the function to call to emit formatted output to be printed.
   Write(b []byte) (n int, err error)
   // Width returns the value of the width option and whether it has been set.
   Width() (wid int, ok bool)
   // Precision returns the value of the precision option and whether it has been set.
   Precision() (prec int, ok bool)

   // Flag reports whether the flag c, a character, has been set.
   Flag(c int) bool
}

Formatter接口

// Formatter is implemented by any value that has a Format method.
// The implementation controls how State and rune are interpreted,
// and may call Sprint(f) or Fprint(f) etc. to generate its output.
type Formatter interface {
   Format(f State, verb rune)
}

pp类型

// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
   buf buffer

   // arg holds the current item, as an interface{}.
   arg any

   // value is used instead of arg for reflect values.
   value reflect.Value

   // fmt is used to format basic items such as integers or strings.
   fmt fmt

   // reordered records whether the format string used argument reordering.
   reordered bool
   // goodArgNum records whether the most recent reordering directive was valid.
   goodArgNum bool
   // panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.
   panicking bool
   // erroring is set when printing an error string to guard against calling handleMethods.
   erroring bool
   // wrapErrs is set when the format string may contain a %w verb.
   wrapErrs bool
   // wrappedErr records the target of the %w verb.
   wrappedErr error
}

// 重要方法,学习free思想
// free saves used pp structs in ppFree; avoids an allocation per invocation.
func (p *pp) free() {
   // Proper usage of a sync.Pool requires each entry to have approximately
   // the same memory cost. To obtain this property when the stored type
   // contains a variably-sized buffer, we add a hard limit on the maximum buffer
   // to place back in the pool.
   //
   // See https://golang.org/issue/23199
   if cap(p.buf) > 64<<10 {
      return
   }

   p.buf = p.buf[:0]
   p.arg = nil
   p.value = reflect.Value{}
   p.wrappedErr = nil
   ppFree.Put(p)
}

// 池化思维
var ppFree = sync.Pool{
   New: func() any { return new(pp) },
}

buffer类型

// Use simple []byte instead of bytes.Buffer to avoid large dependency.
type buffer []byte

func (b *buffer) write(p []byte) {
   *b = append(*b, p...)
}

func (b *buffer) writeString(s string) {
   *b = append(*b, s...)
}

func (b *buffer) writeByte(c byte) {
   *b = append(*b, c)
}

func (bp *buffer) writeRune(r rune) {
   if r < utf8.RuneSelf {
      *bp = append(*bp, byte(r))
      return
   }

   b := *bp
   n := len(b)
   for n+utf8.UTFMax > cap(b) {
      b = append(b, 0)
   }
   w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
   *bp = b[:n+w]
}

fmt类型,把输出内容写到buffer中

// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
   buf *buffer

   fmtFlags

   wid  int // width
   prec int // precision

   // intbuf is large enough to store %b of an int64 with a sign and
   // avoids padding at the end of the struct on 32 bit architectures.
   intbuf [68]byte
}

pp类型的重要方法

  1. doPrintf
  2. printArg
  3. printValue
  4. handleMethods,该方法会调用Formatter接口实例的Format方法,如下:
    // Is it a Formatter?
    if formatter, ok := p.arg.(Formatter); ok {
       handled = true
       defer p.catchPanic(p.arg, verb, "Format")
       formatter.Format(p, verb)
       return
    }
    

好文推荐

blog.cyeam.com/golang/2018…

%p格式化输出的源码

%p的作用:格式化输出指针值。

示例:

func main() {
    a := 1
    fmt.Printf("%d %p\n", a, &a)
    a = 2
    fmt.Printf("%d %p\n", a, &a)    
}

fmt包源码:

func (p *pp) printArg(arg any, verb rune) {
   p.arg = arg
   p.value = reflect.Value{}

   if arg == nil {
      switch verb {
      case 'T', 'v':
         p.fmt.padString(nilAngleString)
      default:
         p.badVerb(verb)
      }
      return
   }

   // Special processing considerations.
   // %T (the value's type) and %p (its address) are special; we always do them first.
   switch verb {
   case 'T':
      p.fmt.fmtS(reflect.TypeOf(arg).String())
      return
   case 'p':
      // 格式化输出指针
      p.fmtPointer(reflect.ValueOf(arg), 'p')
      return
   }

   ......
}


func (p *pp) fmtPointer(value reflect.Value, verb rune) {
   var u uintptr
   switch value.Kind() {
   // 格式化输出的值必须是这些类型,这些类型都是引用类型
   case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:
      // 调用value的Pointer方法
      u = value.Pointer()
   default:
      p.badVerb(verb)
      return
   }

   switch verb {
   case 'v':
      if p.fmt.sharpV {
         p.buf.writeByte('(')
         p.buf.writeString(value.Type().String())
         p.buf.writeString(")(")
         if u == 0 {
            p.buf.writeString(nilString)
         } else {
            p.fmt0x64(uint64(u), true)
         }
         p.buf.writeByte(')')
      } else {
         if u == 0 {
            p.fmt.padString(nilAngleString)
         } else {
            p.fmt0x64(uint64(u), !p.fmt.sharp)
         }
      }
   case 'p':
      // 输出指针值
      p.fmt0x64(uint64(u), !p.fmt.sharp)
   case 'b', 'o', 'd', 'x', 'X':
      p.fmtInteger(uint64(u), unsigned, verb)
   default:
      p.badVerb(verb)
   }
}

func (v Value) Pointer() uintptr {
   k := v.kind()
   switch k {
   case Pointer:
      if v.typ.ptrdata == 0 {
         val := *(*uintptr)(v.ptr)
         // Since it is a not-in-heap pointer, all pointers to the heap are
         // forbidden! See comment in Value.Elem and issue #48399.
         if !verifyNotInHeapPtr(val) {
            panic("reflect: reflect.Value.Pointer on an invalid notinheap pointer")
         }
         return val
      }
      fallthrough
   case Chan, Map, UnsafePointer:
      return uintptr(v.pointer())
   case Func:
      if v.flag&flagMethod != 0 {
         // As the doc comment says, the returned pointer is an
         // underlying code pointer but not necessarily enough to
         // identify a single function uniquely. All method expressions
         // created via reflect have the same underlying code pointer,
         // so their Pointers are equal. The function used here must
         // match the one used in makeMethodValue.
         return methodValueCallCodePtr()
      }
      p := v.pointer()
      // Non-nil func value points at data block.
      // First word of data block is actual code.
      if p != nil {
         p = *(*unsafe.Pointer)(p)
      }
      return uintptr(p)

   case Slice:
      // 输出slice底层数据结构保存的指针
      return (*SliceHeader)(v.ptr).Data
   }
   panic(&ValueError{"reflect.Value.Pointer", v.kind()})
}