gitee.com/johng/gf@v1.4.7/third/gopkg.in/yaml.v2/resolve.go (about)

     1  package yaml
     2  
     3  import (
     4  	"encoding/base64"
     5  	"math"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  	"unicode/utf8"
    10  )
    11  
    12  type resolveMapItem struct {
    13  	value interface{}
    14  	tag   string
    15  }
    16  
    17  var resolveTable = make([]byte, 256)
    18  var resolveMap = make(map[string]resolveMapItem)
    19  
    20  func init() {
    21  	t := resolveTable
    22  	t[int('+')] = 'S' // Sign
    23  	t[int('-')] = 'S'
    24  	for _, c := range "0123456789" {
    25  		t[int(c)] = 'D' // Digit
    26  	}
    27  	for _, c := range "yYnNtTfFoO~" {
    28  		t[int(c)] = 'M' // In map
    29  	}
    30  	t[int('.')] = '.' // Float (potentially in map)
    31  
    32  	var resolveMapList = []struct {
    33  		v   interface{}
    34  		tag string
    35  		l   []string
    36  	}{
    37  		{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
    38  		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
    39  		{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
    40  		{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
    41  		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
    42  		{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
    43  		{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
    44  		{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
    45  		{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
    46  		{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
    47  		{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
    48  		{"<<", yaml_MERGE_TAG, []string{"<<"}},
    49  	}
    50  
    51  	m := resolveMap
    52  	for _, item := range resolveMapList {
    53  		for _, s := range item.l {
    54  			m[s] = resolveMapItem{item.v, item.tag}
    55  		}
    56  	}
    57  }
    58  
    59  const longTagPrefix = "tag:yaml.org,2002:"
    60  
    61  func shortTag(tag string) string {
    62  	// TODO This can easily be made faster and produce less garbage.
    63  	if strings.HasPrefix(tag, longTagPrefix) {
    64  		return "!!" + tag[len(longTagPrefix):]
    65  	}
    66  	return tag
    67  }
    68  
    69  func longTag(tag string) string {
    70  	if strings.HasPrefix(tag, "!!") {
    71  		return longTagPrefix + tag[2:]
    72  	}
    73  	return tag
    74  }
    75  
    76  func resolvableTag(tag string) bool {
    77  	switch tag {
    78  	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
    79  		return true
    80  	}
    81  	return false
    82  }
    83  
    84  var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`)
    85  
    86  func resolve(tag string, in string) (rtag string, out interface{}) {
    87  	if !resolvableTag(tag) {
    88  		return tag, in
    89  	}
    90  
    91  	defer func() {
    92  		switch tag {
    93  		case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
    94  			return
    95  		}
    96  		failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
    97  	}()
    98  
    99  	// Any data is accepted as a !!str or !!binary.
   100  	// Otherwise, the prefix is enough of a hint about what it might be.
   101  	hint := byte('N')
   102  	if in != "" {
   103  		hint = resolveTable[in[0]]
   104  	}
   105  	if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
   106  		// Handle things we can lookup in a map.
   107  		if item, ok := resolveMap[in]; ok {
   108  			return item.tag, item.value
   109  		}
   110  
   111  		// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
   112  		// are purposefully unsupported here. They're still quoted on
   113  		// the way out for compatibility with other parser, though.
   114  
   115  		switch hint {
   116  		case 'M':
   117  			// We've already checked the map above.
   118  
   119  		case '.':
   120  			// Not in the map, so maybe a normal float.
   121  			floatv, err := strconv.ParseFloat(in, 64)
   122  			if err == nil {
   123  				return yaml_FLOAT_TAG, floatv
   124  			}
   125  
   126  		case 'D', 'S':
   127  			// Int, float, or timestamp.
   128  			plain := strings.Replace(in, "_", "", -1)
   129  			intv, err := strconv.ParseInt(plain, 0, 64)
   130  			if err == nil {
   131  				if intv == int64(int(intv)) {
   132  					return yaml_INT_TAG, int(intv)
   133  				} else {
   134  					return yaml_INT_TAG, intv
   135  				}
   136  			}
   137  			uintv, err := strconv.ParseUint(plain, 0, 64)
   138  			if err == nil {
   139  				return yaml_INT_TAG, uintv
   140  			}
   141  			if yamlStyleFloat.MatchString(plain) {
   142  				floatv, err := strconv.ParseFloat(plain, 64)
   143  				if err == nil {
   144  					return yaml_FLOAT_TAG, floatv
   145  				}
   146  			}
   147  			if strings.HasPrefix(plain, "0b") {
   148  				intv, err := strconv.ParseInt(plain[2:], 2, 64)
   149  				if err == nil {
   150  					if intv == int64(int(intv)) {
   151  						return yaml_INT_TAG, int(intv)
   152  					} else {
   153  						return yaml_INT_TAG, intv
   154  					}
   155  				}
   156  				uintv, err := strconv.ParseUint(plain[2:], 2, 64)
   157  				if err == nil {
   158  					return yaml_INT_TAG, uintv
   159  				}
   160  			} else if strings.HasPrefix(plain, "-0b") {
   161  				intv, err := strconv.ParseInt(plain[3:], 2, 64)
   162  				if err == nil {
   163  					if intv == int64(int(intv)) {
   164  						return yaml_INT_TAG, -int(intv)
   165  					} else {
   166  						return yaml_INT_TAG, -intv
   167  					}
   168  				}
   169  			}
   170  			// XXX Handle timestamps here.
   171  
   172  		default:
   173  			panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
   174  		}
   175  	}
   176  	if tag == yaml_BINARY_TAG {
   177  		return yaml_BINARY_TAG, in
   178  	}
   179  	if utf8.ValidString(in) {
   180  		return yaml_STR_TAG, in
   181  	}
   182  	return yaml_BINARY_TAG, encodeBase64(in)
   183  }
   184  
   185  // encodeBase64 encodes s as base64 that is broken up into multiple lines
   186  // as appropriate for the resulting length.
   187  func encodeBase64(s string) string {
   188  	const lineLen = 70
   189  	encLen := base64.StdEncoding.EncodedLen(len(s))
   190  	lines := encLen/lineLen + 1
   191  	buf := make([]byte, encLen*2+lines)
   192  	in := buf[0:encLen]
   193  	out := buf[encLen:]
   194  	base64.StdEncoding.Encode(in, []byte(s))
   195  	k := 0
   196  	for i := 0; i < len(in); i += lineLen {
   197  		j := i + lineLen
   198  		if j > len(in) {
   199  			j = len(in)
   200  		}
   201  		k += copy(out[k:], in[i:j])
   202  		if lines > 1 {
   203  			out[k] = '\n'
   204  			k++
   205  		}
   206  	}
   207  	return string(out[:k])
   208  }