gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

strutil.go (19778B)


      1 // Copyright (c) 2014 The sortutil Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package strutil collects utils supplemental to the standard strings package.
      6 package strutil // import "modernc.org/strutil"
      7 
      8 import (
      9 	"bytes"
     10 	"encoding/base32"
     11 	"encoding/base64"
     12 	"fmt"
     13 	"io"
     14 	"os"
     15 	"path/filepath"
     16 	"reflect"
     17 	"runtime"
     18 	"sort"
     19 	"strconv"
     20 	"strings"
     21 	"sync"
     22 )
     23 
     24 // Base32ExtDecode decodes base32 extended (RFC 4648) text to binary data.
     25 func Base32ExtDecode(text []byte) (data []byte, err error) {
     26 	n := base32.HexEncoding.DecodedLen(len(text))
     27 	data = make([]byte, n)
     28 	decoder := base32.NewDecoder(base32.HexEncoding, bytes.NewBuffer(text))
     29 	if n, err = decoder.Read(data); err != nil {
     30 		n = 0
     31 	}
     32 	data = data[:n]
     33 	return
     34 }
     35 
     36 // Base32ExtEncode encodes binary data to base32 extended (RFC 4648) encoded text.
     37 func Base32ExtEncode(data []byte) (text []byte) {
     38 	n := base32.HexEncoding.EncodedLen(len(data))
     39 	buf := bytes.NewBuffer(make([]byte, 0, n))
     40 	encoder := base32.NewEncoder(base32.HexEncoding, buf)
     41 	encoder.Write(data)
     42 	encoder.Close()
     43 	if buf.Len() != n {
     44 		panic("internal error")
     45 	}
     46 	return buf.Bytes()
     47 }
     48 
     49 // Base64Decode decodes base64 text to binary data.
     50 func Base64Decode(text []byte) (data []byte, err error) {
     51 	n := base64.StdEncoding.DecodedLen(len(text))
     52 	data = make([]byte, n)
     53 	decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(text))
     54 	if n, err = decoder.Read(data); err != nil {
     55 		n = 0
     56 	}
     57 	data = data[:n]
     58 	return
     59 }
     60 
     61 // Base64Encode encodes binary data to base64 encoded text.
     62 func Base64Encode(data []byte) (text []byte) {
     63 	n := base64.StdEncoding.EncodedLen(len(data))
     64 	buf := bytes.NewBuffer(make([]byte, 0, n))
     65 	encoder := base64.NewEncoder(base64.StdEncoding, buf)
     66 	encoder.Write(data)
     67 	encoder.Close()
     68 	if buf.Len() != n {
     69 		panic("internal error")
     70 	}
     71 	return buf.Bytes()
     72 }
     73 
     74 // Formatter is an io.Writer extended by a fmt.Printf like function Format
     75 type Formatter interface {
     76 	io.Writer
     77 	Format(format string, args ...interface{}) (n int, errno error)
     78 }
     79 
     80 type indentFormatter struct {
     81 	io.Writer
     82 	indent      []byte
     83 	indentLevel int
     84 	state       int
     85 }
     86 
     87 const (
     88 	st0 = iota
     89 	stBOL
     90 	stPERC
     91 	stBOLPERC
     92 )
     93 
     94 // IndentFormatter returns a new Formatter which interprets %i and %u in the
     95 // Format() format string as indent and undent commands. The commands can
     96 // nest. The Formatter writes to io.Writer 'w' and inserts one 'indent'
     97 // string per current indent level value.
     98 // Behaviour of commands reaching negative indent levels is undefined.
     99 //	IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
    100 // output:
    101 //	abc3%e
    102 //		x
    103 //		y
    104 //	z
    105 // The Go quoted string literal form of the above is:
    106 //	"abc%%e\n\tx\n\tx\nz\n"
    107 // The commands can be scattered between separate invocations of Format(),
    108 // i.e. the formatter keeps track of the indent level and knows if it is
    109 // positioned on start of a line and should emit indentation(s).
    110 // The same output as above can be produced by e.g.:
    111 //	f := IndentFormatter(os.Stdout, " ")
    112 //	f.Format("abc%d%%e%i\nx\n", 3)
    113 //	f.Format("y\n%uz\n")
    114 func IndentFormatter(w io.Writer, indent string) Formatter {
    115 	return &indentFormatter{w, []byte(indent), 0, stBOL}
    116 }
    117 
    118 func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) {
    119 	buf := []byte{}
    120 	for i := 0; i < len(format); i++ {
    121 		c := format[i]
    122 		switch f.state {
    123 		case st0:
    124 			switch c {
    125 			case '\n':
    126 				cc := c
    127 				if flat && f.indentLevel != 0 {
    128 					cc = ' '
    129 				}
    130 				buf = append(buf, cc)
    131 				f.state = stBOL
    132 			case '%':
    133 				f.state = stPERC
    134 			default:
    135 				buf = append(buf, c)
    136 			}
    137 		case stBOL:
    138 			switch c {
    139 			case '\n':
    140 				cc := c
    141 				if flat && f.indentLevel != 0 {
    142 					cc = ' '
    143 				}
    144 				buf = append(buf, cc)
    145 			case '%':
    146 				f.state = stBOLPERC
    147 			default:
    148 				if !flat {
    149 					for i := 0; i < f.indentLevel; i++ {
    150 						buf = append(buf, f.indent...)
    151 					}
    152 				}
    153 				buf = append(buf, c)
    154 				f.state = st0
    155 			}
    156 		case stBOLPERC:
    157 			switch c {
    158 			case 'i':
    159 				f.indentLevel++
    160 				f.state = stBOL
    161 			case 'u':
    162 				f.indentLevel--
    163 				f.state = stBOL
    164 			default:
    165 				if !flat {
    166 					for i := 0; i < f.indentLevel; i++ {
    167 						buf = append(buf, f.indent...)
    168 					}
    169 				}
    170 				buf = append(buf, '%', c)
    171 				f.state = st0
    172 			}
    173 		case stPERC:
    174 			switch c {
    175 			case 'i':
    176 				f.indentLevel++
    177 				f.state = st0
    178 			case 'u':
    179 				f.indentLevel--
    180 				f.state = st0
    181 			default:
    182 				buf = append(buf, '%', c)
    183 				f.state = st0
    184 			}
    185 		default:
    186 			panic("unexpected state")
    187 		}
    188 	}
    189 	switch f.state {
    190 	case stPERC, stBOLPERC:
    191 		buf = append(buf, '%')
    192 	}
    193 	return f.Write([]byte(fmt.Sprintf(string(buf), args...)))
    194 }
    195 
    196 func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) {
    197 	return f.format(false, format, args...)
    198 }
    199 
    200 type flatFormatter indentFormatter
    201 
    202 // FlatFormatter returns a newly created Formatter with the same functionality as the one returned
    203 // by IndentFormatter except it allows a newline in the 'format' string argument of Format
    204 // to pass through iff indent level is currently zero.
    205 //
    206 // If indent level is non-zero then such new lines are changed to a space character.
    207 // There is no indent string, the %i and %u format verbs are used solely to determine the indent level.
    208 //
    209 // The FlatFormatter is intended for flattening of normally nested structure textual representation to
    210 // a one top level structure per line form.
    211 //	FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
    212 // output in the form of a Go quoted string literal:
    213 //	"abc3%%e x y z\n"
    214 func FlatFormatter(w io.Writer) Formatter {
    215 	return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter))
    216 }
    217 
    218 func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) {
    219 	return (*indentFormatter)(f).format(true, format, args...)
    220 }
    221 
    222 // Pool handles aligning of strings having equal values to the same string instance.
    223 // Intended use is to conserve some memory e.g. where a large number of identically valued strings
    224 // with non identical backing arrays may exists in several semantically distinct instances of some structs.
    225 // Pool is *not* concurrent access safe. It doesn't handle common prefix/suffix aligning,
    226 // e.g. having s1 == "abc" and s2 == "bc", s2 is not automatically aligned as s1[1:].
    227 type Pool struct {
    228 	pool map[string]string
    229 }
    230 
    231 // NewPool returns a newly created Pool.
    232 func NewPool() *Pool {
    233 	return &Pool{map[string]string{}}
    234 }
    235 
    236 // Align returns a string with the same value as its argument. It guarantees that
    237 // all aligned strings share a single instance in memory.
    238 func (p *Pool) Align(s string) string {
    239 	if a, ok := p.pool[s]; ok {
    240 		return a
    241 	}
    242 
    243 	s = StrPack(s)
    244 	p.pool[s] = s
    245 	return s
    246 }
    247 
    248 // Count returns the number of items in the pool.
    249 func (p *Pool) Count() int {
    250 	return len(p.pool)
    251 }
    252 
    253 // GoPool is a concurrent access safe version of Pool.
    254 type GoPool struct {
    255 	pool map[string]string
    256 	rwm  *sync.RWMutex
    257 }
    258 
    259 // NewGoPool returns a newly created GoPool.
    260 func NewGoPool() (p *GoPool) {
    261 	return &GoPool{map[string]string{}, &sync.RWMutex{}}
    262 }
    263 
    264 // Align returns a string with the same value as its argument. It guarantees that
    265 // all aligned strings share a single instance in memory.
    266 func (p *GoPool) Align(s string) (y string) {
    267 	if s != "" {
    268 		p.rwm.RLock()               // R++
    269 		if a, ok := p.pool[s]; ok { // found
    270 			p.rwm.RUnlock() // R--
    271 			return a
    272 		}
    273 
    274 		p.rwm.RUnlock() // R--
    275 		// not found but with a race condition, retry within a write lock
    276 		p.rwm.Lock()                // W++
    277 		defer p.rwm.Unlock()        // W--
    278 		if a, ok := p.pool[s]; ok { // done in a race
    279 			return a
    280 		}
    281 
    282 		// we won
    283 		s = StrPack(s)
    284 		p.pool[s] = s
    285 		return s
    286 	}
    287 
    288 	return
    289 }
    290 
    291 // Count returns the number of items in the pool.
    292 func (p *GoPool) Count() int {
    293 	return len(p.pool)
    294 }
    295 
    296 // Dict is a string <-> id bijection. Dict is *not* concurrent access safe for assigning new ids
    297 // to strings not yet contained in the bijection.
    298 // Id for an empty string is guaranteed to be 0,
    299 // thus Id for any non empty string is guaranteed to be non zero.
    300 type Dict struct {
    301 	si map[string]int
    302 	is []string
    303 }
    304 
    305 // NewDict returns a newly created Dict.
    306 func NewDict() (d *Dict) {
    307 	d = &Dict{map[string]int{}, []string{}}
    308 	d.Id("")
    309 	return
    310 }
    311 
    312 // Count returns the number of items in the dict.
    313 func (d *Dict) Count() int {
    314 	return len(d.is)
    315 }
    316 
    317 // Id maps string s to its numeric identificator.
    318 func (d *Dict) Id(s string) (y int) {
    319 	if y, ok := d.si[s]; ok {
    320 		return y
    321 	}
    322 
    323 	s = StrPack(s)
    324 	y = len(d.is)
    325 	d.si[s] = y
    326 	d.is = append(d.is, s)
    327 	return
    328 }
    329 
    330 // S maps an id to its string value and ok == true. Id values not contained in the bijection
    331 // return "", false.
    332 func (d *Dict) S(id int) (s string, ok bool) {
    333 	if id >= len(d.is) {
    334 		return "", false
    335 	}
    336 	return d.is[id], true
    337 }
    338 
    339 // GoDict is a concurrent access safe version of Dict.
    340 type GoDict struct {
    341 	si  map[string]int
    342 	is  []string
    343 	rwm *sync.RWMutex
    344 }
    345 
    346 // NewGoDict returns a newly created GoDict.
    347 func NewGoDict() (d *GoDict) {
    348 	d = &GoDict{map[string]int{}, []string{}, &sync.RWMutex{}}
    349 	d.Id("")
    350 	return
    351 }
    352 
    353 // Count returns the number of items in the dict.
    354 func (d *GoDict) Count() int {
    355 	return len(d.is)
    356 }
    357 
    358 // Id maps string s to its numeric identificator. The implementation honors getting
    359 // an existing id at the cost of assigning a new one.
    360 func (d *GoDict) Id(s string) (y int) {
    361 	d.rwm.RLock()             // R++
    362 	if y, ok := d.si[s]; ok { // found
    363 		d.rwm.RUnlock() // R--
    364 		return y
    365 	}
    366 
    367 	d.rwm.RUnlock() // R--
    368 
    369 	// not found but with a race condition
    370 	d.rwm.Lock()              // W++ recheck with write lock
    371 	defer d.rwm.Unlock()      // W--
    372 	if y, ok := d.si[s]; ok { // some other goroutine won already
    373 		return y
    374 	}
    375 
    376 	// a race free not found state => insert the string
    377 	s = StrPack(s)
    378 	y = len(d.is)
    379 	d.si[s] = y
    380 	d.is = append(d.is, s)
    381 	return
    382 }
    383 
    384 // S maps an id to its string value and ok == true. Id values not contained in the bijection
    385 // return "", false.
    386 func (d *GoDict) S(id int) (s string, ok bool) {
    387 	d.rwm.RLock()         // R++
    388 	defer d.rwm.RUnlock() // R--
    389 	if id >= len(d.is) {
    390 		return "", false
    391 	}
    392 	return d.is[id], true
    393 }
    394 
    395 // StrPack returns a new instance of s which is tightly packed in memory.
    396 // It is intended for avoiding the situation where having a live reference
    397 // to a string slice over an unreferenced biger underlying string keeps the biger one
    398 // in memory anyway - it can't be GCed.
    399 func StrPack(s string) string {
    400 	return string([]byte(s)) // T(U(T)) intentional.
    401 }
    402 
    403 // JoinFields returns strings in flds joined by sep. Flds may contain arbitrary
    404 // bytes, including the sep as they are safely escaped. JoinFields panics if
    405 // sep is the backslash character or if len(sep) != 1.
    406 func JoinFields(flds []string, sep string) string {
    407 	if len(sep) != 1 || sep == "\\" {
    408 		panic("invalid separator")
    409 	}
    410 
    411 	a := make([]string, len(flds))
    412 	for i, v := range flds {
    413 		v = strings.Replace(v, "\\", "\\0", -1)
    414 		a[i] = strings.Replace(v, sep, "\\1", -1)
    415 	}
    416 	return strings.Join(a, sep)
    417 }
    418 
    419 // SplitFields splits s, which must be produced by JoinFields using the same
    420 // sep, into flds.  SplitFields panics if sep is the backslash character or if
    421 // len(sep) != 1.
    422 func SplitFields(s, sep string) (flds []string) {
    423 	if len(sep) != 1 || sep == "\\" {
    424 		panic("invalid separator")
    425 	}
    426 
    427 	a := strings.Split(s, sep)
    428 	r := make([]string, len(a))
    429 	for i, v := range a {
    430 		v = strings.Replace(v, "\\1", sep, -1)
    431 		r[i] = strings.Replace(v, "\\0", "\\", -1)
    432 	}
    433 	return r
    434 }
    435 
    436 // PrettyPrintHooks allow to customize the result of PrettyPrint for types
    437 // listed in the map value.
    438 type PrettyPrintHooks map[reflect.Type]func(f Formatter, v interface{}, prefix, suffix string)
    439 
    440 // PrettyString returns the output of PrettyPrint as a string.
    441 func PrettyString(v interface{}, prefix, suffix string, hooks PrettyPrintHooks) string {
    442 	var b bytes.Buffer
    443 	PrettyPrint(&b, v, prefix, suffix, hooks)
    444 	return b.String()
    445 }
    446 
    447 // PrettyPrint pretty prints v to w. Zero values and unexported struct fields
    448 // are omitted.
    449 //
    450 // Force printing of zero values of struct fields by including in the field tag
    451 // PrettyPrint:"zero".
    452 //
    453 // Enable using a String method, if any, of a struct field type by including in
    454 // the field tag PrettyPrint:"stringer".
    455 //
    456 // The tags can be combined as in PrettyPrint:"zero,stringer". The order is not
    457 // important, so PrettyPrint:stringer,zero has the same effect.
    458 //
    459 // A hook attached to the field type has priority over the struct field tag
    460 // described above.
    461 func PrettyPrint(w io.Writer, v interface{}, prefix, suffix string, hooks PrettyPrintHooks) {
    462 	if v == nil {
    463 		return
    464 	}
    465 
    466 	f := IndentFormatter(w, "· ")
    467 
    468 	defer func() {
    469 		if e := recover(); e != nil {
    470 			f.Format("\npanic: %v", e)
    471 		}
    472 	}()
    473 
    474 	prettyPrint(nil, f, prefix, suffix, v, hooks, false, false)
    475 }
    476 
    477 func prettyPrint(protect map[interface{}]struct{}, sf Formatter, prefix, suffix string, v interface{}, hooks PrettyPrintHooks, zero, stringer bool) {
    478 	if v == nil {
    479 		return
    480 	}
    481 
    482 	rt := reflect.TypeOf(v)
    483 	if handler := hooks[rt]; handler != nil {
    484 		handler(sf, v, prefix, suffix)
    485 		return
    486 	}
    487 
    488 	rv := reflect.ValueOf(v)
    489 	if stringer {
    490 		if _, ok := v.(fmt.Stringer); ok {
    491 			sf.Format("%s%s", prefix, v)
    492 			sf.Format(suffix)
    493 			return
    494 		}
    495 	}
    496 
    497 	switch rt.Kind() {
    498 	case reflect.Slice:
    499 		if rv.Len() == 0 && !zero {
    500 			return
    501 		}
    502 
    503 		sf.Format("%s[]%T{ // len %d%i\n", prefix, rv.Index(0).Interface(), rv.Len())
    504 		for i := 0; i < rv.Len(); i++ {
    505 			prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks, false, false)
    506 		}
    507 		suffix = strings.Replace(suffix, "%", "%%", -1)
    508 		sf.Format("%u}" + suffix)
    509 	case reflect.Array:
    510 		if reflect.Zero(rt).Interface() == rv.Interface() && !zero {
    511 			return
    512 		}
    513 
    514 		sf.Format("%s[%d]%T{%i\n", prefix, rv.Len(), rv.Index(0).Interface())
    515 		for i := 0; i < rv.Len(); i++ {
    516 			prettyPrint(protect, sf, fmt.Sprintf("%d: ", i), ",\n", rv.Index(i).Interface(), hooks, false, false)
    517 		}
    518 		suffix = strings.Replace(suffix, "%", "%%", -1)
    519 		sf.Format("%u}" + suffix)
    520 	case reflect.Struct:
    521 		if rt.NumField() == 0 {
    522 			return
    523 		}
    524 
    525 		if reflect.DeepEqual(reflect.Zero(rt).Interface(), rv.Interface()) && !zero {
    526 			return
    527 		}
    528 
    529 		sf.Format("%s%T{%i\n", prefix, v)
    530 		for i := 0; i < rt.NumField(); i++ {
    531 			f := rv.Field(i)
    532 			if !f.CanInterface() {
    533 				continue
    534 			}
    535 
    536 			var stringer, zero bool
    537 			ft := rt.Field(i)
    538 			if tag, ok := ft.Tag.Lookup("PrettyPrint"); ok {
    539 				a := strings.Split(tag, ",")
    540 				for _, v := range a {
    541 					switch strings.TrimSpace(v) {
    542 					case "stringer":
    543 						stringer = true
    544 					case "zero":
    545 						zero = true
    546 					}
    547 				}
    548 			}
    549 			prettyPrint(protect, sf, fmt.Sprintf("%s: ", rt.Field(i).Name), ",\n", f.Interface(), hooks, zero, stringer)
    550 		}
    551 		suffix = strings.Replace(suffix, "%", "%%", -1)
    552 		sf.Format("%u}" + suffix)
    553 	case reflect.Ptr:
    554 		if rv.IsNil() && !zero {
    555 			return
    556 		}
    557 
    558 		rvi := rv.Interface()
    559 		if _, ok := protect[rvi]; ok {
    560 			suffix = strings.Replace(suffix, "%", "%%", -1)
    561 			sf.Format("%s&%T{ /* recursive/repetitive pointee not shown */ }"+suffix, prefix, rv.Elem().Interface())
    562 			return
    563 		}
    564 
    565 		if protect == nil {
    566 			protect = map[interface{}]struct{}{}
    567 		}
    568 		protect[rvi] = struct{}{}
    569 		prettyPrint(protect, sf, prefix+"&", suffix, rv.Elem().Interface(), hooks, false, false)
    570 	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
    571 		if v := rv.Int(); v != 0 || zero {
    572 			suffix = strings.Replace(suffix, "%", "%%", -1)
    573 			sf.Format("%s%v"+suffix, prefix, v)
    574 		}
    575 	case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8:
    576 		if v := rv.Uint(); v != 0 || zero {
    577 			suffix = strings.Replace(suffix, "%", "%%", -1)
    578 			sf.Format("%s%v"+suffix, prefix, v)
    579 		}
    580 	case reflect.Float32, reflect.Float64:
    581 		if v := rv.Float(); v != 0 || zero {
    582 			suffix = strings.Replace(suffix, "%", "%%", -1)
    583 			sf.Format("%s%v"+suffix, prefix, v)
    584 		}
    585 	case reflect.Complex64, reflect.Complex128:
    586 		if v := rv.Complex(); v != 0 || zero {
    587 			suffix = strings.Replace(suffix, "%", "%%", -1)
    588 			sf.Format("%s%v"+suffix, prefix, v)
    589 		}
    590 	case reflect.Uintptr:
    591 		if v := rv.Uint(); v != 0 || zero {
    592 			suffix = strings.Replace(suffix, "%", "%%", -1)
    593 			sf.Format("%s%v"+suffix, prefix, v)
    594 		}
    595 	case reflect.UnsafePointer:
    596 		s := fmt.Sprintf("%p", rv.Interface())
    597 		if s == "0x0" && !zero {
    598 			return
    599 		}
    600 
    601 		suffix = strings.Replace(suffix, "%", "%%", -1)
    602 		sf.Format("%s%s"+suffix, prefix, s)
    603 	case reflect.Bool:
    604 		if v := rv.Bool(); v || zero {
    605 			suffix = strings.Replace(suffix, "%", "%%", -1)
    606 			sf.Format("%s%v"+suffix, prefix, rv.Bool())
    607 		}
    608 	case reflect.String:
    609 		s := rv.Interface().(string)
    610 		if s == "" && !zero {
    611 			return
    612 		}
    613 
    614 		suffix = strings.Replace(suffix, "%", "%%", -1)
    615 		sf.Format("%s%q"+suffix, prefix, s)
    616 	case reflect.Chan:
    617 		if reflect.Zero(rt).Interface() == rv.Interface() && !zero {
    618 			return
    619 		}
    620 
    621 		c := rv.Cap()
    622 		s := ""
    623 		if c != 0 {
    624 			s = fmt.Sprintf("// capacity: %d", c)
    625 		}
    626 		suffix = strings.Replace(suffix, "%", "%%", -1)
    627 		sf.Format("%s%s %s%s"+suffix, prefix, rt.ChanDir(), rt.Elem().Name(), s)
    628 	case reflect.Func:
    629 		if rv.IsNil() && !zero {
    630 			return
    631 		}
    632 
    633 		var in, out []string
    634 		for i := 0; i < rt.NumIn(); i++ {
    635 			x := reflect.Zero(rt.In(i))
    636 			in = append(in, fmt.Sprintf("%T", x.Interface()))
    637 		}
    638 		if rt.IsVariadic() {
    639 			i := len(in) - 1
    640 			in[i] = "..." + in[i][2:]
    641 		}
    642 		for i := 0; i < rt.NumOut(); i++ {
    643 			out = append(out, rt.Out(i).Name())
    644 		}
    645 		s := "(" + strings.Join(in, ", ") + ")"
    646 		t := strings.Join(out, ", ")
    647 		if len(out) > 1 {
    648 			t = "(" + t + ")"
    649 		}
    650 		if t != "" {
    651 			t = " " + t
    652 		}
    653 		suffix = strings.Replace(suffix, "%", "%%", -1)
    654 		sf.Format("%sfunc%s%s { ... }"+suffix, prefix, s, t)
    655 	case reflect.Map:
    656 		keys := rv.MapKeys()
    657 		if len(keys) == 0 && !zero {
    658 			return
    659 		}
    660 
    661 		var buf bytes.Buffer
    662 		nf := IndentFormatter(&buf, "· ")
    663 		var skeys []string
    664 		for i, k := range keys {
    665 			prettyPrint(protect, nf, "", "", k.Interface(), hooks, false, false)
    666 			skeys = append(skeys, fmt.Sprintf("%s%10d", buf.Bytes(), i))
    667 			buf.Reset()
    668 		}
    669 		sort.Strings(skeys)
    670 		sf.Format("%s%T{%i\n", prefix, v)
    671 		for _, k := range skeys {
    672 			si := strings.TrimSpace(k[len(k)-10:])
    673 			k = k[:len(k)-10]
    674 			n, _ := strconv.ParseUint(si, 10, 64)
    675 			mv := rv.MapIndex(keys[n])
    676 			prettyPrint(protect, sf, fmt.Sprintf("%s: ", k), ",\n", mv.Interface(), hooks, false, false)
    677 		}
    678 		suffix = strings.Replace(suffix, "%", "%%", -1)
    679 		sf.Format("%u}" + suffix)
    680 	}
    681 }
    682 
    683 // Gopath returns the value of the $GOPATH environment variable or its default
    684 // value if not set.
    685 func Gopath() string {
    686 	if r := os.Getenv("GOPATH"); r != "" {
    687 		return r
    688 	}
    689 
    690 	// go1.8: https://github.com/golang/go/blob/74628a8b9f102bddd5078ee426efe0fd57033115/doc/code.html#L122
    691 	switch runtime.GOOS {
    692 	case "plan9":
    693 		return os.Getenv("home")
    694 	case "windows":
    695 		return filepath.Join(os.Getenv("USERPROFILE"), "go")
    696 	default:
    697 		return filepath.Join(os.Getenv("HOME"), "go")
    698 	}
    699 }
    700 
    701 // Homepath returns the user's home directory path.
    702 func Homepath() string {
    703 	// go1.8: https://github.com/golang/go/blob/74628a8b9f102bddd5078ee426efe0fd57033115/doc/code.html#L122
    704 	switch runtime.GOOS {
    705 	case "plan9":
    706 		return os.Getenv("home")
    707 	case "windows":
    708 		return os.Getenv("USERPROFILE")
    709 	default:
    710 		return os.Getenv("HOME")
    711 	}
    712 }
    713 
    714 // ImportPath returns the import path of the caller or an error, if any.
    715 func ImportPath() (string, error) {
    716 	_, file, _, ok := runtime.Caller(1)
    717 	if !ok {
    718 		return "", fmt.Errorf("runtime.Caller failed")
    719 	}
    720 
    721 	gopath := Gopath()
    722 	for _, v := range filepath.SplitList(gopath) {
    723 		gp := filepath.Join(v, "src")
    724 		path, err := filepath.Rel(gp, file)
    725 		if err != nil {
    726 			continue
    727 		}
    728 
    729 		return filepath.Dir(path), nil
    730 	}
    731 
    732 	return "", fmt.Errorf("cannot determine import path using GOPATH=%s", gopath)
    733 }