@@ -43,25 +43,40 @@ func DecodeString(s string) ([]byte, error) {
4343var EncodeToString = b64 .EncodeToString
4444
4545const ColumnsPerLine = 64
46+
4647const BytesPerLine = ColumnsPerLine / 4 * 3
4748
48- // NewlineWriter returns a Writer that writes to dst, inserting an LF character
49- // every ColumnsPerLine bytes. It does not insert a newline neither at the
50- // beginning nor at the end of the stream, but it ensures the last line is
51- // shorter than ColumnsPerLine, which means it might be empty.
52- func NewlineWriter (dst io.Writer ) io.Writer {
53- return & newlineWriter {dst : dst }
49+ // NewWrappedBase64Encoder returns a WrappedBase64Encoder that writes to dst.
50+ func NewWrappedBase64Encoder (enc * base64.Encoding , dst io.Writer ) * WrappedBase64Encoder {
51+ w := & WrappedBase64Encoder {dst : dst }
52+ w .enc = base64 .NewEncoder (enc , WriterFunc (w .writeWrapped ))
53+ return w
5454}
5555
56- type newlineWriter struct {
56+ type WriterFunc func (p []byte ) (int , error )
57+
58+ func (f WriterFunc ) Write (p []byte ) (int , error ) { return f (p ) }
59+
60+ // WrappedBase64Encoder is a standard base64 encoder that inserts an LF
61+ // character every ColumnsPerLine bytes. It does not insert a newline neither at
62+ // the beginning nor at the end of the stream, but it ensures the last line is
63+ // shorter than ColumnsPerLine, which means it might be empty.
64+ type WrappedBase64Encoder struct {
65+ enc io.WriteCloser
5766 dst io.Writer
5867 written int
5968 buf bytes.Buffer
6069}
6170
62- func (w * newlineWriter ) Write (p []byte ) (int , error ) {
71+ func (w * WrappedBase64Encoder ) Write (p []byte ) (int , error ) { return w .enc .Write (p ) }
72+
73+ func (w * WrappedBase64Encoder ) Close () error {
74+ return w .enc .Close ()
75+ }
76+
77+ func (w * WrappedBase64Encoder ) writeWrapped (p []byte ) (int , error ) {
6378 if w .buf .Len () != 0 {
64- panic ("age: internal error: non-empty newlineWriter .buf" )
79+ panic ("age: internal error: non-empty WrappedBase64Encoder .buf" )
6580 }
6681 for len (p ) > 0 {
6782 toWrite := ColumnsPerLine - (w .written % ColumnsPerLine )
@@ -84,9 +99,18 @@ func (w *newlineWriter) Write(p []byte) (int, error) {
8499 return len (p ), nil
85100}
86101
102+ // LastLineIsEmpty returns whether the last output line was empty, either
103+ // because no input was written, or because a multiple of BytesPerLine was.
104+ //
105+ // Calling LastLineIsEmpty before Close is meaningless.
106+ func (w * WrappedBase64Encoder ) LastLineIsEmpty () bool {
107+ return w .written % ColumnsPerLine == 0
108+ }
109+
87110const intro = "age-encryption.org/v1\n "
88111
89112var recipientPrefix = []byte ("->" )
113+
90114var footerPrefix = []byte ("---" )
91115
92116func (r * Stanza ) Marshal (w io.Writer ) error {
@@ -101,7 +125,7 @@ func (r *Stanza) Marshal(w io.Writer) error {
101125 if _ , err := io .WriteString (w , "\n " ); err != nil {
102126 return err
103127 }
104- ww := base64 . NewEncoder (b64 , NewlineWriter ( w ) )
128+ ww := NewWrappedBase64Encoder (b64 , w )
105129 if _ , err := ww .Write (r .Body ); err != nil {
106130 return err
107131 }
0 commit comments