-
Notifications
You must be signed in to change notification settings - Fork 3
/
cbor_codec.go
91 lines (83 loc) · 3.08 KB
/
cbor_codec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lb
import (
"fmt"
"io"
cbor "github.com/fxamacker/cbor/v2"
"github.com/matrix-org/gomatrixserverlib"
)
// CBORCodec allows the conversion between JSON and CBOR.
type CBORCodec struct {
keys map[string]int
enumKeys map[int]string
// If set:
// - CBORToJSON emits Canonical JSON: https://matrix.org/docs/spec/appendices#canonical-json
// - JSONToCBOR emits Canonical CBOR: RFC 7049 Section 3.9
canonical bool
}
// NewCBORCodec creates a CBOR codec which will map the enum keys given. If canonical is set,
// the output from this codec with be in canonical format for CBOR (RFC 7049 Section 3.9) and in
// Matrix Canonical JSON for JSON (https://matrix.org/docs/spec/appendices#canonical-json). Generally,
// you don't want to set canonical to true unless you are performing tests which need to produce a
// deterministic output (e.g sorted keys).
//
// Users of this library should prefer NewCBORCodecV1 which sets up all the enum keys for you. This
// function is exposed for bleeding edge or custom enums.
func NewCBORCodec(keys map[string]int, canonical bool) (*CBORCodec, error) {
c := &CBORCodec{
keys: keys,
enumKeys: make(map[int]string),
canonical: canonical,
}
for k, v := range keys {
if _, ok := c.enumKeys[v]; ok {
return nil, fmt.Errorf("cbor key map: duplicate integer %d - %s", v, k)
}
c.enumKeys[v] = k
}
return c, nil
}
// CBORToJSON converts a single CBOR object into a single JSON object
func (c *CBORCodec) CBORToJSON(input io.Reader) ([]byte, error) {
var intermediate interface{}
if err := cbor.NewDecoder(input).Decode(&intermediate); err != nil {
return nil, fmt.Errorf("CBORToJSON: unmarshalling cbor: %w", err)
}
intermediate = cborInterfaceToJSONInterface(intermediate, c.enumKeys)
b, err := json.Marshal(intermediate)
if err != nil {
return nil, err
}
if c.canonical {
return gomatrixserverlib.CanonicalJSON(b)
}
return b, nil
}
// JSONToCBOR converts a single JSON object into a single CBOR object
func (c *CBORCodec) JSONToCBOR(input io.Reader) ([]byte, error) {
var intermediate interface{}
if err := json.NewDecoder(input).Decode(&intermediate); err != nil {
return nil, fmt.Errorf("JSONToCBOR: unmarshalling json: %w", err)
}
intermediate = jsonInterfaceToCBORInterface(intermediate, c.keys)
if c.canonical {
enc, err := cbor.CanonicalEncOptions().EncMode()
if err != nil {
return nil, fmt.Errorf("JSONToCBOR: failed to make EncMode: %w", err)
}
return enc.Marshal(intermediate)
}
return cbor.Marshal(intermediate)
}