/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-2025 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 "attributes.h" |
14 | | #include "ccompat.h" |
15 | | #include "crypto_core.h" |
16 | | #include "mem.h" |
17 | | #include "mono_time.h" |
18 | | |
19 | | typedef struct Ping_Array_Entry { |
20 | | uint8_t *data; |
21 | | uint32_t length; |
22 | | uint64_t ping_time; |
23 | | uint64_t ping_id; |
24 | | } Ping_Array_Entry; |
25 | | |
26 | | struct Ping_Array { |
27 | | const Memory *mem; |
28 | | Ping_Array_Entry *entries; |
29 | | |
30 | | uint32_t last_deleted; /* number representing the next entry to be deleted. */ |
31 | | uint32_t last_added; /* number representing the last entry to be added. */ |
32 | | uint32_t total_size; /* The length of entries */ |
33 | | uint32_t timeout; /* The timeout after which entries are cleared. */ |
34 | | }; |
35 | | |
36 | | Ping_Array *ping_array_new(const Memory *mem, uint32_t size, uint32_t timeout) |
37 | 9.11k | { |
38 | 9.11k | if (size == 0 || timeout == 0) { |
39 | 2 | return nullptr; |
40 | 2 | } |
41 | | |
42 | 9.11k | if ((size & (size - 1)) != 0) { |
43 | | // Not a power of 2. |
44 | 2 | return nullptr; |
45 | 2 | } |
46 | | |
47 | 9.11k | Ping_Array *const empty_array = (Ping_Array *)mem_alloc(mem, sizeof(Ping_Array)); |
48 | | |
49 | 9.11k | if (empty_array == nullptr) { |
50 | 53 | return nullptr; |
51 | 53 | } |
52 | | |
53 | 9.06k | Ping_Array_Entry *entries = (Ping_Array_Entry *)mem_valloc(mem, size, sizeof(Ping_Array_Entry)); |
54 | | |
55 | 9.06k | if (entries == nullptr) { |
56 | 53 | mem_delete(mem, empty_array); |
57 | 53 | return nullptr; |
58 | 53 | } |
59 | | |
60 | 9.00k | empty_array->mem = mem; |
61 | 9.00k | empty_array->entries = entries; |
62 | 9.00k | empty_array->last_deleted = 0; |
63 | 9.00k | empty_array->last_added = 0; |
64 | 9.00k | empty_array->total_size = size; |
65 | 9.00k | empty_array->timeout = timeout; |
66 | 9.00k | return empty_array; |
67 | 9.06k | } |
68 | | |
69 | | static void clear_entry(Ping_Array *_Nonnull array, uint32_t index) |
70 | 297k | { |
71 | 297k | const Ping_Array_Entry empty = {nullptr}; |
72 | 297k | mem_delete(array->mem, array->entries[index].data); |
73 | 297k | array->entries[index] = empty; |
74 | 297k | } |
75 | | |
76 | | void ping_array_kill(Ping_Array *array) |
77 | 6.51k | { |
78 | 6.51k | if (array == nullptr) { |
79 | 127 | return; |
80 | 127 | } |
81 | | |
82 | 32.5k | while (array->last_deleted != array->last_added) { |
83 | 26.1k | const uint32_t index = array->last_deleted % array->total_size; |
84 | 26.1k | clear_entry(array, index); |
85 | 26.1k | ++array->last_deleted; |
86 | 26.1k | } |
87 | | |
88 | 6.38k | mem_delete(array->mem, array->entries); |
89 | 6.38k | mem_delete(array->mem, array); |
90 | 6.38k | } |
91 | | |
92 | | /** Clear timed out entries. */ |
93 | | static void ping_array_clear_timedout(Ping_Array *_Nonnull array, const Mono_Time *_Nonnull mono_time) |
94 | 170k | { |
95 | 307k | while (array->last_deleted != array->last_added) { |
96 | 287k | const uint32_t index = array->last_deleted % array->total_size; |
97 | | |
98 | 287k | if (!mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) { |
99 | 149k | break; |
100 | 149k | } |
101 | | |
102 | 137k | clear_entry(array, index); |
103 | 137k | ++array->last_deleted; |
104 | 137k | } |
105 | 170k | } |
106 | | |
107 | | uint64_t ping_array_add(Ping_Array *array, const Mono_Time *mono_time, const Random *rng, |
108 | | const uint8_t *data, uint32_t length) |
109 | 170k | { |
110 | 170k | ping_array_clear_timedout(array, mono_time); |
111 | 170k | const uint32_t index = array->last_added % array->total_size; |
112 | | |
113 | 170k | if (array->entries[index].data != nullptr) { |
114 | 3.51k | array->last_deleted = array->last_added - array->total_size; |
115 | 3.51k | clear_entry(array, index); |
116 | 3.51k | } |
117 | | |
118 | 170k | uint8_t *entry_data = (uint8_t *)mem_balloc(array->mem, length); |
119 | | |
120 | 170k | if (entry_data == nullptr) { |
121 | 90 | array->entries[index].data = nullptr; |
122 | 90 | return 0; |
123 | 90 | } |
124 | | |
125 | 170k | memcpy(entry_data, data, length); |
126 | | |
127 | 170k | array->entries[index].data = entry_data; |
128 | 170k | array->entries[index].length = length; |
129 | 170k | array->entries[index].ping_time = mono_time_get(mono_time); |
130 | 170k | ++array->last_added; |
131 | 170k | uint64_t ping_id = random_u64(rng); |
132 | 170k | ping_id /= array->total_size; |
133 | 170k | ping_id *= array->total_size; |
134 | 170k | ping_id += index; |
135 | | |
136 | 170k | if (ping_id == 0) { |
137 | 0 | ping_id += array->total_size; |
138 | 0 | } |
139 | | |
140 | 170k | array->entries[index].ping_id = ping_id; |
141 | 170k | return ping_id; |
142 | 170k | } |
143 | | |
144 | | int32_t ping_array_check(Ping_Array *array, const Mono_Time *mono_time, uint8_t *data, |
145 | | size_t length, uint64_t ping_id) |
146 | 134k | { |
147 | 134k | if (ping_id == 0) { |
148 | 1 | return -1; |
149 | 1 | } |
150 | | |
151 | 134k | const uint32_t index = ping_id % array->total_size; |
152 | | |
153 | 134k | if (array->entries[index].ping_id != ping_id) { |
154 | 3.66k | return -1; |
155 | 3.66k | } |
156 | | |
157 | 130k | if (mono_time_is_timeout(mono_time, array->entries[index].ping_time, array->timeout)) { |
158 | 149 | return -1; |
159 | 149 | } |
160 | | |
161 | 130k | if (array->entries[index].length > length) { |
162 | 1 | return -1; |
163 | 1 | } |
164 | | |
165 | | // TODO(iphydf): This can't happen? If it indeed can't, turn it into an assert. |
166 | 130k | if (array->entries[index].data == nullptr) { |
167 | 0 | return -1; |
168 | 0 | } |
169 | | |
170 | 130k | memcpy(data, array->entries[index].data, array->entries[index].length); |
171 | 130k | const uint32_t len = array->entries[index].length; |
172 | 130k | clear_entry(array, index); |
173 | 130k | return len; |
174 | 130k | } |