1 // Copyright 2017 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.
12 // A Builder builds byte strings from fixed-length and length-prefixed values.
13 // The zero value is a usable Builder that allocates space as needed.
24 // NewBuilder creates a Builder that appends its output to the given buffer.
25 // Like append(), the slice will be reallocated if its capacity is exceeded.
26 // Use Bytes to get the final buffer.
27 func NewBuilder(buffer []byte) *Builder {
33 // NewFixedBuilder creates a Builder that appends its output into the given
34 // buffer. This builder does not reallocate the output buffer. Writes that
35 // would exceed the buffer's capacity are treated as an error.
36 func NewFixedBuilder(buffer []byte) *Builder {
43 // Bytes returns the bytes written by the builder or an error if one has
44 // occurred during during building.
45 func (b *Builder) Bytes() ([]byte, error) {
49 return b.result[b.offset:], nil
52 // BytesOrPanic returns the bytes written by the builder or panics if an error
53 // has occurred during building.
54 func (b *Builder) BytesOrPanic() []byte {
58 return b.result[b.offset:]
61 // AddUint8 appends an 8-bit value to the byte string.
62 func (b *Builder) AddUint8(v uint8) {
66 // AddUint16 appends a big-endian, 16-bit value to the byte string.
67 func (b *Builder) AddUint16(v uint16) {
68 b.add(byte(v>>8), byte(v))
71 // AddUint24 appends a big-endian, 24-bit value to the byte string. The highest
72 // byte of the 32-bit input value is silently truncated.
73 func (b *Builder) AddUint24(v uint32) {
74 b.add(byte(v>>16), byte(v>>8), byte(v))
77 // AddUint32 appends a big-endian, 32-bit value to the byte string.
78 func (b *Builder) AddUint32(v uint32) {
79 b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
82 // AddBytes appends a sequence of bytes to the byte string.
83 func (b *Builder) AddBytes(v []byte) {
87 // BuilderContinuation is continuation-passing interface for building
88 // length-prefixed byte sequences. Builder methods for length-prefixed
89 // sequences (AddUint8LengthPrefixed etc.) will invoke the BuilderContinuation
90 // supplied to them. The child builder passed to the continuation can be used
91 // to build the content of the length-prefixed sequence. Example:
93 // parent := cryptobyte.NewBuilder()
94 // parent.AddUint8LengthPrefixed(func (child *Builder) {
96 // child.AddUint8LengthPrefixed(func (grandchild *Builder) {
97 // grandchild.AddUint8(5)
101 // It is an error to write more bytes to the child than allowed by the reserved
102 // length prefix. After the continuation returns, the child must be considered
103 // invalid, i.e. users must not store any copies or references of the child
104 // that outlive the continuation.
105 type BuilderContinuation func(child *Builder)
107 // AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence.
108 func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
109 b.addLengthPrefixed(1, false, f)
112 // AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence.
113 func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) {
114 b.addLengthPrefixed(2, false, f)
117 // AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence.
118 func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) {
119 b.addLengthPrefixed(3, false, f)
122 func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) {
123 // Subsequent writes can be ignored if the builder has encountered an error.
128 offset := len(b.result)
129 b.add(make([]byte, lenLen)...)
133 fixedSize: b.fixedSize,
135 pendingLenLen: lenLen,
136 pendingIsASN1: isASN1,
142 panic("cryptobyte: internal error")
146 func (b *Builder) flushChild() {
154 if child.err != nil {
159 length := len(child.result) - child.pendingLenLen - child.offset
162 panic("cryptobyte: internal error") // result unexpectedly shrunk
165 if child.pendingIsASN1 {
166 // For ASN.1, we reserved a single byte for the length. If that turned out
167 // to be incorrect, we have to move the contents along in order to make
169 if child.pendingLenLen != 1 {
170 panic("cryptobyte: internal error")
172 var lenLen, lenByte uint8
173 if int64(length) > 0xfffffffe {
174 b.err = errors.New("pending ASN.1 child too long")
176 } else if length > 0xffffff {
179 } else if length > 0xffff {
182 } else if length > 0xff {
185 } else if length > 0x7f {
190 lenByte = uint8(length)
194 // Insert the initial length byte, make space for successive length bytes,
195 // and adjust the offset.
196 child.result[child.offset] = lenByte
197 extraBytes := int(lenLen - 1)
199 child.add(make([]byte, extraBytes)...)
200 childStart := child.offset + child.pendingLenLen
201 copy(child.result[childStart+extraBytes:], child.result[childStart:])
204 child.pendingLenLen = extraBytes
208 for i := child.pendingLenLen - 1; i >= 0; i-- {
209 child.result[child.offset+i] = uint8(l)
213 b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen)
218 b.result = child.result // In case child reallocated result.
222 func (b *Builder) add(bytes ...byte) {
227 panic("attempted write while child is pending")
229 if len(b.result)+len(bytes) < len(bytes) {
230 b.err = errors.New("cryptobyte: length overflow")
232 if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) {
233 b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer")
236 b.result = append(b.result, bytes...)
239 // A MarshalingValue marshals itself into a Builder.
240 type MarshalingValue interface {
241 // Marshal is called by Builder.AddValue. It receives a pointer to a builder
242 // to marshal itself into. It may return an error that occurred during
243 // marshaling, such as unset or invalid values.
244 Marshal(b *Builder) error
247 // AddValue calls Marshal on v, passing a pointer to the builder to append to.
248 // If Marshal returns an error, it is set on the Builder so that subsequent
249 // appends don't have an effect.
250 func (b *Builder) AddValue(v MarshalingValue) {