GCC Code Coverage Report


Directory: ./
File: common/src/tlv.c
Date: 2026-11-08 20:01:15
Exec Total Coverage
Lines: 113 113 100.0%
Functions: 18 18 100.0%
Branches: 40 40 100.0%

Line Branch Exec Source
1 /*
2 * Common code for dealing with tag, length, value arrays.
3 * Copyright © 2009-2026, albinoloverats ~ Software Development
4 * email: webmaster@albinoloverats.net
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <stdlib.h>
22
23 #include <string.h>
24 #include <stdint.h>
25 #include <stdbool.h>
26
27 #ifndef _WIN32
28 #include <netinet/in.h>
29 #endif
30
31 #include "common.h"
32 #include "mem.h"
33 #include "non-gnu.h"
34 #include "tlv.h"
35 #include "list.h"
36 #include "error.h"
37
38 /*
39 * TODO this could be simplified to just a LIST
40 */
41 typedef struct
42 {
43 list_t tags;
44 byte_t *export;
45 }
46 tlv_private_s;
47
48 static int tlv_compare(const void *a, const void *b);
49 static void tlv_free(void *tlv);
50
51 20 extern tlv_t tlv_init(void)
52 {
53 20 tlv_private_s *t = m_calloc(sizeof( tlv_private_s ), sizeof( byte_t ));
54 20 t->tags = list_init(tlv_compare, false, false);
55 20 return t;
56 }
57
58 21 extern void tlv_deinit(tlv_t ptr)
59 {
60 4 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
61
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3 times.
21 if (!tlv_ptr)
62 1 return;
63
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
20 if (tlv_ptr->export)
64 2 free(tlv_ptr->export);
65 20 list_deinit(tlv_ptr->tags, tlv_free);
66 20 free(tlv_ptr);
67 20 return;
68 }
69
70 167 extern bool tlv_append(tlv_t ptr, tlv_entry_s tlv)
71 {
72 8 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
73
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (!tlv_ptr)
74 1 return false;
75 166 tlv_entry_s *t = m_malloc(sizeof tlv);
76 166 t->tag = tlv.tag;
77 166 t->length = tlv.length;
78 166 t->value = m_malloc(t->length);
79 166 memcpy(t->value, tlv.value, t->length);
80 166 bool r = list_append(tlv_ptr->tags, t);
81
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 165 times.
166 if (!r)
82 {
83 1 free(t->value);
84 1 free(t);
85 }
86 7 return r;
87 }
88
89 5 extern const tlv_entry_s *tlv_remove(tlv_t ptr, tlv_entry_s tlv)
90 {
91 5 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
92
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (!tlv_ptr)
93 2 return NULL;
94 3 return list_remove_item(tlv_ptr->tags, &tlv);
95 }
96
97 2 extern const tlv_entry_s *tlv_remove_tag(tlv_t ptr, uint8_t tag)
98 {
99 2 return tlv_remove(ptr, (tlv_entry_s){ tag, 0, NULL });
100 }
101
102 11 extern const tlv_entry_s *tlv_get(tlv_t ptr, uint8_t tag)
103 {
104 11 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
105
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
11 if (!tlv_ptr)
106 2 return NULL;
107 9 return list_contains(tlv_ptr->tags, &((tlv_entry_s){ tag, 0, NULL }));
108 }
109
110 133 extern bool tlv_has_tag(tlv_t ptr, uint8_t tag)
111 {
112 133 return tlv_get(ptr, tag);
113 }
114
115 130 extern byte_t *tlv_value_of_aux(tlv_t ptr, uint8_t tag, uint8_t *def)
116 {
117 4 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
118
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (!tlv_ptr)
119 2 return NULL;
120 2 const tlv_entry_s *t = tlv_get(ptr, tag);
121
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 1 times.
128 return t ? t->value : def;
122 }
123
124 130 extern uint16_t tlv_length_of(tlv_t ptr, uint8_t tag)
125 {
126 3 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
127
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!tlv_ptr)
128 1 return 0;
129 2 const tlv_entry_s *t = tlv_get(ptr, tag);
130
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 1 times.
129 return t ? t->length : 0;
131 }
132
133 5 extern byte_t *tlv_export_aux(tlv_t ptr, bool nbo)
134 {
135 5 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
136
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (!tlv_ptr)
137 1 return NULL;
138 4 size_t size = tlv_length(tlv_ptr);
139
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (tlv_ptr->export)
140 2 free(tlv_ptr->export);
141 4 tlv_ptr->export = m_malloc(size);
142 4 size_t off = 0;
143 4 iter_t iter = list_iterator(tlv_ptr->tags);
144
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
9 while (list_has_next(iter))
145 {
146 5 const tlv_entry_s *tlv = list_get_next(iter);
147 5 memcpy(tlv_ptr->export + off, &tlv->tag, sizeof tlv->tag);
148 5 off += sizeof tlv->tag;
149
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 uint16_t l = nbo ? htons(tlv->length) : tlv->length;
150 5 memcpy(tlv_ptr->export + off, &l, sizeof tlv->length);
151 5 off += sizeof tlv->length;
152 5 memcpy(tlv_ptr->export + off, tlv->value, tlv->length);
153 5 off += tlv->length;
154 }
155 4 return tlv_ptr->export;
156 }
157
158 2 extern uint16_t tlv_size(tlv_t ptr)
159 {
160 2 return list_size(((tlv_private_s *)ptr)->tags);
161 }
162
163 7 extern size_t tlv_length(tlv_t ptr)
164 {
165 7 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
166
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (!tlv_ptr)
167 1 return 0;
168 6 size_t size = 0;
169 6 iter_t iter = list_iterator(tlv_ptr->tags);
170
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 6 times.
13 while (list_has_next(iter))
171 7 size += sizeof( uint8_t ) + sizeof( uint16_t ) + ((tlv_entry_s *)list_get_next(iter))->length;
172 6 return size;
173 }
174
175 2 extern iter_t tlv_iterator(tlv_t ptr)
176 {
177 2 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
178
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!tlv_ptr)
179 1 return NULL;
180 1 return list_iterator(tlv_ptr->tags);
181 }
182
183 2 extern const tlv_entry_s *tlv_get_next(iter_t ptr)
184 {
185 2 return list_get_next(ptr);
186 }
187
188 3 extern bool tlv_has_next(iter_t ptr)
189 {
190 3 return list_has_next(ptr);
191 }
192
193 3 extern void tlv_for_each(tlv_t ptr, void f(uint8_t , uint16_t, const void *))
194 {
195 3 tlv_private_s *tlv_ptr = (tlv_private_s *)ptr;
196
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!tlv_ptr)
197 1 return;
198
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (!list_size(tlv_ptr->tags))
199 1 return;
200 1 iter_t iter = list_iterator(tlv_ptr->tags);
201 do
202 {
203 2 const tlv_entry_s *entry = (tlv_entry_s *)list_get_next(iter);
204 2 f(entry->tag, entry->length, entry->value);
205 }
206
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 while (list_has_next(iter));
207 1 free(iter);
208 1 return;
209 }
210
211 2711 static int tlv_compare(const void *a, const void *b)
212 {
213 17 const tlv_entry_s *x = a;
214 17 const tlv_entry_s *y = b;
215 /*
216 * cast to int allows us to overlook that tags are uint8_t's and
217 * get a negative result if applicable
218 */
219 2711 return (int)x->tag - (int)y->tag;
220 }
221
222 163 static void tlv_free(void *tlv)
223 {
224 163 free(((tlv_entry_s *)tlv)->value);
225 163 free(tlv);
226 163 return;
227 }
228