├── .github └── workflows │ └── go.yaml ├── LICENSE ├── NOTICE ├── README.md ├── apic.go ├── decode.go ├── decode_test.go ├── emitterc.go ├── encode.go ├── encode_test.go ├── example_embedded_test.go ├── go.mod ├── limit_test.go ├── node_test.go ├── parserc.go ├── readerc.go ├── resolve.go ├── scannerc.go ├── sorter.go ├── suite_test.go ├── writerc.go ├── yaml.go ├── yamlh.go └── yamlprivateh.go /.github/workflows/go.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Go 3 | on: [push, pull_request] 4 | jobs: 5 | test: 6 | name: Test 7 | runs-on: ubuntu-20.04 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | go: 12 | - "1.5" 13 | - "1.6" 14 | - "1.7" 15 | - "1.8" 16 | - "1.9" 17 | - "1.10" 18 | - "1.11" 19 | - "1.12" 20 | - "1.13" 21 | - "1.14" 22 | - "1.15" 23 | - "1.16.0-beta1" 24 | - "tip" 25 | env: 26 | GOPATH: ${{ github.workspace }}/go 27 | steps: 28 | - name: Check out code into the Go module directory 29 | uses: actions/checkout@v2 30 | with: 31 | path: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3 32 | - name: Set up Go ${{ matrix.go }} 33 | if: matrix.go != 'tip' 34 | uses: actions/setup-go@v2 35 | with: 36 | go-version: ${{ matrix.go }} 37 | stable: false 38 | - name: Set up Go ${{ matrix.go }} 39 | if: matrix.go == 'tip' 40 | run: | 41 | export GOROOT_BOOTSTRAP=`go env GOROOT` 42 | export GOROOT=$HOME/gotip 43 | mkdir $HOME/gotip 44 | cd $HOME/gotip 45 | 46 | curl -s 'https://go.googlesource.com/go/+/refs/heads/master?format=JSON' | awk '/"commit"/{print substr($2,2,40);exit}' >HEAD 47 | awk '{printf("gotip-%s",substr($0,0,7))}' VERSION 48 | 49 | curl -s -o go.tar.gz https://go.googlesource.com/go/+archive/`cat HEAD`.tar.gz 50 | tar xfz go.tar.gz 51 | 52 | cd src 53 | bash make.bash 54 | 55 | echo "GOROOT=$GOROOT" >> $GITHUB_ENV 56 | echo "$GOROOT/bin" >> $GITHUB_PATH 57 | - run: go version 58 | - run: go get -t ./... 59 | working-directory: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3 60 | - run: go test . 61 | working-directory: ${{ github.workspace }}/go/src/gopkg.in/yaml.v3 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | This project is covered by two different licenses: MIT and Apache. 3 | 4 | #### MIT License #### 5 | 6 | The following files were ported to Go from C files of libyaml, and thus 7 | are still covered by their original MIT license, with the additional 8 | copyright staring in 2011 when the project was ported over: 9 | 10 | apic.go emitterc.go parserc.go readerc.go scannerc.go 11 | writerc.go yamlh.go yamlprivateh.go 12 | 13 | Copyright (c) 2006-2010 Kirill Simonov 14 | Copyright (c) 2006-2011 Kirill Simonov 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of 17 | this software and associated documentation files (the "Software"), to deal in 18 | the Software without restriction, including without limitation the rights to 19 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 20 | of the Software, and to permit persons to whom the Software is furnished to do 21 | so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | 34 | ### Apache License ### 35 | 36 | All the remaining project files are covered by the Apache license: 37 | 38 | Copyright (c) 2011-2019 Canonical Ltd 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS PROJECT IS UNMAINTAINED 2 | 3 | This was one of my first Go projects, bootstapped over the christmas break of 2010 and well maintained for over a decade, often with help from contributors. Sadly, in the last few years my own free time, both personal and professional, became less common, and none of the contributions turned into more extensive long term engagements. I was hoping to address the situation by moving it into a dedicated professional team at a more resourceful home such as Canonical, Google, etc, but that hasn't materialized in time either. So I'm now taking the more explicit action of clearly labeling the project as unmaintained, to inform the community of what should already be obvious by now. 4 | 5 | There's still a chance I may come back and bring this project to where I wish it should go, but something else will have to change for this to be viable. Unfortunately, despite my own lack of time for the project, I cannot just "hand off" maintenance to an individual or to a small group either, due to the likelyhood of the project going back into an unmaintained, unstable, or even abused state. 6 | 7 | Sorry about that. 8 | 9 | _-- Gustavo Niemeyer_ 10 | 11 | # ORIGINAL README 12 | 13 | ## YAML support for the Go language 14 | 15 | Introduction 16 | ------------ 17 | 18 | The yaml package enables Go programs to comfortably encode and decode YAML 19 | values. It was developed within [Canonical](https://www.canonical.com) as 20 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 21 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 22 | C library to parse and generate YAML data quickly and reliably. 23 | 24 | Compatibility 25 | ------------- 26 | 27 | The yaml package supports most of YAML 1.2, but preserves some behavior 28 | from 1.1 for backwards compatibility. 29 | 30 | Specifically, as of v3 of the yaml package: 31 | 32 | - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being 33 | decoded into a typed bool value. Otherwise they behave as a string. Booleans 34 | in YAML 1.2 are _true/false_ only. 35 | - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ 36 | as specified in YAML 1.2, because most parsers still use the old format. 37 | Octals in the _0o777_ format are supported though, so new files work. 38 | - Does not support base-60 floats. These are gone from YAML 1.2, and were 39 | actually never supported by this package as it's clearly a poor choice. 40 | 41 | and offers backwards 42 | compatibility with YAML 1.1 in some cases. 43 | 1.2, including support for 44 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 45 | implemented, and base-60 floats from YAML 1.1 are purposefully not 46 | supported since they're a poor design and are gone in YAML 1.2. 47 | 48 | Installation and usage 49 | ---------------------- 50 | 51 | The import path for the package is *gopkg.in/yaml.v3*. 52 | 53 | To install it, run: 54 | 55 | go get gopkg.in/yaml.v3 56 | 57 | API documentation 58 | ----------------- 59 | 60 | If opened in a browser, the import path itself leads to the API documentation: 61 | 62 | - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) 63 | 64 | API stability 65 | ------------- 66 | 67 | The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). 68 | 69 | 70 | License 71 | ------- 72 | 73 | The yaml package is licensed under the MIT and Apache License 2.0 licenses. 74 | Please see the LICENSE file for details. 75 | 76 | 77 | Example 78 | ------- 79 | 80 | ```Go 81 | package main 82 | 83 | import ( 84 | "fmt" 85 | "log" 86 | 87 | "gopkg.in/yaml.v3" 88 | ) 89 | 90 | var data = ` 91 | a: Easy! 92 | b: 93 | c: 2 94 | d: [3, 4] 95 | ` 96 | 97 | // Note: struct fields must be public in order for unmarshal to 98 | // correctly populate the data. 99 | type T struct { 100 | A string 101 | B struct { 102 | RenamedC int `yaml:"c"` 103 | D []int `yaml:",flow"` 104 | } 105 | } 106 | 107 | func main() { 108 | t := T{} 109 | 110 | err := yaml.Unmarshal([]byte(data), &t) 111 | if err != nil { 112 | log.Fatalf("error: %v", err) 113 | } 114 | fmt.Printf("--- t:\n%v\n\n", t) 115 | 116 | d, err := yaml.Marshal(&t) 117 | if err != nil { 118 | log.Fatalf("error: %v", err) 119 | } 120 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 121 | 122 | m := make(map[interface{}]interface{}) 123 | 124 | err = yaml.Unmarshal([]byte(data), &m) 125 | if err != nil { 126 | log.Fatalf("error: %v", err) 127 | } 128 | fmt.Printf("--- m:\n%v\n\n", m) 129 | 130 | d, err = yaml.Marshal(&m) 131 | if err != nil { 132 | log.Fatalf("error: %v", err) 133 | } 134 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 135 | } 136 | ``` 137 | 138 | This example will generate the following output: 139 | 140 | ``` 141 | --- t: 142 | {Easy! {2 [3 4]}} 143 | 144 | --- t dump: 145 | a: Easy! 146 | b: 147 | c: 2 148 | d: [3, 4] 149 | 150 | 151 | --- m: 152 | map[a:Easy! b:map[c:2 d:[3 4]]] 153 | 154 | --- m dump: 155 | a: Easy! 156 | b: 157 | c: 2 158 | d: 159 | - 3 160 | - 4 161 | ``` 162 | 163 | -------------------------------------------------------------------------------- /apic.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | import ( 26 | "io" 27 | ) 28 | 29 | func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { 30 | //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) 31 | 32 | // Check if we can move the queue at the beginning of the buffer. 33 | if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { 34 | if parser.tokens_head != len(parser.tokens) { 35 | copy(parser.tokens, parser.tokens[parser.tokens_head:]) 36 | } 37 | parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] 38 | parser.tokens_head = 0 39 | } 40 | parser.tokens = append(parser.tokens, *token) 41 | if pos < 0 { 42 | return 43 | } 44 | copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) 45 | parser.tokens[parser.tokens_head+pos] = *token 46 | } 47 | 48 | // Create a new parser object. 49 | func yaml_parser_initialize(parser *yaml_parser_t) bool { 50 | *parser = yaml_parser_t{ 51 | raw_buffer: make([]byte, 0, input_raw_buffer_size), 52 | buffer: make([]byte, 0, input_buffer_size), 53 | } 54 | return true 55 | } 56 | 57 | // Destroy a parser object. 58 | func yaml_parser_delete(parser *yaml_parser_t) { 59 | *parser = yaml_parser_t{} 60 | } 61 | 62 | // String read handler. 63 | func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { 64 | if parser.input_pos == len(parser.input) { 65 | return 0, io.EOF 66 | } 67 | n = copy(buffer, parser.input[parser.input_pos:]) 68 | parser.input_pos += n 69 | return n, nil 70 | } 71 | 72 | // Reader read handler. 73 | func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { 74 | return parser.input_reader.Read(buffer) 75 | } 76 | 77 | // Set a string input. 78 | func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { 79 | if parser.read_handler != nil { 80 | panic("must set the input source only once") 81 | } 82 | parser.read_handler = yaml_string_read_handler 83 | parser.input = input 84 | parser.input_pos = 0 85 | } 86 | 87 | // Set a file input. 88 | func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { 89 | if parser.read_handler != nil { 90 | panic("must set the input source only once") 91 | } 92 | parser.read_handler = yaml_reader_read_handler 93 | parser.input_reader = r 94 | } 95 | 96 | // Set the source encoding. 97 | func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { 98 | if parser.encoding != yaml_ANY_ENCODING { 99 | panic("must set the encoding only once") 100 | } 101 | parser.encoding = encoding 102 | } 103 | 104 | // Create a new emitter object. 105 | func yaml_emitter_initialize(emitter *yaml_emitter_t) { 106 | *emitter = yaml_emitter_t{ 107 | buffer: make([]byte, output_buffer_size), 108 | raw_buffer: make([]byte, 0, output_raw_buffer_size), 109 | states: make([]yaml_emitter_state_t, 0, initial_stack_size), 110 | events: make([]yaml_event_t, 0, initial_queue_size), 111 | best_width: -1, 112 | } 113 | } 114 | 115 | // Destroy an emitter object. 116 | func yaml_emitter_delete(emitter *yaml_emitter_t) { 117 | *emitter = yaml_emitter_t{} 118 | } 119 | 120 | // String write handler. 121 | func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { 122 | *emitter.output_buffer = append(*emitter.output_buffer, buffer...) 123 | return nil 124 | } 125 | 126 | // yaml_writer_write_handler uses emitter.output_writer to write the 127 | // emitted text. 128 | func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { 129 | _, err := emitter.output_writer.Write(buffer) 130 | return err 131 | } 132 | 133 | // Set a string output. 134 | func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { 135 | if emitter.write_handler != nil { 136 | panic("must set the output target only once") 137 | } 138 | emitter.write_handler = yaml_string_write_handler 139 | emitter.output_buffer = output_buffer 140 | } 141 | 142 | // Set a file output. 143 | func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { 144 | if emitter.write_handler != nil { 145 | panic("must set the output target only once") 146 | } 147 | emitter.write_handler = yaml_writer_write_handler 148 | emitter.output_writer = w 149 | } 150 | 151 | // Set the output encoding. 152 | func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { 153 | if emitter.encoding != yaml_ANY_ENCODING { 154 | panic("must set the output encoding only once") 155 | } 156 | emitter.encoding = encoding 157 | } 158 | 159 | // Set the canonical output style. 160 | func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { 161 | emitter.canonical = canonical 162 | } 163 | 164 | // Set the indentation increment. 165 | func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { 166 | if indent < 2 || indent > 9 { 167 | indent = 2 168 | } 169 | emitter.best_indent = indent 170 | } 171 | 172 | // Set the preferred line width. 173 | func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { 174 | if width < 0 { 175 | width = -1 176 | } 177 | emitter.best_width = width 178 | } 179 | 180 | // Set if unescaped non-ASCII characters are allowed. 181 | func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { 182 | emitter.unicode = unicode 183 | } 184 | 185 | // Set the preferred line break character. 186 | func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { 187 | emitter.line_break = line_break 188 | } 189 | 190 | ///* 191 | // * Destroy a token object. 192 | // */ 193 | // 194 | //YAML_DECLARE(void) 195 | //yaml_token_delete(yaml_token_t *token) 196 | //{ 197 | // assert(token); // Non-NULL token object expected. 198 | // 199 | // switch (token.type) 200 | // { 201 | // case YAML_TAG_DIRECTIVE_TOKEN: 202 | // yaml_free(token.data.tag_directive.handle); 203 | // yaml_free(token.data.tag_directive.prefix); 204 | // break; 205 | // 206 | // case YAML_ALIAS_TOKEN: 207 | // yaml_free(token.data.alias.value); 208 | // break; 209 | // 210 | // case YAML_ANCHOR_TOKEN: 211 | // yaml_free(token.data.anchor.value); 212 | // break; 213 | // 214 | // case YAML_TAG_TOKEN: 215 | // yaml_free(token.data.tag.handle); 216 | // yaml_free(token.data.tag.suffix); 217 | // break; 218 | // 219 | // case YAML_SCALAR_TOKEN: 220 | // yaml_free(token.data.scalar.value); 221 | // break; 222 | // 223 | // default: 224 | // break; 225 | // } 226 | // 227 | // memset(token, 0, sizeof(yaml_token_t)); 228 | //} 229 | // 230 | ///* 231 | // * Check if a string is a valid UTF-8 sequence. 232 | // * 233 | // * Check 'reader.c' for more details on UTF-8 encoding. 234 | // */ 235 | // 236 | //static int 237 | //yaml_check_utf8(yaml_char_t *start, size_t length) 238 | //{ 239 | // yaml_char_t *end = start+length; 240 | // yaml_char_t *pointer = start; 241 | // 242 | // while (pointer < end) { 243 | // unsigned char octet; 244 | // unsigned int width; 245 | // unsigned int value; 246 | // size_t k; 247 | // 248 | // octet = pointer[0]; 249 | // width = (octet & 0x80) == 0x00 ? 1 : 250 | // (octet & 0xE0) == 0xC0 ? 2 : 251 | // (octet & 0xF0) == 0xE0 ? 3 : 252 | // (octet & 0xF8) == 0xF0 ? 4 : 0; 253 | // value = (octet & 0x80) == 0x00 ? octet & 0x7F : 254 | // (octet & 0xE0) == 0xC0 ? octet & 0x1F : 255 | // (octet & 0xF0) == 0xE0 ? octet & 0x0F : 256 | // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; 257 | // if (!width) return 0; 258 | // if (pointer+width > end) return 0; 259 | // for (k = 1; k < width; k ++) { 260 | // octet = pointer[k]; 261 | // if ((octet & 0xC0) != 0x80) return 0; 262 | // value = (value << 6) + (octet & 0x3F); 263 | // } 264 | // if (!((width == 1) || 265 | // (width == 2 && value >= 0x80) || 266 | // (width == 3 && value >= 0x800) || 267 | // (width == 4 && value >= 0x10000))) return 0; 268 | // 269 | // pointer += width; 270 | // } 271 | // 272 | // return 1; 273 | //} 274 | // 275 | 276 | // Create STREAM-START. 277 | func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { 278 | *event = yaml_event_t{ 279 | typ: yaml_STREAM_START_EVENT, 280 | encoding: encoding, 281 | } 282 | } 283 | 284 | // Create STREAM-END. 285 | func yaml_stream_end_event_initialize(event *yaml_event_t) { 286 | *event = yaml_event_t{ 287 | typ: yaml_STREAM_END_EVENT, 288 | } 289 | } 290 | 291 | // Create DOCUMENT-START. 292 | func yaml_document_start_event_initialize( 293 | event *yaml_event_t, 294 | version_directive *yaml_version_directive_t, 295 | tag_directives []yaml_tag_directive_t, 296 | implicit bool, 297 | ) { 298 | *event = yaml_event_t{ 299 | typ: yaml_DOCUMENT_START_EVENT, 300 | version_directive: version_directive, 301 | tag_directives: tag_directives, 302 | implicit: implicit, 303 | } 304 | } 305 | 306 | // Create DOCUMENT-END. 307 | func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { 308 | *event = yaml_event_t{ 309 | typ: yaml_DOCUMENT_END_EVENT, 310 | implicit: implicit, 311 | } 312 | } 313 | 314 | // Create ALIAS. 315 | func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { 316 | *event = yaml_event_t{ 317 | typ: yaml_ALIAS_EVENT, 318 | anchor: anchor, 319 | } 320 | return true 321 | } 322 | 323 | // Create SCALAR. 324 | func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { 325 | *event = yaml_event_t{ 326 | typ: yaml_SCALAR_EVENT, 327 | anchor: anchor, 328 | tag: tag, 329 | value: value, 330 | implicit: plain_implicit, 331 | quoted_implicit: quoted_implicit, 332 | style: yaml_style_t(style), 333 | } 334 | return true 335 | } 336 | 337 | // Create SEQUENCE-START. 338 | func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { 339 | *event = yaml_event_t{ 340 | typ: yaml_SEQUENCE_START_EVENT, 341 | anchor: anchor, 342 | tag: tag, 343 | implicit: implicit, 344 | style: yaml_style_t(style), 345 | } 346 | return true 347 | } 348 | 349 | // Create SEQUENCE-END. 350 | func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { 351 | *event = yaml_event_t{ 352 | typ: yaml_SEQUENCE_END_EVENT, 353 | } 354 | return true 355 | } 356 | 357 | // Create MAPPING-START. 358 | func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { 359 | *event = yaml_event_t{ 360 | typ: yaml_MAPPING_START_EVENT, 361 | anchor: anchor, 362 | tag: tag, 363 | implicit: implicit, 364 | style: yaml_style_t(style), 365 | } 366 | } 367 | 368 | // Create MAPPING-END. 369 | func yaml_mapping_end_event_initialize(event *yaml_event_t) { 370 | *event = yaml_event_t{ 371 | typ: yaml_MAPPING_END_EVENT, 372 | } 373 | } 374 | 375 | // Destroy an event object. 376 | func yaml_event_delete(event *yaml_event_t) { 377 | *event = yaml_event_t{} 378 | } 379 | 380 | ///* 381 | // * Create a document object. 382 | // */ 383 | // 384 | //YAML_DECLARE(int) 385 | //yaml_document_initialize(document *yaml_document_t, 386 | // version_directive *yaml_version_directive_t, 387 | // tag_directives_start *yaml_tag_directive_t, 388 | // tag_directives_end *yaml_tag_directive_t, 389 | // start_implicit int, end_implicit int) 390 | //{ 391 | // struct { 392 | // error yaml_error_type_t 393 | // } context 394 | // struct { 395 | // start *yaml_node_t 396 | // end *yaml_node_t 397 | // top *yaml_node_t 398 | // } nodes = { NULL, NULL, NULL } 399 | // version_directive_copy *yaml_version_directive_t = NULL 400 | // struct { 401 | // start *yaml_tag_directive_t 402 | // end *yaml_tag_directive_t 403 | // top *yaml_tag_directive_t 404 | // } tag_directives_copy = { NULL, NULL, NULL } 405 | // value yaml_tag_directive_t = { NULL, NULL } 406 | // mark yaml_mark_t = { 0, 0, 0 } 407 | // 408 | // assert(document) // Non-NULL document object is expected. 409 | // assert((tag_directives_start && tag_directives_end) || 410 | // (tag_directives_start == tag_directives_end)) 411 | // // Valid tag directives are expected. 412 | // 413 | // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error 414 | // 415 | // if (version_directive) { 416 | // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) 417 | // if (!version_directive_copy) goto error 418 | // version_directive_copy.major = version_directive.major 419 | // version_directive_copy.minor = version_directive.minor 420 | // } 421 | // 422 | // if (tag_directives_start != tag_directives_end) { 423 | // tag_directive *yaml_tag_directive_t 424 | // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) 425 | // goto error 426 | // for (tag_directive = tag_directives_start 427 | // tag_directive != tag_directives_end; tag_directive ++) { 428 | // assert(tag_directive.handle) 429 | // assert(tag_directive.prefix) 430 | // if (!yaml_check_utf8(tag_directive.handle, 431 | // strlen((char *)tag_directive.handle))) 432 | // goto error 433 | // if (!yaml_check_utf8(tag_directive.prefix, 434 | // strlen((char *)tag_directive.prefix))) 435 | // goto error 436 | // value.handle = yaml_strdup(tag_directive.handle) 437 | // value.prefix = yaml_strdup(tag_directive.prefix) 438 | // if (!value.handle || !value.prefix) goto error 439 | // if (!PUSH(&context, tag_directives_copy, value)) 440 | // goto error 441 | // value.handle = NULL 442 | // value.prefix = NULL 443 | // } 444 | // } 445 | // 446 | // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, 447 | // tag_directives_copy.start, tag_directives_copy.top, 448 | // start_implicit, end_implicit, mark, mark) 449 | // 450 | // return 1 451 | // 452 | //error: 453 | // STACK_DEL(&context, nodes) 454 | // yaml_free(version_directive_copy) 455 | // while (!STACK_EMPTY(&context, tag_directives_copy)) { 456 | // value yaml_tag_directive_t = POP(&context, tag_directives_copy) 457 | // yaml_free(value.handle) 458 | // yaml_free(value.prefix) 459 | // } 460 | // STACK_DEL(&context, tag_directives_copy) 461 | // yaml_free(value.handle) 462 | // yaml_free(value.prefix) 463 | // 464 | // return 0 465 | //} 466 | // 467 | ///* 468 | // * Destroy a document object. 469 | // */ 470 | // 471 | //YAML_DECLARE(void) 472 | //yaml_document_delete(document *yaml_document_t) 473 | //{ 474 | // struct { 475 | // error yaml_error_type_t 476 | // } context 477 | // tag_directive *yaml_tag_directive_t 478 | // 479 | // context.error = YAML_NO_ERROR // Eliminate a compiler warning. 480 | // 481 | // assert(document) // Non-NULL document object is expected. 482 | // 483 | // while (!STACK_EMPTY(&context, document.nodes)) { 484 | // node yaml_node_t = POP(&context, document.nodes) 485 | // yaml_free(node.tag) 486 | // switch (node.type) { 487 | // case YAML_SCALAR_NODE: 488 | // yaml_free(node.data.scalar.value) 489 | // break 490 | // case YAML_SEQUENCE_NODE: 491 | // STACK_DEL(&context, node.data.sequence.items) 492 | // break 493 | // case YAML_MAPPING_NODE: 494 | // STACK_DEL(&context, node.data.mapping.pairs) 495 | // break 496 | // default: 497 | // assert(0) // Should not happen. 498 | // } 499 | // } 500 | // STACK_DEL(&context, document.nodes) 501 | // 502 | // yaml_free(document.version_directive) 503 | // for (tag_directive = document.tag_directives.start 504 | // tag_directive != document.tag_directives.end 505 | // tag_directive++) { 506 | // yaml_free(tag_directive.handle) 507 | // yaml_free(tag_directive.prefix) 508 | // } 509 | // yaml_free(document.tag_directives.start) 510 | // 511 | // memset(document, 0, sizeof(yaml_document_t)) 512 | //} 513 | // 514 | ///** 515 | // * Get a document node. 516 | // */ 517 | // 518 | //YAML_DECLARE(yaml_node_t *) 519 | //yaml_document_get_node(document *yaml_document_t, index int) 520 | //{ 521 | // assert(document) // Non-NULL document object is expected. 522 | // 523 | // if (index > 0 && document.nodes.start + index <= document.nodes.top) { 524 | // return document.nodes.start + index - 1 525 | // } 526 | // return NULL 527 | //} 528 | // 529 | ///** 530 | // * Get the root object. 531 | // */ 532 | // 533 | //YAML_DECLARE(yaml_node_t *) 534 | //yaml_document_get_root_node(document *yaml_document_t) 535 | //{ 536 | // assert(document) // Non-NULL document object is expected. 537 | // 538 | // if (document.nodes.top != document.nodes.start) { 539 | // return document.nodes.start 540 | // } 541 | // return NULL 542 | //} 543 | // 544 | ///* 545 | // * Add a scalar node to a document. 546 | // */ 547 | // 548 | //YAML_DECLARE(int) 549 | //yaml_document_add_scalar(document *yaml_document_t, 550 | // tag *yaml_char_t, value *yaml_char_t, length int, 551 | // style yaml_scalar_style_t) 552 | //{ 553 | // struct { 554 | // error yaml_error_type_t 555 | // } context 556 | // mark yaml_mark_t = { 0, 0, 0 } 557 | // tag_copy *yaml_char_t = NULL 558 | // value_copy *yaml_char_t = NULL 559 | // node yaml_node_t 560 | // 561 | // assert(document) // Non-NULL document object is expected. 562 | // assert(value) // Non-NULL value is expected. 563 | // 564 | // if (!tag) { 565 | // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG 566 | // } 567 | // 568 | // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error 569 | // tag_copy = yaml_strdup(tag) 570 | // if (!tag_copy) goto error 571 | // 572 | // if (length < 0) { 573 | // length = strlen((char *)value) 574 | // } 575 | // 576 | // if (!yaml_check_utf8(value, length)) goto error 577 | // value_copy = yaml_malloc(length+1) 578 | // if (!value_copy) goto error 579 | // memcpy(value_copy, value, length) 580 | // value_copy[length] = '\0' 581 | // 582 | // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) 583 | // if (!PUSH(&context, document.nodes, node)) goto error 584 | // 585 | // return document.nodes.top - document.nodes.start 586 | // 587 | //error: 588 | // yaml_free(tag_copy) 589 | // yaml_free(value_copy) 590 | // 591 | // return 0 592 | //} 593 | // 594 | ///* 595 | // * Add a sequence node to a document. 596 | // */ 597 | // 598 | //YAML_DECLARE(int) 599 | //yaml_document_add_sequence(document *yaml_document_t, 600 | // tag *yaml_char_t, style yaml_sequence_style_t) 601 | //{ 602 | // struct { 603 | // error yaml_error_type_t 604 | // } context 605 | // mark yaml_mark_t = { 0, 0, 0 } 606 | // tag_copy *yaml_char_t = NULL 607 | // struct { 608 | // start *yaml_node_item_t 609 | // end *yaml_node_item_t 610 | // top *yaml_node_item_t 611 | // } items = { NULL, NULL, NULL } 612 | // node yaml_node_t 613 | // 614 | // assert(document) // Non-NULL document object is expected. 615 | // 616 | // if (!tag) { 617 | // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG 618 | // } 619 | // 620 | // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error 621 | // tag_copy = yaml_strdup(tag) 622 | // if (!tag_copy) goto error 623 | // 624 | // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error 625 | // 626 | // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, 627 | // style, mark, mark) 628 | // if (!PUSH(&context, document.nodes, node)) goto error 629 | // 630 | // return document.nodes.top - document.nodes.start 631 | // 632 | //error: 633 | // STACK_DEL(&context, items) 634 | // yaml_free(tag_copy) 635 | // 636 | // return 0 637 | //} 638 | // 639 | ///* 640 | // * Add a mapping node to a document. 641 | // */ 642 | // 643 | //YAML_DECLARE(int) 644 | //yaml_document_add_mapping(document *yaml_document_t, 645 | // tag *yaml_char_t, style yaml_mapping_style_t) 646 | //{ 647 | // struct { 648 | // error yaml_error_type_t 649 | // } context 650 | // mark yaml_mark_t = { 0, 0, 0 } 651 | // tag_copy *yaml_char_t = NULL 652 | // struct { 653 | // start *yaml_node_pair_t 654 | // end *yaml_node_pair_t 655 | // top *yaml_node_pair_t 656 | // } pairs = { NULL, NULL, NULL } 657 | // node yaml_node_t 658 | // 659 | // assert(document) // Non-NULL document object is expected. 660 | // 661 | // if (!tag) { 662 | // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG 663 | // } 664 | // 665 | // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error 666 | // tag_copy = yaml_strdup(tag) 667 | // if (!tag_copy) goto error 668 | // 669 | // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error 670 | // 671 | // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, 672 | // style, mark, mark) 673 | // if (!PUSH(&context, document.nodes, node)) goto error 674 | // 675 | // return document.nodes.top - document.nodes.start 676 | // 677 | //error: 678 | // STACK_DEL(&context, pairs) 679 | // yaml_free(tag_copy) 680 | // 681 | // return 0 682 | //} 683 | // 684 | ///* 685 | // * Append an item to a sequence node. 686 | // */ 687 | // 688 | //YAML_DECLARE(int) 689 | //yaml_document_append_sequence_item(document *yaml_document_t, 690 | // sequence int, item int) 691 | //{ 692 | // struct { 693 | // error yaml_error_type_t 694 | // } context 695 | // 696 | // assert(document) // Non-NULL document is required. 697 | // assert(sequence > 0 698 | // && document.nodes.start + sequence <= document.nodes.top) 699 | // // Valid sequence id is required. 700 | // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) 701 | // // A sequence node is required. 702 | // assert(item > 0 && document.nodes.start + item <= document.nodes.top) 703 | // // Valid item id is required. 704 | // 705 | // if (!PUSH(&context, 706 | // document.nodes.start[sequence-1].data.sequence.items, item)) 707 | // return 0 708 | // 709 | // return 1 710 | //} 711 | // 712 | ///* 713 | // * Append a pair of a key and a value to a mapping node. 714 | // */ 715 | // 716 | //YAML_DECLARE(int) 717 | //yaml_document_append_mapping_pair(document *yaml_document_t, 718 | // mapping int, key int, value int) 719 | //{ 720 | // struct { 721 | // error yaml_error_type_t 722 | // } context 723 | // 724 | // pair yaml_node_pair_t 725 | // 726 | // assert(document) // Non-NULL document is required. 727 | // assert(mapping > 0 728 | // && document.nodes.start + mapping <= document.nodes.top) 729 | // // Valid mapping id is required. 730 | // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) 731 | // // A mapping node is required. 732 | // assert(key > 0 && document.nodes.start + key <= document.nodes.top) 733 | // // Valid key id is required. 734 | // assert(value > 0 && document.nodes.start + value <= document.nodes.top) 735 | // // Valid value id is required. 736 | // 737 | // pair.key = key 738 | // pair.value = value 739 | // 740 | // if (!PUSH(&context, 741 | // document.nodes.start[mapping-1].data.mapping.pairs, pair)) 742 | // return 0 743 | // 744 | // return 1 745 | //} 746 | // 747 | // 748 | -------------------------------------------------------------------------------- /decode.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "encoding" 20 | "encoding/base64" 21 | "fmt" 22 | "io" 23 | "math" 24 | "reflect" 25 | "strconv" 26 | "time" 27 | ) 28 | 29 | // ---------------------------------------------------------------------------- 30 | // Parser, produces a node tree out of a libyaml event stream. 31 | 32 | type parser struct { 33 | parser yaml_parser_t 34 | event yaml_event_t 35 | doc *Node 36 | anchors map[string]*Node 37 | doneInit bool 38 | textless bool 39 | } 40 | 41 | func newParser(b []byte) *parser { 42 | p := parser{} 43 | if !yaml_parser_initialize(&p.parser) { 44 | panic("failed to initialize YAML emitter") 45 | } 46 | if len(b) == 0 { 47 | b = []byte{'\n'} 48 | } 49 | yaml_parser_set_input_string(&p.parser, b) 50 | return &p 51 | } 52 | 53 | func newParserFromReader(r io.Reader) *parser { 54 | p := parser{} 55 | if !yaml_parser_initialize(&p.parser) { 56 | panic("failed to initialize YAML emitter") 57 | } 58 | yaml_parser_set_input_reader(&p.parser, r) 59 | return &p 60 | } 61 | 62 | func (p *parser) init() { 63 | if p.doneInit { 64 | return 65 | } 66 | p.anchors = make(map[string]*Node) 67 | p.expect(yaml_STREAM_START_EVENT) 68 | p.doneInit = true 69 | } 70 | 71 | func (p *parser) destroy() { 72 | if p.event.typ != yaml_NO_EVENT { 73 | yaml_event_delete(&p.event) 74 | } 75 | yaml_parser_delete(&p.parser) 76 | } 77 | 78 | // expect consumes an event from the event stream and 79 | // checks that it's of the expected type. 80 | func (p *parser) expect(e yaml_event_type_t) { 81 | if p.event.typ == yaml_NO_EVENT { 82 | if !yaml_parser_parse(&p.parser, &p.event) { 83 | p.fail() 84 | } 85 | } 86 | if p.event.typ == yaml_STREAM_END_EVENT { 87 | failf("attempted to go past the end of stream; corrupted value?") 88 | } 89 | if p.event.typ != e { 90 | p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) 91 | p.fail() 92 | } 93 | yaml_event_delete(&p.event) 94 | p.event.typ = yaml_NO_EVENT 95 | } 96 | 97 | // peek peeks at the next event in the event stream, 98 | // puts the results into p.event and returns the event type. 99 | func (p *parser) peek() yaml_event_type_t { 100 | if p.event.typ != yaml_NO_EVENT { 101 | return p.event.typ 102 | } 103 | // It's curious choice from the underlying API to generally return a 104 | // positive result on success, but on this case return true in an error 105 | // scenario. This was the source of bugs in the past (issue #666). 106 | if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { 107 | p.fail() 108 | } 109 | return p.event.typ 110 | } 111 | 112 | func (p *parser) fail() { 113 | var where string 114 | var line int 115 | if p.parser.context_mark.line != 0 { 116 | line = p.parser.context_mark.line 117 | // Scanner errors don't iterate line before returning error 118 | if p.parser.error == yaml_SCANNER_ERROR { 119 | line++ 120 | } 121 | } else if p.parser.problem_mark.line != 0 { 122 | line = p.parser.problem_mark.line 123 | // Scanner errors don't iterate line before returning error 124 | if p.parser.error == yaml_SCANNER_ERROR { 125 | line++ 126 | } 127 | } 128 | if line != 0 { 129 | where = "line " + strconv.Itoa(line) + ": " 130 | } 131 | var msg string 132 | if len(p.parser.problem) > 0 { 133 | msg = p.parser.problem 134 | } else { 135 | msg = "unknown problem parsing YAML content" 136 | } 137 | failf("%s%s", where, msg) 138 | } 139 | 140 | func (p *parser) anchor(n *Node, anchor []byte) { 141 | if anchor != nil { 142 | n.Anchor = string(anchor) 143 | p.anchors[n.Anchor] = n 144 | } 145 | } 146 | 147 | func (p *parser) parse() *Node { 148 | p.init() 149 | switch p.peek() { 150 | case yaml_SCALAR_EVENT: 151 | return p.scalar() 152 | case yaml_ALIAS_EVENT: 153 | return p.alias() 154 | case yaml_MAPPING_START_EVENT: 155 | return p.mapping() 156 | case yaml_SEQUENCE_START_EVENT: 157 | return p.sequence() 158 | case yaml_DOCUMENT_START_EVENT: 159 | return p.document() 160 | case yaml_STREAM_END_EVENT: 161 | // Happens when attempting to decode an empty buffer. 162 | return nil 163 | case yaml_TAIL_COMMENT_EVENT: 164 | panic("internal error: unexpected tail comment event (please report)") 165 | default: 166 | panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) 167 | } 168 | } 169 | 170 | func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { 171 | var style Style 172 | if tag != "" && tag != "!" { 173 | tag = shortTag(tag) 174 | style = TaggedStyle 175 | } else if defaultTag != "" { 176 | tag = defaultTag 177 | } else if kind == ScalarNode { 178 | tag, _ = resolve("", value) 179 | } 180 | n := &Node{ 181 | Kind: kind, 182 | Tag: tag, 183 | Value: value, 184 | Style: style, 185 | } 186 | if !p.textless { 187 | n.Line = p.event.start_mark.line + 1 188 | n.Column = p.event.start_mark.column + 1 189 | n.HeadComment = string(p.event.head_comment) 190 | n.LineComment = string(p.event.line_comment) 191 | n.FootComment = string(p.event.foot_comment) 192 | } 193 | return n 194 | } 195 | 196 | func (p *parser) parseChild(parent *Node) *Node { 197 | child := p.parse() 198 | parent.Content = append(parent.Content, child) 199 | return child 200 | } 201 | 202 | func (p *parser) document() *Node { 203 | n := p.node(DocumentNode, "", "", "") 204 | p.doc = n 205 | p.expect(yaml_DOCUMENT_START_EVENT) 206 | p.parseChild(n) 207 | if p.peek() == yaml_DOCUMENT_END_EVENT { 208 | n.FootComment = string(p.event.foot_comment) 209 | } 210 | p.expect(yaml_DOCUMENT_END_EVENT) 211 | return n 212 | } 213 | 214 | func (p *parser) alias() *Node { 215 | n := p.node(AliasNode, "", "", string(p.event.anchor)) 216 | n.Alias = p.anchors[n.Value] 217 | if n.Alias == nil { 218 | failf("unknown anchor '%s' referenced", n.Value) 219 | } 220 | p.expect(yaml_ALIAS_EVENT) 221 | return n 222 | } 223 | 224 | func (p *parser) scalar() *Node { 225 | var parsedStyle = p.event.scalar_style() 226 | var nodeStyle Style 227 | switch { 228 | case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: 229 | nodeStyle = DoubleQuotedStyle 230 | case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: 231 | nodeStyle = SingleQuotedStyle 232 | case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: 233 | nodeStyle = LiteralStyle 234 | case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: 235 | nodeStyle = FoldedStyle 236 | } 237 | var nodeValue = string(p.event.value) 238 | var nodeTag = string(p.event.tag) 239 | var defaultTag string 240 | if nodeStyle == 0 { 241 | if nodeValue == "<<" { 242 | defaultTag = mergeTag 243 | } 244 | } else { 245 | defaultTag = strTag 246 | } 247 | n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) 248 | n.Style |= nodeStyle 249 | p.anchor(n, p.event.anchor) 250 | p.expect(yaml_SCALAR_EVENT) 251 | return n 252 | } 253 | 254 | func (p *parser) sequence() *Node { 255 | n := p.node(SequenceNode, seqTag, string(p.event.tag), "") 256 | if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { 257 | n.Style |= FlowStyle 258 | } 259 | p.anchor(n, p.event.anchor) 260 | p.expect(yaml_SEQUENCE_START_EVENT) 261 | for p.peek() != yaml_SEQUENCE_END_EVENT { 262 | p.parseChild(n) 263 | } 264 | n.LineComment = string(p.event.line_comment) 265 | n.FootComment = string(p.event.foot_comment) 266 | p.expect(yaml_SEQUENCE_END_EVENT) 267 | return n 268 | } 269 | 270 | func (p *parser) mapping() *Node { 271 | n := p.node(MappingNode, mapTag, string(p.event.tag), "") 272 | block := true 273 | if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { 274 | block = false 275 | n.Style |= FlowStyle 276 | } 277 | p.anchor(n, p.event.anchor) 278 | p.expect(yaml_MAPPING_START_EVENT) 279 | for p.peek() != yaml_MAPPING_END_EVENT { 280 | k := p.parseChild(n) 281 | if block && k.FootComment != "" { 282 | // Must be a foot comment for the prior value when being dedented. 283 | if len(n.Content) > 2 { 284 | n.Content[len(n.Content)-3].FootComment = k.FootComment 285 | k.FootComment = "" 286 | } 287 | } 288 | v := p.parseChild(n) 289 | if k.FootComment == "" && v.FootComment != "" { 290 | k.FootComment = v.FootComment 291 | v.FootComment = "" 292 | } 293 | if p.peek() == yaml_TAIL_COMMENT_EVENT { 294 | if k.FootComment == "" { 295 | k.FootComment = string(p.event.foot_comment) 296 | } 297 | p.expect(yaml_TAIL_COMMENT_EVENT) 298 | } 299 | } 300 | n.LineComment = string(p.event.line_comment) 301 | n.FootComment = string(p.event.foot_comment) 302 | if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { 303 | n.Content[len(n.Content)-2].FootComment = n.FootComment 304 | n.FootComment = "" 305 | } 306 | p.expect(yaml_MAPPING_END_EVENT) 307 | return n 308 | } 309 | 310 | // ---------------------------------------------------------------------------- 311 | // Decoder, unmarshals a node into a provided value. 312 | 313 | type decoder struct { 314 | doc *Node 315 | aliases map[*Node]bool 316 | terrors []string 317 | 318 | stringMapType reflect.Type 319 | generalMapType reflect.Type 320 | 321 | knownFields bool 322 | uniqueKeys bool 323 | decodeCount int 324 | aliasCount int 325 | aliasDepth int 326 | 327 | mergedFields map[interface{}]bool 328 | } 329 | 330 | var ( 331 | nodeType = reflect.TypeOf(Node{}) 332 | durationType = reflect.TypeOf(time.Duration(0)) 333 | stringMapType = reflect.TypeOf(map[string]interface{}{}) 334 | generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) 335 | ifaceType = generalMapType.Elem() 336 | timeType = reflect.TypeOf(time.Time{}) 337 | ptrTimeType = reflect.TypeOf(&time.Time{}) 338 | ) 339 | 340 | func newDecoder() *decoder { 341 | d := &decoder{ 342 | stringMapType: stringMapType, 343 | generalMapType: generalMapType, 344 | uniqueKeys: true, 345 | } 346 | d.aliases = make(map[*Node]bool) 347 | return d 348 | } 349 | 350 | func (d *decoder) terror(n *Node, tag string, out reflect.Value) { 351 | if n.Tag != "" { 352 | tag = n.Tag 353 | } 354 | value := n.Value 355 | if tag != seqTag && tag != mapTag { 356 | if len(value) > 10 { 357 | value = " `" + value[:7] + "...`" 358 | } else { 359 | value = " `" + value + "`" 360 | } 361 | } 362 | d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) 363 | } 364 | 365 | func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { 366 | err := u.UnmarshalYAML(n) 367 | if e, ok := err.(*TypeError); ok { 368 | d.terrors = append(d.terrors, e.Errors...) 369 | return false 370 | } 371 | if err != nil { 372 | fail(err) 373 | } 374 | return true 375 | } 376 | 377 | func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { 378 | terrlen := len(d.terrors) 379 | err := u.UnmarshalYAML(func(v interface{}) (err error) { 380 | defer handleErr(&err) 381 | d.unmarshal(n, reflect.ValueOf(v)) 382 | if len(d.terrors) > terrlen { 383 | issues := d.terrors[terrlen:] 384 | d.terrors = d.terrors[:terrlen] 385 | return &TypeError{issues} 386 | } 387 | return nil 388 | }) 389 | if e, ok := err.(*TypeError); ok { 390 | d.terrors = append(d.terrors, e.Errors...) 391 | return false 392 | } 393 | if err != nil { 394 | fail(err) 395 | } 396 | return true 397 | } 398 | 399 | // d.prepare initializes and dereferences pointers and calls UnmarshalYAML 400 | // if a value is found to implement it. 401 | // It returns the initialized and dereferenced out value, whether 402 | // unmarshalling was already done by UnmarshalYAML, and if so whether 403 | // its types unmarshalled appropriately. 404 | // 405 | // If n holds a null value, prepare returns before doing anything. 406 | func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { 407 | if n.ShortTag() == nullTag { 408 | return out, false, false 409 | } 410 | again := true 411 | for again { 412 | again = false 413 | if out.Kind() == reflect.Ptr { 414 | if out.IsNil() { 415 | out.Set(reflect.New(out.Type().Elem())) 416 | } 417 | out = out.Elem() 418 | again = true 419 | } 420 | if out.CanAddr() { 421 | outi := out.Addr().Interface() 422 | if u, ok := outi.(Unmarshaler); ok { 423 | good = d.callUnmarshaler(n, u) 424 | return out, true, good 425 | } 426 | if u, ok := outi.(obsoleteUnmarshaler); ok { 427 | good = d.callObsoleteUnmarshaler(n, u) 428 | return out, true, good 429 | } 430 | } 431 | } 432 | return out, false, false 433 | } 434 | 435 | func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { 436 | if n.ShortTag() == nullTag { 437 | return reflect.Value{} 438 | } 439 | for _, num := range index { 440 | for { 441 | if v.Kind() == reflect.Ptr { 442 | if v.IsNil() { 443 | v.Set(reflect.New(v.Type().Elem())) 444 | } 445 | v = v.Elem() 446 | continue 447 | } 448 | break 449 | } 450 | v = v.Field(num) 451 | } 452 | return v 453 | } 454 | 455 | const ( 456 | // 400,000 decode operations is ~500kb of dense object declarations, or 457 | // ~5kb of dense object declarations with 10000% alias expansion 458 | alias_ratio_range_low = 400000 459 | 460 | // 4,000,000 decode operations is ~5MB of dense object declarations, or 461 | // ~4.5MB of dense object declarations with 10% alias expansion 462 | alias_ratio_range_high = 4000000 463 | 464 | // alias_ratio_range is the range over which we scale allowed alias ratios 465 | alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) 466 | ) 467 | 468 | func allowedAliasRatio(decodeCount int) float64 { 469 | switch { 470 | case decodeCount <= alias_ratio_range_low: 471 | // allow 99% to come from alias expansion for small-to-medium documents 472 | return 0.99 473 | case decodeCount >= alias_ratio_range_high: 474 | // allow 10% to come from alias expansion for very large documents 475 | return 0.10 476 | default: 477 | // scale smoothly from 99% down to 10% over the range. 478 | // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. 479 | // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). 480 | return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) 481 | } 482 | } 483 | 484 | func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { 485 | d.decodeCount++ 486 | if d.aliasDepth > 0 { 487 | d.aliasCount++ 488 | } 489 | if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { 490 | failf("document contains excessive aliasing") 491 | } 492 | if out.Type() == nodeType { 493 | out.Set(reflect.ValueOf(n).Elem()) 494 | return true 495 | } 496 | switch n.Kind { 497 | case DocumentNode: 498 | return d.document(n, out) 499 | case AliasNode: 500 | return d.alias(n, out) 501 | } 502 | out, unmarshaled, good := d.prepare(n, out) 503 | if unmarshaled { 504 | return good 505 | } 506 | switch n.Kind { 507 | case ScalarNode: 508 | good = d.scalar(n, out) 509 | case MappingNode: 510 | good = d.mapping(n, out) 511 | case SequenceNode: 512 | good = d.sequence(n, out) 513 | case 0: 514 | if n.IsZero() { 515 | return d.null(out) 516 | } 517 | fallthrough 518 | default: 519 | failf("cannot decode node with unknown kind %d", n.Kind) 520 | } 521 | return good 522 | } 523 | 524 | func (d *decoder) document(n *Node, out reflect.Value) (good bool) { 525 | if len(n.Content) == 1 { 526 | d.doc = n 527 | d.unmarshal(n.Content[0], out) 528 | return true 529 | } 530 | return false 531 | } 532 | 533 | func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { 534 | if d.aliases[n] { 535 | // TODO this could actually be allowed in some circumstances. 536 | failf("anchor '%s' value contains itself", n.Value) 537 | } 538 | d.aliases[n] = true 539 | d.aliasDepth++ 540 | good = d.unmarshal(n.Alias, out) 541 | d.aliasDepth-- 542 | delete(d.aliases, n) 543 | return good 544 | } 545 | 546 | var zeroValue reflect.Value 547 | 548 | func resetMap(out reflect.Value) { 549 | for _, k := range out.MapKeys() { 550 | out.SetMapIndex(k, zeroValue) 551 | } 552 | } 553 | 554 | func (d *decoder) null(out reflect.Value) bool { 555 | if out.CanAddr() { 556 | switch out.Kind() { 557 | case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: 558 | out.Set(reflect.Zero(out.Type())) 559 | return true 560 | } 561 | } 562 | return false 563 | } 564 | 565 | func (d *decoder) scalar(n *Node, out reflect.Value) bool { 566 | var tag string 567 | var resolved interface{} 568 | if n.indicatedString() { 569 | tag = strTag 570 | resolved = n.Value 571 | } else { 572 | tag, resolved = resolve(n.Tag, n.Value) 573 | if tag == binaryTag { 574 | data, err := base64.StdEncoding.DecodeString(resolved.(string)) 575 | if err != nil { 576 | failf("!!binary value contains invalid base64 data") 577 | } 578 | resolved = string(data) 579 | } 580 | } 581 | if resolved == nil { 582 | return d.null(out) 583 | } 584 | if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { 585 | // We've resolved to exactly the type we want, so use that. 586 | out.Set(resolvedv) 587 | return true 588 | } 589 | // Perhaps we can use the value as a TextUnmarshaler to 590 | // set its value. 591 | if out.CanAddr() { 592 | u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) 593 | if ok { 594 | var text []byte 595 | if tag == binaryTag { 596 | text = []byte(resolved.(string)) 597 | } else { 598 | // We let any value be unmarshaled into TextUnmarshaler. 599 | // That might be more lax than we'd like, but the 600 | // TextUnmarshaler itself should bowl out any dubious values. 601 | text = []byte(n.Value) 602 | } 603 | err := u.UnmarshalText(text) 604 | if err != nil { 605 | fail(err) 606 | } 607 | return true 608 | } 609 | } 610 | switch out.Kind() { 611 | case reflect.String: 612 | if tag == binaryTag { 613 | out.SetString(resolved.(string)) 614 | return true 615 | } 616 | out.SetString(n.Value) 617 | return true 618 | case reflect.Interface: 619 | out.Set(reflect.ValueOf(resolved)) 620 | return true 621 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 622 | // This used to work in v2, but it's very unfriendly. 623 | isDuration := out.Type() == durationType 624 | 625 | switch resolved := resolved.(type) { 626 | case int: 627 | if !isDuration && !out.OverflowInt(int64(resolved)) { 628 | out.SetInt(int64(resolved)) 629 | return true 630 | } 631 | case int64: 632 | if !isDuration && !out.OverflowInt(resolved) { 633 | out.SetInt(resolved) 634 | return true 635 | } 636 | case uint64: 637 | if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { 638 | out.SetInt(int64(resolved)) 639 | return true 640 | } 641 | case float64: 642 | if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { 643 | out.SetInt(int64(resolved)) 644 | return true 645 | } 646 | case string: 647 | if out.Type() == durationType { 648 | d, err := time.ParseDuration(resolved) 649 | if err == nil { 650 | out.SetInt(int64(d)) 651 | return true 652 | } 653 | } 654 | } 655 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 656 | switch resolved := resolved.(type) { 657 | case int: 658 | if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { 659 | out.SetUint(uint64(resolved)) 660 | return true 661 | } 662 | case int64: 663 | if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { 664 | out.SetUint(uint64(resolved)) 665 | return true 666 | } 667 | case uint64: 668 | if !out.OverflowUint(uint64(resolved)) { 669 | out.SetUint(uint64(resolved)) 670 | return true 671 | } 672 | case float64: 673 | if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { 674 | out.SetUint(uint64(resolved)) 675 | return true 676 | } 677 | } 678 | case reflect.Bool: 679 | switch resolved := resolved.(type) { 680 | case bool: 681 | out.SetBool(resolved) 682 | return true 683 | case string: 684 | // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). 685 | // It only works if explicitly attempting to unmarshal into a typed bool value. 686 | switch resolved { 687 | case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": 688 | out.SetBool(true) 689 | return true 690 | case "n", "N", "no", "No", "NO", "off", "Off", "OFF": 691 | out.SetBool(false) 692 | return true 693 | } 694 | } 695 | case reflect.Float32, reflect.Float64: 696 | switch resolved := resolved.(type) { 697 | case int: 698 | out.SetFloat(float64(resolved)) 699 | return true 700 | case int64: 701 | out.SetFloat(float64(resolved)) 702 | return true 703 | case uint64: 704 | out.SetFloat(float64(resolved)) 705 | return true 706 | case float64: 707 | out.SetFloat(resolved) 708 | return true 709 | } 710 | case reflect.Struct: 711 | if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { 712 | out.Set(resolvedv) 713 | return true 714 | } 715 | case reflect.Ptr: 716 | panic("yaml internal error: please report the issue") 717 | } 718 | d.terror(n, tag, out) 719 | return false 720 | } 721 | 722 | func settableValueOf(i interface{}) reflect.Value { 723 | v := reflect.ValueOf(i) 724 | sv := reflect.New(v.Type()).Elem() 725 | sv.Set(v) 726 | return sv 727 | } 728 | 729 | func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { 730 | l := len(n.Content) 731 | 732 | var iface reflect.Value 733 | switch out.Kind() { 734 | case reflect.Slice: 735 | out.Set(reflect.MakeSlice(out.Type(), l, l)) 736 | case reflect.Array: 737 | if l != out.Len() { 738 | failf("invalid array: want %d elements but got %d", out.Len(), l) 739 | } 740 | case reflect.Interface: 741 | // No type hints. Will have to use a generic sequence. 742 | iface = out 743 | out = settableValueOf(make([]interface{}, l)) 744 | default: 745 | d.terror(n, seqTag, out) 746 | return false 747 | } 748 | et := out.Type().Elem() 749 | 750 | j := 0 751 | for i := 0; i < l; i++ { 752 | e := reflect.New(et).Elem() 753 | if ok := d.unmarshal(n.Content[i], e); ok { 754 | out.Index(j).Set(e) 755 | j++ 756 | } 757 | } 758 | if out.Kind() != reflect.Array { 759 | out.Set(out.Slice(0, j)) 760 | } 761 | if iface.IsValid() { 762 | iface.Set(out) 763 | } 764 | return true 765 | } 766 | 767 | func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { 768 | l := len(n.Content) 769 | if d.uniqueKeys { 770 | nerrs := len(d.terrors) 771 | for i := 0; i < l; i += 2 { 772 | ni := n.Content[i] 773 | for j := i + 2; j < l; j += 2 { 774 | nj := n.Content[j] 775 | if ni.Kind == nj.Kind && ni.Value == nj.Value { 776 | d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) 777 | } 778 | } 779 | } 780 | if len(d.terrors) > nerrs { 781 | return false 782 | } 783 | } 784 | switch out.Kind() { 785 | case reflect.Struct: 786 | return d.mappingStruct(n, out) 787 | case reflect.Map: 788 | // okay 789 | case reflect.Interface: 790 | iface := out 791 | if isStringMap(n) { 792 | out = reflect.MakeMap(d.stringMapType) 793 | } else { 794 | out = reflect.MakeMap(d.generalMapType) 795 | } 796 | iface.Set(out) 797 | default: 798 | d.terror(n, mapTag, out) 799 | return false 800 | } 801 | 802 | outt := out.Type() 803 | kt := outt.Key() 804 | et := outt.Elem() 805 | 806 | stringMapType := d.stringMapType 807 | generalMapType := d.generalMapType 808 | if outt.Elem() == ifaceType { 809 | if outt.Key().Kind() == reflect.String { 810 | d.stringMapType = outt 811 | } else if outt.Key() == ifaceType { 812 | d.generalMapType = outt 813 | } 814 | } 815 | 816 | mergedFields := d.mergedFields 817 | d.mergedFields = nil 818 | 819 | var mergeNode *Node 820 | 821 | mapIsNew := false 822 | if out.IsNil() { 823 | out.Set(reflect.MakeMap(outt)) 824 | mapIsNew = true 825 | } 826 | for i := 0; i < l; i += 2 { 827 | if isMerge(n.Content[i]) { 828 | mergeNode = n.Content[i+1] 829 | continue 830 | } 831 | k := reflect.New(kt).Elem() 832 | if d.unmarshal(n.Content[i], k) { 833 | if mergedFields != nil { 834 | ki := k.Interface() 835 | if mergedFields[ki] { 836 | continue 837 | } 838 | mergedFields[ki] = true 839 | } 840 | kkind := k.Kind() 841 | if kkind == reflect.Interface { 842 | kkind = k.Elem().Kind() 843 | } 844 | if kkind == reflect.Map || kkind == reflect.Slice { 845 | failf("invalid map key: %#v", k.Interface()) 846 | } 847 | e := reflect.New(et).Elem() 848 | if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { 849 | out.SetMapIndex(k, e) 850 | } 851 | } 852 | } 853 | 854 | d.mergedFields = mergedFields 855 | if mergeNode != nil { 856 | d.merge(n, mergeNode, out) 857 | } 858 | 859 | d.stringMapType = stringMapType 860 | d.generalMapType = generalMapType 861 | return true 862 | } 863 | 864 | func isStringMap(n *Node) bool { 865 | if n.Kind != MappingNode { 866 | return false 867 | } 868 | l := len(n.Content) 869 | for i := 0; i < l; i += 2 { 870 | shortTag := n.Content[i].ShortTag() 871 | if shortTag != strTag && shortTag != mergeTag { 872 | return false 873 | } 874 | } 875 | return true 876 | } 877 | 878 | func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { 879 | sinfo, err := getStructInfo(out.Type()) 880 | if err != nil { 881 | panic(err) 882 | } 883 | 884 | var inlineMap reflect.Value 885 | var elemType reflect.Type 886 | if sinfo.InlineMap != -1 { 887 | inlineMap = out.Field(sinfo.InlineMap) 888 | elemType = inlineMap.Type().Elem() 889 | } 890 | 891 | for _, index := range sinfo.InlineUnmarshalers { 892 | field := d.fieldByIndex(n, out, index) 893 | d.prepare(n, field) 894 | } 895 | 896 | mergedFields := d.mergedFields 897 | d.mergedFields = nil 898 | var mergeNode *Node 899 | var doneFields []bool 900 | if d.uniqueKeys { 901 | doneFields = make([]bool, len(sinfo.FieldsList)) 902 | } 903 | name := settableValueOf("") 904 | l := len(n.Content) 905 | for i := 0; i < l; i += 2 { 906 | ni := n.Content[i] 907 | if isMerge(ni) { 908 | mergeNode = n.Content[i+1] 909 | continue 910 | } 911 | if !d.unmarshal(ni, name) { 912 | continue 913 | } 914 | sname := name.String() 915 | if mergedFields != nil { 916 | if mergedFields[sname] { 917 | continue 918 | } 919 | mergedFields[sname] = true 920 | } 921 | if info, ok := sinfo.FieldsMap[sname]; ok { 922 | if d.uniqueKeys { 923 | if doneFields[info.Id] { 924 | d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) 925 | continue 926 | } 927 | doneFields[info.Id] = true 928 | } 929 | var field reflect.Value 930 | if info.Inline == nil { 931 | field = out.Field(info.Num) 932 | } else { 933 | field = d.fieldByIndex(n, out, info.Inline) 934 | } 935 | d.unmarshal(n.Content[i+1], field) 936 | } else if sinfo.InlineMap != -1 { 937 | if inlineMap.IsNil() { 938 | inlineMap.Set(reflect.MakeMap(inlineMap.Type())) 939 | } 940 | value := reflect.New(elemType).Elem() 941 | d.unmarshal(n.Content[i+1], value) 942 | inlineMap.SetMapIndex(name, value) 943 | } else if d.knownFields { 944 | d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) 945 | } 946 | } 947 | 948 | d.mergedFields = mergedFields 949 | if mergeNode != nil { 950 | d.merge(n, mergeNode, out) 951 | } 952 | return true 953 | } 954 | 955 | func failWantMap() { 956 | failf("map merge requires map or sequence of maps as the value") 957 | } 958 | 959 | func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { 960 | mergedFields := d.mergedFields 961 | if mergedFields == nil { 962 | d.mergedFields = make(map[interface{}]bool) 963 | for i := 0; i < len(parent.Content); i += 2 { 964 | k := reflect.New(ifaceType).Elem() 965 | if d.unmarshal(parent.Content[i], k) { 966 | d.mergedFields[k.Interface()] = true 967 | } 968 | } 969 | } 970 | 971 | switch merge.Kind { 972 | case MappingNode: 973 | d.unmarshal(merge, out) 974 | case AliasNode: 975 | if merge.Alias != nil && merge.Alias.Kind != MappingNode { 976 | failWantMap() 977 | } 978 | d.unmarshal(merge, out) 979 | case SequenceNode: 980 | for i := 0; i < len(merge.Content); i++ { 981 | ni := merge.Content[i] 982 | if ni.Kind == AliasNode { 983 | if ni.Alias != nil && ni.Alias.Kind != MappingNode { 984 | failWantMap() 985 | } 986 | } else if ni.Kind != MappingNode { 987 | failWantMap() 988 | } 989 | d.unmarshal(ni, out) 990 | } 991 | default: 992 | failWantMap() 993 | } 994 | 995 | d.mergedFields = mergedFields 996 | } 997 | 998 | func isMerge(n *Node) bool { 999 | return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) 1000 | } 1001 | -------------------------------------------------------------------------------- /encode.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "encoding" 20 | "fmt" 21 | "io" 22 | "reflect" 23 | "regexp" 24 | "sort" 25 | "strconv" 26 | "strings" 27 | "time" 28 | "unicode/utf8" 29 | ) 30 | 31 | type encoder struct { 32 | emitter yaml_emitter_t 33 | event yaml_event_t 34 | out []byte 35 | flow bool 36 | indent int 37 | doneInit bool 38 | } 39 | 40 | func newEncoder() *encoder { 41 | e := &encoder{} 42 | yaml_emitter_initialize(&e.emitter) 43 | yaml_emitter_set_output_string(&e.emitter, &e.out) 44 | yaml_emitter_set_unicode(&e.emitter, true) 45 | return e 46 | } 47 | 48 | func newEncoderWithWriter(w io.Writer) *encoder { 49 | e := &encoder{} 50 | yaml_emitter_initialize(&e.emitter) 51 | yaml_emitter_set_output_writer(&e.emitter, w) 52 | yaml_emitter_set_unicode(&e.emitter, true) 53 | return e 54 | } 55 | 56 | func (e *encoder) init() { 57 | if e.doneInit { 58 | return 59 | } 60 | if e.indent == 0 { 61 | e.indent = 4 62 | } 63 | e.emitter.best_indent = e.indent 64 | yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) 65 | e.emit() 66 | e.doneInit = true 67 | } 68 | 69 | func (e *encoder) finish() { 70 | e.emitter.open_ended = false 71 | yaml_stream_end_event_initialize(&e.event) 72 | e.emit() 73 | } 74 | 75 | func (e *encoder) destroy() { 76 | yaml_emitter_delete(&e.emitter) 77 | } 78 | 79 | func (e *encoder) emit() { 80 | // This will internally delete the e.event value. 81 | e.must(yaml_emitter_emit(&e.emitter, &e.event)) 82 | } 83 | 84 | func (e *encoder) must(ok bool) { 85 | if !ok { 86 | msg := e.emitter.problem 87 | if msg == "" { 88 | msg = "unknown problem generating YAML content" 89 | } 90 | failf("%s", msg) 91 | } 92 | } 93 | 94 | func (e *encoder) marshalDoc(tag string, in reflect.Value) { 95 | e.init() 96 | var node *Node 97 | if in.IsValid() { 98 | node, _ = in.Interface().(*Node) 99 | } 100 | if node != nil && node.Kind == DocumentNode { 101 | e.nodev(in) 102 | } else { 103 | yaml_document_start_event_initialize(&e.event, nil, nil, true) 104 | e.emit() 105 | e.marshal(tag, in) 106 | yaml_document_end_event_initialize(&e.event, true) 107 | e.emit() 108 | } 109 | } 110 | 111 | func (e *encoder) marshal(tag string, in reflect.Value) { 112 | tag = shortTag(tag) 113 | if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { 114 | e.nilv() 115 | return 116 | } 117 | iface := in.Interface() 118 | switch value := iface.(type) { 119 | case *Node: 120 | e.nodev(in) 121 | return 122 | case Node: 123 | if !in.CanAddr() { 124 | var n = reflect.New(in.Type()).Elem() 125 | n.Set(in) 126 | in = n 127 | } 128 | e.nodev(in.Addr()) 129 | return 130 | case time.Time: 131 | e.timev(tag, in) 132 | return 133 | case *time.Time: 134 | e.timev(tag, in.Elem()) 135 | return 136 | case time.Duration: 137 | e.stringv(tag, reflect.ValueOf(value.String())) 138 | return 139 | case Marshaler: 140 | v, err := value.MarshalYAML() 141 | if err != nil { 142 | fail(err) 143 | } 144 | if v == nil { 145 | e.nilv() 146 | return 147 | } 148 | e.marshal(tag, reflect.ValueOf(v)) 149 | return 150 | case encoding.TextMarshaler: 151 | text, err := value.MarshalText() 152 | if err != nil { 153 | fail(err) 154 | } 155 | in = reflect.ValueOf(string(text)) 156 | case nil: 157 | e.nilv() 158 | return 159 | } 160 | switch in.Kind() { 161 | case reflect.Interface: 162 | e.marshal(tag, in.Elem()) 163 | case reflect.Map: 164 | e.mapv(tag, in) 165 | case reflect.Ptr: 166 | e.marshal(tag, in.Elem()) 167 | case reflect.Struct: 168 | e.structv(tag, in) 169 | case reflect.Slice, reflect.Array: 170 | e.slicev(tag, in) 171 | case reflect.String: 172 | e.stringv(tag, in) 173 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 174 | e.intv(tag, in) 175 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 176 | e.uintv(tag, in) 177 | case reflect.Float32, reflect.Float64: 178 | e.floatv(tag, in) 179 | case reflect.Bool: 180 | e.boolv(tag, in) 181 | default: 182 | panic("cannot marshal type: " + in.Type().String()) 183 | } 184 | } 185 | 186 | func (e *encoder) mapv(tag string, in reflect.Value) { 187 | e.mappingv(tag, func() { 188 | keys := keyList(in.MapKeys()) 189 | sort.Sort(keys) 190 | for _, k := range keys { 191 | e.marshal("", k) 192 | e.marshal("", in.MapIndex(k)) 193 | } 194 | }) 195 | } 196 | 197 | func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { 198 | for _, num := range index { 199 | for { 200 | if v.Kind() == reflect.Ptr { 201 | if v.IsNil() { 202 | return reflect.Value{} 203 | } 204 | v = v.Elem() 205 | continue 206 | } 207 | break 208 | } 209 | v = v.Field(num) 210 | } 211 | return v 212 | } 213 | 214 | func (e *encoder) structv(tag string, in reflect.Value) { 215 | sinfo, err := getStructInfo(in.Type()) 216 | if err != nil { 217 | panic(err) 218 | } 219 | e.mappingv(tag, func() { 220 | for _, info := range sinfo.FieldsList { 221 | var value reflect.Value 222 | if info.Inline == nil { 223 | value = in.Field(info.Num) 224 | } else { 225 | value = e.fieldByIndex(in, info.Inline) 226 | if !value.IsValid() { 227 | continue 228 | } 229 | } 230 | if info.OmitEmpty && isZero(value) { 231 | continue 232 | } 233 | e.marshal("", reflect.ValueOf(info.Key)) 234 | e.flow = info.Flow 235 | e.marshal("", value) 236 | } 237 | if sinfo.InlineMap >= 0 { 238 | m := in.Field(sinfo.InlineMap) 239 | if m.Len() > 0 { 240 | e.flow = false 241 | keys := keyList(m.MapKeys()) 242 | sort.Sort(keys) 243 | for _, k := range keys { 244 | if _, found := sinfo.FieldsMap[k.String()]; found { 245 | panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) 246 | } 247 | e.marshal("", k) 248 | e.flow = false 249 | e.marshal("", m.MapIndex(k)) 250 | } 251 | } 252 | } 253 | }) 254 | } 255 | 256 | func (e *encoder) mappingv(tag string, f func()) { 257 | implicit := tag == "" 258 | style := yaml_BLOCK_MAPPING_STYLE 259 | if e.flow { 260 | e.flow = false 261 | style = yaml_FLOW_MAPPING_STYLE 262 | } 263 | yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) 264 | e.emit() 265 | f() 266 | yaml_mapping_end_event_initialize(&e.event) 267 | e.emit() 268 | } 269 | 270 | func (e *encoder) slicev(tag string, in reflect.Value) { 271 | implicit := tag == "" 272 | style := yaml_BLOCK_SEQUENCE_STYLE 273 | if e.flow { 274 | e.flow = false 275 | style = yaml_FLOW_SEQUENCE_STYLE 276 | } 277 | e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 278 | e.emit() 279 | n := in.Len() 280 | for i := 0; i < n; i++ { 281 | e.marshal("", in.Index(i)) 282 | } 283 | e.must(yaml_sequence_end_event_initialize(&e.event)) 284 | e.emit() 285 | } 286 | 287 | // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 288 | // 289 | // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 290 | // in YAML 1.2 and by this package, but these should be marshalled quoted for 291 | // the time being for compatibility with other parsers. 292 | func isBase60Float(s string) (result bool) { 293 | // Fast path. 294 | if s == "" { 295 | return false 296 | } 297 | c := s[0] 298 | if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 299 | return false 300 | } 301 | // Do the full match. 302 | return base60float.MatchString(s) 303 | } 304 | 305 | // From http://yaml.org/type/float.html, except the regular expression there 306 | // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 307 | var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 308 | 309 | // isOldBool returns whether s is bool notation as defined in YAML 1.1. 310 | // 311 | // We continue to force strings that YAML 1.1 would interpret as booleans to be 312 | // rendered as quotes strings so that the marshalled output valid for YAML 1.1 313 | // parsing. 314 | func isOldBool(s string) (result bool) { 315 | switch s { 316 | case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", 317 | "n", "N", "no", "No", "NO", "off", "Off", "OFF": 318 | return true 319 | default: 320 | return false 321 | } 322 | } 323 | 324 | func (e *encoder) stringv(tag string, in reflect.Value) { 325 | var style yaml_scalar_style_t 326 | s := in.String() 327 | canUsePlain := true 328 | switch { 329 | case !utf8.ValidString(s): 330 | if tag == binaryTag { 331 | failf("explicitly tagged !!binary data must be base64-encoded") 332 | } 333 | if tag != "" { 334 | failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 335 | } 336 | // It can't be encoded directly as YAML so use a binary tag 337 | // and encode it as base64. 338 | tag = binaryTag 339 | s = encodeBase64(s) 340 | case tag == "": 341 | // Check to see if it would resolve to a specific 342 | // tag when encoded unquoted. If it doesn't, 343 | // there's no need to quote it. 344 | rtag, _ := resolve("", s) 345 | canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) 346 | } 347 | // Note: it's possible for user code to emit invalid YAML 348 | // if they explicitly specify a tag and a string containing 349 | // text that's incompatible with that tag. 350 | switch { 351 | case strings.Contains(s, "\n"): 352 | if e.flow { 353 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 354 | } else { 355 | style = yaml_LITERAL_SCALAR_STYLE 356 | } 357 | case canUsePlain: 358 | style = yaml_PLAIN_SCALAR_STYLE 359 | default: 360 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 361 | } 362 | e.emitScalar(s, "", tag, style, nil, nil, nil, nil) 363 | } 364 | 365 | func (e *encoder) boolv(tag string, in reflect.Value) { 366 | var s string 367 | if in.Bool() { 368 | s = "true" 369 | } else { 370 | s = "false" 371 | } 372 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 373 | } 374 | 375 | func (e *encoder) intv(tag string, in reflect.Value) { 376 | s := strconv.FormatInt(in.Int(), 10) 377 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 378 | } 379 | 380 | func (e *encoder) uintv(tag string, in reflect.Value) { 381 | s := strconv.FormatUint(in.Uint(), 10) 382 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 383 | } 384 | 385 | func (e *encoder) timev(tag string, in reflect.Value) { 386 | t := in.Interface().(time.Time) 387 | s := t.Format(time.RFC3339Nano) 388 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 389 | } 390 | 391 | func (e *encoder) floatv(tag string, in reflect.Value) { 392 | // Issue #352: When formatting, use the precision of the underlying value 393 | precision := 64 394 | if in.Kind() == reflect.Float32 { 395 | precision = 32 396 | } 397 | 398 | s := strconv.FormatFloat(in.Float(), 'g', -1, precision) 399 | switch s { 400 | case "+Inf": 401 | s = ".inf" 402 | case "-Inf": 403 | s = "-.inf" 404 | case "NaN": 405 | s = ".nan" 406 | } 407 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 408 | } 409 | 410 | func (e *encoder) nilv() { 411 | e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) 412 | } 413 | 414 | func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { 415 | // TODO Kill this function. Replace all initialize calls by their underlining Go literals. 416 | implicit := tag == "" 417 | if !implicit { 418 | tag = longTag(tag) 419 | } 420 | e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 421 | e.event.head_comment = head 422 | e.event.line_comment = line 423 | e.event.foot_comment = foot 424 | e.event.tail_comment = tail 425 | e.emit() 426 | } 427 | 428 | func (e *encoder) nodev(in reflect.Value) { 429 | e.node(in.Interface().(*Node), "") 430 | } 431 | 432 | func (e *encoder) node(node *Node, tail string) { 433 | // Zero nodes behave as nil. 434 | if node.Kind == 0 && node.IsZero() { 435 | e.nilv() 436 | return 437 | } 438 | 439 | // If the tag was not explicitly requested, and dropping it won't change the 440 | // implicit tag of the value, don't include it in the presentation. 441 | var tag = node.Tag 442 | var stag = shortTag(tag) 443 | var forceQuoting bool 444 | if tag != "" && node.Style&TaggedStyle == 0 { 445 | if node.Kind == ScalarNode { 446 | if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { 447 | tag = "" 448 | } else { 449 | rtag, _ := resolve("", node.Value) 450 | if rtag == stag { 451 | tag = "" 452 | } else if stag == strTag { 453 | tag = "" 454 | forceQuoting = true 455 | } 456 | } 457 | } else { 458 | var rtag string 459 | switch node.Kind { 460 | case MappingNode: 461 | rtag = mapTag 462 | case SequenceNode: 463 | rtag = seqTag 464 | } 465 | if rtag == stag { 466 | tag = "" 467 | } 468 | } 469 | } 470 | 471 | switch node.Kind { 472 | case DocumentNode: 473 | yaml_document_start_event_initialize(&e.event, nil, nil, true) 474 | e.event.head_comment = []byte(node.HeadComment) 475 | e.emit() 476 | for _, node := range node.Content { 477 | e.node(node, "") 478 | } 479 | yaml_document_end_event_initialize(&e.event, true) 480 | e.event.foot_comment = []byte(node.FootComment) 481 | e.emit() 482 | 483 | case SequenceNode: 484 | style := yaml_BLOCK_SEQUENCE_STYLE 485 | if node.Style&FlowStyle != 0 { 486 | style = yaml_FLOW_SEQUENCE_STYLE 487 | } 488 | e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) 489 | e.event.head_comment = []byte(node.HeadComment) 490 | e.emit() 491 | for _, node := range node.Content { 492 | e.node(node, "") 493 | } 494 | e.must(yaml_sequence_end_event_initialize(&e.event)) 495 | e.event.line_comment = []byte(node.LineComment) 496 | e.event.foot_comment = []byte(node.FootComment) 497 | e.emit() 498 | 499 | case MappingNode: 500 | style := yaml_BLOCK_MAPPING_STYLE 501 | if node.Style&FlowStyle != 0 { 502 | style = yaml_FLOW_MAPPING_STYLE 503 | } 504 | yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) 505 | e.event.tail_comment = []byte(tail) 506 | e.event.head_comment = []byte(node.HeadComment) 507 | e.emit() 508 | 509 | // The tail logic below moves the foot comment of prior keys to the following key, 510 | // since the value for each key may be a nested structure and the foot needs to be 511 | // processed only the entirety of the value is streamed. The last tail is processed 512 | // with the mapping end event. 513 | var tail string 514 | for i := 0; i+1 < len(node.Content); i += 2 { 515 | k := node.Content[i] 516 | foot := k.FootComment 517 | if foot != "" { 518 | kopy := *k 519 | kopy.FootComment = "" 520 | k = &kopy 521 | } 522 | e.node(k, tail) 523 | tail = foot 524 | 525 | v := node.Content[i+1] 526 | e.node(v, "") 527 | } 528 | 529 | yaml_mapping_end_event_initialize(&e.event) 530 | e.event.tail_comment = []byte(tail) 531 | e.event.line_comment = []byte(node.LineComment) 532 | e.event.foot_comment = []byte(node.FootComment) 533 | e.emit() 534 | 535 | case AliasNode: 536 | yaml_alias_event_initialize(&e.event, []byte(node.Value)) 537 | e.event.head_comment = []byte(node.HeadComment) 538 | e.event.line_comment = []byte(node.LineComment) 539 | e.event.foot_comment = []byte(node.FootComment) 540 | e.emit() 541 | 542 | case ScalarNode: 543 | value := node.Value 544 | if !utf8.ValidString(value) { 545 | if stag == binaryTag { 546 | failf("explicitly tagged !!binary data must be base64-encoded") 547 | } 548 | if stag != "" { 549 | failf("cannot marshal invalid UTF-8 data as %s", stag) 550 | } 551 | // It can't be encoded directly as YAML so use a binary tag 552 | // and encode it as base64. 553 | tag = binaryTag 554 | value = encodeBase64(value) 555 | } 556 | 557 | style := yaml_PLAIN_SCALAR_STYLE 558 | switch { 559 | case node.Style&DoubleQuotedStyle != 0: 560 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 561 | case node.Style&SingleQuotedStyle != 0: 562 | style = yaml_SINGLE_QUOTED_SCALAR_STYLE 563 | case node.Style&LiteralStyle != 0: 564 | style = yaml_LITERAL_SCALAR_STYLE 565 | case node.Style&FoldedStyle != 0: 566 | style = yaml_FOLDED_SCALAR_STYLE 567 | case strings.Contains(value, "\n"): 568 | style = yaml_LITERAL_SCALAR_STYLE 569 | case forceQuoting: 570 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 571 | } 572 | 573 | e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) 574 | default: 575 | failf("cannot encode node with unknown kind %d", node.Kind) 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /encode_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml_test 17 | 18 | import ( 19 | "bytes" 20 | "fmt" 21 | "math" 22 | "strconv" 23 | "strings" 24 | "time" 25 | 26 | "net" 27 | "os" 28 | 29 | . "gopkg.in/check.v1" 30 | "gopkg.in/yaml.v3" 31 | ) 32 | 33 | var marshalIntTest = 123 34 | 35 | var marshalTests = []struct { 36 | value interface{} 37 | data string 38 | }{ 39 | { 40 | nil, 41 | "null\n", 42 | }, { 43 | (*marshalerType)(nil), 44 | "null\n", 45 | }, { 46 | &struct{}{}, 47 | "{}\n", 48 | }, { 49 | map[string]string{"v": "hi"}, 50 | "v: hi\n", 51 | }, { 52 | map[string]interface{}{"v": "hi"}, 53 | "v: hi\n", 54 | }, { 55 | map[string]string{"v": "true"}, 56 | "v: \"true\"\n", 57 | }, { 58 | map[string]string{"v": "false"}, 59 | "v: \"false\"\n", 60 | }, { 61 | map[string]interface{}{"v": true}, 62 | "v: true\n", 63 | }, { 64 | map[string]interface{}{"v": false}, 65 | "v: false\n", 66 | }, { 67 | map[string]interface{}{"v": 10}, 68 | "v: 10\n", 69 | }, { 70 | map[string]interface{}{"v": -10}, 71 | "v: -10\n", 72 | }, { 73 | map[string]uint{"v": 42}, 74 | "v: 42\n", 75 | }, { 76 | map[string]interface{}{"v": int64(4294967296)}, 77 | "v: 4294967296\n", 78 | }, { 79 | map[string]int64{"v": int64(4294967296)}, 80 | "v: 4294967296\n", 81 | }, { 82 | map[string]uint64{"v": 4294967296}, 83 | "v: 4294967296\n", 84 | }, { 85 | map[string]interface{}{"v": "10"}, 86 | "v: \"10\"\n", 87 | }, { 88 | map[string]interface{}{"v": 0.1}, 89 | "v: 0.1\n", 90 | }, { 91 | map[string]interface{}{"v": float64(0.1)}, 92 | "v: 0.1\n", 93 | }, { 94 | map[string]interface{}{"v": float32(0.99)}, 95 | "v: 0.99\n", 96 | }, { 97 | map[string]interface{}{"v": -0.1}, 98 | "v: -0.1\n", 99 | }, { 100 | map[string]interface{}{"v": math.Inf(+1)}, 101 | "v: .inf\n", 102 | }, { 103 | map[string]interface{}{"v": math.Inf(-1)}, 104 | "v: -.inf\n", 105 | }, { 106 | map[string]interface{}{"v": math.NaN()}, 107 | "v: .nan\n", 108 | }, { 109 | map[string]interface{}{"v": nil}, 110 | "v: null\n", 111 | }, { 112 | map[string]interface{}{"v": ""}, 113 | "v: \"\"\n", 114 | }, { 115 | map[string][]string{"v": []string{"A", "B"}}, 116 | "v:\n - A\n - B\n", 117 | }, { 118 | map[string][]string{"v": []string{"A", "B\nC"}}, 119 | "v:\n - A\n - |-\n B\n C\n", 120 | }, { 121 | map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, 122 | "v:\n - A\n - 1\n - B:\n - 2\n - 3\n", 123 | }, { 124 | map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, 125 | "a:\n b: c\n", 126 | }, { 127 | map[string]interface{}{"a": "-"}, 128 | "a: '-'\n", 129 | }, 130 | 131 | // Simple values. 132 | { 133 | &marshalIntTest, 134 | "123\n", 135 | }, 136 | 137 | // Structures 138 | { 139 | &struct{ Hello string }{"world"}, 140 | "hello: world\n", 141 | }, { 142 | &struct { 143 | A struct { 144 | B string 145 | } 146 | }{struct{ B string }{"c"}}, 147 | "a:\n b: c\n", 148 | }, { 149 | &struct { 150 | A *struct { 151 | B string 152 | } 153 | }{&struct{ B string }{"c"}}, 154 | "a:\n b: c\n", 155 | }, { 156 | &struct { 157 | A *struct { 158 | B string 159 | } 160 | }{}, 161 | "a: null\n", 162 | }, { 163 | &struct{ A int }{1}, 164 | "a: 1\n", 165 | }, { 166 | &struct{ A []int }{[]int{1, 2}}, 167 | "a:\n - 1\n - 2\n", 168 | }, { 169 | &struct{ A [2]int }{[2]int{1, 2}}, 170 | "a:\n - 1\n - 2\n", 171 | }, { 172 | &struct { 173 | B int "a" 174 | }{1}, 175 | "a: 1\n", 176 | }, { 177 | &struct{ A bool }{true}, 178 | "a: true\n", 179 | }, { 180 | &struct{ A string }{"true"}, 181 | "a: \"true\"\n", 182 | }, { 183 | &struct{ A string }{"off"}, 184 | "a: \"off\"\n", 185 | }, 186 | 187 | // Conditional flag 188 | { 189 | &struct { 190 | A int "a,omitempty" 191 | B int "b,omitempty" 192 | }{1, 0}, 193 | "a: 1\n", 194 | }, { 195 | &struct { 196 | A int "a,omitempty" 197 | B int "b,omitempty" 198 | }{0, 0}, 199 | "{}\n", 200 | }, { 201 | &struct { 202 | A *struct{ X, y int } "a,omitempty,flow" 203 | }{&struct{ X, y int }{1, 2}}, 204 | "a: {x: 1}\n", 205 | }, { 206 | &struct { 207 | A *struct{ X, y int } "a,omitempty,flow" 208 | }{nil}, 209 | "{}\n", 210 | }, { 211 | &struct { 212 | A *struct{ X, y int } "a,omitempty,flow" 213 | }{&struct{ X, y int }{}}, 214 | "a: {x: 0}\n", 215 | }, { 216 | &struct { 217 | A struct{ X, y int } "a,omitempty,flow" 218 | }{struct{ X, y int }{1, 2}}, 219 | "a: {x: 1}\n", 220 | }, { 221 | &struct { 222 | A struct{ X, y int } "a,omitempty,flow" 223 | }{struct{ X, y int }{0, 1}}, 224 | "{}\n", 225 | }, { 226 | &struct { 227 | A float64 "a,omitempty" 228 | B float64 "b,omitempty" 229 | }{1, 0}, 230 | "a: 1\n", 231 | }, 232 | { 233 | &struct { 234 | T1 time.Time "t1,omitempty" 235 | T2 time.Time "t2,omitempty" 236 | T3 *time.Time "t3,omitempty" 237 | T4 *time.Time "t4,omitempty" 238 | }{ 239 | T2: time.Date(2018, 1, 9, 10, 40, 47, 0, time.UTC), 240 | T4: newTime(time.Date(2098, 1, 9, 10, 40, 47, 0, time.UTC)), 241 | }, 242 | "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", 243 | }, 244 | // Nil interface that implements Marshaler. 245 | { 246 | map[string]yaml.Marshaler{ 247 | "a": nil, 248 | }, 249 | "a: null\n", 250 | }, 251 | 252 | // Flow flag 253 | { 254 | &struct { 255 | A []int "a,flow" 256 | }{[]int{1, 2}}, 257 | "a: [1, 2]\n", 258 | }, { 259 | &struct { 260 | A map[string]string "a,flow" 261 | }{map[string]string{"b": "c", "d": "e"}}, 262 | "a: {b: c, d: e}\n", 263 | }, { 264 | &struct { 265 | A struct { 266 | B, D string 267 | } "a,flow" 268 | }{struct{ B, D string }{"c", "e"}}, 269 | "a: {b: c, d: e}\n", 270 | }, { 271 | &struct { 272 | A string "a,flow" 273 | }{"b\nc"}, 274 | "a: \"b\\nc\"\n", 275 | }, 276 | 277 | // Unexported field 278 | { 279 | &struct { 280 | u int 281 | A int 282 | }{0, 1}, 283 | "a: 1\n", 284 | }, 285 | 286 | // Ignored field 287 | { 288 | &struct { 289 | A int 290 | B int "-" 291 | }{1, 2}, 292 | "a: 1\n", 293 | }, 294 | 295 | // Struct inlining 296 | { 297 | &struct { 298 | A int 299 | C inlineB `yaml:",inline"` 300 | }{1, inlineB{2, inlineC{3}}}, 301 | "a: 1\nb: 2\nc: 3\n", 302 | }, 303 | // Struct inlining as a pointer 304 | { 305 | &struct { 306 | A int 307 | C *inlineB `yaml:",inline"` 308 | }{1, &inlineB{2, inlineC{3}}}, 309 | "a: 1\nb: 2\nc: 3\n", 310 | }, { 311 | &struct { 312 | A int 313 | C *inlineB `yaml:",inline"` 314 | }{1, nil}, 315 | "a: 1\n", 316 | }, { 317 | &struct { 318 | A int 319 | D *inlineD `yaml:",inline"` 320 | }{1, &inlineD{&inlineC{3}, 4}}, 321 | "a: 1\nc: 3\nd: 4\n", 322 | }, 323 | 324 | // Map inlining 325 | { 326 | &struct { 327 | A int 328 | C map[string]int `yaml:",inline"` 329 | }{1, map[string]int{"b": 2, "c": 3}}, 330 | "a: 1\nb: 2\nc: 3\n", 331 | }, 332 | 333 | // Duration 334 | { 335 | map[string]time.Duration{"a": 3 * time.Second}, 336 | "a: 3s\n", 337 | }, 338 | 339 | // Issue #24: bug in map merging logic. 340 | { 341 | map[string]string{"a": ""}, 342 | "a: \n", 343 | }, 344 | 345 | // Issue #34: marshal unsupported base 60 floats quoted for compatibility 346 | // with old YAML 1.1 parsers. 347 | { 348 | map[string]string{"a": "1:1"}, 349 | "a: \"1:1\"\n", 350 | }, 351 | 352 | // Binary data. 353 | { 354 | map[string]string{"a": "\x00"}, 355 | "a: \"\\0\"\n", 356 | }, { 357 | map[string]string{"a": "\x80\x81\x82"}, 358 | "a: !!binary gIGC\n", 359 | }, { 360 | map[string]string{"a": strings.Repeat("\x90", 54)}, 361 | "a: !!binary |\n " + strings.Repeat("kJCQ", 17) + "kJ\n CQ\n", 362 | }, 363 | 364 | // Encode unicode as utf-8 rather than in escaped form. 365 | { 366 | map[string]string{"a": "你好"}, 367 | "a: 你好\n", 368 | }, 369 | 370 | // Support encoding.TextMarshaler. 371 | { 372 | map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)}, 373 | "a: 1.2.3.4\n", 374 | }, 375 | // time.Time gets a timestamp tag. 376 | { 377 | map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)}, 378 | "a: 2015-02-24T18:19:39Z\n", 379 | }, 380 | { 381 | map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))}, 382 | "a: 2015-02-24T18:19:39Z\n", 383 | }, 384 | { 385 | // This is confirmed to be properly decoded in Python (libyaml) without a timestamp tag. 386 | map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))}, 387 | "a: 2015-02-24T18:19:39.123456789-03:00\n", 388 | }, 389 | // Ensure timestamp-like strings are quoted. 390 | { 391 | map[string]string{"a": "2015-02-24T18:19:39Z"}, 392 | "a: \"2015-02-24T18:19:39Z\"\n", 393 | }, 394 | 395 | // Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible). 396 | { 397 | map[string]string{"a": "b: c"}, 398 | "a: 'b: c'\n", 399 | }, 400 | 401 | // Containing hash mark ('#') in string should be quoted 402 | { 403 | map[string]string{"a": "Hello #comment"}, 404 | "a: 'Hello #comment'\n", 405 | }, 406 | { 407 | map[string]string{"a": "你好 #comment"}, 408 | "a: '你好 #comment'\n", 409 | }, 410 | 411 | // Ensure MarshalYAML also gets called on the result of MarshalYAML itself. 412 | { 413 | &marshalerType{marshalerType{true}}, 414 | "true\n", 415 | }, { 416 | &marshalerType{&marshalerType{true}}, 417 | "true\n", 418 | }, 419 | 420 | // Check indentation of maps inside sequences inside maps. 421 | { 422 | map[string]interface{}{"a": map[string]interface{}{"b": []map[string]int{{"c": 1, "d": 2}}}}, 423 | "a:\n b:\n - c: 1\n d: 2\n", 424 | }, 425 | 426 | // Strings with tabs were disallowed as literals (issue #471). 427 | { 428 | map[string]string{"a": "\tB\n\tC\n"}, 429 | "a: |\n \tB\n \tC\n", 430 | }, 431 | 432 | // Ensure that strings do not wrap 433 | { 434 | map[string]string{"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 "}, 435 | "a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n", 436 | }, 437 | 438 | // yaml.Node 439 | { 440 | &struct { 441 | Value yaml.Node 442 | }{ 443 | yaml.Node{ 444 | Kind: yaml.ScalarNode, 445 | Tag: "!!str", 446 | Value: "foo", 447 | Style: yaml.SingleQuotedStyle, 448 | }, 449 | }, 450 | "value: 'foo'\n", 451 | }, { 452 | yaml.Node{ 453 | Kind: yaml.ScalarNode, 454 | Tag: "!!str", 455 | Value: "foo", 456 | Style: yaml.SingleQuotedStyle, 457 | }, 458 | "'foo'\n", 459 | }, 460 | 461 | // Enforced tagging with shorthand notation (issue #616). 462 | { 463 | &struct { 464 | Value yaml.Node 465 | }{ 466 | yaml.Node{ 467 | Kind: yaml.ScalarNode, 468 | Style: yaml.TaggedStyle, 469 | Value: "foo", 470 | Tag: "!!str", 471 | }, 472 | }, 473 | "value: !!str foo\n", 474 | }, { 475 | &struct { 476 | Value yaml.Node 477 | }{ 478 | yaml.Node{ 479 | Kind: yaml.MappingNode, 480 | Style: yaml.TaggedStyle, 481 | Tag: "!!map", 482 | }, 483 | }, 484 | "value: !!map {}\n", 485 | }, { 486 | &struct { 487 | Value yaml.Node 488 | }{ 489 | yaml.Node{ 490 | Kind: yaml.SequenceNode, 491 | Style: yaml.TaggedStyle, 492 | Tag: "!!seq", 493 | }, 494 | }, 495 | "value: !!seq []\n", 496 | }, 497 | } 498 | 499 | func (s *S) TestMarshal(c *C) { 500 | defer os.Setenv("TZ", os.Getenv("TZ")) 501 | os.Setenv("TZ", "UTC") 502 | for i, item := range marshalTests { 503 | c.Logf("test %d: %q", i, item.data) 504 | data, err := yaml.Marshal(item.value) 505 | c.Assert(err, IsNil) 506 | c.Assert(string(data), Equals, item.data) 507 | } 508 | } 509 | 510 | func (s *S) TestEncoderSingleDocument(c *C) { 511 | for i, item := range marshalTests { 512 | c.Logf("test %d. %q", i, item.data) 513 | var buf bytes.Buffer 514 | enc := yaml.NewEncoder(&buf) 515 | err := enc.Encode(item.value) 516 | c.Assert(err, Equals, nil) 517 | err = enc.Close() 518 | c.Assert(err, Equals, nil) 519 | c.Assert(buf.String(), Equals, item.data) 520 | } 521 | } 522 | 523 | func (s *S) TestEncoderMultipleDocuments(c *C) { 524 | var buf bytes.Buffer 525 | enc := yaml.NewEncoder(&buf) 526 | err := enc.Encode(map[string]string{"a": "b"}) 527 | c.Assert(err, Equals, nil) 528 | err = enc.Encode(map[string]string{"c": "d"}) 529 | c.Assert(err, Equals, nil) 530 | err = enc.Close() 531 | c.Assert(err, Equals, nil) 532 | c.Assert(buf.String(), Equals, "a: b\n---\nc: d\n") 533 | } 534 | 535 | func (s *S) TestEncoderWriteError(c *C) { 536 | enc := yaml.NewEncoder(errorWriter{}) 537 | err := enc.Encode(map[string]string{"a": "b"}) 538 | c.Assert(err, ErrorMatches, `yaml: write error: some write error`) // Data not flushed yet 539 | } 540 | 541 | type errorWriter struct{} 542 | 543 | func (errorWriter) Write([]byte) (int, error) { 544 | return 0, fmt.Errorf("some write error") 545 | } 546 | 547 | var marshalErrorTests = []struct { 548 | value interface{} 549 | error string 550 | panic string 551 | }{{ 552 | value: &struct { 553 | B int 554 | inlineB ",inline" 555 | }{1, inlineB{2, inlineC{3}}}, 556 | panic: `duplicated key 'b' in struct struct \{ B int; .*`, 557 | }, { 558 | value: &struct { 559 | A int 560 | B map[string]int ",inline" 561 | }{1, map[string]int{"a": 2}}, 562 | panic: `cannot have key "a" in inlined map: conflicts with struct field`, 563 | }} 564 | 565 | func (s *S) TestMarshalErrors(c *C) { 566 | for _, item := range marshalErrorTests { 567 | if item.panic != "" { 568 | c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic) 569 | } else { 570 | _, err := yaml.Marshal(item.value) 571 | c.Assert(err, ErrorMatches, item.error) 572 | } 573 | } 574 | } 575 | 576 | func (s *S) TestMarshalTypeCache(c *C) { 577 | var data []byte 578 | var err error 579 | func() { 580 | type T struct{ A int } 581 | data, err = yaml.Marshal(&T{}) 582 | c.Assert(err, IsNil) 583 | }() 584 | func() { 585 | type T struct{ B int } 586 | data, err = yaml.Marshal(&T{}) 587 | c.Assert(err, IsNil) 588 | }() 589 | c.Assert(string(data), Equals, "b: 0\n") 590 | } 591 | 592 | var marshalerTests = []struct { 593 | data string 594 | value interface{} 595 | }{ 596 | {"_:\n hi: there\n", map[interface{}]interface{}{"hi": "there"}}, 597 | {"_:\n - 1\n - A\n", []interface{}{1, "A"}}, 598 | {"_: 10\n", 10}, 599 | {"_: null\n", nil}, 600 | {"_: BAR!\n", "BAR!"}, 601 | } 602 | 603 | type marshalerType struct { 604 | value interface{} 605 | } 606 | 607 | func (o marshalerType) MarshalText() ([]byte, error) { 608 | panic("MarshalText called on type with MarshalYAML") 609 | } 610 | 611 | func (o marshalerType) MarshalYAML() (interface{}, error) { 612 | return o.value, nil 613 | } 614 | 615 | type marshalerValue struct { 616 | Field marshalerType "_" 617 | } 618 | 619 | func (s *S) TestMarshaler(c *C) { 620 | for _, item := range marshalerTests { 621 | obj := &marshalerValue{} 622 | obj.Field.value = item.value 623 | data, err := yaml.Marshal(obj) 624 | c.Assert(err, IsNil) 625 | c.Assert(string(data), Equals, string(item.data)) 626 | } 627 | } 628 | 629 | func (s *S) TestMarshalerWholeDocument(c *C) { 630 | obj := &marshalerType{} 631 | obj.value = map[string]string{"hello": "world!"} 632 | data, err := yaml.Marshal(obj) 633 | c.Assert(err, IsNil) 634 | c.Assert(string(data), Equals, "hello: world!\n") 635 | } 636 | 637 | type failingMarshaler struct{} 638 | 639 | func (ft *failingMarshaler) MarshalYAML() (interface{}, error) { 640 | return nil, failingErr 641 | } 642 | 643 | func (s *S) TestMarshalerError(c *C) { 644 | _, err := yaml.Marshal(&failingMarshaler{}) 645 | c.Assert(err, Equals, failingErr) 646 | } 647 | 648 | func (s *S) TestSetIndent(c *C) { 649 | var buf bytes.Buffer 650 | enc := yaml.NewEncoder(&buf) 651 | enc.SetIndent(8) 652 | err := enc.Encode(map[string]interface{}{"a": map[string]interface{}{"b": map[string]string{"c": "d"}}}) 653 | c.Assert(err, Equals, nil) 654 | err = enc.Close() 655 | c.Assert(err, Equals, nil) 656 | c.Assert(buf.String(), Equals, "a:\n b:\n c: d\n") 657 | } 658 | 659 | func (s *S) TestSortedOutput(c *C) { 660 | order := []interface{}{ 661 | false, 662 | true, 663 | 1, 664 | uint(1), 665 | 1.0, 666 | 1.1, 667 | 1.2, 668 | 2, 669 | uint(2), 670 | 2.0, 671 | 2.1, 672 | "", 673 | ".1", 674 | ".2", 675 | ".a", 676 | "1", 677 | "2", 678 | "a!10", 679 | "a/0001", 680 | "a/002", 681 | "a/3", 682 | "a/10", 683 | "a/11", 684 | "a/0012", 685 | "a/100", 686 | "a~10", 687 | "ab/1", 688 | "b/1", 689 | "b/01", 690 | "b/2", 691 | "b/02", 692 | "b/3", 693 | "b/03", 694 | "b1", 695 | "b01", 696 | "b3", 697 | "c2.10", 698 | "c10.2", 699 | "d1", 700 | "d7", 701 | "d7abc", 702 | "d12", 703 | "d12a", 704 | "e2b", 705 | "e4b", 706 | "e21a", 707 | } 708 | m := make(map[interface{}]int) 709 | for _, k := range order { 710 | m[k] = 1 711 | } 712 | data, err := yaml.Marshal(m) 713 | c.Assert(err, IsNil) 714 | out := "\n" + string(data) 715 | last := 0 716 | for i, k := range order { 717 | repr := fmt.Sprint(k) 718 | if s, ok := k.(string); ok { 719 | if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil { 720 | repr = `"` + repr + `"` 721 | } 722 | } 723 | index := strings.Index(out, "\n"+repr+":") 724 | if index == -1 { 725 | c.Fatalf("%#v is not in the output: %#v", k, out) 726 | } 727 | if index < last { 728 | c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out) 729 | } 730 | last = index 731 | } 732 | } 733 | 734 | func newTime(t time.Time) *time.Time { 735 | return &t 736 | } 737 | -------------------------------------------------------------------------------- /example_embedded_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml_test 17 | 18 | import ( 19 | "fmt" 20 | "log" 21 | 22 | "gopkg.in/yaml.v3" 23 | ) 24 | 25 | // An example showing how to unmarshal embedded 26 | // structs from YAML. 27 | 28 | type StructA struct { 29 | A string `yaml:"a"` 30 | } 31 | 32 | type StructB struct { 33 | // Embedded structs are not treated as embedded in YAML by default. To do that, 34 | // add the ",inline" annotation below 35 | StructA `yaml:",inline"` 36 | B string `yaml:"b"` 37 | } 38 | 39 | var data = ` 40 | a: a string from struct A 41 | b: a string from struct B 42 | ` 43 | 44 | func ExampleUnmarshal_embedded() { 45 | var b StructB 46 | 47 | err := yaml.Unmarshal([]byte(data), &b) 48 | if err != nil { 49 | log.Fatalf("cannot unmarshal data: %v", err) 50 | } 51 | fmt.Println(b.A) 52 | fmt.Println(b.B) 53 | // Output: 54 | // a string from struct A 55 | // a string from struct B 56 | } 57 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module "gopkg.in/yaml.v3" 2 | 3 | require ( 4 | "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 5 | ) 6 | -------------------------------------------------------------------------------- /limit_test.go: -------------------------------------------------------------------------------- 1 | package yaml_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | . "gopkg.in/check.v1" 8 | "gopkg.in/yaml.v3" 9 | ) 10 | 11 | var limitTests = []struct { 12 | name string 13 | data []byte 14 | error string 15 | }{ 16 | { 17 | name: "1000kb of maps with 100 aliases", 18 | data: []byte(`{a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-100) + `], b: &b [*a` + strings.Repeat(`,*a`, 99) + `]}`), 19 | error: "yaml: document contains excessive aliasing", 20 | }, { 21 | name: "1000kb of deeply nested slices", 22 | data: []byte(strings.Repeat(`[`, 1000*1024)), 23 | error: "yaml: exceeded max depth of 10000", 24 | }, { 25 | name: "1000kb of deeply nested maps", 26 | data: []byte("x: " + strings.Repeat(`{`, 1000*1024)), 27 | error: "yaml: exceeded max depth of 10000", 28 | }, { 29 | name: "1000kb of deeply nested indents", 30 | data: []byte(strings.Repeat(`- `, 1000*1024)), 31 | error: "yaml: exceeded max depth of 10000", 32 | }, { 33 | name: "1000kb of 1000-indent lines", 34 | data: []byte(strings.Repeat(strings.Repeat(`- `, 1000)+"\n", 1024/2)), 35 | }, 36 | {name: "1kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1*1024/4-1) + `]`)}, 37 | {name: "10kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 10*1024/4-1) + `]`)}, 38 | {name: "100kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 100*1024/4-1) + `]`)}, 39 | {name: "1000kb of maps", data: []byte(`a: &a [{a}` + strings.Repeat(`,{a}`, 1000*1024/4-1) + `]`)}, 40 | {name: "1000kb slice nested at max-depth", data: []byte(strings.Repeat(`[`, 10000) + `1` + strings.Repeat(`,1`, 1000*1024/2-20000-1) + strings.Repeat(`]`, 10000))}, 41 | {name: "1000kb slice nested in maps at max-depth", data: []byte("{a,b:\n" + strings.Repeat(" {a,b:", 10000-2) + ` [1` + strings.Repeat(",1", 1000*1024/2-6*10000-1) + `]` + strings.Repeat(`}`, 10000-1))}, 42 | {name: "1000kb of 10000-nested lines", data: []byte(strings.Repeat(`- `+strings.Repeat(`[`, 10000)+strings.Repeat(`]`, 10000)+"\n", 1000*1024/20000))}, 43 | } 44 | 45 | func (s *S) TestLimits(c *C) { 46 | if testing.Short() { 47 | return 48 | } 49 | for _, tc := range limitTests { 50 | var v interface{} 51 | err := yaml.Unmarshal(tc.data, &v) 52 | if len(tc.error) > 0 { 53 | c.Assert(err, ErrorMatches, tc.error, Commentf("testcase: %s", tc.name)) 54 | } else { 55 | c.Assert(err, IsNil, Commentf("testcase: %s", tc.name)) 56 | } 57 | } 58 | } 59 | 60 | func Benchmark1000KB100Aliases(b *testing.B) { 61 | benchmark(b, "1000kb of maps with 100 aliases") 62 | } 63 | func Benchmark1000KBDeeplyNestedSlices(b *testing.B) { 64 | benchmark(b, "1000kb of deeply nested slices") 65 | } 66 | func Benchmark1000KBDeeplyNestedMaps(b *testing.B) { 67 | benchmark(b, "1000kb of deeply nested maps") 68 | } 69 | func Benchmark1000KBDeeplyNestedIndents(b *testing.B) { 70 | benchmark(b, "1000kb of deeply nested indents") 71 | } 72 | func Benchmark1000KB1000IndentLines(b *testing.B) { 73 | benchmark(b, "1000kb of 1000-indent lines") 74 | } 75 | func Benchmark1KBMaps(b *testing.B) { 76 | benchmark(b, "1kb of maps") 77 | } 78 | func Benchmark10KBMaps(b *testing.B) { 79 | benchmark(b, "10kb of maps") 80 | } 81 | func Benchmark100KBMaps(b *testing.B) { 82 | benchmark(b, "100kb of maps") 83 | } 84 | func Benchmark1000KBMaps(b *testing.B) { 85 | benchmark(b, "1000kb of maps") 86 | } 87 | 88 | func BenchmarkDeepSlice(b *testing.B) { 89 | benchmark(b, "1000kb slice nested at max-depth") 90 | } 91 | 92 | func BenchmarkDeepFlow(b *testing.B) { 93 | benchmark(b, "1000kb slice nested in maps at max-depth") 94 | } 95 | 96 | func Benchmark1000KBMaxDepthNested(b *testing.B) { 97 | benchmark(b, "1000kb of 10000-nested lines") 98 | } 99 | 100 | func benchmark(b *testing.B, name string) { 101 | for _, t := range limitTests { 102 | if t.name != name { 103 | continue 104 | } 105 | 106 | b.ResetTimer() 107 | 108 | for i := 0; i < b.N; i++ { 109 | var v interface{} 110 | err := yaml.Unmarshal(t.data, &v) 111 | if len(t.error) > 0 { 112 | if err == nil { 113 | b.Errorf("expected error, got none") 114 | } else if err.Error() != t.error { 115 | b.Errorf("expected error '%s', got '%s'", t.error, err.Error()) 116 | } 117 | } else { 118 | if err != nil { 119 | b.Errorf("unexpected error: %v", err) 120 | } 121 | } 122 | } 123 | 124 | return 125 | } 126 | 127 | b.Errorf("testcase %q not found", name) 128 | } 129 | -------------------------------------------------------------------------------- /readerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | import ( 26 | "io" 27 | ) 28 | 29 | // Set the reader error and return 0. 30 | func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { 31 | parser.error = yaml_READER_ERROR 32 | parser.problem = problem 33 | parser.problem_offset = offset 34 | parser.problem_value = value 35 | return false 36 | } 37 | 38 | // Byte order marks. 39 | const ( 40 | bom_UTF8 = "\xef\xbb\xbf" 41 | bom_UTF16LE = "\xff\xfe" 42 | bom_UTF16BE = "\xfe\xff" 43 | ) 44 | 45 | // Determine the input stream encoding by checking the BOM symbol. If no BOM is 46 | // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. 47 | func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { 48 | // Ensure that we had enough bytes in the raw buffer. 49 | for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { 50 | if !yaml_parser_update_raw_buffer(parser) { 51 | return false 52 | } 53 | } 54 | 55 | // Determine the encoding. 56 | buf := parser.raw_buffer 57 | pos := parser.raw_buffer_pos 58 | avail := len(buf) - pos 59 | if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { 60 | parser.encoding = yaml_UTF16LE_ENCODING 61 | parser.raw_buffer_pos += 2 62 | parser.offset += 2 63 | } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { 64 | parser.encoding = yaml_UTF16BE_ENCODING 65 | parser.raw_buffer_pos += 2 66 | parser.offset += 2 67 | } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { 68 | parser.encoding = yaml_UTF8_ENCODING 69 | parser.raw_buffer_pos += 3 70 | parser.offset += 3 71 | } else { 72 | parser.encoding = yaml_UTF8_ENCODING 73 | } 74 | return true 75 | } 76 | 77 | // Update the raw buffer. 78 | func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { 79 | size_read := 0 80 | 81 | // Return if the raw buffer is full. 82 | if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { 83 | return true 84 | } 85 | 86 | // Return on EOF. 87 | if parser.eof { 88 | return true 89 | } 90 | 91 | // Move the remaining bytes in the raw buffer to the beginning. 92 | if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { 93 | copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) 94 | } 95 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] 96 | parser.raw_buffer_pos = 0 97 | 98 | // Call the read handler to fill the buffer. 99 | size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) 100 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] 101 | if err == io.EOF { 102 | parser.eof = true 103 | } else if err != nil { 104 | return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) 105 | } 106 | return true 107 | } 108 | 109 | // Ensure that the buffer contains at least `length` characters. 110 | // Return true on success, false on failure. 111 | // 112 | // The length is supposed to be significantly less that the buffer size. 113 | func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { 114 | if parser.read_handler == nil { 115 | panic("read handler must be set") 116 | } 117 | 118 | // [Go] This function was changed to guarantee the requested length size at EOF. 119 | // The fact we need to do this is pretty awful, but the description above implies 120 | // for that to be the case, and there are tests 121 | 122 | // If the EOF flag is set and the raw buffer is empty, do nothing. 123 | if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { 124 | // [Go] ACTUALLY! Read the documentation of this function above. 125 | // This is just broken. To return true, we need to have the 126 | // given length in the buffer. Not doing that means every single 127 | // check that calls this function to make sure the buffer has a 128 | // given length is Go) panicking; or C) accessing invalid memory. 129 | //return true 130 | } 131 | 132 | // Return if the buffer contains enough characters. 133 | if parser.unread >= length { 134 | return true 135 | } 136 | 137 | // Determine the input encoding if it is not known yet. 138 | if parser.encoding == yaml_ANY_ENCODING { 139 | if !yaml_parser_determine_encoding(parser) { 140 | return false 141 | } 142 | } 143 | 144 | // Move the unread characters to the beginning of the buffer. 145 | buffer_len := len(parser.buffer) 146 | if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { 147 | copy(parser.buffer, parser.buffer[parser.buffer_pos:]) 148 | buffer_len -= parser.buffer_pos 149 | parser.buffer_pos = 0 150 | } else if parser.buffer_pos == buffer_len { 151 | buffer_len = 0 152 | parser.buffer_pos = 0 153 | } 154 | 155 | // Open the whole buffer for writing, and cut it before returning. 156 | parser.buffer = parser.buffer[:cap(parser.buffer)] 157 | 158 | // Fill the buffer until it has enough characters. 159 | first := true 160 | for parser.unread < length { 161 | 162 | // Fill the raw buffer if necessary. 163 | if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { 164 | if !yaml_parser_update_raw_buffer(parser) { 165 | parser.buffer = parser.buffer[:buffer_len] 166 | return false 167 | } 168 | } 169 | first = false 170 | 171 | // Decode the raw buffer. 172 | inner: 173 | for parser.raw_buffer_pos != len(parser.raw_buffer) { 174 | var value rune 175 | var width int 176 | 177 | raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos 178 | 179 | // Decode the next character. 180 | switch parser.encoding { 181 | case yaml_UTF8_ENCODING: 182 | // Decode a UTF-8 character. Check RFC 3629 183 | // (http://www.ietf.org/rfc/rfc3629.txt) for more details. 184 | // 185 | // The following table (taken from the RFC) is used for 186 | // decoding. 187 | // 188 | // Char. number range | UTF-8 octet sequence 189 | // (hexadecimal) | (binary) 190 | // --------------------+------------------------------------ 191 | // 0000 0000-0000 007F | 0xxxxxxx 192 | // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 193 | // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 194 | // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 195 | // 196 | // Additionally, the characters in the range 0xD800-0xDFFF 197 | // are prohibited as they are reserved for use with UTF-16 198 | // surrogate pairs. 199 | 200 | // Determine the length of the UTF-8 sequence. 201 | octet := parser.raw_buffer[parser.raw_buffer_pos] 202 | switch { 203 | case octet&0x80 == 0x00: 204 | width = 1 205 | case octet&0xE0 == 0xC0: 206 | width = 2 207 | case octet&0xF0 == 0xE0: 208 | width = 3 209 | case octet&0xF8 == 0xF0: 210 | width = 4 211 | default: 212 | // The leading octet is invalid. 213 | return yaml_parser_set_reader_error(parser, 214 | "invalid leading UTF-8 octet", 215 | parser.offset, int(octet)) 216 | } 217 | 218 | // Check if the raw buffer contains an incomplete character. 219 | if width > raw_unread { 220 | if parser.eof { 221 | return yaml_parser_set_reader_error(parser, 222 | "incomplete UTF-8 octet sequence", 223 | parser.offset, -1) 224 | } 225 | break inner 226 | } 227 | 228 | // Decode the leading octet. 229 | switch { 230 | case octet&0x80 == 0x00: 231 | value = rune(octet & 0x7F) 232 | case octet&0xE0 == 0xC0: 233 | value = rune(octet & 0x1F) 234 | case octet&0xF0 == 0xE0: 235 | value = rune(octet & 0x0F) 236 | case octet&0xF8 == 0xF0: 237 | value = rune(octet & 0x07) 238 | default: 239 | value = 0 240 | } 241 | 242 | // Check and decode the trailing octets. 243 | for k := 1; k < width; k++ { 244 | octet = parser.raw_buffer[parser.raw_buffer_pos+k] 245 | 246 | // Check if the octet is valid. 247 | if (octet & 0xC0) != 0x80 { 248 | return yaml_parser_set_reader_error(parser, 249 | "invalid trailing UTF-8 octet", 250 | parser.offset+k, int(octet)) 251 | } 252 | 253 | // Decode the octet. 254 | value = (value << 6) + rune(octet&0x3F) 255 | } 256 | 257 | // Check the length of the sequence against the value. 258 | switch { 259 | case width == 1: 260 | case width == 2 && value >= 0x80: 261 | case width == 3 && value >= 0x800: 262 | case width == 4 && value >= 0x10000: 263 | default: 264 | return yaml_parser_set_reader_error(parser, 265 | "invalid length of a UTF-8 sequence", 266 | parser.offset, -1) 267 | } 268 | 269 | // Check the range of the value. 270 | if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { 271 | return yaml_parser_set_reader_error(parser, 272 | "invalid Unicode character", 273 | parser.offset, int(value)) 274 | } 275 | 276 | case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: 277 | var low, high int 278 | if parser.encoding == yaml_UTF16LE_ENCODING { 279 | low, high = 0, 1 280 | } else { 281 | low, high = 1, 0 282 | } 283 | 284 | // The UTF-16 encoding is not as simple as one might 285 | // naively think. Check RFC 2781 286 | // (http://www.ietf.org/rfc/rfc2781.txt). 287 | // 288 | // Normally, two subsequent bytes describe a Unicode 289 | // character. However a special technique (called a 290 | // surrogate pair) is used for specifying character 291 | // values larger than 0xFFFF. 292 | // 293 | // A surrogate pair consists of two pseudo-characters: 294 | // high surrogate area (0xD800-0xDBFF) 295 | // low surrogate area (0xDC00-0xDFFF) 296 | // 297 | // The following formulas are used for decoding 298 | // and encoding characters using surrogate pairs: 299 | // 300 | // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) 301 | // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) 302 | // W1 = 110110yyyyyyyyyy 303 | // W2 = 110111xxxxxxxxxx 304 | // 305 | // where U is the character value, W1 is the high surrogate 306 | // area, W2 is the low surrogate area. 307 | 308 | // Check for incomplete UTF-16 character. 309 | if raw_unread < 2 { 310 | if parser.eof { 311 | return yaml_parser_set_reader_error(parser, 312 | "incomplete UTF-16 character", 313 | parser.offset, -1) 314 | } 315 | break inner 316 | } 317 | 318 | // Get the character. 319 | value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + 320 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) 321 | 322 | // Check for unexpected low surrogate area. 323 | if value&0xFC00 == 0xDC00 { 324 | return yaml_parser_set_reader_error(parser, 325 | "unexpected low surrogate area", 326 | parser.offset, int(value)) 327 | } 328 | 329 | // Check for a high surrogate area. 330 | if value&0xFC00 == 0xD800 { 331 | width = 4 332 | 333 | // Check for incomplete surrogate pair. 334 | if raw_unread < 4 { 335 | if parser.eof { 336 | return yaml_parser_set_reader_error(parser, 337 | "incomplete UTF-16 surrogate pair", 338 | parser.offset, -1) 339 | } 340 | break inner 341 | } 342 | 343 | // Get the next character. 344 | value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + 345 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) 346 | 347 | // Check for a low surrogate area. 348 | if value2&0xFC00 != 0xDC00 { 349 | return yaml_parser_set_reader_error(parser, 350 | "expected low surrogate area", 351 | parser.offset+2, int(value2)) 352 | } 353 | 354 | // Generate the value of the surrogate pair. 355 | value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) 356 | } else { 357 | width = 2 358 | } 359 | 360 | default: 361 | panic("impossible") 362 | } 363 | 364 | // Check if the character is in the allowed range: 365 | // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) 366 | // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) 367 | // | [#x10000-#x10FFFF] (32 bit) 368 | switch { 369 | case value == 0x09: 370 | case value == 0x0A: 371 | case value == 0x0D: 372 | case value >= 0x20 && value <= 0x7E: 373 | case value == 0x85: 374 | case value >= 0xA0 && value <= 0xD7FF: 375 | case value >= 0xE000 && value <= 0xFFFD: 376 | case value >= 0x10000 && value <= 0x10FFFF: 377 | default: 378 | return yaml_parser_set_reader_error(parser, 379 | "control characters are not allowed", 380 | parser.offset, int(value)) 381 | } 382 | 383 | // Move the raw pointers. 384 | parser.raw_buffer_pos += width 385 | parser.offset += width 386 | 387 | // Finally put the character into the buffer. 388 | if value <= 0x7F { 389 | // 0000 0000-0000 007F . 0xxxxxxx 390 | parser.buffer[buffer_len+0] = byte(value) 391 | buffer_len += 1 392 | } else if value <= 0x7FF { 393 | // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx 394 | parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) 395 | parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) 396 | buffer_len += 2 397 | } else if value <= 0xFFFF { 398 | // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx 399 | parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) 400 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) 401 | parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) 402 | buffer_len += 3 403 | } else { 404 | // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 405 | parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) 406 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) 407 | parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) 408 | parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) 409 | buffer_len += 4 410 | } 411 | 412 | parser.unread++ 413 | } 414 | 415 | // On EOF, put NUL into the buffer and return. 416 | if parser.eof { 417 | parser.buffer[buffer_len] = 0 418 | buffer_len++ 419 | parser.unread++ 420 | break 421 | } 422 | } 423 | // [Go] Read the documentation of this function above. To return true, 424 | // we need to have the given length in the buffer. Not doing that means 425 | // every single check that calls this function to make sure the buffer 426 | // has a given length is Go) panicking; or C) accessing invalid memory. 427 | // This happens here due to the EOF above breaking early. 428 | for buffer_len < length { 429 | parser.buffer[buffer_len] = 0 430 | buffer_len++ 431 | } 432 | parser.buffer = parser.buffer[:buffer_len] 433 | return true 434 | } 435 | -------------------------------------------------------------------------------- /resolve.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "encoding/base64" 20 | "math" 21 | "regexp" 22 | "strconv" 23 | "strings" 24 | "time" 25 | ) 26 | 27 | type resolveMapItem struct { 28 | value interface{} 29 | tag string 30 | } 31 | 32 | var resolveTable = make([]byte, 256) 33 | var resolveMap = make(map[string]resolveMapItem) 34 | 35 | func init() { 36 | t := resolveTable 37 | t[int('+')] = 'S' // Sign 38 | t[int('-')] = 'S' 39 | for _, c := range "0123456789" { 40 | t[int(c)] = 'D' // Digit 41 | } 42 | for _, c := range "yYnNtTfFoO~" { 43 | t[int(c)] = 'M' // In map 44 | } 45 | t[int('.')] = '.' // Float (potentially in map) 46 | 47 | var resolveMapList = []struct { 48 | v interface{} 49 | tag string 50 | l []string 51 | }{ 52 | {true, boolTag, []string{"true", "True", "TRUE"}}, 53 | {false, boolTag, []string{"false", "False", "FALSE"}}, 54 | {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, 55 | {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, 56 | {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, 57 | {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, 58 | {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, 59 | {"<<", mergeTag, []string{"<<"}}, 60 | } 61 | 62 | m := resolveMap 63 | for _, item := range resolveMapList { 64 | for _, s := range item.l { 65 | m[s] = resolveMapItem{item.v, item.tag} 66 | } 67 | } 68 | } 69 | 70 | const ( 71 | nullTag = "!!null" 72 | boolTag = "!!bool" 73 | strTag = "!!str" 74 | intTag = "!!int" 75 | floatTag = "!!float" 76 | timestampTag = "!!timestamp" 77 | seqTag = "!!seq" 78 | mapTag = "!!map" 79 | binaryTag = "!!binary" 80 | mergeTag = "!!merge" 81 | ) 82 | 83 | var longTags = make(map[string]string) 84 | var shortTags = make(map[string]string) 85 | 86 | func init() { 87 | for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { 88 | ltag := longTag(stag) 89 | longTags[stag] = ltag 90 | shortTags[ltag] = stag 91 | } 92 | } 93 | 94 | const longTagPrefix = "tag:yaml.org,2002:" 95 | 96 | func shortTag(tag string) string { 97 | if strings.HasPrefix(tag, longTagPrefix) { 98 | if stag, ok := shortTags[tag]; ok { 99 | return stag 100 | } 101 | return "!!" + tag[len(longTagPrefix):] 102 | } 103 | return tag 104 | } 105 | 106 | func longTag(tag string) string { 107 | if strings.HasPrefix(tag, "!!") { 108 | if ltag, ok := longTags[tag]; ok { 109 | return ltag 110 | } 111 | return longTagPrefix + tag[2:] 112 | } 113 | return tag 114 | } 115 | 116 | func resolvableTag(tag string) bool { 117 | switch tag { 118 | case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: 119 | return true 120 | } 121 | return false 122 | } 123 | 124 | var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) 125 | 126 | func resolve(tag string, in string) (rtag string, out interface{}) { 127 | tag = shortTag(tag) 128 | if !resolvableTag(tag) { 129 | return tag, in 130 | } 131 | 132 | defer func() { 133 | switch tag { 134 | case "", rtag, strTag, binaryTag: 135 | return 136 | case floatTag: 137 | if rtag == intTag { 138 | switch v := out.(type) { 139 | case int64: 140 | rtag = floatTag 141 | out = float64(v) 142 | return 143 | case int: 144 | rtag = floatTag 145 | out = float64(v) 146 | return 147 | } 148 | } 149 | } 150 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 151 | }() 152 | 153 | // Any data is accepted as a !!str or !!binary. 154 | // Otherwise, the prefix is enough of a hint about what it might be. 155 | hint := byte('N') 156 | if in != "" { 157 | hint = resolveTable[in[0]] 158 | } 159 | if hint != 0 && tag != strTag && tag != binaryTag { 160 | // Handle things we can lookup in a map. 161 | if item, ok := resolveMap[in]; ok { 162 | return item.tag, item.value 163 | } 164 | 165 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 166 | // are purposefully unsupported here. They're still quoted on 167 | // the way out for compatibility with other parser, though. 168 | 169 | switch hint { 170 | case 'M': 171 | // We've already checked the map above. 172 | 173 | case '.': 174 | // Not in the map, so maybe a normal float. 175 | floatv, err := strconv.ParseFloat(in, 64) 176 | if err == nil { 177 | return floatTag, floatv 178 | } 179 | 180 | case 'D', 'S': 181 | // Int, float, or timestamp. 182 | // Only try values as a timestamp if the value is unquoted or there's an explicit 183 | // !!timestamp tag. 184 | if tag == "" || tag == timestampTag { 185 | t, ok := parseTimestamp(in) 186 | if ok { 187 | return timestampTag, t 188 | } 189 | } 190 | 191 | plain := strings.Replace(in, "_", "", -1) 192 | intv, err := strconv.ParseInt(plain, 0, 64) 193 | if err == nil { 194 | if intv == int64(int(intv)) { 195 | return intTag, int(intv) 196 | } else { 197 | return intTag, intv 198 | } 199 | } 200 | uintv, err := strconv.ParseUint(plain, 0, 64) 201 | if err == nil { 202 | return intTag, uintv 203 | } 204 | if yamlStyleFloat.MatchString(plain) { 205 | floatv, err := strconv.ParseFloat(plain, 64) 206 | if err == nil { 207 | return floatTag, floatv 208 | } 209 | } 210 | if strings.HasPrefix(plain, "0b") { 211 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 212 | if err == nil { 213 | if intv == int64(int(intv)) { 214 | return intTag, int(intv) 215 | } else { 216 | return intTag, intv 217 | } 218 | } 219 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 220 | if err == nil { 221 | return intTag, uintv 222 | } 223 | } else if strings.HasPrefix(plain, "-0b") { 224 | intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) 225 | if err == nil { 226 | if true || intv == int64(int(intv)) { 227 | return intTag, int(intv) 228 | } else { 229 | return intTag, intv 230 | } 231 | } 232 | } 233 | // Octals as introduced in version 1.2 of the spec. 234 | // Octals from the 1.1 spec, spelled as 0777, are still 235 | // decoded by default in v3 as well for compatibility. 236 | // May be dropped in v4 depending on how usage evolves. 237 | if strings.HasPrefix(plain, "0o") { 238 | intv, err := strconv.ParseInt(plain[2:], 8, 64) 239 | if err == nil { 240 | if intv == int64(int(intv)) { 241 | return intTag, int(intv) 242 | } else { 243 | return intTag, intv 244 | } 245 | } 246 | uintv, err := strconv.ParseUint(plain[2:], 8, 64) 247 | if err == nil { 248 | return intTag, uintv 249 | } 250 | } else if strings.HasPrefix(plain, "-0o") { 251 | intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) 252 | if err == nil { 253 | if true || intv == int64(int(intv)) { 254 | return intTag, int(intv) 255 | } else { 256 | return intTag, intv 257 | } 258 | } 259 | } 260 | default: 261 | panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") 262 | } 263 | } 264 | return strTag, in 265 | } 266 | 267 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 268 | // as appropriate for the resulting length. 269 | func encodeBase64(s string) string { 270 | const lineLen = 70 271 | encLen := base64.StdEncoding.EncodedLen(len(s)) 272 | lines := encLen/lineLen + 1 273 | buf := make([]byte, encLen*2+lines) 274 | in := buf[0:encLen] 275 | out := buf[encLen:] 276 | base64.StdEncoding.Encode(in, []byte(s)) 277 | k := 0 278 | for i := 0; i < len(in); i += lineLen { 279 | j := i + lineLen 280 | if j > len(in) { 281 | j = len(in) 282 | } 283 | k += copy(out[k:], in[i:j]) 284 | if lines > 1 { 285 | out[k] = '\n' 286 | k++ 287 | } 288 | } 289 | return string(out[:k]) 290 | } 291 | 292 | // This is a subset of the formats allowed by the regular expression 293 | // defined at http://yaml.org/type/timestamp.html. 294 | var allowedTimestampFormats = []string{ 295 | "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. 296 | "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". 297 | "2006-1-2 15:4:5.999999999", // space separated with no time zone 298 | "2006-1-2", // date only 299 | // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" 300 | // from the set of examples. 301 | } 302 | 303 | // parseTimestamp parses s as a timestamp string and 304 | // returns the timestamp and reports whether it succeeded. 305 | // Timestamp formats are defined at http://yaml.org/type/timestamp.html 306 | func parseTimestamp(s string) (time.Time, bool) { 307 | // TODO write code to check all the formats supported by 308 | // http://yaml.org/type/timestamp.html instead of using time.Parse. 309 | 310 | // Quick check: all date formats start with YYYY-. 311 | i := 0 312 | for ; i < len(s); i++ { 313 | if c := s[i]; c < '0' || c > '9' { 314 | break 315 | } 316 | } 317 | if i != 4 || i == len(s) || s[i] != '-' { 318 | return time.Time{}, false 319 | } 320 | for _, format := range allowedTimestampFormats { 321 | if t, err := time.Parse(format, s); err == nil { 322 | return t, true 323 | } 324 | } 325 | return time.Time{}, false 326 | } 327 | -------------------------------------------------------------------------------- /sorter.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml 17 | 18 | import ( 19 | "reflect" 20 | "unicode" 21 | ) 22 | 23 | type keyList []reflect.Value 24 | 25 | func (l keyList) Len() int { return len(l) } 26 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 27 | func (l keyList) Less(i, j int) bool { 28 | a := l[i] 29 | b := l[j] 30 | ak := a.Kind() 31 | bk := b.Kind() 32 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 33 | a = a.Elem() 34 | ak = a.Kind() 35 | } 36 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 37 | b = b.Elem() 38 | bk = b.Kind() 39 | } 40 | af, aok := keyFloat(a) 41 | bf, bok := keyFloat(b) 42 | if aok && bok { 43 | if af != bf { 44 | return af < bf 45 | } 46 | if ak != bk { 47 | return ak < bk 48 | } 49 | return numLess(a, b) 50 | } 51 | if ak != reflect.String || bk != reflect.String { 52 | return ak < bk 53 | } 54 | ar, br := []rune(a.String()), []rune(b.String()) 55 | digits := false 56 | for i := 0; i < len(ar) && i < len(br); i++ { 57 | if ar[i] == br[i] { 58 | digits = unicode.IsDigit(ar[i]) 59 | continue 60 | } 61 | al := unicode.IsLetter(ar[i]) 62 | bl := unicode.IsLetter(br[i]) 63 | if al && bl { 64 | return ar[i] < br[i] 65 | } 66 | if al || bl { 67 | if digits { 68 | return al 69 | } else { 70 | return bl 71 | } 72 | } 73 | var ai, bi int 74 | var an, bn int64 75 | if ar[i] == '0' || br[i] == '0' { 76 | for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 77 | if ar[j] != '0' { 78 | an = 1 79 | bn = 1 80 | break 81 | } 82 | } 83 | } 84 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 85 | an = an*10 + int64(ar[ai]-'0') 86 | } 87 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 88 | bn = bn*10 + int64(br[bi]-'0') 89 | } 90 | if an != bn { 91 | return an < bn 92 | } 93 | if ai != bi { 94 | return ai < bi 95 | } 96 | return ar[i] < br[i] 97 | } 98 | return len(ar) < len(br) 99 | } 100 | 101 | // keyFloat returns a float value for v if it is a number/bool 102 | // and whether it is a number/bool or not. 103 | func keyFloat(v reflect.Value) (f float64, ok bool) { 104 | switch v.Kind() { 105 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 106 | return float64(v.Int()), true 107 | case reflect.Float32, reflect.Float64: 108 | return v.Float(), true 109 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 110 | return float64(v.Uint()), true 111 | case reflect.Bool: 112 | if v.Bool() { 113 | return 1, true 114 | } 115 | return 0, true 116 | } 117 | return 0, false 118 | } 119 | 120 | // numLess returns whether a < b. 121 | // a and b must necessarily have the same kind. 122 | func numLess(a, b reflect.Value) bool { 123 | switch a.Kind() { 124 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 125 | return a.Int() < b.Int() 126 | case reflect.Float32, reflect.Float64: 127 | return a.Float() < b.Float() 128 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 129 | return a.Uint() < b.Uint() 130 | case reflect.Bool: 131 | return !a.Bool() && b.Bool() 132 | } 133 | panic("not a number") 134 | } 135 | -------------------------------------------------------------------------------- /suite_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package yaml_test 17 | 18 | import ( 19 | . "gopkg.in/check.v1" 20 | "testing" 21 | ) 22 | 23 | func Test(t *testing.T) { TestingT(t) } 24 | 25 | type S struct{} 26 | 27 | var _ = Suite(&S{}) 28 | -------------------------------------------------------------------------------- /writerc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | // Set the writer error and return false. 26 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 27 | emitter.error = yaml_WRITER_ERROR 28 | emitter.problem = problem 29 | return false 30 | } 31 | 32 | // Flush the output buffer. 33 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 34 | if emitter.write_handler == nil { 35 | panic("write handler not set") 36 | } 37 | 38 | // Check if the buffer is empty. 39 | if emitter.buffer_pos == 0 { 40 | return true 41 | } 42 | 43 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 44 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 45 | } 46 | emitter.buffer_pos = 0 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /yaml.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Package yaml implements YAML support for the Go language. 17 | // 18 | // Source code and other details for the project are available at GitHub: 19 | // 20 | // https://github.com/go-yaml/yaml 21 | // 22 | package yaml 23 | 24 | import ( 25 | "errors" 26 | "fmt" 27 | "io" 28 | "reflect" 29 | "strings" 30 | "sync" 31 | "unicode/utf8" 32 | ) 33 | 34 | // The Unmarshaler interface may be implemented by types to customize their 35 | // behavior when being unmarshaled from a YAML document. 36 | type Unmarshaler interface { 37 | UnmarshalYAML(value *Node) error 38 | } 39 | 40 | type obsoleteUnmarshaler interface { 41 | UnmarshalYAML(unmarshal func(interface{}) error) error 42 | } 43 | 44 | // The Marshaler interface may be implemented by types to customize their 45 | // behavior when being marshaled into a YAML document. The returned value 46 | // is marshaled in place of the original value implementing Marshaler. 47 | // 48 | // If an error is returned by MarshalYAML, the marshaling procedure stops 49 | // and returns with the provided error. 50 | type Marshaler interface { 51 | MarshalYAML() (interface{}, error) 52 | } 53 | 54 | // Unmarshal decodes the first document found within the in byte slice 55 | // and assigns decoded values into the out value. 56 | // 57 | // Maps and pointers (to a struct, string, int, etc) are accepted as out 58 | // values. If an internal pointer within a struct is not initialized, 59 | // the yaml package will initialize it if necessary for unmarshalling 60 | // the provided data. The out parameter must not be nil. 61 | // 62 | // The type of the decoded values should be compatible with the respective 63 | // values in out. If one or more values cannot be decoded due to a type 64 | // mismatches, decoding continues partially until the end of the YAML 65 | // content, and a *yaml.TypeError is returned with details for all 66 | // missed values. 67 | // 68 | // Struct fields are only unmarshalled if they are exported (have an 69 | // upper case first letter), and are unmarshalled using the field name 70 | // lowercased as the default key. Custom keys may be defined via the 71 | // "yaml" name in the field tag: the content preceding the first comma 72 | // is used as the key, and the following comma-separated options are 73 | // used to tweak the marshalling process (see Marshal). 74 | // Conflicting names result in a runtime error. 75 | // 76 | // For example: 77 | // 78 | // type T struct { 79 | // F int `yaml:"a,omitempty"` 80 | // B int 81 | // } 82 | // var t T 83 | // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) 84 | // 85 | // See the documentation of Marshal for the format of tags and a list of 86 | // supported tag options. 87 | // 88 | func Unmarshal(in []byte, out interface{}) (err error) { 89 | return unmarshal(in, out, false) 90 | } 91 | 92 | // A Decoder reads and decodes YAML values from an input stream. 93 | type Decoder struct { 94 | parser *parser 95 | knownFields bool 96 | } 97 | 98 | // NewDecoder returns a new decoder that reads from r. 99 | // 100 | // The decoder introduces its own buffering and may read 101 | // data from r beyond the YAML values requested. 102 | func NewDecoder(r io.Reader) *Decoder { 103 | return &Decoder{ 104 | parser: newParserFromReader(r), 105 | } 106 | } 107 | 108 | // KnownFields ensures that the keys in decoded mappings to 109 | // exist as fields in the struct being decoded into. 110 | func (dec *Decoder) KnownFields(enable bool) { 111 | dec.knownFields = enable 112 | } 113 | 114 | // Decode reads the next YAML-encoded value from its input 115 | // and stores it in the value pointed to by v. 116 | // 117 | // See the documentation for Unmarshal for details about the 118 | // conversion of YAML into a Go value. 119 | func (dec *Decoder) Decode(v interface{}) (err error) { 120 | d := newDecoder() 121 | d.knownFields = dec.knownFields 122 | defer handleErr(&err) 123 | node := dec.parser.parse() 124 | if node == nil { 125 | return io.EOF 126 | } 127 | out := reflect.ValueOf(v) 128 | if out.Kind() == reflect.Ptr && !out.IsNil() { 129 | out = out.Elem() 130 | } 131 | d.unmarshal(node, out) 132 | if len(d.terrors) > 0 { 133 | return &TypeError{d.terrors} 134 | } 135 | return nil 136 | } 137 | 138 | // Decode decodes the node and stores its data into the value pointed to by v. 139 | // 140 | // See the documentation for Unmarshal for details about the 141 | // conversion of YAML into a Go value. 142 | func (n *Node) Decode(v interface{}) (err error) { 143 | d := newDecoder() 144 | defer handleErr(&err) 145 | out := reflect.ValueOf(v) 146 | if out.Kind() == reflect.Ptr && !out.IsNil() { 147 | out = out.Elem() 148 | } 149 | d.unmarshal(n, out) 150 | if len(d.terrors) > 0 { 151 | return &TypeError{d.terrors} 152 | } 153 | return nil 154 | } 155 | 156 | func unmarshal(in []byte, out interface{}, strict bool) (err error) { 157 | defer handleErr(&err) 158 | d := newDecoder() 159 | p := newParser(in) 160 | defer p.destroy() 161 | node := p.parse() 162 | if node != nil { 163 | v := reflect.ValueOf(out) 164 | if v.Kind() == reflect.Ptr && !v.IsNil() { 165 | v = v.Elem() 166 | } 167 | d.unmarshal(node, v) 168 | } 169 | if len(d.terrors) > 0 { 170 | return &TypeError{d.terrors} 171 | } 172 | return nil 173 | } 174 | 175 | // Marshal serializes the value provided into a YAML document. The structure 176 | // of the generated document will reflect the structure of the value itself. 177 | // Maps and pointers (to struct, string, int, etc) are accepted as the in value. 178 | // 179 | // Struct fields are only marshalled if they are exported (have an upper case 180 | // first letter), and are marshalled using the field name lowercased as the 181 | // default key. Custom keys may be defined via the "yaml" name in the field 182 | // tag: the content preceding the first comma is used as the key, and the 183 | // following comma-separated options are used to tweak the marshalling process. 184 | // Conflicting names result in a runtime error. 185 | // 186 | // The field tag format accepted is: 187 | // 188 | // `(...) yaml:"[][,[,]]" (...)` 189 | // 190 | // The following flags are currently supported: 191 | // 192 | // omitempty Only include the field if it's not set to the zero 193 | // value for the type or to empty slices or maps. 194 | // Zero valued structs will be omitted if all their public 195 | // fields are zero, unless they implement an IsZero 196 | // method (see the IsZeroer interface type), in which 197 | // case the field will be excluded if IsZero returns true. 198 | // 199 | // flow Marshal using a flow style (useful for structs, 200 | // sequences and maps). 201 | // 202 | // inline Inline the field, which must be a struct or a map, 203 | // causing all of its fields or keys to be processed as if 204 | // they were part of the outer struct. For maps, keys must 205 | // not conflict with the yaml keys of other struct fields. 206 | // 207 | // In addition, if the key is "-", the field is ignored. 208 | // 209 | // For example: 210 | // 211 | // type T struct { 212 | // F int `yaml:"a,omitempty"` 213 | // B int 214 | // } 215 | // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" 216 | // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" 217 | // 218 | func Marshal(in interface{}) (out []byte, err error) { 219 | defer handleErr(&err) 220 | e := newEncoder() 221 | defer e.destroy() 222 | e.marshalDoc("", reflect.ValueOf(in)) 223 | e.finish() 224 | out = e.out 225 | return 226 | } 227 | 228 | // An Encoder writes YAML values to an output stream. 229 | type Encoder struct { 230 | encoder *encoder 231 | } 232 | 233 | // NewEncoder returns a new encoder that writes to w. 234 | // The Encoder should be closed after use to flush all data 235 | // to w. 236 | func NewEncoder(w io.Writer) *Encoder { 237 | return &Encoder{ 238 | encoder: newEncoderWithWriter(w), 239 | } 240 | } 241 | 242 | // Encode writes the YAML encoding of v to the stream. 243 | // If multiple items are encoded to the stream, the 244 | // second and subsequent document will be preceded 245 | // with a "---" document separator, but the first will not. 246 | // 247 | // See the documentation for Marshal for details about the conversion of Go 248 | // values to YAML. 249 | func (e *Encoder) Encode(v interface{}) (err error) { 250 | defer handleErr(&err) 251 | e.encoder.marshalDoc("", reflect.ValueOf(v)) 252 | return nil 253 | } 254 | 255 | // Encode encodes value v and stores its representation in n. 256 | // 257 | // See the documentation for Marshal for details about the 258 | // conversion of Go values into YAML. 259 | func (n *Node) Encode(v interface{}) (err error) { 260 | defer handleErr(&err) 261 | e := newEncoder() 262 | defer e.destroy() 263 | e.marshalDoc("", reflect.ValueOf(v)) 264 | e.finish() 265 | p := newParser(e.out) 266 | p.textless = true 267 | defer p.destroy() 268 | doc := p.parse() 269 | *n = *doc.Content[0] 270 | return nil 271 | } 272 | 273 | // SetIndent changes the used indentation used when encoding. 274 | func (e *Encoder) SetIndent(spaces int) { 275 | if spaces < 0 { 276 | panic("yaml: cannot indent to a negative number of spaces") 277 | } 278 | e.encoder.indent = spaces 279 | } 280 | 281 | // Close closes the encoder by writing any remaining data. 282 | // It does not write a stream terminating string "...". 283 | func (e *Encoder) Close() (err error) { 284 | defer handleErr(&err) 285 | e.encoder.finish() 286 | return nil 287 | } 288 | 289 | func handleErr(err *error) { 290 | if v := recover(); v != nil { 291 | if e, ok := v.(yamlError); ok { 292 | *err = e.err 293 | } else { 294 | panic(v) 295 | } 296 | } 297 | } 298 | 299 | type yamlError struct { 300 | err error 301 | } 302 | 303 | func fail(err error) { 304 | panic(yamlError{err}) 305 | } 306 | 307 | func failf(format string, args ...interface{}) { 308 | panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) 309 | } 310 | 311 | // A TypeError is returned by Unmarshal when one or more fields in 312 | // the YAML document cannot be properly decoded into the requested 313 | // types. When this error is returned, the value is still 314 | // unmarshaled partially. 315 | type TypeError struct { 316 | Errors []string 317 | } 318 | 319 | func (e *TypeError) Error() string { 320 | return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) 321 | } 322 | 323 | type Kind uint32 324 | 325 | const ( 326 | DocumentNode Kind = 1 << iota 327 | SequenceNode 328 | MappingNode 329 | ScalarNode 330 | AliasNode 331 | ) 332 | 333 | type Style uint32 334 | 335 | const ( 336 | TaggedStyle Style = 1 << iota 337 | DoubleQuotedStyle 338 | SingleQuotedStyle 339 | LiteralStyle 340 | FoldedStyle 341 | FlowStyle 342 | ) 343 | 344 | // Node represents an element in the YAML document hierarchy. While documents 345 | // are typically encoded and decoded into higher level types, such as structs 346 | // and maps, Node is an intermediate representation that allows detailed 347 | // control over the content being decoded or encoded. 348 | // 349 | // It's worth noting that although Node offers access into details such as 350 | // line numbers, colums, and comments, the content when re-encoded will not 351 | // have its original textual representation preserved. An effort is made to 352 | // render the data plesantly, and to preserve comments near the data they 353 | // describe, though. 354 | // 355 | // Values that make use of the Node type interact with the yaml package in the 356 | // same way any other type would do, by encoding and decoding yaml data 357 | // directly or indirectly into them. 358 | // 359 | // For example: 360 | // 361 | // var person struct { 362 | // Name string 363 | // Address yaml.Node 364 | // } 365 | // err := yaml.Unmarshal(data, &person) 366 | // 367 | // Or by itself: 368 | // 369 | // var person Node 370 | // err := yaml.Unmarshal(data, &person) 371 | // 372 | type Node struct { 373 | // Kind defines whether the node is a document, a mapping, a sequence, 374 | // a scalar value, or an alias to another node. The specific data type of 375 | // scalar nodes may be obtained via the ShortTag and LongTag methods. 376 | Kind Kind 377 | 378 | // Style allows customizing the apperance of the node in the tree. 379 | Style Style 380 | 381 | // Tag holds the YAML tag defining the data type for the value. 382 | // When decoding, this field will always be set to the resolved tag, 383 | // even when it wasn't explicitly provided in the YAML content. 384 | // When encoding, if this field is unset the value type will be 385 | // implied from the node properties, and if it is set, it will only 386 | // be serialized into the representation if TaggedStyle is used or 387 | // the implicit tag diverges from the provided one. 388 | Tag string 389 | 390 | // Value holds the unescaped and unquoted represenation of the value. 391 | Value string 392 | 393 | // Anchor holds the anchor name for this node, which allows aliases to point to it. 394 | Anchor string 395 | 396 | // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. 397 | Alias *Node 398 | 399 | // Content holds contained nodes for documents, mappings, and sequences. 400 | Content []*Node 401 | 402 | // HeadComment holds any comments in the lines preceding the node and 403 | // not separated by an empty line. 404 | HeadComment string 405 | 406 | // LineComment holds any comments at the end of the line where the node is in. 407 | LineComment string 408 | 409 | // FootComment holds any comments following the node and before empty lines. 410 | FootComment string 411 | 412 | // Line and Column hold the node position in the decoded YAML text. 413 | // These fields are not respected when encoding the node. 414 | Line int 415 | Column int 416 | } 417 | 418 | // IsZero returns whether the node has all of its fields unset. 419 | func (n *Node) IsZero() bool { 420 | return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && 421 | n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 422 | } 423 | 424 | 425 | // LongTag returns the long form of the tag that indicates the data type for 426 | // the node. If the Tag field isn't explicitly defined, one will be computed 427 | // based on the node properties. 428 | func (n *Node) LongTag() string { 429 | return longTag(n.ShortTag()) 430 | } 431 | 432 | // ShortTag returns the short form of the YAML tag that indicates data type for 433 | // the node. If the Tag field isn't explicitly defined, one will be computed 434 | // based on the node properties. 435 | func (n *Node) ShortTag() string { 436 | if n.indicatedString() { 437 | return strTag 438 | } 439 | if n.Tag == "" || n.Tag == "!" { 440 | switch n.Kind { 441 | case MappingNode: 442 | return mapTag 443 | case SequenceNode: 444 | return seqTag 445 | case AliasNode: 446 | if n.Alias != nil { 447 | return n.Alias.ShortTag() 448 | } 449 | case ScalarNode: 450 | tag, _ := resolve("", n.Value) 451 | return tag 452 | case 0: 453 | // Special case to make the zero value convenient. 454 | if n.IsZero() { 455 | return nullTag 456 | } 457 | } 458 | return "" 459 | } 460 | return shortTag(n.Tag) 461 | } 462 | 463 | func (n *Node) indicatedString() bool { 464 | return n.Kind == ScalarNode && 465 | (shortTag(n.Tag) == strTag || 466 | (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) 467 | } 468 | 469 | // SetString is a convenience function that sets the node to a string value 470 | // and defines its style in a pleasant way depending on its content. 471 | func (n *Node) SetString(s string) { 472 | n.Kind = ScalarNode 473 | if utf8.ValidString(s) { 474 | n.Value = s 475 | n.Tag = strTag 476 | } else { 477 | n.Value = encodeBase64(s) 478 | n.Tag = binaryTag 479 | } 480 | if strings.Contains(n.Value, "\n") { 481 | n.Style = LiteralStyle 482 | } 483 | } 484 | 485 | // -------------------------------------------------------------------------- 486 | // Maintain a mapping of keys to structure field indexes 487 | 488 | // The code in this section was copied from mgo/bson. 489 | 490 | // structInfo holds details for the serialization of fields of 491 | // a given struct. 492 | type structInfo struct { 493 | FieldsMap map[string]fieldInfo 494 | FieldsList []fieldInfo 495 | 496 | // InlineMap is the number of the field in the struct that 497 | // contains an ,inline map, or -1 if there's none. 498 | InlineMap int 499 | 500 | // InlineUnmarshalers holds indexes to inlined fields that 501 | // contain unmarshaler values. 502 | InlineUnmarshalers [][]int 503 | } 504 | 505 | type fieldInfo struct { 506 | Key string 507 | Num int 508 | OmitEmpty bool 509 | Flow bool 510 | // Id holds the unique field identifier, so we can cheaply 511 | // check for field duplicates without maintaining an extra map. 512 | Id int 513 | 514 | // Inline holds the field index if the field is part of an inlined struct. 515 | Inline []int 516 | } 517 | 518 | var structMap = make(map[reflect.Type]*structInfo) 519 | var fieldMapMutex sync.RWMutex 520 | var unmarshalerType reflect.Type 521 | 522 | func init() { 523 | var v Unmarshaler 524 | unmarshalerType = reflect.ValueOf(&v).Elem().Type() 525 | } 526 | 527 | func getStructInfo(st reflect.Type) (*structInfo, error) { 528 | fieldMapMutex.RLock() 529 | sinfo, found := structMap[st] 530 | fieldMapMutex.RUnlock() 531 | if found { 532 | return sinfo, nil 533 | } 534 | 535 | n := st.NumField() 536 | fieldsMap := make(map[string]fieldInfo) 537 | fieldsList := make([]fieldInfo, 0, n) 538 | inlineMap := -1 539 | inlineUnmarshalers := [][]int(nil) 540 | for i := 0; i != n; i++ { 541 | field := st.Field(i) 542 | if field.PkgPath != "" && !field.Anonymous { 543 | continue // Private field 544 | } 545 | 546 | info := fieldInfo{Num: i} 547 | 548 | tag := field.Tag.Get("yaml") 549 | if tag == "" && strings.Index(string(field.Tag), ":") < 0 { 550 | tag = string(field.Tag) 551 | } 552 | if tag == "-" { 553 | continue 554 | } 555 | 556 | inline := false 557 | fields := strings.Split(tag, ",") 558 | if len(fields) > 1 { 559 | for _, flag := range fields[1:] { 560 | switch flag { 561 | case "omitempty": 562 | info.OmitEmpty = true 563 | case "flow": 564 | info.Flow = true 565 | case "inline": 566 | inline = true 567 | default: 568 | return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) 569 | } 570 | } 571 | tag = fields[0] 572 | } 573 | 574 | if inline { 575 | switch field.Type.Kind() { 576 | case reflect.Map: 577 | if inlineMap >= 0 { 578 | return nil, errors.New("multiple ,inline maps in struct " + st.String()) 579 | } 580 | if field.Type.Key() != reflect.TypeOf("") { 581 | return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) 582 | } 583 | inlineMap = info.Num 584 | case reflect.Struct, reflect.Ptr: 585 | ftype := field.Type 586 | for ftype.Kind() == reflect.Ptr { 587 | ftype = ftype.Elem() 588 | } 589 | if ftype.Kind() != reflect.Struct { 590 | return nil, errors.New("option ,inline may only be used on a struct or map field") 591 | } 592 | if reflect.PtrTo(ftype).Implements(unmarshalerType) { 593 | inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) 594 | } else { 595 | sinfo, err := getStructInfo(ftype) 596 | if err != nil { 597 | return nil, err 598 | } 599 | for _, index := range sinfo.InlineUnmarshalers { 600 | inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) 601 | } 602 | for _, finfo := range sinfo.FieldsList { 603 | if _, found := fieldsMap[finfo.Key]; found { 604 | msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() 605 | return nil, errors.New(msg) 606 | } 607 | if finfo.Inline == nil { 608 | finfo.Inline = []int{i, finfo.Num} 609 | } else { 610 | finfo.Inline = append([]int{i}, finfo.Inline...) 611 | } 612 | finfo.Id = len(fieldsList) 613 | fieldsMap[finfo.Key] = finfo 614 | fieldsList = append(fieldsList, finfo) 615 | } 616 | } 617 | default: 618 | return nil, errors.New("option ,inline may only be used on a struct or map field") 619 | } 620 | continue 621 | } 622 | 623 | if tag != "" { 624 | info.Key = tag 625 | } else { 626 | info.Key = strings.ToLower(field.Name) 627 | } 628 | 629 | if _, found = fieldsMap[info.Key]; found { 630 | msg := "duplicated key '" + info.Key + "' in struct " + st.String() 631 | return nil, errors.New(msg) 632 | } 633 | 634 | info.Id = len(fieldsList) 635 | fieldsList = append(fieldsList, info) 636 | fieldsMap[info.Key] = info 637 | } 638 | 639 | sinfo = &structInfo{ 640 | FieldsMap: fieldsMap, 641 | FieldsList: fieldsList, 642 | InlineMap: inlineMap, 643 | InlineUnmarshalers: inlineUnmarshalers, 644 | } 645 | 646 | fieldMapMutex.Lock() 647 | structMap[st] = sinfo 648 | fieldMapMutex.Unlock() 649 | return sinfo, nil 650 | } 651 | 652 | // IsZeroer is used to check whether an object is zero to 653 | // determine whether it should be omitted when marshaling 654 | // with the omitempty flag. One notable implementation 655 | // is time.Time. 656 | type IsZeroer interface { 657 | IsZero() bool 658 | } 659 | 660 | func isZero(v reflect.Value) bool { 661 | kind := v.Kind() 662 | if z, ok := v.Interface().(IsZeroer); ok { 663 | if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { 664 | return true 665 | } 666 | return z.IsZero() 667 | } 668 | switch kind { 669 | case reflect.String: 670 | return len(v.String()) == 0 671 | case reflect.Interface, reflect.Ptr: 672 | return v.IsNil() 673 | case reflect.Slice: 674 | return v.Len() == 0 675 | case reflect.Map: 676 | return v.Len() == 0 677 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 678 | return v.Int() == 0 679 | case reflect.Float32, reflect.Float64: 680 | return v.Float() == 0 681 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 682 | return v.Uint() == 0 683 | case reflect.Bool: 684 | return !v.Bool() 685 | case reflect.Struct: 686 | vt := v.Type() 687 | for i := v.NumField() - 1; i >= 0; i-- { 688 | if vt.Field(i).PkgPath != "" { 689 | continue // Private field 690 | } 691 | if !isZero(v.Field(i)) { 692 | return false 693 | } 694 | } 695 | return true 696 | } 697 | return false 698 | } 699 | -------------------------------------------------------------------------------- /yamlh.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | import ( 26 | "fmt" 27 | "io" 28 | ) 29 | 30 | // The version directive data. 31 | type yaml_version_directive_t struct { 32 | major int8 // The major version number. 33 | minor int8 // The minor version number. 34 | } 35 | 36 | // The tag directive data. 37 | type yaml_tag_directive_t struct { 38 | handle []byte // The tag handle. 39 | prefix []byte // The tag prefix. 40 | } 41 | 42 | type yaml_encoding_t int 43 | 44 | // The stream encoding. 45 | const ( 46 | // Let the parser choose the encoding. 47 | yaml_ANY_ENCODING yaml_encoding_t = iota 48 | 49 | yaml_UTF8_ENCODING // The default UTF-8 encoding. 50 | yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. 51 | yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. 52 | ) 53 | 54 | type yaml_break_t int 55 | 56 | // Line break types. 57 | const ( 58 | // Let the parser choose the break type. 59 | yaml_ANY_BREAK yaml_break_t = iota 60 | 61 | yaml_CR_BREAK // Use CR for line breaks (Mac style). 62 | yaml_LN_BREAK // Use LN for line breaks (Unix style). 63 | yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). 64 | ) 65 | 66 | type yaml_error_type_t int 67 | 68 | // Many bad things could happen with the parser and emitter. 69 | const ( 70 | // No error is produced. 71 | yaml_NO_ERROR yaml_error_type_t = iota 72 | 73 | yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. 74 | yaml_READER_ERROR // Cannot read or decode the input stream. 75 | yaml_SCANNER_ERROR // Cannot scan the input stream. 76 | yaml_PARSER_ERROR // Cannot parse the input stream. 77 | yaml_COMPOSER_ERROR // Cannot compose a YAML document. 78 | yaml_WRITER_ERROR // Cannot write to the output stream. 79 | yaml_EMITTER_ERROR // Cannot emit a YAML stream. 80 | ) 81 | 82 | // The pointer position. 83 | type yaml_mark_t struct { 84 | index int // The position index. 85 | line int // The position line. 86 | column int // The position column. 87 | } 88 | 89 | // Node Styles 90 | 91 | type yaml_style_t int8 92 | 93 | type yaml_scalar_style_t yaml_style_t 94 | 95 | // Scalar styles. 96 | const ( 97 | // Let the emitter choose the style. 98 | yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 99 | 100 | yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. 101 | yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. 102 | yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. 103 | yaml_LITERAL_SCALAR_STYLE // The literal scalar style. 104 | yaml_FOLDED_SCALAR_STYLE // The folded scalar style. 105 | ) 106 | 107 | type yaml_sequence_style_t yaml_style_t 108 | 109 | // Sequence styles. 110 | const ( 111 | // Let the emitter choose the style. 112 | yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota 113 | 114 | yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. 115 | yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. 116 | ) 117 | 118 | type yaml_mapping_style_t yaml_style_t 119 | 120 | // Mapping styles. 121 | const ( 122 | // Let the emitter choose the style. 123 | yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota 124 | 125 | yaml_BLOCK_MAPPING_STYLE // The block mapping style. 126 | yaml_FLOW_MAPPING_STYLE // The flow mapping style. 127 | ) 128 | 129 | // Tokens 130 | 131 | type yaml_token_type_t int 132 | 133 | // Token types. 134 | const ( 135 | // An empty token. 136 | yaml_NO_TOKEN yaml_token_type_t = iota 137 | 138 | yaml_STREAM_START_TOKEN // A STREAM-START token. 139 | yaml_STREAM_END_TOKEN // A STREAM-END token. 140 | 141 | yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. 142 | yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. 143 | yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. 144 | yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. 145 | 146 | yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. 147 | yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. 148 | yaml_BLOCK_END_TOKEN // A BLOCK-END token. 149 | 150 | yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. 151 | yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. 152 | yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. 153 | yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. 154 | 155 | yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. 156 | yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. 157 | yaml_KEY_TOKEN // A KEY token. 158 | yaml_VALUE_TOKEN // A VALUE token. 159 | 160 | yaml_ALIAS_TOKEN // An ALIAS token. 161 | yaml_ANCHOR_TOKEN // An ANCHOR token. 162 | yaml_TAG_TOKEN // A TAG token. 163 | yaml_SCALAR_TOKEN // A SCALAR token. 164 | ) 165 | 166 | func (tt yaml_token_type_t) String() string { 167 | switch tt { 168 | case yaml_NO_TOKEN: 169 | return "yaml_NO_TOKEN" 170 | case yaml_STREAM_START_TOKEN: 171 | return "yaml_STREAM_START_TOKEN" 172 | case yaml_STREAM_END_TOKEN: 173 | return "yaml_STREAM_END_TOKEN" 174 | case yaml_VERSION_DIRECTIVE_TOKEN: 175 | return "yaml_VERSION_DIRECTIVE_TOKEN" 176 | case yaml_TAG_DIRECTIVE_TOKEN: 177 | return "yaml_TAG_DIRECTIVE_TOKEN" 178 | case yaml_DOCUMENT_START_TOKEN: 179 | return "yaml_DOCUMENT_START_TOKEN" 180 | case yaml_DOCUMENT_END_TOKEN: 181 | return "yaml_DOCUMENT_END_TOKEN" 182 | case yaml_BLOCK_SEQUENCE_START_TOKEN: 183 | return "yaml_BLOCK_SEQUENCE_START_TOKEN" 184 | case yaml_BLOCK_MAPPING_START_TOKEN: 185 | return "yaml_BLOCK_MAPPING_START_TOKEN" 186 | case yaml_BLOCK_END_TOKEN: 187 | return "yaml_BLOCK_END_TOKEN" 188 | case yaml_FLOW_SEQUENCE_START_TOKEN: 189 | return "yaml_FLOW_SEQUENCE_START_TOKEN" 190 | case yaml_FLOW_SEQUENCE_END_TOKEN: 191 | return "yaml_FLOW_SEQUENCE_END_TOKEN" 192 | case yaml_FLOW_MAPPING_START_TOKEN: 193 | return "yaml_FLOW_MAPPING_START_TOKEN" 194 | case yaml_FLOW_MAPPING_END_TOKEN: 195 | return "yaml_FLOW_MAPPING_END_TOKEN" 196 | case yaml_BLOCK_ENTRY_TOKEN: 197 | return "yaml_BLOCK_ENTRY_TOKEN" 198 | case yaml_FLOW_ENTRY_TOKEN: 199 | return "yaml_FLOW_ENTRY_TOKEN" 200 | case yaml_KEY_TOKEN: 201 | return "yaml_KEY_TOKEN" 202 | case yaml_VALUE_TOKEN: 203 | return "yaml_VALUE_TOKEN" 204 | case yaml_ALIAS_TOKEN: 205 | return "yaml_ALIAS_TOKEN" 206 | case yaml_ANCHOR_TOKEN: 207 | return "yaml_ANCHOR_TOKEN" 208 | case yaml_TAG_TOKEN: 209 | return "yaml_TAG_TOKEN" 210 | case yaml_SCALAR_TOKEN: 211 | return "yaml_SCALAR_TOKEN" 212 | } 213 | return "" 214 | } 215 | 216 | // The token structure. 217 | type yaml_token_t struct { 218 | // The token type. 219 | typ yaml_token_type_t 220 | 221 | // The start/end of the token. 222 | start_mark, end_mark yaml_mark_t 223 | 224 | // The stream encoding (for yaml_STREAM_START_TOKEN). 225 | encoding yaml_encoding_t 226 | 227 | // The alias/anchor/scalar value or tag/tag directive handle 228 | // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). 229 | value []byte 230 | 231 | // The tag suffix (for yaml_TAG_TOKEN). 232 | suffix []byte 233 | 234 | // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). 235 | prefix []byte 236 | 237 | // The scalar style (for yaml_SCALAR_TOKEN). 238 | style yaml_scalar_style_t 239 | 240 | // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). 241 | major, minor int8 242 | } 243 | 244 | // Events 245 | 246 | type yaml_event_type_t int8 247 | 248 | // Event types. 249 | const ( 250 | // An empty event. 251 | yaml_NO_EVENT yaml_event_type_t = iota 252 | 253 | yaml_STREAM_START_EVENT // A STREAM-START event. 254 | yaml_STREAM_END_EVENT // A STREAM-END event. 255 | yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. 256 | yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. 257 | yaml_ALIAS_EVENT // An ALIAS event. 258 | yaml_SCALAR_EVENT // A SCALAR event. 259 | yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. 260 | yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. 261 | yaml_MAPPING_START_EVENT // A MAPPING-START event. 262 | yaml_MAPPING_END_EVENT // A MAPPING-END event. 263 | yaml_TAIL_COMMENT_EVENT 264 | ) 265 | 266 | var eventStrings = []string{ 267 | yaml_NO_EVENT: "none", 268 | yaml_STREAM_START_EVENT: "stream start", 269 | yaml_STREAM_END_EVENT: "stream end", 270 | yaml_DOCUMENT_START_EVENT: "document start", 271 | yaml_DOCUMENT_END_EVENT: "document end", 272 | yaml_ALIAS_EVENT: "alias", 273 | yaml_SCALAR_EVENT: "scalar", 274 | yaml_SEQUENCE_START_EVENT: "sequence start", 275 | yaml_SEQUENCE_END_EVENT: "sequence end", 276 | yaml_MAPPING_START_EVENT: "mapping start", 277 | yaml_MAPPING_END_EVENT: "mapping end", 278 | yaml_TAIL_COMMENT_EVENT: "tail comment", 279 | } 280 | 281 | func (e yaml_event_type_t) String() string { 282 | if e < 0 || int(e) >= len(eventStrings) { 283 | return fmt.Sprintf("unknown event %d", e) 284 | } 285 | return eventStrings[e] 286 | } 287 | 288 | // The event structure. 289 | type yaml_event_t struct { 290 | 291 | // The event type. 292 | typ yaml_event_type_t 293 | 294 | // The start and end of the event. 295 | start_mark, end_mark yaml_mark_t 296 | 297 | // The document encoding (for yaml_STREAM_START_EVENT). 298 | encoding yaml_encoding_t 299 | 300 | // The version directive (for yaml_DOCUMENT_START_EVENT). 301 | version_directive *yaml_version_directive_t 302 | 303 | // The list of tag directives (for yaml_DOCUMENT_START_EVENT). 304 | tag_directives []yaml_tag_directive_t 305 | 306 | // The comments 307 | head_comment []byte 308 | line_comment []byte 309 | foot_comment []byte 310 | tail_comment []byte 311 | 312 | // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). 313 | anchor []byte 314 | 315 | // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). 316 | tag []byte 317 | 318 | // The scalar value (for yaml_SCALAR_EVENT). 319 | value []byte 320 | 321 | // Is the document start/end indicator implicit, or the tag optional? 322 | // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). 323 | implicit bool 324 | 325 | // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). 326 | quoted_implicit bool 327 | 328 | // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). 329 | style yaml_style_t 330 | } 331 | 332 | func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } 333 | func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } 334 | func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } 335 | 336 | // Nodes 337 | 338 | const ( 339 | yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. 340 | yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. 341 | yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. 342 | yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. 343 | yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. 344 | yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. 345 | 346 | yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. 347 | yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. 348 | 349 | // Not in original libyaml. 350 | yaml_BINARY_TAG = "tag:yaml.org,2002:binary" 351 | yaml_MERGE_TAG = "tag:yaml.org,2002:merge" 352 | 353 | yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. 354 | yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. 355 | yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. 356 | ) 357 | 358 | type yaml_node_type_t int 359 | 360 | // Node types. 361 | const ( 362 | // An empty node. 363 | yaml_NO_NODE yaml_node_type_t = iota 364 | 365 | yaml_SCALAR_NODE // A scalar node. 366 | yaml_SEQUENCE_NODE // A sequence node. 367 | yaml_MAPPING_NODE // A mapping node. 368 | ) 369 | 370 | // An element of a sequence node. 371 | type yaml_node_item_t int 372 | 373 | // An element of a mapping node. 374 | type yaml_node_pair_t struct { 375 | key int // The key of the element. 376 | value int // The value of the element. 377 | } 378 | 379 | // The node structure. 380 | type yaml_node_t struct { 381 | typ yaml_node_type_t // The node type. 382 | tag []byte // The node tag. 383 | 384 | // The node data. 385 | 386 | // The scalar parameters (for yaml_SCALAR_NODE). 387 | scalar struct { 388 | value []byte // The scalar value. 389 | length int // The length of the scalar value. 390 | style yaml_scalar_style_t // The scalar style. 391 | } 392 | 393 | // The sequence parameters (for YAML_SEQUENCE_NODE). 394 | sequence struct { 395 | items_data []yaml_node_item_t // The stack of sequence items. 396 | style yaml_sequence_style_t // The sequence style. 397 | } 398 | 399 | // The mapping parameters (for yaml_MAPPING_NODE). 400 | mapping struct { 401 | pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). 402 | pairs_start *yaml_node_pair_t // The beginning of the stack. 403 | pairs_end *yaml_node_pair_t // The end of the stack. 404 | pairs_top *yaml_node_pair_t // The top of the stack. 405 | style yaml_mapping_style_t // The mapping style. 406 | } 407 | 408 | start_mark yaml_mark_t // The beginning of the node. 409 | end_mark yaml_mark_t // The end of the node. 410 | 411 | } 412 | 413 | // The document structure. 414 | type yaml_document_t struct { 415 | 416 | // The document nodes. 417 | nodes []yaml_node_t 418 | 419 | // The version directive. 420 | version_directive *yaml_version_directive_t 421 | 422 | // The list of tag directives. 423 | tag_directives_data []yaml_tag_directive_t 424 | tag_directives_start int // The beginning of the tag directives list. 425 | tag_directives_end int // The end of the tag directives list. 426 | 427 | start_implicit int // Is the document start indicator implicit? 428 | end_implicit int // Is the document end indicator implicit? 429 | 430 | // The start/end of the document. 431 | start_mark, end_mark yaml_mark_t 432 | } 433 | 434 | // The prototype of a read handler. 435 | // 436 | // The read handler is called when the parser needs to read more bytes from the 437 | // source. The handler should write not more than size bytes to the buffer. 438 | // The number of written bytes should be set to the size_read variable. 439 | // 440 | // [in,out] data A pointer to an application data specified by 441 | // yaml_parser_set_input(). 442 | // [out] buffer The buffer to write the data from the source. 443 | // [in] size The size of the buffer. 444 | // [out] size_read The actual number of bytes read from the source. 445 | // 446 | // On success, the handler should return 1. If the handler failed, 447 | // the returned value should be 0. On EOF, the handler should set the 448 | // size_read to 0 and return 1. 449 | type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) 450 | 451 | // This structure holds information about a potential simple key. 452 | type yaml_simple_key_t struct { 453 | possible bool // Is a simple key possible? 454 | required bool // Is a simple key required? 455 | token_number int // The number of the token. 456 | mark yaml_mark_t // The position mark. 457 | } 458 | 459 | // The states of the parser. 460 | type yaml_parser_state_t int 461 | 462 | const ( 463 | yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota 464 | 465 | yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. 466 | yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. 467 | yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. 468 | yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. 469 | yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. 470 | yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. 471 | yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. 472 | yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. 473 | yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. 474 | yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. 475 | yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. 476 | yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. 477 | yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. 478 | yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. 479 | yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. 480 | yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. 481 | yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. 482 | yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. 483 | yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. 484 | yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. 485 | yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. 486 | yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. 487 | yaml_PARSE_END_STATE // Expect nothing. 488 | ) 489 | 490 | func (ps yaml_parser_state_t) String() string { 491 | switch ps { 492 | case yaml_PARSE_STREAM_START_STATE: 493 | return "yaml_PARSE_STREAM_START_STATE" 494 | case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: 495 | return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" 496 | case yaml_PARSE_DOCUMENT_START_STATE: 497 | return "yaml_PARSE_DOCUMENT_START_STATE" 498 | case yaml_PARSE_DOCUMENT_CONTENT_STATE: 499 | return "yaml_PARSE_DOCUMENT_CONTENT_STATE" 500 | case yaml_PARSE_DOCUMENT_END_STATE: 501 | return "yaml_PARSE_DOCUMENT_END_STATE" 502 | case yaml_PARSE_BLOCK_NODE_STATE: 503 | return "yaml_PARSE_BLOCK_NODE_STATE" 504 | case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: 505 | return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" 506 | case yaml_PARSE_FLOW_NODE_STATE: 507 | return "yaml_PARSE_FLOW_NODE_STATE" 508 | case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: 509 | return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" 510 | case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: 511 | return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" 512 | case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: 513 | return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" 514 | case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: 515 | return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" 516 | case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: 517 | return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" 518 | case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: 519 | return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" 520 | case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: 521 | return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" 522 | case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: 523 | return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" 524 | case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: 525 | return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" 526 | case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: 527 | return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" 528 | case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: 529 | return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" 530 | case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: 531 | return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" 532 | case yaml_PARSE_FLOW_MAPPING_KEY_STATE: 533 | return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" 534 | case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: 535 | return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" 536 | case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: 537 | return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" 538 | case yaml_PARSE_END_STATE: 539 | return "yaml_PARSE_END_STATE" 540 | } 541 | return "" 542 | } 543 | 544 | // This structure holds aliases data. 545 | type yaml_alias_data_t struct { 546 | anchor []byte // The anchor. 547 | index int // The node id. 548 | mark yaml_mark_t // The anchor mark. 549 | } 550 | 551 | // The parser structure. 552 | // 553 | // All members are internal. Manage the structure using the 554 | // yaml_parser_ family of functions. 555 | type yaml_parser_t struct { 556 | 557 | // Error handling 558 | 559 | error yaml_error_type_t // Error type. 560 | 561 | problem string // Error description. 562 | 563 | // The byte about which the problem occurred. 564 | problem_offset int 565 | problem_value int 566 | problem_mark yaml_mark_t 567 | 568 | // The error context. 569 | context string 570 | context_mark yaml_mark_t 571 | 572 | // Reader stuff 573 | 574 | read_handler yaml_read_handler_t // Read handler. 575 | 576 | input_reader io.Reader // File input data. 577 | input []byte // String input data. 578 | input_pos int 579 | 580 | eof bool // EOF flag 581 | 582 | buffer []byte // The working buffer. 583 | buffer_pos int // The current position of the buffer. 584 | 585 | unread int // The number of unread characters in the buffer. 586 | 587 | newlines int // The number of line breaks since last non-break/non-blank character 588 | 589 | raw_buffer []byte // The raw buffer. 590 | raw_buffer_pos int // The current position of the buffer. 591 | 592 | encoding yaml_encoding_t // The input encoding. 593 | 594 | offset int // The offset of the current position (in bytes). 595 | mark yaml_mark_t // The mark of the current position. 596 | 597 | // Comments 598 | 599 | head_comment []byte // The current head comments 600 | line_comment []byte // The current line comments 601 | foot_comment []byte // The current foot comments 602 | tail_comment []byte // Foot comment that happens at the end of a block. 603 | stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) 604 | 605 | comments []yaml_comment_t // The folded comments for all parsed tokens 606 | comments_head int 607 | 608 | // Scanner stuff 609 | 610 | stream_start_produced bool // Have we started to scan the input stream? 611 | stream_end_produced bool // Have we reached the end of the input stream? 612 | 613 | flow_level int // The number of unclosed '[' and '{' indicators. 614 | 615 | tokens []yaml_token_t // The tokens queue. 616 | tokens_head int // The head of the tokens queue. 617 | tokens_parsed int // The number of tokens fetched from the queue. 618 | token_available bool // Does the tokens queue contain a token ready for dequeueing. 619 | 620 | indent int // The current indentation level. 621 | indents []int // The indentation levels stack. 622 | 623 | simple_key_allowed bool // May a simple key occur at the current position? 624 | simple_keys []yaml_simple_key_t // The stack of simple keys. 625 | simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number 626 | 627 | // Parser stuff 628 | 629 | state yaml_parser_state_t // The current parser state. 630 | states []yaml_parser_state_t // The parser states stack. 631 | marks []yaml_mark_t // The stack of marks. 632 | tag_directives []yaml_tag_directive_t // The list of TAG directives. 633 | 634 | // Dumper stuff 635 | 636 | aliases []yaml_alias_data_t // The alias data. 637 | 638 | document *yaml_document_t // The currently parsed document. 639 | } 640 | 641 | type yaml_comment_t struct { 642 | 643 | scan_mark yaml_mark_t // Position where scanning for comments started 644 | token_mark yaml_mark_t // Position after which tokens will be associated with this comment 645 | start_mark yaml_mark_t // Position of '#' comment mark 646 | end_mark yaml_mark_t // Position where comment terminated 647 | 648 | head []byte 649 | line []byte 650 | foot []byte 651 | } 652 | 653 | // Emitter Definitions 654 | 655 | // The prototype of a write handler. 656 | // 657 | // The write handler is called when the emitter needs to flush the accumulated 658 | // characters to the output. The handler should write @a size bytes of the 659 | // @a buffer to the output. 660 | // 661 | // @param[in,out] data A pointer to an application data specified by 662 | // yaml_emitter_set_output(). 663 | // @param[in] buffer The buffer with bytes to be written. 664 | // @param[in] size The size of the buffer. 665 | // 666 | // @returns On success, the handler should return @c 1. If the handler failed, 667 | // the returned value should be @c 0. 668 | // 669 | type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error 670 | 671 | type yaml_emitter_state_t int 672 | 673 | // The emitter states. 674 | const ( 675 | // Expect STREAM-START. 676 | yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota 677 | 678 | yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. 679 | yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. 680 | yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. 681 | yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. 682 | yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. 683 | yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out 684 | yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. 685 | yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. 686 | yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out 687 | yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. 688 | yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. 689 | yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. 690 | yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. 691 | yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. 692 | yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. 693 | yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. 694 | yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. 695 | yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. 696 | yaml_EMIT_END_STATE // Expect nothing. 697 | ) 698 | 699 | // The emitter structure. 700 | // 701 | // All members are internal. Manage the structure using the @c yaml_emitter_ 702 | // family of functions. 703 | type yaml_emitter_t struct { 704 | 705 | // Error handling 706 | 707 | error yaml_error_type_t // Error type. 708 | problem string // Error description. 709 | 710 | // Writer stuff 711 | 712 | write_handler yaml_write_handler_t // Write handler. 713 | 714 | output_buffer *[]byte // String output data. 715 | output_writer io.Writer // File output data. 716 | 717 | buffer []byte // The working buffer. 718 | buffer_pos int // The current position of the buffer. 719 | 720 | raw_buffer []byte // The raw buffer. 721 | raw_buffer_pos int // The current position of the buffer. 722 | 723 | encoding yaml_encoding_t // The stream encoding. 724 | 725 | // Emitter stuff 726 | 727 | canonical bool // If the output is in the canonical style? 728 | best_indent int // The number of indentation spaces. 729 | best_width int // The preferred width of the output lines. 730 | unicode bool // Allow unescaped non-ASCII characters? 731 | line_break yaml_break_t // The preferred line break. 732 | 733 | state yaml_emitter_state_t // The current emitter state. 734 | states []yaml_emitter_state_t // The stack of states. 735 | 736 | events []yaml_event_t // The event queue. 737 | events_head int // The head of the event queue. 738 | 739 | indents []int // The stack of indentation levels. 740 | 741 | tag_directives []yaml_tag_directive_t // The list of tag directives. 742 | 743 | indent int // The current indentation level. 744 | 745 | flow_level int // The current flow level. 746 | 747 | root_context bool // Is it the document root context? 748 | sequence_context bool // Is it a sequence context? 749 | mapping_context bool // Is it a mapping context? 750 | simple_key_context bool // Is it a simple mapping key context? 751 | 752 | line int // The current line. 753 | column int // The current column. 754 | whitespace bool // If the last character was a whitespace? 755 | indention bool // If the last character was an indentation character (' ', '-', '?', ':')? 756 | open_ended bool // If an explicit document end is required? 757 | 758 | space_above bool // Is there's an empty line above? 759 | foot_indent int // The indent used to write the foot comment above, or -1 if none. 760 | 761 | // Anchor analysis. 762 | anchor_data struct { 763 | anchor []byte // The anchor value. 764 | alias bool // Is it an alias? 765 | } 766 | 767 | // Tag analysis. 768 | tag_data struct { 769 | handle []byte // The tag handle. 770 | suffix []byte // The tag suffix. 771 | } 772 | 773 | // Scalar analysis. 774 | scalar_data struct { 775 | value []byte // The scalar value. 776 | multiline bool // Does the scalar contain line breaks? 777 | flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? 778 | block_plain_allowed bool // Can the scalar be expressed in the block plain style? 779 | single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? 780 | block_allowed bool // Can the scalar be expressed in the literal or folded styles? 781 | style yaml_scalar_style_t // The output style. 782 | } 783 | 784 | // Comments 785 | head_comment []byte 786 | line_comment []byte 787 | foot_comment []byte 788 | tail_comment []byte 789 | 790 | key_line_comment []byte 791 | 792 | // Dumper stuff 793 | 794 | opened bool // If the stream was already opened? 795 | closed bool // If the stream was already closed? 796 | 797 | // The information associated with the document nodes. 798 | anchors *struct { 799 | references int // The number of references. 800 | anchor int // The anchor id. 801 | serialized bool // If the node has been emitted? 802 | } 803 | 804 | last_anchor_id int // The last assigned anchor id. 805 | 806 | document *yaml_document_t // The currently emitted document. 807 | } 808 | -------------------------------------------------------------------------------- /yamlprivateh.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2019 Canonical Ltd 3 | // Copyright (c) 2006-2010 Kirill Simonov 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | // of the Software, and to permit persons to whom the Software is furnished to do 10 | // so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | package yaml 24 | 25 | const ( 26 | // The size of the input raw buffer. 27 | input_raw_buffer_size = 512 28 | 29 | // The size of the input buffer. 30 | // It should be possible to decode the whole raw buffer. 31 | input_buffer_size = input_raw_buffer_size * 3 32 | 33 | // The size of the output buffer. 34 | output_buffer_size = 128 35 | 36 | // The size of the output raw buffer. 37 | // It should be possible to encode the whole output buffer. 38 | output_raw_buffer_size = (output_buffer_size*2 + 2) 39 | 40 | // The size of other stacks and queues. 41 | initial_stack_size = 16 42 | initial_queue_size = 16 43 | initial_string_size = 16 44 | ) 45 | 46 | // Check if the character at the specified position is an alphabetical 47 | // character, a digit, '_', or '-'. 48 | func is_alpha(b []byte, i int) bool { 49 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 50 | } 51 | 52 | // Check if the character at the specified position is a digit. 53 | func is_digit(b []byte, i int) bool { 54 | return b[i] >= '0' && b[i] <= '9' 55 | } 56 | 57 | // Get the value of a digit. 58 | func as_digit(b []byte, i int) int { 59 | return int(b[i]) - '0' 60 | } 61 | 62 | // Check if the character at the specified position is a hex-digit. 63 | func is_hex(b []byte, i int) bool { 64 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 65 | } 66 | 67 | // Get the value of a hex-digit. 68 | func as_hex(b []byte, i int) int { 69 | bi := b[i] 70 | if bi >= 'A' && bi <= 'F' { 71 | return int(bi) - 'A' + 10 72 | } 73 | if bi >= 'a' && bi <= 'f' { 74 | return int(bi) - 'a' + 10 75 | } 76 | return int(bi) - '0' 77 | } 78 | 79 | // Check if the character is ASCII. 80 | func is_ascii(b []byte, i int) bool { 81 | return b[i] <= 0x7F 82 | } 83 | 84 | // Check if the character at the start of the buffer can be printed unescaped. 85 | func is_printable(b []byte, i int) bool { 86 | return ((b[i] == 0x0A) || // . == #x0A 87 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 88 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 89 | (b[i] > 0xC2 && b[i] < 0xED) || 90 | (b[i] == 0xED && b[i+1] < 0xA0) || 91 | (b[i] == 0xEE) || 92 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 93 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 94 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 95 | } 96 | 97 | // Check if the character at the specified position is NUL. 98 | func is_z(b []byte, i int) bool { 99 | return b[i] == 0x00 100 | } 101 | 102 | // Check if the beginning of the buffer is a BOM. 103 | func is_bom(b []byte, i int) bool { 104 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 105 | } 106 | 107 | // Check if the character at the specified position is space. 108 | func is_space(b []byte, i int) bool { 109 | return b[i] == ' ' 110 | } 111 | 112 | // Check if the character at the specified position is tab. 113 | func is_tab(b []byte, i int) bool { 114 | return b[i] == '\t' 115 | } 116 | 117 | // Check if the character at the specified position is blank (space or tab). 118 | func is_blank(b []byte, i int) bool { 119 | //return is_space(b, i) || is_tab(b, i) 120 | return b[i] == ' ' || b[i] == '\t' 121 | } 122 | 123 | // Check if the character at the specified position is a line break. 124 | func is_break(b []byte, i int) bool { 125 | return (b[i] == '\r' || // CR (#xD) 126 | b[i] == '\n' || // LF (#xA) 127 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 128 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 129 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 130 | } 131 | 132 | func is_crlf(b []byte, i int) bool { 133 | return b[i] == '\r' && b[i+1] == '\n' 134 | } 135 | 136 | // Check if the character is a line break or NUL. 137 | func is_breakz(b []byte, i int) bool { 138 | //return is_break(b, i) || is_z(b, i) 139 | return ( 140 | // is_break: 141 | b[i] == '\r' || // CR (#xD) 142 | b[i] == '\n' || // LF (#xA) 143 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 144 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 145 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 146 | // is_z: 147 | b[i] == 0) 148 | } 149 | 150 | // Check if the character is a line break, space, or NUL. 151 | func is_spacez(b []byte, i int) bool { 152 | //return is_space(b, i) || is_breakz(b, i) 153 | return ( 154 | // is_space: 155 | b[i] == ' ' || 156 | // is_breakz: 157 | b[i] == '\r' || // CR (#xD) 158 | b[i] == '\n' || // LF (#xA) 159 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 160 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 161 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 162 | b[i] == 0) 163 | } 164 | 165 | // Check if the character is a line break, space, tab, or NUL. 166 | func is_blankz(b []byte, i int) bool { 167 | //return is_blank(b, i) || is_breakz(b, i) 168 | return ( 169 | // is_blank: 170 | b[i] == ' ' || b[i] == '\t' || 171 | // is_breakz: 172 | b[i] == '\r' || // CR (#xD) 173 | b[i] == '\n' || // LF (#xA) 174 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 175 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 176 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 177 | b[i] == 0) 178 | } 179 | 180 | // Determine the width of the character. 181 | func width(b byte) int { 182 | // Don't replace these by a switch without first 183 | // confirming that it is being inlined. 184 | if b&0x80 == 0x00 { 185 | return 1 186 | } 187 | if b&0xE0 == 0xC0 { 188 | return 2 189 | } 190 | if b&0xF0 == 0xE0 { 191 | return 3 192 | } 193 | if b&0xF8 == 0xF0 { 194 | return 4 195 | } 196 | return 0 197 | 198 | } 199 | --------------------------------------------------------------------------------