1package cbor
2
3import (
4	"errors"
5	"fmt"
6	"reflect"
7	"sync"
8)
9
10// Tag represents CBOR tag data, including tag number and unmarshaled tag content.
11type Tag struct {
12	Number  uint64
13	Content interface{}
14}
15
16// RawTag represents CBOR tag data, including tag number and raw tag content.
17// RawTag implements Unmarshaler and Marshaler interfaces.
18type RawTag struct {
19	Number  uint64
20	Content RawMessage
21}
22
23// UnmarshalCBOR sets *t with tag number and raw tag content copied from data.
24func (t *RawTag) UnmarshalCBOR(data []byte) error {
25	if t == nil {
26		return errors.New("cbor.RawTag: UnmarshalCBOR on nil pointer")
27	}
28
29	// Decoding CBOR null and undefined to cbor.RawTag is no-op.
30	if len(data) == 1 && (data[0] == 0xf6 || data[0] == 0xf7) {
31		return nil
32	}
33
34	d := decoder{data: data, dm: defaultDecMode}
35
36	// Unmarshal tag number.
37	typ, _, num := d.getHead()
38	if typ != cborTypeTag {
39		return &UnmarshalTypeError{CBORType: typ.String(), GoType: typeRawTag.String()}
40	}
41	t.Number = num
42
43	// Unmarshal tag content.
44	c := d.data[d.off:]
45	t.Content = make([]byte, len(c))
46	copy(t.Content, c)
47	return nil
48}
49
50// MarshalCBOR returns CBOR encoding of t.
51func (t RawTag) MarshalCBOR() ([]byte, error) {
52	if t.Number == 0 && len(t.Content) == 0 {
53		// Marshal uninitialized cbor.RawTag
54		b := make([]byte, len(cborNil))
55		copy(b, cborNil)
56		return b, nil
57	}
58
59	e := getEncoderBuffer()
60
61	encodeHead(e, byte(cborTypeTag), t.Number)
62
63	content := t.Content
64	if len(content) == 0 {
65		content = cborNil
66	}
67
68	buf := make([]byte, len(e.Bytes())+len(content))
69	n := copy(buf, e.Bytes())
70	copy(buf[n:], content)
71
72	putEncoderBuffer(e)
73	return buf, nil
74}
75
76// DecTagMode specifies how decoder handles tag number.
77type DecTagMode int
78
79const (
80	// DecTagIgnored makes decoder ignore tag number (skips if present).
81	DecTagIgnored DecTagMode = iota
82
83	// DecTagOptional makes decoder verify tag number if it's present.
84	DecTagOptional
85
86	// DecTagRequired makes decoder verify tag number and tag number must be present.
87	DecTagRequired
88
89	maxDecTagMode
90)
91
92func (dtm DecTagMode) valid() bool {
93	return dtm < maxDecTagMode
94}
95
96// EncTagMode specifies how encoder handles tag number.
97type EncTagMode int
98
99const (
100	// EncTagNone makes encoder not encode tag number.
101	EncTagNone EncTagMode = iota
102
103	// EncTagRequired makes encoder encode tag number.
104	EncTagRequired
105
106	maxEncTagMode
107)
108
109func (etm EncTagMode) valid() bool {
110	return etm < maxEncTagMode
111}
112
113// TagOptions specifies how encoder and decoder handle tag number.
114type TagOptions struct {
115	DecTag DecTagMode
116	EncTag EncTagMode
117}
118
119// TagSet is an interface to add and remove tag info.  It is used by EncMode and DecMode
120// to provide CBOR tag support.
121type TagSet interface {
122	// Add adds given tag number(s), content type, and tag options to TagSet.
123	Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error
124
125	// Remove removes given tag content type from TagSet.
126	Remove(contentType reflect.Type)
127
128	tagProvider
129}
130
131type tagProvider interface {
132	getTagItemFromType(t reflect.Type) *tagItem
133	getTypeFromTagNum(num []uint64) reflect.Type
134}
135
136type tagItem struct {
137	num         []uint64
138	cborTagNum  []byte
139	contentType reflect.Type
140	opts        TagOptions
141}
142
143func (t *tagItem) equalTagNum(num []uint64) bool {
144	// Fast path to compare 1 tag number
145	if len(t.num) == 1 && len(num) == 1 && t.num[0] == num[0] {
146		return true
147	}
148
149	if len(t.num) != len(num) {
150		return false
151	}
152
153	for i := 0; i < len(t.num); i++ {
154		if t.num[i] != num[i] {
155			return false
156		}
157	}
158
159	return true
160}
161
162type (
163	tagSet map[reflect.Type]*tagItem
164
165	syncTagSet struct {
166		sync.RWMutex
167		t tagSet
168	}
169)
170
171func (t tagSet) getTagItemFromType(typ reflect.Type) *tagItem {
172	return t[typ]
173}
174
175func (t tagSet) getTypeFromTagNum(num []uint64) reflect.Type {
176	for typ, tag := range t {
177		if tag.equalTagNum(num) {
178			return typ
179		}
180	}
181	return nil
182}
183
184// NewTagSet returns TagSet (safe for concurrency).
185func NewTagSet() TagSet {
186	return &syncTagSet{t: make(map[reflect.Type]*tagItem)}
187}
188
189// Add adds given tag number(s), content type, and tag options to TagSet.
190func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error {
191	if contentType == nil {
192		return errors.New("cbor: cannot add nil content type to TagSet")
193	}
194	for contentType.Kind() == reflect.Ptr {
195		contentType = contentType.Elem()
196	}
197	tag, err := newTagItem(opts, contentType, num, nestedNum...)
198	if err != nil {
199		return err
200	}
201	t.Lock()
202	defer t.Unlock()
203	for typ, ti := range t.t {
204		if typ == contentType {
205			return errors.New("cbor: content type " + contentType.String() + " already exists in TagSet")
206		}
207		if ti.equalTagNum(tag.num) {
208			return fmt.Errorf("cbor: tag number %v already exists in TagSet", tag.num)
209		}
210	}
211	t.t[contentType] = tag
212	return nil
213}
214
215// Remove removes given tag content type from TagSet.
216func (t *syncTagSet) Remove(contentType reflect.Type) {
217	for contentType.Kind() == reflect.Ptr {
218		contentType = contentType.Elem()
219	}
220	t.Lock()
221	delete(t.t, contentType)
222	t.Unlock()
223}
224
225func (t *syncTagSet) getTagItemFromType(typ reflect.Type) *tagItem {
226	t.RLock()
227	ti := t.t[typ]
228	t.RUnlock()
229	return ti
230}
231
232func (t *syncTagSet) getTypeFromTagNum(num []uint64) reflect.Type {
233	t.RLock()
234	rt := t.t.getTypeFromTagNum(num)
235	t.RUnlock()
236	return rt
237}
238
239func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) (*tagItem, error) {
240	if opts.DecTag == DecTagIgnored && opts.EncTag == EncTagNone {
241		return nil, errors.New("cbor: cannot add tag with DecTagIgnored and EncTagNone options to TagSet")
242	}
243	if contentType.PkgPath() == "" || contentType.Kind() == reflect.Interface {
244		return nil, errors.New("cbor: can only add named types to TagSet, got " + contentType.String())
245	}
246	if contentType == typeTime {
247		return nil, errors.New("cbor: cannot add time.Time to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead")
248	}
249	if contentType == typeBigInt {
250		return nil, errors.New("cbor: cannot add big.Int to TagSet, it's built-in and supported automatically")
251	}
252	if contentType == typeTag {
253		return nil, errors.New("cbor: cannot add cbor.Tag to TagSet")
254	}
255	if contentType == typeRawTag {
256		return nil, errors.New("cbor: cannot add cbor.RawTag to TagSet")
257	}
258	if num == 0 || num == 1 {
259		return nil, errors.New("cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead")
260	}
261	if num == 2 || num == 3 {
262		return nil, errors.New("cbor: cannot add tag number 2 or 3 to TagSet, it's built-in and supported automatically")
263	}
264	if num == selfDescribedCBORTagNum {
265		return nil, errors.New("cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically")
266	}
267
268	te := tagItem{num: []uint64{num}, opts: opts, contentType: contentType}
269	te.num = append(te.num, nestedNum...)
270
271	// Cache encoded tag numbers
272	e := getEncoderBuffer()
273	for _, n := range te.num {
274		encodeHead(e, byte(cborTypeTag), n)
275	}
276	te.cborTagNum = make([]byte, e.Len())
277	copy(te.cborTagNum, e.Bytes())
278	putEncoderBuffer(e)
279
280	return &te, nil
281}
282
283var (
284	typeTag    = reflect.TypeOf(Tag{})
285	typeRawTag = reflect.TypeOf(RawTag{})
286)
287
288// WrongTagError describes mismatch between CBOR tag and registered tag.
289type WrongTagError struct {
290	RegisteredType   reflect.Type
291	RegisteredTagNum []uint64
292	TagNum           []uint64
293}
294
295func (e *WrongTagError) Error() string {
296	return fmt.Sprintf("cbor: wrong tag number for %s, got %v, expected %v", e.RegisteredType.String(), e.TagNum, e.RegisteredTagNum)
297}
298