...
1
2
3
4
5 package armor
6
7 import (
8 "encoding/base64"
9 "io"
10 )
11
12 var armorHeaderSep = []byte(": ")
13 var blockEnd = []byte("\n=")
14 var newline = []byte("\n")
15 var armorEndOfLineOut = []byte("-----\n")
16
17
18 func writeSlices(out io.Writer, slices ...[]byte) (err error) {
19 for _, s := range slices {
20 _, err = out.Write(s)
21 if err != nil {
22 return err
23 }
24 }
25 return
26 }
27
28
29
30 type lineBreaker struct {
31 lineLength int
32 line []byte
33 used int
34 out io.Writer
35 haveWritten bool
36 }
37
38 func newLineBreaker(out io.Writer, lineLength int) *lineBreaker {
39 return &lineBreaker{
40 lineLength: lineLength,
41 line: make([]byte, lineLength),
42 used: 0,
43 out: out,
44 }
45 }
46
47 func (l *lineBreaker) Write(b []byte) (n int, err error) {
48 n = len(b)
49
50 if n == 0 {
51 return
52 }
53
54 if l.used == 0 && l.haveWritten {
55 _, err = l.out.Write([]byte{'\n'})
56 if err != nil {
57 return
58 }
59 }
60
61 if l.used+len(b) < l.lineLength {
62 l.used += copy(l.line[l.used:], b)
63 return
64 }
65
66 l.haveWritten = true
67 _, err = l.out.Write(l.line[0:l.used])
68 if err != nil {
69 return
70 }
71 excess := l.lineLength - l.used
72 l.used = 0
73
74 _, err = l.out.Write(b[0:excess])
75 if err != nil {
76 return
77 }
78
79 _, err = l.Write(b[excess:])
80 return
81 }
82
83 func (l *lineBreaker) Close() (err error) {
84 if l.used > 0 {
85 _, err = l.out.Write(l.line[0:l.used])
86 if err != nil {
87 return
88 }
89 }
90
91 return
92 }
93
94
95
96
97
98
99
100
101 type encoding struct {
102 out io.Writer
103 breaker *lineBreaker
104 b64 io.WriteCloser
105 crc uint32
106 blockType []byte
107 }
108
109 func (e *encoding) Write(data []byte) (n int, err error) {
110 e.crc = crc24(e.crc, data)
111 return e.b64.Write(data)
112 }
113
114 func (e *encoding) Close() (err error) {
115 err = e.b64.Close()
116 if err != nil {
117 return
118 }
119 e.breaker.Close()
120
121 var checksumBytes [3]byte
122 checksumBytes[0] = byte(e.crc >> 16)
123 checksumBytes[1] = byte(e.crc >> 8)
124 checksumBytes[2] = byte(e.crc)
125
126 var b64ChecksumBytes [4]byte
127 base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:])
128
129 return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine)
130 }
131
132
133
134 func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) {
135 bType := []byte(blockType)
136 err = writeSlices(out, armorStart, bType, armorEndOfLineOut)
137 if err != nil {
138 return
139 }
140
141 for k, v := range headers {
142 err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline)
143 if err != nil {
144 return
145 }
146 }
147
148 _, err = out.Write(newline)
149 if err != nil {
150 return
151 }
152
153 e := &encoding{
154 out: out,
155 breaker: newLineBreaker(out, 64),
156 crc: crc24Init,
157 blockType: bType,
158 }
159 e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker)
160 return e, nil
161 }
162
View as plain text