Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / txtar / archive.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package txtar implements a trivial text-based file archive format.
6 //
7 // The goals for the format are:
8 //
9 //      - be trivial enough to create and edit by hand.
10 //      - be able to store trees of text files describing go command test cases.
11 //      - diff nicely in git history and code reviews.
12 //
13 // Non-goals include being a completely general archive format,
14 // storing binary data, storing file modes, storing special files like
15 // symbolic links, and so on.
16 //
17 // Txtar format
18 //
19 // A txtar archive is zero or more comment lines and then a sequence of file entries.
20 // Each file entry begins with a file marker line of the form "-- FILENAME --"
21 // and is followed by zero or more file content lines making up the file data.
22 // The comment or file content ends at the next file marker line.
23 // The file marker line must begin with the three-byte sequence "-- "
24 // and end with the three-byte sequence " --", but the enclosed
25 // file name can be surrounding by additional white space,
26 // all of which is stripped.
27 //
28 // If the txtar file is missing a trailing newline on the final line,
29 // parsers should consider a final newline to be present anyway.
30 //
31 // There are no possible syntax errors in a txtar archive.
32 package txtar
33
34 import (
35         "bytes"
36         "fmt"
37         "io/ioutil"
38         "strings"
39 )
40
41 // An Archive is a collection of files.
42 type Archive struct {
43         Comment []byte
44         Files   []File
45 }
46
47 // A File is a single file in an archive.
48 type File struct {
49         Name string // name of file ("foo/bar.txt")
50         Data []byte // text content of file
51 }
52
53 // Format returns the serialized form of an Archive.
54 // It is assumed that the Archive data structure is well-formed:
55 // a.Comment and all a.File[i].Data contain no file marker lines,
56 // and all a.File[i].Name is non-empty.
57 func Format(a *Archive) []byte {
58         var buf bytes.Buffer
59         buf.Write(fixNL(a.Comment))
60         for _, f := range a.Files {
61                 fmt.Fprintf(&buf, "-- %s --\n", f.Name)
62                 buf.Write(fixNL(f.Data))
63         }
64         return buf.Bytes()
65 }
66
67 // ParseFile parses the named file as an archive.
68 func ParseFile(file string) (*Archive, error) {
69         data, err := ioutil.ReadFile(file)
70         if err != nil {
71                 return nil, err
72         }
73         return Parse(data), nil
74 }
75
76 // Parse parses the serialized form of an Archive.
77 // The returned Archive holds slices of data.
78 func Parse(data []byte) *Archive {
79         a := new(Archive)
80         var name string
81         a.Comment, name, data = findFileMarker(data)
82         for name != "" {
83                 f := File{name, nil}
84                 f.Data, name, data = findFileMarker(data)
85                 a.Files = append(a.Files, f)
86         }
87         return a
88 }
89
90 var (
91         newlineMarker = []byte("\n-- ")
92         marker        = []byte("-- ")
93         markerEnd     = []byte(" --")
94 )
95
96 // findFileMarker finds the next file marker in data,
97 // extracts the file name, and returns the data before the marker,
98 // the file name, and the data after the marker.
99 // If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil.
100 func findFileMarker(data []byte) (before []byte, name string, after []byte) {
101         var i int
102         for {
103                 if name, after = isMarker(data[i:]); name != "" {
104                         return data[:i], name, after
105                 }
106                 j := bytes.Index(data[i:], newlineMarker)
107                 if j < 0 {
108                         return fixNL(data), "", nil
109                 }
110                 i += j + 1 // positioned at start of new possible marker
111         }
112 }
113
114 // isMarker checks whether data begins with a file marker line.
115 // If so, it returns the name from the line and the data after the line.
116 // Otherwise it returns name == "" with an unspecified after.
117 func isMarker(data []byte) (name string, after []byte) {
118         if !bytes.HasPrefix(data, marker) {
119                 return "", nil
120         }
121         if i := bytes.IndexByte(data, '\n'); i >= 0 {
122                 data, after = data[:i], data[i+1:]
123         }
124         if !bytes.HasSuffix(data, markerEnd) {
125                 return "", nil
126         }
127         return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after
128 }
129
130 // If data is empty or ends in \n, fixNL returns data.
131 // Otherwise fixNL returns a new slice consisting of data with a final \n added.
132 func fixNL(data []byte) []byte {
133         if len(data) == 0 || data[len(data)-1] == '\n' {
134                 return data
135         }
136         d := make([]byte, len(data)+1)
137         copy(d, data)
138         d[len(data)] = '\n'
139         return d
140 }