• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tox-rs / tox / 7247588774

18 Dec 2023 11:45AM UTC coverage: 94.85% (-0.03%) from 94.876%
7247588774

Pull #477

github

web-flow
Merge 6a5a935cc into 5ab95a61f
Pull Request #477: Add functions for tox crate version information

0 of 10 new or added lines in 1 file covered. (0.0%)

13 existing lines in 7 files now uncovered.

16816 of 17729 relevant lines covered (94.85%)

1.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.3
/tox_core/src/relay/links.rs
1
/*! The implementation of links used by server and clients
2
*/
3

4
use tox_crypto::*;
5

6
use std::collections::HashMap;
7

8
/// This constant is defined by c-toxcore
9
pub const MAX_LINKS_N: u8 = 240;
10

11
/// The status of the Link
12
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
13
pub enum LinkStatus {
14
    /// The link is registered on one side only.
15
    ///
16
    /// We received `RouteResponse` packet with connection id but can't use it
17
    /// until we get `ConnectNotification` packet.
18
    Registered,
19
    /// The link is registered on both sides: both clients are linked.
20
    /// It means that both clients sent RouteRequest and received ConnectNotification
21
    Online,
22
}
23

24
#[derive(Debug, Clone)]
25
pub struct Link {
26
    /// The link is linked with the given PK
27
    pub pk: PublicKey,
28
    /// The status of the link
29
    pub status: LinkStatus,
30
}
31

32
impl Link {
33
    /// Create a new link with the given PK and with status = Registered
34
    fn new(pk: PublicKey) -> Link {
1✔
35
        Link {
36
            pk,
37
            status: LinkStatus::Registered,
38
        }
39
    }
40
    /// Change status to Registered
41
    fn downgrade(&mut self) {
1✔
42
        self.status = LinkStatus::Registered;
1✔
43
    }
44
    /// Change status to Online
45
    fn upgrade(&mut self) {
1✔
46
        self.status = LinkStatus::Online;
1✔
47
    }
48
}
49

50
/// The structure contains `MAX_LINKS_N` of links
51
pub struct Links {
52
    /** An array of Link with `MAX_LINKS_N` item
53
    None means there is a hole and you can allocate the `Link`
54
    and respond the id with RouteResponse. Packets like `Data`
55
    use connection_id as an index for links.
56
    */
57
    links: [Option<Link>; MAX_LINKS_N as usize],
58
    /// Map PK -> id to index links with O(1)
59
    pk_to_id: HashMap<PublicKey, u8>,
60
}
61

62
impl Default for Links {
63
    fn default() -> Self {
1✔
64
        Links::new()
1✔
65
    }
66
}
67

68
impl Links {
69
    /// Create empty `Links`
70
    pub fn new() -> Links {
1✔
71
        const LINK: Option<Link> = None;
72
        Links {
73
            links: [LINK; 240],
74
            pk_to_id: HashMap::new(),
1✔
75
        }
76
    }
77
    /** Try to find a hole inside `links` and insert a new `Link` to the given PK
78
    Return `Some(id)` if there was a `Link` or if there is a room for a new `Link`
79
    Return `None` if there is no room for a new `Link`
80
    */
81
    pub fn insert(&mut self, pk: PublicKey) -> Option<u8> {
1✔
82
        let possible_index = { self.pk_to_id.get(&pk).cloned() };
1✔
83
        match possible_index {
1✔
84
            Some(index) => Some(index), // already inserted
1✔
85
            None => {
86
                if let Some(index) = self.links.iter().position(|link| link.is_none()) {
6✔
87
                    let link = Link::new(pk.clone());
1✔
88
                    self.links[index] = Some(link);
2✔
89
                    self.pk_to_id.insert(pk, index as u8);
1✔
90
                    Some(index as u8)
1✔
91
                } else {
92
                    // no enough room for a link
93
                    None
1✔
94
                }
95
            }
96
        }
97
    }
98
    /** Try to insert a new `Link` to the given PK at links[id]
99
    Return true if succeeded to insert a new `Link`
100
    Return false if there is a `Link` with such PK or there is no hole at links[id]
101
    */
102
    pub fn insert_by_id(&mut self, pk: PublicKey, index: u8) -> bool {
1✔
UNCOV
103
        assert!(
×
104
            index < MAX_LINKS_N,
1✔
105
            "The index {} must be lower than {}",
106
            index,
107
            MAX_LINKS_N
108
        );
109
        if !self.pk_to_id.contains_key(&pk) && self.links[index as usize].is_none() {
3✔
110
            let link = Link::new(pk.clone());
1✔
111
            self.links[index as usize] = Some(link);
1✔
112
            self.pk_to_id.insert(pk, index);
1✔
113
            true
1✔
114
        } else {
115
            false
1✔
116
        }
117
    }
118
    /// Get `Link` by id
119
    pub fn by_id(&self, index: u8) -> Option<&Link> {
1✔
120
        if index < MAX_LINKS_N {
2✔
121
            self.links[index as usize].as_ref()
2✔
122
        } else {
123
            None
1✔
124
        }
125
    }
126
    /// Get index of the link by PK
127
    pub fn id_by_pk(&self, pk: &PublicKey) -> Option<u8> {
1✔
128
        self.pk_to_id.get(pk).cloned()
1✔
129
    }
130
    /// Takes the link out of the links, leaving a None in its place
131
    pub fn take(&mut self, index: u8) -> Option<Link> {
1✔
132
        if index < MAX_LINKS_N {
2✔
133
            if let Some(link) = self.links[index as usize].take() {
4✔
134
                self.pk_to_id.remove(&link.pk);
1✔
135
                Some(link)
1✔
136
            } else {
137
                None
1✔
138
            }
139
        } else {
140
            None
1✔
141
        }
142
    }
143
    /// Call Links::downgrade on the `Link` by id
144
    /// Return false of links[id] is None
145
    pub fn downgrade(&mut self, index: u8) -> bool {
1✔
UNCOV
146
        assert!(
×
147
            index < MAX_LINKS_N,
1✔
148
            "The index {} must be lower than {}",
149
            index,
150
            MAX_LINKS_N
151
        );
152
        if let Some(ref mut link) = self.links[index as usize] {
2✔
153
            link.downgrade();
1✔
154
            true
1✔
155
        } else {
156
            false
1✔
157
        }
158
    }
159
    /// Call Links::upgrade on the `Link` by id
160
    /// Return false of links[id] is None
161
    pub fn upgrade(&mut self, index: u8) -> bool {
1✔
UNCOV
162
        assert!(
×
163
            index < MAX_LINKS_N,
1✔
164
            "The index {} must be lower than {}",
165
            index,
166
            MAX_LINKS_N
167
        );
168
        if let Some(ref mut link) = self.links[index as usize] {
2✔
169
            link.upgrade();
1✔
170
            true
1✔
171
        } else {
172
            false
1✔
173
        }
174
    }
175
    /// Iter over each non-empty link in self.links
176
    pub fn iter_links(&self) -> impl Iterator<Item = Link> + '_ {
1✔
177
        self.links.iter().filter_map(|link| link.clone())
3✔
178
    }
