├── .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 |
--------------------------------------------------------------------------------