mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 05:22:25 -06:00 
			
		
		
		
	
		
			
	
	
		
			734 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			734 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright (c) 2014 The sortutil Authors. All rights reserved.
							 | 
						||
| 
								 | 
							
								// Use of this source code is governed by a BSD-style
							 | 
						||
| 
								 | 
							
								// license that can be found in the LICENSE file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Package strutil collects utils supplemental to the standard strings package.
							 | 
						||
| 
								 | 
							
								package strutil // import "modernc.org/strutil"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"bytes"
							 | 
						||
| 
								 | 
							
									"encoding/base32"
							 | 
						||
| 
								 | 
							
									"encoding/base64"
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"io"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"path/filepath"
							 | 
						||
| 
								 | 
							
									"reflect"
							 | 
						||
| 
								 | 
							
									"runtime"
							 | 
						||
| 
								 | 
							
									"sort"
							 | 
						||
| 
								 | 
							
									"strconv"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Base32ExtDecode decodes base32 extended (RFC 4648) text to binary data.
							 | 
						||
| 
								 | 
							
								func Base32ExtDecode(text []byte) (data []byte, err error) {
							 | 
						||
| 
								 | 
							
									n := base32.HexEncoding.DecodedLen(len(text))
							 | 
						||
| 
								 | 
							
									data = make([]byte, n)
							 | 
						||
| 
								 | 
							
									decoder := base32.NewDecoder(base32.HexEncoding, bytes.NewBuffer(text))
							 | 
						||
| 
								 | 
							
									if n, err = decoder.Read(data); err != nil {
							 | 
						||
| 
								 | 
							
										n = 0
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									data = data[:n]
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Base32ExtEncode encodes binary data to base32 extended (RFC 4648) encoded text.
							 | 
						||
| 
								 | 
							
								func Base32ExtEncode(data []byte) (text []byte) {
							 | 
						||
| 
								 | 
							
									n := base32.HexEncoding.EncodedLen(len(data))
							 | 
						||
| 
								 | 
							
									buf := bytes.NewBuffer(make([]byte, 0, n))
							 | 
						||
| 
								 | 
							
									encoder := base32.NewEncoder(base32.HexEncoding, buf)
							 | 
						||
| 
								 | 
							
									encoder.Write(data)
							 | 
						||
| 
								 | 
							
									encoder.Close()
							 | 
						||
| 
								 | 
							
									if buf.Len() != n {
							 | 
						||
| 
								 | 
							
										panic("internal error")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return buf.Bytes()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Base64Decode decodes base64 text to binary data.
							 | 
						||
| 
								 | 
							
								func Base64Decode(text []byte) (data []byte, err error) {
							 | 
						||
| 
								 | 
							
									n := base64.StdEncoding.DecodedLen(len(text))
							 | 
						||
| 
								 | 
							
									data = make([]byte, n)
							 | 
						||
| 
								 | 
							
									decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(text))
							 | 
						||
| 
								 | 
							
									if n, err = decoder.Read(data); err != nil {
							 | 
						||
| 
								 | 
							
										n = 0
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									data = data[:n]
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Base64Encode encodes binary data to base64 encoded text.
							 | 
						||
| 
								 | 
							
								func Base64Encode(data []byte) (text []byte) {
							 | 
						||
| 
								 | 
							
									n := base64.StdEncoding.EncodedLen(len(data))
							 | 
						||
| 
								 | 
							
									buf := bytes.NewBuffer(make([]byte, 0, n))
							 | 
						||
| 
								 | 
							
									encoder := base64.NewEncoder(base64.StdEncoding, buf)
							 | 
						||
| 
								 | 
							
									encoder.Write(data)
							 | 
						||
| 
								 | 
							
									encoder.Close()
							 | 
						||
| 
								 | 
							
									if buf.Len() != n {
							 | 
						||
| 
								 | 
							
										panic("internal error")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return buf.Bytes()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Formatter is an io.Writer extended by a fmt.Printf like function Format
							 | 
						||
| 
								 | 
							
								type Formatter interface {
							 | 
						||
| 
								 | 
							
									io.Writer
							 | 
						||
| 
								 | 
							
									Format(format string, args ...interface{}) (n int, errno error)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type indentFormatter struct {
							 | 
						||
| 
								 | 
							
									io.Writer
							 | 
						||
| 
								 | 
							
									indent      []byte
							 | 
						||
| 
								 | 
							
									indentLevel int
							 | 
						||
| 
								 | 
							
									state       int
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const (
							 | 
						||
| 
								 | 
							
									st0 = iota
							 | 
						||
| 
								 | 
							
									stBOL
							 | 
						||
| 
								 | 
							
									stPERC
							 | 
						||
| 
								 | 
							
									stBOLPERC
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// IndentFormatter returns a new Formatter which interprets %i and %u in the
							 | 
						||
| 
								 | 
							
								// Format() format string as indent and undent commands. The commands can
							 | 
						||
| 
								 | 
							
								// nest. The Formatter writes to io.Writer 'w' and inserts one 'indent'
							 | 
						||
| 
								 | 
							
								// string per current indent level value.
							 | 
						||
| 
								 | 
							
								// Behaviour of commands reaching negative indent levels is undefined.
							 | 
						||
| 
								 | 
							
								//	IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
							 | 
						||
| 
								 | 
							
								// output:
							 | 
						||
| 
								 | 
							
								//	abc3%e
							 | 
						||
| 
								 | 
							
								//		x
							 | 
						||
| 
								 | 
							
								//		y
							 | 
						||
| 
								 | 
							
								//	z
							 | 
						||
| 
								 | 
							
								// The Go quoted string literal form of the above is:
							 | 
						||
| 
								 | 
							
								//	"abc%%e\n\tx\n\tx\nz\n"
							 | 
						||
| 
								 | 
							
								// The commands can be scattered between separate invocations of Format(),
							 | 
						||
| 
								 | 
							
								// i.e. the formatter keeps track of the indent level and knows if it is
							 | 
						||
| 
								 | 
							
								// positioned on start of a line and should emit indentation(s).
							 | 
						||
| 
								 | 
							
								// The same output as above can be produced by e.g.:
							 | 
						||
| 
								 | 
							
								//	f := IndentFormatter(os.Stdout, " ")
							 | 
						||
| 
								 | 
							
								//	f.Format("abc%d%%e%i\nx\n", 3)
							 | 
						||
| 
								 | 
							
								//	f.Format("y\n%uz\n")
							 | 
						||
| 
								 | 
							
								func IndentFormatter(w io.Writer, indent string) Formatter {
							 | 
						||
| 
								 | 
							
									return &indentFormatter{w, []byte(indent), 0, stBOL}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) {
							 | 
						||
| 
								 | 
							
									buf := []byte{}
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(format); i++ {
							 | 
						||
| 
								 | 
							
										c := format[i]
							 | 
						||
| 
								 | 
							
										switch f.state {
							 | 
						||
| 
								 | 
							
										case st0:
							 | 
						||
| 
								 | 
							
											switch c {
							 | 
						||
| 
								 | 
							
											case '\n':
							 | 
						||
| 
								 | 
							
												cc := c
							 | 
						||
| 
								 | 
							
												if flat && f.indentLevel != 0 {
							 | 
						||
| 
								 | 
							
													cc = ' '
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												buf = append(buf, cc)
							 | 
						||
| 
								 | 
							
												f.state = stBOL
							 | 
						||
| 
								 | 
							
											case '%':
							 | 
						||
| 
								 | 
							
												f.state = stPERC
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												buf = append(buf, c)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										case stBOL:
							 | 
						||
| 
								 | 
							
											switch c {
							 | 
						||
| 
								 | 
							
											case '\n':
							 | 
						||
| 
								 | 
							
												cc := c
							 | 
						||
| 
								 | 
							
												if flat && f.indentLevel != 0 {
							 | 
						||
| 
								 | 
							
													cc = ' '
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												buf = append(buf, cc)
							 | 
						||
| 
								 | 
							
											case '%':
							 | 
						||
| 
								 | 
							
												f.state = stBOLPERC
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												if !flat {
							 | 
						||
| 
								 | 
							
													for i := 0; i < f.indentLevel; i++ {
							 | 
						||
| 
								 | 
							
														buf = append(buf, f.indent...)
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												buf = append(buf, c)
							 | 
						||
| 
								 | 
							
												f.state = st0
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										case stBOLPERC:
							 | 
						||
| 
								 | 
							
											switch c {
							 | 
						||
| 
								 | 
							
											case 'i':
							 | 
						||
| 
								 | 
							
												f.indentLevel++
							 | 
						||
| 
								 | 
							
												f.state = stBOL
							 | 
						||
| 
								 | 
							
											case 'u':
							 | 
						||
| 
								 | 
							
												f.indentLevel--
							 | 
						||
| 
								 | 
							
												f.state = stBOL
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												if !flat {
							 | 
						||
| 
								 | 
							
													for i := 0; i < f.indentLevel; i++ {
							 | 
						||
| 
								 | 
							
														buf = append(buf, f.indent...)
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												buf = append(buf, '%', c)
							 | 
						||
| 
								 | 
							
												f.state = st0
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										case stPERC:
							 | 
						||
| 
								 | 
							
											switch c {
							 | 
						||
| 
								 | 
							
											case 'i':
							 | 
						||
| 
								 | 
							
												f.indentLevel++
							 | 
						||
| 
								 | 
							
												f.state = st0
							 | 
						||
| 
								 | 
							
											case 'u':
							 | 
						||
| 
								 | 
							
												f.indentLevel--
							 | 
						||
| 
								 | 
							
												f.state = st0
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												buf = append(buf, '%', c)
							 | 
						||
| 
								 | 
							
												f.state = st0
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											panic("unexpected state")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									switch f.state {
							 | 
						||
| 
								 | 
							
									case stPERC, stBOLPERC:
							 | 
						||
| 
								 | 
							
										buf = append(buf, '%')
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return f.Write([]byte(fmt.Sprintf(string(buf), args...)))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) {
							 | 
						||
| 
								 | 
							
									return f.format(false, format, args...)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type flatFormatter indentFormatter
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// FlatFormatter returns a newly created Formatter with the same functionality as the one returned
							 | 
						||
| 
								 | 
							
								// by IndentFormatter except it allows a newline in the 'format' string argument of Format
							 | 
						||
| 
								 | 
							
								// to pass through iff indent level is currently zero.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// If indent level is non-zero then such new lines are changed to a space character.
							 | 
						||
| 
								 | 
							
								// There is no indent string, the %i and %u format verbs are used solely to determine the indent level.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The FlatFormatter is intended for flattening of normally nested structure textual representation to
							 | 
						||
| 
								 | 
							
								// a one top level structure per line form.
							 | 
						||
| 
								 | 
							
								//	FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
							 | 
						||
| 
								 | 
							
								// output in the form of a Go quoted string literal:
							 | 
						||
| 
								 | 
							
								//	"abc3%%e x y z\n"
							 | 
						||
| 
								 | 
							
								func FlatFormatter(w io.Writer) Formatter {
							 | 
						||
| 
								 | 
							
									return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) {
							 | 
						||
| 
								 | 
							
									return (*indentFormatter)(f).format(true, format, args...)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Pool handles aligning of strings having equal values to the same string instance.
							 | 
						||
| 
								 | 
							
								// Intended use is to conserve some memory e.g. where a large number of identically valued strings
							 | 
						||
| 
								 | 
							
								// with non identical backing arrays may exists in several semantically distinct instances of some structs.
							 | 
						||
| 
								 | 
							
								// Pool is *not* concurrent access safe. It doesn't handle common prefix/suffix aligning,
							 | 
						||
| 
								 | 
							
								// e.g. having s1 == "abc" and s2 == "bc", s2 is not automatically aligned as s1[1:].
							 | 
						||
| 
								 | 
							
								type Pool struct {
							 | 
						||
| 
								 | 
							
									pool map[string]string
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewPool returns a newly created Pool.
							 | 
						||
| 
								 | 
							
								func NewPool() *Pool {
							 | 
						||
| 
								 | 
							
									return &Pool{map[string]string{}}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Align returns a string with the same value as its argument. It guarantees that
							 | 
						||
| 
								 | 
							
								// all aligned strings share a single instance in memory.
							 | 
						||
| 
								 | 
							
								func (p *Pool) Align(s string) string {
							 | 
						||
| 
								 | 
							
									if a, ok := p.pool[s]; ok {
							 | 
						||
| 
								 | 
							
										return a
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									s = StrPack(s)
							 | 
						||
| 
								 | 
							
									p.pool[s] = s
							 | 
						||
| 
								 | 
							
									return s
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Count returns the number of items in the pool.
							 | 
						||
| 
								 | 
							
								func (p *Pool) Count() int {
							 | 
						||
| 
								 | 
							
									return len(p.pool)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// GoPool is a concurrent access safe version of Pool.
							 | 
						||
| 
								 | 
							
								type GoPool struct {
							 | 
						||
| 
								 | 
							
									pool map[string]string
							 | 
						||
| 
								 | 
							
									rwm  *sync.RWMutex
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewGoPool returns a newly created GoPool.
							 | 
						||
| 
								 | 
							
								func NewGoPool() (p *GoPool) {
							 | 
						||
| 
								 | 
							
									return &GoPool{map[string]string{}, &sync.RWMutex{}}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Align returns a string with the same value as its argument. It guarantees that
							 | 
						||
| 
								 | 
							
								// all aligned strings share a single instance in memory.
							 | 
						||
| 
								 | 
							
								func (p *GoPool) Align(s string) (y string) {
							 | 
						||
| 
								 | 
							
									if s != "" {
							 | 
						||
| 
								 | 
							
										p.rwm.RLock()               // R++
							 | 
						||
| 
								 | 
							
										if a, ok := p.pool[s]; ok { // found
							 | 
						||
| 
								 | 
							
											p.rwm.RUnlock() // R--
							 | 
						||
| 
								 | 
							
											return a
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										p.rwm.RUnlock() // R--
							 | 
						||
| 
								 | 
							
										// not found but with a race condition, retry within a write lock
							 | 
						||
| 
								 | 
							
										p.rwm.Lock()                // W++
							 | 
						||
| 
								 | 
							
										defer p.rwm.Unlock()        // W--
							 | 
						||
| 
								 | 
							
										if a, ok := p.pool[s]; ok { // done in a race
							 | 
						||
| 
								 | 
							
											return a
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// we won
							 | 
						||
| 
								 | 
							
										s = StrPack(s)
							 | 
						||
| 
								 | 
							
										p.pool[s] = s
							 | 
						||
| 
								 | 
							
										return s
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Count returns the number of items in the pool.
							 | 
						||
| 
								 | 
							
								func (p *GoPool) Count() int {
							 | 
						||
| 
								 | 
							
									return len(p.pool)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Dict is a string <-> id bijection. Dict is *not* concurrent access safe for assigning new ids
							 | 
						||
| 
								 | 
							
								// to strings not yet contained in the bijection.
							 | 
						||
| 
								 | 
							
								// Id for an empty string is guaranteed to be 0,
							 | 
						||
| 
								 | 
							
								// thus Id for any non empty string is guaranteed to be non zero.
							 | 
						||
| 
								 | 
							
								type Dict struct {
							 | 
						||
| 
								 | 
							
									si map[string]int
							 | 
						||
| 
								 | 
							
									is []string
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewDict returns a newly created Dict.
							 | 
						||
| 
								 | 
							
								func NewDict() (d *Dict) {
							 | 
						||
| 
								 | 
							
									d = &Dict{map[string]int{}, []string{}}
							 | 
						||
| 
								 | 
							
									d.Id("")
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Count returns the number of items in the dict.
							 | 
						||
| 
								 | 
							
								func (d *Dict) Count() int {
							 | 
						||
| 
								 | 
							
									return len(d.is)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Id maps string s to its numeric identificator.
							 | 
						||
| 
								 | 
							
								func (d *Dict) Id(s string) (y int) {
							 | 
						||
| 
								 | 
							
									if y, ok := d.si[s]; ok {
							 | 
						||
| 
								 | 
							
										return y
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									s = StrPack(s)
							 | 
						||
| 
								 | 
							
									y = len(d.is)
							 | 
						||
| 
								 | 
							
									d.si[s] = y
							 | 
						||
| 
								 | 
							
									d.is = append(d.is, s)
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// S maps an id to its string value and ok == true. Id values not contained in the bijection
							 | 
						||
| 
								 | 
							
								// return "", false.
							 | 
						||
| 
								 | 
							
								func (d *Dict) S(id int) (s string, ok bool) {
							 | 
						||
| 
								 | 
							
									if id >= len(d.is) {
							 | 
						||
| 
								 | 
							
										return "", false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return d.is[id], true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// GoDict is a concurrent access safe version of Dict.
							 | 
						||
| 
								 | 
							
								type GoDict struct {
							 | 
						||
| 
								 | 
							
									si  map[string]int
							 | 
						||
| 
								 | 
							
									is  []string
							 | 
						||
| 
								 | 
							
									rwm *sync.RWMutex
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewGoDict returns a newly created GoDict.
							 | 
						||
| 
								 | 
							
								func NewGoDict() (d *GoDict) {
							 | 
						||
| 
								 | 
							
									d = &GoDict{map[string]int{}, []string{}, &sync.RWMutex{}}
							 | 
						||
| 
								 | 
							
									d.Id("")
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Count returns the number of items in the dict.
							 | 
						||
| 
								 | 
							
								func (d *GoDict) Count() int {
							 | 
						||
| 
								 | 
							
									return len(d.is)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Id maps string s to its numeric identificator. The implementation honors getting
							 | 
						||
| 
								 | 
							
								// an existing id at the cost of assigning a new one.
							 | 
						||
| 
								 | 
							
								func (d *GoDict) Id(s string) (y int) {
							 | 
						||
| 
								 | 
							
									d.rwm.RLock()             // R++
							 | 
						||
| 
								 | 
							
									if y, ok := d.si[s]; ok { // found
							 | 
						||
| 
								 | 
							
										d.rwm.RUnlock() // R--
							 | 
						||
| 
								 | 
							
										return y
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									d.rwm.RUnlock() // R--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// not found but with a race condition
							 | 
						||
| 
								 | 
							
									d.rwm.Lock()              // W++ recheck with write lock
							 | 
						||
| 
								 | 
							
									defer d.rwm.Unlock()      // W--
							 | 
						||
| 
								 | 
							
									if y, ok := d.si[s]; ok { // some other goroutine won already
							 | 
						||
| 
								 | 
							
										return y
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// a race free not found state => insert the string
							 | 
						||
| 
								 | 
							
									s = StrPack(s)
							 | 
						||
| 
								 | 
							
									y = len(d.is)
							 | 
						||
| 
								 | 
							
									d.si[s] = y
							 | 
						||
| 
								 | 
							
									d.is = append(d.is, s)
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// S maps an id to its string value and ok == true. Id values not contained in the bijection
							 | 
						||
| 
								 | 
							
								// return "", false.
							 | 
						||
| 
								 | 
							
								func (d *GoDict) S(id int) (s string, ok bool) {
							 | 
						||
| 
								 | 
							
									d.rwm.RLock()         // R++
							 | 
						||
| 
								 | 
							
									defer d.rwm.RUnlock() // R--
							 | 
						||
| 
								 | 
							
									if id >= len(d.is) {
							 | 
						||
| 
								 | 
							
										return "", false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return d.is[id], true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// StrPack returns a new instance of s which is tightly packed in memory.
							 | 
						||
| 
								 | 
							
								// It is intended for avoiding the situation where having a live reference
							 | 
						||
| 
								 | 
							
								// to a string slice over an unreferenced biger underlying string keeps the biger one
							 | 
						||
| 
								 | 
							
								// in memory anyway - it can't be GCed.
							 | 
						||
| 
								 | 
							
								func StrPack(s string) string {
							 | 
						||
| 
								 | 
							
									return string([]byte(s)) // T(U(T)) intentional.
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// JoinFields returns strings in flds joined by sep. Flds may contain arbitrary
							 | 
						||
| 
								 | 
							
								// bytes, including the sep as they are safely escaped. JoinFields panics if
							 | 
						||
| 
								 | 
							
								// sep is the backslash character or if len(sep) != 1.
							 | 
						||
| 
								 | 
							
								func JoinFields(flds []string, sep string) string {
							 | 
						||
| 
								 | 
							
									if len(sep) != 1 || sep == "\\" {
							 | 
						||
| 
								 | 
							
										panic("invalid separator")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									a := make([]string, len(flds))
							 | 
						||
| 
								 | 
							
									for i, v := range flds {
							 | 
						||
| 
								 | 
							
										v = strings.Replace(v, "\\", "\\0", -1)
							 | 
						||
| 
								 | 
							
										a[i] = strings.Replace(v, sep, "\\1", -1)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return strings.Join(a, sep)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// SplitFields splits s, which must be produced by JoinFields using the same
							 | 
						||
| 
								 | 
							
								// sep, into flds.  SplitFields panics if sep is the backslash character or if
							 | 
						||
| 
								 | 
							
								// len(sep) != 1.
							 | 
						||
| 
								 | 
							
								func SplitFields(s, sep string) (flds []string) {
							 | 
						||
| 
								 | 
							
									if len(sep) != 1 || sep == "\\" {
							 | 
						||
| 
								 | 
							
										panic("invalid separator")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									a := strings.Split(s, sep)
							 | 
						||
| 
								 | 
							
									r := make([]string, len(a))
							 | 
						||
| 
								 | 
							
									for i, v := range a {
							 | 
						||
| 
								 | 
							
										v = strings.Replace(v, "\\1", sep, -1)
							 | 
						||
| 
								 | 
							
										r[i] = strings.Replace(v, "\\0", "\\", -1)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return r
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// PrettyPrintHooks allow to customize the result of PrettyPrint for types
							 | 
						||
| 
								 | 
							
								// listed in the map value.
							 | 
						||
| 
								 | 
							
								type PrettyPrintHooks map[reflect.Type]func(f Formatter, v interface{}, prefix, suffix string)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// PrettyString returns the output of PrettyPrint as a string.
							 | 
						||
| 
								 | 
							
								func PrettyString(v interface{}, prefix, suffix string, hooks PrettyPrintHooks) string {
							 | 
						||
| 
								 | 
							
									var b bytes.Buffer
							 | 
						||
| 
								 | 
							
									PrettyPrint(&b, v, prefix, suffix, hooks)
							 | 
						||
| 
								 | 
							
									return b.String()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// PrettyPrint pretty prints v to w. Zero values and unexported struct fields
							 | 
						||
| 
								 | 
							
								// are omitted.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Force printing of zero values of struct fields by including in the field tag
							 | 
						||
| 
								 | 
							
								// PrettyPrint:"zero".
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Enable using a String method, if any, of a struct field type by including in
							 | 
						||
| 
								 | 
							
								// the field tag PrettyPrint:"stringer".
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The tags can be combined as in PrettyPrint:"zero,stringer". The order is not
							 | 
						||
| 
								 | 
							
								// important, so PrettyPrint:stringer,zero has the same effect.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// A hook attached to the field type has priority over the struct field tag
							 | 
						||
| 
								 | 
							
								// described above.
							 | 
						||
| 
								 | 
							
								func PrettyPrint(w io.Writer, v interface{}, prefix, suffix string, hooks PrettyPrintHooks) {
							 | 
						||
| 
								 | 
							
									if v == nil {
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									f := IndentFormatter(w, "· ")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if e := recover(); e != nil {
							 | 
						||
| 
								 | 
							
											f.Format("\npanic: %v", e)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									prettyPrint(nil, f, prefix, suffix, v, hooks, false, false)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func prettyPrint(protect map[interface{}]struct{}, sf Formatter, prefix, suffix string, v interface{}, hooks PrettyPrintHooks, zero, stringer bool) {
							 | 
						||
| 
								 | 
							
									if v == nil {
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									rt := reflect.TypeOf(v)
							 | 
						||
| 
								 | 
							
									if handler := hooks[rt]; handler != nil {
							 | 
						||
| 
								 | 
							
										handler(sf, v, prefix, suffix)
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									rv := reflect.ValueOf(v)
							 | 
						||
| 
								 | 
							
									if stringer {
							 | 
						||
| 
								 | 
							
										if _, ok := v.(fmt.Stringer); ok {
							 | 
						||
| 
								 | 
							
											sf.Format("%s%s", prefix, v)
							 | 
						||
| 
								 | 
							
											sf.Format(suffix)
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch rt.Kind() {
							 | 
						||
| 
								 | 
							
									case reflect.Slice:
							 | 
						||
| 
								 | 
							
										if rv.Len() == 0 && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										sf.Format("%s[]%T{ // len %d%i\n", prefix, rv.Index(0).Interface(), rv.Len())
							 | 
						||
| 
								 | 
							
										for i := 0; i < rv.Len(); i++ {
							 | 
						||
| 
								 | 
							
											prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks, false, false)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%u}" + suffix)
							 | 
						||
| 
								 | 
							
									case reflect.Array:
							 | 
						||
| 
								 | 
							
										if reflect.Zero(rt).Interface() == rv.Interface() && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										sf.Format("%s[%d]%T{%i\n", prefix, rv.Len(), rv.Index(0).Interface())
							 | 
						||
| 
								 | 
							
										for i := 0; i < rv.Len(); i++ {
							 | 
						||
| 
								 | 
							
											prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks, false, false)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%u}" + suffix)
							 | 
						||
| 
								 | 
							
									case reflect.Struct:
							 | 
						||
| 
								 | 
							
										if rt.NumField() == 0 {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if reflect.DeepEqual(reflect.Zero(rt).Interface(), rv.Interface()) && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										sf.Format("%s%T{%i\n", prefix, v)
							 | 
						||
| 
								 | 
							
										for i := 0; i < rt.NumField(); i++ {
							 | 
						||
| 
								 | 
							
											f := rv.Field(i)
							 | 
						||
| 
								 | 
							
											if !f.CanInterface() {
							 | 
						||
| 
								 | 
							
												continue
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											var stringer, zero bool
							 | 
						||
| 
								 | 
							
											ft := rt.Field(i)
							 | 
						||
| 
								 | 
							
											if tag, ok := ft.Tag.Lookup("PrettyPrint"); ok {
							 | 
						||
| 
								 | 
							
												a := strings.Split(tag, ",")
							 | 
						||
| 
								 | 
							
												for _, v := range a {
							 | 
						||
| 
								 | 
							
													switch strings.TrimSpace(v) {
							 | 
						||
| 
								 | 
							
													case "stringer":
							 | 
						||
| 
								 | 
							
														stringer = true
							 | 
						||
| 
								 | 
							
													case "zero":
							 | 
						||
| 
								 | 
							
														zero = true
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											prettyPrint(protect, sf, fmt.Sprintf("%s: ", rt.Field(i).Name), ",\n", f.Interface(), hooks, zero, stringer)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%u}" + suffix)
							 | 
						||
| 
								 | 
							
									case reflect.Ptr:
							 | 
						||
| 
								 | 
							
										if rv.IsNil() && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										rvi := rv.Interface()
							 | 
						||
| 
								 | 
							
										if _, ok := protect[rvi]; ok {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s&%T{ /* recursive/repetitive pointee not shown */ }"+suffix, prefix, rv.Elem().Interface())
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if protect == nil {
							 | 
						||
| 
								 | 
							
											protect = map[interface{}]struct{}{}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										protect[rvi] = struct{}{}
							 | 
						||
| 
								 | 
							
										prettyPrint(protect, sf, prefix+"&", suffix, rv.Elem().Interface(), hooks, false, false)
							 | 
						||
| 
								 | 
							
									case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
							 | 
						||
| 
								 | 
							
										if v := rv.Int(); v != 0 || zero {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s%v"+suffix, prefix, v)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8:
							 | 
						||
| 
								 | 
							
										if v := rv.Uint(); v != 0 || zero {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s%v"+suffix, prefix, v)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									case reflect.Float32, reflect.Float64:
							 | 
						||
| 
								 | 
							
										if v := rv.Float(); v != 0 || zero {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s%v"+suffix, prefix, v)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									case reflect.Complex64, reflect.Complex128:
							 | 
						||
| 
								 | 
							
										if v := rv.Complex(); v != 0 || zero {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s%v"+suffix, prefix, v)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									case reflect.Uintptr:
							 | 
						||
| 
								 | 
							
										if v := rv.Uint(); v != 0 || zero {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s%v"+suffix, prefix, v)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									case reflect.UnsafePointer:
							 | 
						||
| 
								 | 
							
										s := fmt.Sprintf("%p", rv.Interface())
							 | 
						||
| 
								 | 
							
										if s == "0x0" && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%s%s"+suffix, prefix, s)
							 | 
						||
| 
								 | 
							
									case reflect.Bool:
							 | 
						||
| 
								 | 
							
										if v := rv.Bool(); v || zero {
							 | 
						||
| 
								 | 
							
											suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
											sf.Format("%s%v"+suffix, prefix, rv.Bool())
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									case reflect.String:
							 | 
						||
| 
								 | 
							
										s := rv.Interface().(string)
							 | 
						||
| 
								 | 
							
										if s == "" && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%s%q"+suffix, prefix, s)
							 | 
						||
| 
								 | 
							
									case reflect.Chan:
							 | 
						||
| 
								 | 
							
										if reflect.Zero(rt).Interface() == rv.Interface() && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										c := rv.Cap()
							 | 
						||
| 
								 | 
							
										s := ""
							 | 
						||
| 
								 | 
							
										if c != 0 {
							 | 
						||
| 
								 | 
							
											s = fmt.Sprintf("// capacity: %d", c)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%s%s %s%s"+suffix, prefix, rt.ChanDir(), rt.Elem().Name(), s)
							 | 
						||
| 
								 | 
							
									case reflect.Func:
							 | 
						||
| 
								 | 
							
										if rv.IsNil() && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var in, out []string
							 | 
						||
| 
								 | 
							
										for i := 0; i < rt.NumIn(); i++ {
							 | 
						||
| 
								 | 
							
											x := reflect.Zero(rt.In(i))
							 | 
						||
| 
								 | 
							
											in = append(in, fmt.Sprintf("%T", x.Interface()))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if rt.IsVariadic() {
							 | 
						||
| 
								 | 
							
											i := len(in) - 1
							 | 
						||
| 
								 | 
							
											in[i] = "..." + in[i][2:]
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for i := 0; i < rt.NumOut(); i++ {
							 | 
						||
| 
								 | 
							
											out = append(out, rt.Out(i).Name())
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										s := "(" + strings.Join(in, ", ") + ")"
							 | 
						||
| 
								 | 
							
										t := strings.Join(out, ", ")
							 | 
						||
| 
								 | 
							
										if len(out) > 1 {
							 | 
						||
| 
								 | 
							
											t = "(" + t + ")"
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if t != "" {
							 | 
						||
| 
								 | 
							
											t = " " + t
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%sfunc%s%s { ... }"+suffix, prefix, s, t)
							 | 
						||
| 
								 | 
							
									case reflect.Map:
							 | 
						||
| 
								 | 
							
										keys := rv.MapKeys()
							 | 
						||
| 
								 | 
							
										if len(keys) == 0 && !zero {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var buf bytes.Buffer
							 | 
						||
| 
								 | 
							
										nf := IndentFormatter(&buf, "· ")
							 | 
						||
| 
								 | 
							
										var skeys []string
							 | 
						||
| 
								 | 
							
										for i, k := range keys {
							 | 
						||
| 
								 | 
							
											prettyPrint(protect, nf, "", "", k.Interface(), hooks, false, false)
							 | 
						||
| 
								 | 
							
											skeys = append(skeys, fmt.Sprintf("%s%10d", buf.Bytes(), i))
							 | 
						||
| 
								 | 
							
											buf.Reset()
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										sort.Strings(skeys)
							 | 
						||
| 
								 | 
							
										sf.Format("%s%T{%i\n", prefix, v)
							 | 
						||
| 
								 | 
							
										for _, k := range skeys {
							 | 
						||
| 
								 | 
							
											si := strings.TrimSpace(k[len(k)-10:])
							 | 
						||
| 
								 | 
							
											k = k[:len(k)-10]
							 | 
						||
| 
								 | 
							
											n, _ := strconv.ParseUint(si, 10, 64)
							 | 
						||
| 
								 | 
							
											mv := rv.MapIndex(keys[n])
							 | 
						||
| 
								 | 
							
											prettyPrint(protect, sf, fmt.Sprintf("%s: ", k), ",\n", mv.Interface(), hooks, false, false)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										suffix = strings.Replace(suffix, "%", "%%", -1)
							 | 
						||
| 
								 | 
							
										sf.Format("%u}" + suffix)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Gopath returns the value of the $GOPATH environment variable or its default
							 | 
						||
| 
								 | 
							
								// value if not set.
							 | 
						||
| 
								 | 
							
								func Gopath() string {
							 | 
						||
| 
								 | 
							
									if r := os.Getenv("GOPATH"); r != "" {
							 | 
						||
| 
								 | 
							
										return r
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// go1.8: https://github.com/golang/go/blob/74628a8b9f102bddd5078ee426efe0fd57033115/doc/code.html#L122
							 | 
						||
| 
								 | 
							
									switch runtime.GOOS {
							 | 
						||
| 
								 | 
							
									case "plan9":
							 | 
						||
| 
								 | 
							
										return os.Getenv("home")
							 | 
						||
| 
								 | 
							
									case "windows":
							 | 
						||
| 
								 | 
							
										return filepath.Join(os.Getenv("USERPROFILE"), "go")
							 | 
						||
| 
								 | 
							
									default:
							 | 
						||
| 
								 | 
							
										return filepath.Join(os.Getenv("HOME"), "go")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Homepath returns the user's home directory path.
							 | 
						||
| 
								 | 
							
								func Homepath() string {
							 | 
						||
| 
								 | 
							
									// go1.8: https://github.com/golang/go/blob/74628a8b9f102bddd5078ee426efe0fd57033115/doc/code.html#L122
							 | 
						||
| 
								 | 
							
									switch runtime.GOOS {
							 | 
						||
| 
								 | 
							
									case "plan9":
							 | 
						||
| 
								 | 
							
										return os.Getenv("home")
							 | 
						||
| 
								 | 
							
									case "windows":
							 | 
						||
| 
								 | 
							
										return os.Getenv("USERPROFILE")
							 | 
						||
| 
								 | 
							
									default:
							 | 
						||
| 
								 | 
							
										return os.Getenv("HOME")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ImportPath returns the import path of the caller or an error, if any.
							 | 
						||
| 
								 | 
							
								func ImportPath() (string, error) {
							 | 
						||
| 
								 | 
							
									_, file, _, ok := runtime.Caller(1)
							 | 
						||
| 
								 | 
							
									if !ok {
							 | 
						||
| 
								 | 
							
										return "", fmt.Errorf("runtime.Caller failed")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									gopath := Gopath()
							 | 
						||
| 
								 | 
							
									for _, v := range filepath.SplitList(gopath) {
							 | 
						||
| 
								 | 
							
										gp := filepath.Join(v, "src")
							 | 
						||
| 
								 | 
							
										path, err := filepath.Rel(gp, file)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return filepath.Dir(path), nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return "", fmt.Errorf("cannot determine import path using GOPATH=%s", gopath)
							 | 
						||
| 
								 | 
							
								}
							 |