179
    /// Clear links
180
    pub fn clear(&mut self) {
1✔
181
        const LINK: Option<Link> = None;
182
        self.links = [LINK; 240];
183
        self.pk_to_id.clear();
1✔
184
    }
185
}
186

187
#[cfg(test)]
188
mod tests {
189
    use rand::thread_rng;
190

191
    use crate::relay::links::*;
192

193
    #[test]
194
    fn link_new() {
3✔
195
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
1✔
196
        let link = Link::new(pk.clone());
1✔
197
        assert_eq!(LinkStatus::Registered, link.status);
1✔
198
        assert_eq!(pk, link.pk);
1✔
199
    }
200

201
    #[test]
202
    fn link_upgrade() {
3✔
203
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
1✔
204
        let mut link = Link::new(pk);
1✔
205
        link.upgrade();
1✔
206
        assert_eq!(LinkStatus::Online, link.status);
1✔
207
    }
208

209
    #[test]
210
    fn link_downgrade() {
3✔
211
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
1✔
212
        let mut link = Link::new(pk);
1✔
213
        link.upgrade();
1✔
214
        link.downgrade();
1✔
215
        assert_eq!(LinkStatus::Registered, link.status);
1✔
216
    }
217

218
    #[test]
219
    fn links_new() {
3✔
220
        let links = Links::new();
1✔
221
        assert!(links.links.iter().all(|link| link.is_none()));
4✔
222
        assert!(links.pk_to_id.is_empty());
1✔
223
    }
224

225
    #[test]
226
    fn links_default() {
3✔
227
        let links = Links::default();
1✔
228
        assert!(links.links.iter().all(|link| link.is_none()));
4✔
229
        assert!(links.pk_to_id.is_empty());
1✔
230
    }
231

232
    #[test]
233
    fn links_insert_240() {
3✔
234
        let mut rng = thread_rng();
1✔
235
        let mut links = Links::new();
1✔
236
        for _ in 0..240 {
2✔
237
            // The first 240 must be inserted successfully
238
            let pk = SecretKey::generate(&mut rng).public_key();
2✔
239
            let id = links.insert(pk);
1✔
240
            assert!(id.is_some());
1✔
241
        }
242
        let pk = SecretKey::generate(&mut rng).public_key();
2✔
243
        let id = links.insert(pk);
1✔
244
        assert!(id.is_none());
1✔
245
    }
246

247
    #[test]
248
    fn links_insert_same_pk() {
3✔
249
        let mut links = Links::new();
1✔
250

251
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
2✔
252
        let id1 = links.insert(pk.clone());
1✔
253
        let id2 = links.insert(pk);
1✔
254

255
        assert!(id1.is_some());
1✔
256
        assert_eq!(id1, id2);
1✔
257
    }
258

259
    #[test]
260
    fn links_insert_alloc_order() {
3✔
261
        let mut rng = thread_rng();
1✔
262
        let mut links = Links::new();
1✔
263

264
        let pk1 = SecretKey::generate(&mut rng).public_key();
2✔
265
        let id1 = links.insert(pk1).unwrap();
1✔
266

267
        let pk2 = SecretKey::generate(&mut rng).public_key();
1✔
268
        let id2 = links.insert(pk2).unwrap();
1✔
269

270
        // Two links inserted, the id of the 1st is 0, the id of the 2nd is 1
271
        assert_eq!(id1, 0);
1✔
272
        assert_eq!(id2, 1);
1✔
273

274
        // Remove link[0]
275
        links.take(0);
1✔
276

277
        // Insert a third link
278
        let pk3 = SecretKey::generate(&mut rng).public_key();
1✔
279
        let id3 = links.insert(pk3).unwrap();
1✔
280

281
        // The id of the link must be 0
282
        assert_eq!(id3, 0);
1✔
283
    }
284

285
    #[test]
286
    fn links_insert_by_id() {
3✔
287
        let mut rng = thread_rng();
1✔
288
        let mut links = Links::new();
1✔
289

290
        let pk1 = SecretKey::generate(&mut rng).public_key();
2✔
291
        let pk2 = SecretKey::generate(&mut rng).public_key();
1✔
292

293
        let id1 = links.insert(pk1.clone()).unwrap();
1✔
294

295
        assert!(!links.insert_by_id(pk1, id1 + 1));
1✔
296
        assert!(!links.insert_by_id(pk2.clone(), id1));
2✔
297
        assert!(links.insert_by_id(pk2.clone(), id1 + 1));
2✔
298

299
        assert_eq!(links.by_id(id1 + 1).unwrap().pk, pk2);
1✔
300
    }
301

302
    #[test]
303
    fn links_by_id() {
3✔
304
        let mut links = Links::new();
1✔
305

306
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
2✔
307
        let id = links.insert(pk.clone()).unwrap();
1✔
308

309
        assert_eq!(pk, links.by_id(id).unwrap().pk);
1✔
310
    }
311

312
    #[test]
313
    fn links_by_id_nonexistent() {
3✔
314
        let links = Links::new();
1✔
315

316
        assert!(links.by_id(MAX_LINKS_N + 1).is_none());
2✔
317
    }
318

319
    #[test]
320
    fn links_by_pk() {
3✔
321
        let mut links = Links::new();
1✔
322

323
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
2✔
324
        let id = links.insert(pk.clone());
1✔
325

326
        assert_eq!(id, links.id_by_pk(&pk));
1✔
327
    }
328

329
    #[test]
330
    fn links_upgrade() {
3✔
331
        let mut links = Links::new();
1✔
332

333
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
2✔
334
        let id = links.insert(pk).unwrap();
1✔
335

336
        assert_eq!(LinkStatus::Registered, links.by_id(id).unwrap().status);
1✔
337

338
        assert!(links.upgrade(id)); // try to upgrade an existent link
1✔
339

340
        assert_eq!(LinkStatus::Online, links.by_id(id).unwrap().status);
1✔
341

342
        assert!(!links.upgrade(id + 1)); // try to upgrade an nonexistent link
1✔
343
    }
344

345
    #[test]
346
    fn links_downgrade() {
3✔
347
        let mut links = Links::new();
1✔
348

349
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
2✔
350
        let id = links.insert(pk).unwrap();
1✔
351

352
        assert_eq!(LinkStatus::Registered, links.by_id(id).unwrap().status);
1✔
353

354
        links.upgrade(id);
1✔
355
        assert!(links.downgrade(id)); // try to downgrade an existent link
1✔
356

357
        assert_eq!(LinkStatus::Registered, links.by_id(id).unwrap().status);
1✔
358

359
        assert!(!links.downgrade(id + 1)); // try to downgrade an nonexistent link
1✔
360
    }
361

362
    #[test]
363
    fn links_take() {
3✔
364
        let mut links = Links::new();
1✔
365

366
        let pk = SecretKey::generate(&mut thread_rng()).public_key();
2✔
367
        let id = links.insert(pk.clone()).unwrap();
1✔
368

369
        assert!(links.by_id(id).is_some());
1✔
370

371
        let link = links.take(id);
1✔
372
        assert_eq!(pk, link.unwrap().pk);
1✔
373

374
        assert!(links.by_id(id).is_none());
1✔
375
    }
376

377
    #[test]
378
    fn links_take_nonexistent() {
3✔
379
        let mut links = Links::new();
1✔
380

381
        assert!(links.take(MAX_LINKS_N + 1).is_none());
2✔
382
    }
383

384
    #[test]
385
    fn links_clear() {
3✔
386
        let mut rng = thread_rng();
1✔
387
        let mut links = Links::new();
1✔
388
        for _ in 0..240 {
2✔
389
            // The first 240 must be inserted successfully
390
            let pk = SecretKey::generate(&mut rng).public_key();
2✔
391
            let id = links.insert(pk);
1✔
392
            assert!(id.is_some());
1✔
393
        }
394

395
        // check links are non empty
396
        for i in 0..240 {
3✔
397
            assert!(links.by_id(i).is_some());
3✔
398
        }
399

400
        links.clear();
1✔
401

402
        // check links are empty
403
        for i in 0..240 {
2✔
404
            assert!(links.by_id(i).is_none());
3✔
405
        }
406
    }
407
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc