forked from gortc/ice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
checklist.go
145 lines (131 loc) · 4.12 KB
/
checklist.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package ice
import (
"fmt"
"sort"
)
// Checklist is set of pairs.
//
//
// From RFC 8455 Section 6.1.2:
//
// There is one checklist for each data stream. To form a checklist,
// initiating and responding ICE agents form candidate pairs, compute
// pair priorities, order pairs by priority, prune pairs, remove lower-
// priority pairs, and set checklist states. If candidates are added to
// a checklist (e.g., due to detection of peer-reflexive candidates),
// the agent will re-perform these steps for the updated checklist.
type Checklist struct {
Pairs Pairs `json:"pairs,omitempty"`
Valid Pairs `json:"valid,omitempty"`
Triggered Pairs `json:"triggered,omitempty"` // FIFO
State ChecklistState `json:"state"`
}
// Equal returns true if checklist c equals to checklist b.
func (c Checklist) Equal(b Checklist) bool {
if c.State != b.State {
return false
}
if len(c.Pairs) != len(b.Pairs) {
return false
}
for i := range c.Pairs {
if !c.Pairs[i].Equal(&b.Pairs[i]) {
return false
}
}
return true
}
// ChecklistState represents the Checklist State.
//
// See RFC 8445 Section 6.1.2.1
type ChecklistState byte
// UnmarshalText implements TextUnmarshaler.
func (s *ChecklistState) UnmarshalText(text []byte) error {
for k, v := range checklistStateToStr {
if string(text) == v {
*s = k
return nil
}
}
return fmt.Errorf("unknown checklist state value: %q", text)
}
// MarshalText implements TextMarshaler.
func (s ChecklistState) MarshalText() (text []byte, err error) {
return []byte(checklistStateToStr[s]), nil
}
var checklistStateToStr = map[ChecklistState]string{
ChecklistRunning: "Running",
ChecklistCompleted: "Completed",
ChecklistFailed: "Failed",
}
func (s ChecklistState) String() string { return checklistStateToStr[s] }
const (
// ChecklistRunning is neither Completed nor Failed yet. Checklists are
// initially set to the Running state.
ChecklistRunning ChecklistState = iota
// ChecklistCompleted contains a nominated pair for each component of the
// data stream.
ChecklistCompleted
// ChecklistFailed does not have a valid pair for each component of the data
// stream, and all of the candidate pairs in the checklist are in either the
// Failed or the Succeeded state. In other words, at least one component of
// the checklist has candidate pairs that are all in the Failed state, which
// means the component has failed, which means the checklist has failed.
ChecklistFailed
)
// ComputePriorities computes priorities for all pairs based on agent role.
//
// The role determines whether local candidate is from controlling or from
// controlled agent.
func (c *Checklist) ComputePriorities(role Role) {
for i := range c.Pairs {
var (
controlling = c.Pairs[i].Local.Priority
controlled = c.Pairs[i].Remote.Priority
)
if role == Controlled {
controlling, controlled = controlled, controlling
}
c.Pairs[i].Priority = PairPriority(controlling, controlled)
}
}
// Sort is ordering pairs by priority descending.
// First element will have highest priority.
func (c *Checklist) Sort() { sort.Sort(c.Pairs) }
// Prune removes redundant candidates.
//
// Two candidate pairs are redundant if their local candidates have the same
// base and their remote candidates are identical
func (c *Checklist) Prune() {
// Pruning algorithm is not optimal but should work for small numbers,
// where len(c.Pairs) ~ 100.
result := make(Pairs, 0, len(c.Pairs))
Loop:
for i := range c.Pairs {
base := c.Pairs[i].Local.Base
for j := range result {
// Check if local candidates have the same base.
if !result[j].Local.Base.Equal(base) {
continue
}
// Check if remote candidates are identical.
if !result[j].Remote.Equal(&c.Pairs[i].Remote) {
continue
}
// Pair is redundant, skipping.
continue Loop
}
result = append(result, c.Pairs[i])
}
c.Pairs = result
}
// Limit ensures maximum length of pairs, removing the pairs with least priority
// if needed.
func (c *Checklist) Limit(max int) {
if len(c.Pairs) <= max {
return
}
c.Pairs = c.Pairs[:max]
}
// Len returns pairs count.
func (c *Checklist) Len() int { return len(c.Pairs) }