/work/toxcore/ping_array.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2018 The TokTok team. |
3 | | * Copyright © 2014 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Implementation of an efficient array to store that we pinged something. |
8 | | */ |
9 | | #include "ping_array.h" |
10 | | |
11 | | #include <string.h> |
12 | | |
13 | | #include "ccompat.h" |
14 | | #include "crypto_core.h" |
15 | | #include "mem.h" |
16 | | #include "mono_time.h" |
17 | | |
18 | | typedef struct Ping_Array_Entry { |
19 | | uint8_t *data; |
20 | | uint32_t length; |
21 | | uint64_t ping_time; |
22 | | uint64_t ping_id; |
23 | | } Ping_Array_Entry; |
24 | | |
25 | | struct Ping_Array { |
26 | | const Memory *mem; |
27 | | Ping_Array_Entry *entries; |
28 | | |
29 | | uint32_t last_deleted; /* number representing the next entry to be deleted. */ |
30 | | uint32_t last_added; /* number representing the last entry to be added. */ |
31 | | uint32_t total_size; /* The length of entries */ |
32 | | uint32_t timeout; /* The timeout after which entries are cleared. */ |
33 | | }; |
34 | | |
35 | | Ping_Array *ping_array_new(const Memory *mem, uint32_t size, uint32_t timeout) |
36 | 12.0k | { |
37 | 12.0k | if (size == 0 || timeout == 0) { |
38 | 2 | return nullptr; |
39 | 2 | } |
40 | | |
41 | 12.0k | if ((size & (size - 1)) != 0) { |
42 | | // Not a power of 2. |
43 | 2 | return nullptr; |
44 | 2 | } |
45 | | |
46 | 12.0k | Ping_Array *const empty_array = (Ping_Array *)mem_alloc(mem, sizeof(Ping_Array)); |
47 | | |
48 | 12.0k | if (empty_array == nullptr) { |
49 | 63 | return nullptr; |
50 | 63 | } |
51 | | |
52 | 11.9k | Ping_Array_Entry *entries = (Ping_Array_Entry *)mem_valloc(mem, size, sizeof(Ping_Array_Entry)); |
53 | | |
54 | 11.9k | if (entries == nullptr) { |
55 | 61 | mem_delete(mem, empty_array); |
56 | 61 | return nullptr; |
57 | 61 | } |
58 | | |
59 | 11.8k | empty_array->mem = mem; |
60 | 11.8k | empty_array->entries = entries; |
61 | 11.8k | empty_array->last_deleted = 0; |
62 | 11.8k | empty_array->last_added = 0; |
63 | 11.8k | empty_array->total_size = size; |
64 | 11.8k | empty_array->timeout = timeout; |
65 | 11.8k | return empty_array; |
66 | 11.9k | } |
67 | | |
68 | | non_null() |
69 | | static void clear_entry(Ping_Array *array, uint32_t index) |
70 | 317k | { |
71 | 317k | const Ping_Array_Entry empty = {nullptr}; |
72 | 317k | mem_delete(array->mem, array->entries[index].data); |
73 | 317k | array->entries[index] = empty; |
74 | 317k | } |
75 | | |
76 | | void ping_array_kill(Ping_Array *array) |
77 | 8.88k | { |
78 | 8.88k | if (array == nullptr) { |
79 | 148 | return; |
80 | 148 | } |
81 | | |
82 | 36.8k | while (array->last_deleted != array->last_added) { |
83 | 28.1k | const uint32_t index = array->last_deleted % array->total_size; |
84 | 28.1k | clear_entry(array, index); |
85 | 28.1k | ++array->last_deleted; |
86 | 28.1k | } |
87 | | |
88 | 8.74k | mem_delete(array->mem, array->entries); |
89 | 8.74k | mem_delete(array->mem, array); |
90 | 8.74k | } |
91 | | |
92 | | /** Clear timed out entries. */ |
93 | | non_null() |
94 | | static void ping_array_clear_timedout(Ping_Array *array, const Mono_Time *mono_time) |
95 | 182k | { |
96 | 323k | while (array->last_deleted != array->last_added) { |
97 | 298k | const uint32_t index = array->last_deleted % array->total_size; |
98 | | |
99 | 298k | if (!mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) { |
100 | 157k | break; |
101 | 157k | } |
102 | | |
103 | 141k | clear_entry(array, index); |
104 | 141k | ++array->last_deleted; |
105 | 141k | } |
106 | 182k | } |
107 | | |
108 | | uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const Random *rng, |
109 | | const uint8_t *data, uint32_t length) |
110 | 182k | { |
111 | 182k | ping_array_clear_timedout(array, mono_time); |
112 | 182k | const uint32_t index = array->last_added % array->total_size; |
113 | | |
114 | 182k | if (array->entries[index].data != nullptr) { |
115 | 5.99k | array->last_deleted = array->last_added - array->total_size; |
116 | 5.99k | clear_entry(array, index); |
117 | 5.99k | } |
118 | | |
119 | 182k | uint8_t *entry_data = (uint8_t *)mem_balloc(array->mem, length); |
120 | | |
121 | 182k | if (entry_data == nullptr) { |
122 | 366 | array->entries[index].data = nullptr; |
123 | 366 | return 0; |
124 | 366 | } |
125 | | |
126 | 182k | memcpy(entry_data, data, length); |
127 | | |
128 | 182k | array->entries[index].data = entry_data; |
129 | 182k | array->entries[index].length = length; |
130 | 182k | array->entries[index].ping_time = mono_time_get(mono_time); |
131 | 182k | ++array->last_added; |
132 | 182k | uint64_t ping_id = random_u64(rng); |
133 | 182k | ping_id /= array->total_size; |
134 | 182k | ping_id *= array->total_size; |
135 | 182k | ping_id += index; |
136 | | |
137 | 182k | if (ping_id == 0) { |
138 | 25 | ping_id += array->total_size; |
139 | 25 | } |
140 | | |
141 | 182k | array->entries[index].ping_id = ping_id; |
142 | 182k | return ping_id; |
143 | 182k | } |
144 | | |
145 | | int32_t ping_array_check(Ping_Array *array, const Mono_Time *mono_time, uint8_t *data, |
146 | | size_t length, uint64_t ping_id) |
147 | 146k | { |
148 | 146k | if (ping_id == 0) { |
149 | 5 | return -1; |
150 | 5 | } |
151 | | |
152 | 146k | const uint32_t index = ping_id % array->total_size; |
153 | | |
154 | 146k | if (array->entries[index].ping_id != ping_id) { |
155 | 3.38k | return -1; |
156 | 3.38k | } |
157 | | |
158 | 142k | if (mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) { |
159 | 90 | return -1; |
160 | 90 | } |
161 | | |
162 | 142k | if (array->entries[index].length > length) { |
163 | 1 | return -1; |
164 | 1 | } |
165 | | |
166 | | // TODO(iphydf): This can't happen? If it indeed can't, turn it into an assert. |
167 | 142k | if (array->entries[index].data == nullptr) { |
168 | 0 | return -1; |
169 | 0 | } |
170 | | |
171 | 142k | memcpy(data, array->entries[index].data, array->entries[index].length); |
172 | 142k | const uint32_t len = array->entries[index].length; |
173 | 142k | clear_entry(array, index); |
174 | 142k | return len; |
175 | 142k | } |