github.com/ssube/gitlab-ci-multi-runner@v1.2.1-0.20160607142738-b8d1285632e6/Godeps/_workspace/src/gopkg.in/yaml.v1/resolve.go (about)

     1  package yaml
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"strings"
     9  	"unicode/utf8"
    10  )
    11  
    12  // TODO: merge, timestamps, base 60 floats, omap.
    13  
    14  type resolveMapItem struct {
    15  	value interface{}
    16  	tag   string
    17  }
    18  
    19  var resolveTable = make([]byte, 256)
    20  var resolveMap = make(map[string]resolveMapItem)
    21  
    22  func init() {
    23  	t := resolveTable
    24  	t[int('+')] = 'S' // Sign
    25  	t[int('-')] = 'S'
    26  	for _, c := range "0123456789" {
    27  		t[int(c)] = 'D' // Digit
    28  	}
    29  	for _, c := range "yYnNtTfFoO~" {
    30  		t[int(c)] = 'M' // In map
    31  	}
    32  	t[int('.')] = '.' // Float (potentially in map)
    33  
    34  	var resolveMapList = []struct {
    35  		v   interface{}
    36  		tag string
    37  		l   []string
    38  	}{
    39  		{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
    40  		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
    41  		{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
    42  		{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
    43  		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
    44  		{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
    45  		{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
    46  		{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
    47  		{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
    48  		{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
    49  		{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
    50  		{"<<", yaml_MERGE_TAG, []string{"<<"}},
    51  	}
    52  
    53  	m := resolveMap
    54  	for _, item := range resolveMapList {
    55  		for _, s := range item.l {
    56  			m[s] = resolveMapItem{item.v, item.tag}
    57  		}
    58  	}
    59  }
    60  
    61  const longTagPrefix = "tag:yaml.org,2002:"
    62  
    63  func shortTag(tag string) string {
    64  	// TODO This can easily be made faster and produce less garbage.
    65  	if strings.HasPrefix(tag, longTagPrefix) {
    66  		return "!!" + tag[len(longTagPrefix):]
    67  	}
    68  	return tag
    69  }
    70  
    71  func longTag(tag string) string {
    72  	if strings.HasPrefix(tag, "!!") {
    73  		return longTagPrefix + tag[2:]
    74  	}
    75  	return tag
    76  }
    77  
    78  func resolvableTag(tag string) bool {
    79  	switch tag {
    80  	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
    81  		return true
    82  	}
    83  	return false
    84  }
    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  		fail(fmt.Sprintf("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  			floatv, err := strconv.ParseFloat(plain, 64)
   138  			if err == nil {
   139  				return yaml_FLOAT_TAG, floatv
   140  			}
   141  			if strings.HasPrefix(plain, "0b") {
   142  				intv, err := strconv.ParseInt(plain[2:], 2, 64)
   143  				if err == nil {
   144  					return yaml_INT_TAG, int(intv)
   145  				}
   146  			} else if strings.HasPrefix(plain, "-0b") {
   147  				intv, err := strconv.ParseInt(plain[3:], 2, 64)
   148  				if err == nil {
   149  					return yaml_INT_TAG, -int(intv)
   150  				}
   151  			}
   152  			// XXX Handle timestamps here.
   153  
   154  		default:
   155  			panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
   156  		}
   157  	}
   158  	if tag == yaml_BINARY_TAG {
   159  		return yaml_BINARY_TAG, in
   160  	}
   161  	if utf8.ValidString(in) {
   162  		return yaml_STR_TAG, in
   163  	}
   164  	return yaml_BINARY_TAG, encodeBase64(in)
   165  }
   166  
   167  // encodeBase64 encodes s as base64 that is broken up into multiple lines
   168  // as appropriate for the resulting length.
   169  func encodeBase64(s string) string {
   170  	const lineLen = 70
   171  	encLen := base64.StdEncoding.EncodedLen(len(s))
   172  	lines := encLen/lineLen + 1
   173  	buf := make([]byte, encLen*2+lines)
   174  	in := buf[0:encLen]
   175  	out := buf[encLen:]
   176  	base64.StdEncoding.Encode(in, []byte(s))
   177  	k := 0
   178  	for i := 0; i < len(in); i += lineLen {
   179  		j := i + lineLen
   180  		if j > len(in) {
   181  			j = len(in)
   182  		}
   183  		k += copy(out[k:], in[i:j])
   184  		if lines > 1 {
   185  			out[k] = '\n'
   186  			k++
   187  		}
   188  	}
   189  	return string(out[:k])
   190  }