Asterisk - The Open Source Telephony Project GIT-master-0a46be9
channelstorage.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2024, Sangoma Technologies Corporation
5 *
6 * George Joseph <gjoseph@sangoma.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19#include "asterisk.h"
20#include "asterisk/options.h"
21#include "channelstorage.h"
22
24
27{
28 if (storage_drivers.elems == NULL) {
30 }
32}
33
35 const char *driver_name)
36{
37 int i;
38
39 for (i = 0; i < AST_VECTOR_SIZE(&storage_drivers); i++) {
40 const struct ast_channelstorage_driver *dt =
42 if (strcasecmp(driver_name, dt->driver_name) == 0) {
43 return dt;
44 }
45 }
46 return NULL;
47}
48
50 const struct ast_channelstorage_driver *storage_driver,
51 const char *instance_name)
52{
53 struct ast_channelstorage_instance *storage_instance = NULL;
54
55 storage_instance = storage_driver->open_instance(instance_name);
56 if (!storage_instance) {
57 ast_log(LOG_ERROR, "Failed to open channel storage driver '%s'\n",
58 storage_driver->driver_name);
59 return NULL;
60 }
61
62 return storage_instance;
63};
64
66{
67 CHANNELSTORAGE_API(storage_instance, close_instance);
68};
69
70int channelstorage_exten_cb(void *obj, void *arg, void *data, int flags)
71{
72 struct ast_channel *chan = (struct ast_channel *)obj;
73 const char *context = (const char *)arg;
74 const char *exten = (const char *)data;
75 int ret = 0;
76
77 ao2_lock(chan);
78 if (strcasecmp(ast_channel_context(chan), context) == 0 &&
79 strcasecmp(ast_channel_exten(chan), exten) == 0) {
80 ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
81 }
82 ao2_unlock(chan);
83
84 return ret;
85}
86
88 const char *exten, const char *context)
89{
90 char *l_exten = (char *) exten;
91 char *l_context = (char *) context;
92
93 return CHANNELSTORAGE_API(driver, callback, channelstorage_exten_cb, l_context, l_exten, 0);
94}
95
96int channelstorage_name_cb(void *obj, void *arg, void *data, int flags)
97{
98 struct ast_channel *chan = obj;
99 const char *name = arg;
100 size_t name_len = *(size_t *) data;
101 int ret = 0;
102
103 if (name_len == 0) {
104 if(strcasecmp(ast_channel_name(chan), name) == 0) {
105 ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
106 }
107 } else {
108 if (strncasecmp(ast_channel_name(chan), name, name_len) == 0) {
109 ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
110 }
111 }
112
113 return ret;
114}
115
117 const char *name)
118{
119 return CHANNELSTORAGE_API(driver, get_by_name_prefix_or_uniqueid, name, 0);
120}
121
123 const char *name, size_t name_len)
124{
125 struct ast_channel *chan = NULL;
126
127 chan = CHANNELSTORAGE_API(driver, get_by_name_prefix, name, name_len);
128 if (chan) {
129 return chan;
130 }
131
132 if (name_len == 0) {
133 chan = CHANNELSTORAGE_API(driver, get_by_uniqueid, name);
134 }
135
136 return chan;
137}
138
139int channelstorage_uniqueid_cb(void *obj, void *arg, void *data, int flags)
140{
141 struct ast_channel *chan = obj;
142 char *uniqueid = arg;
143 int ret = 0;
144
145 if(strcasecmp(ast_channel_uniqueid(chan), uniqueid) == 0) {
146 ret = CMP_MATCH | CMP_STOP;
147 }
148
149 return ret;
150}
151
153 const char *uniqueid)
154{
156}
157
158#ifdef TEST_FRAMEWORK
159#include "asterisk/test.h"
160#include "channel_private.h"
161
162static void mock_channel_destructor(void *obj)
163{
164 struct ast_channel *chan = obj;
166}
167
168struct test_info {
169 struct ast_test *test;
170 struct ast_channelstorage_instance *storage_instance;
171 enum ast_test_result_state res;
172};
173
174static void *test_storage_thread(void *data)
175{
176 struct test_info *test_info = data;
177 struct ast_test *test = test_info->test;
178 struct ast_channelstorage_instance *storage_instance = test_info->storage_instance;
179 struct ast_channel *mock_channel;
181 int i;
182 struct timeval start;
183 struct timeval end;
184 int64_t elapsed;
185 char search1[128];
186 char search2[128];
187 int rc = 0;
188 long int rand = ast_random();
189 struct ast_channel_iterator *iter;
190 int collen = 25;
191 int CHANNEL_COUNT = 500;
192 struct ast_cli_args *cli_args = ast_test_get_cli_args(test);
193 struct ast_channel **test_channels;
194
195 for (i = 0; i < cli_args->argc; i++) {
196 if (ast_begins_with(cli_args->argv[i], "channel-count=")) {
197 sscanf(cli_args->argv[i], "channel-count=%d", &CHANNEL_COUNT);
198 }
199 }
200 test_channels = ast_calloc(CHANNEL_COUNT, sizeof(*test_channels));
201 ast_test_status_update(test, "%*s: %8d\n", collen, "Channel Count", CHANNEL_COUNT);
202
203 start = ast_tvnow();
204 for (i = 0; i < CHANNEL_COUNT; i++) {
205 test_channels[i] = ao2_alloc(sizeof(*mock_channel), mock_channel_destructor);
206 ast_test_validate_cleanup(test, test_channels[i], res, done);
207 ast_string_field_init(test_channels[i], 128);
208 ast_string_field_build(test_channels[i], name, "TestChannel-%ld-%04d-something", rand, i);
209 snprintf(test_channels[i]->context, AST_MAX_CONTEXT, "TestContext-%ld-%04d", rand, i % 100);
210 snprintf(test_channels[i]->exten, AST_MAX_EXTENSION, "TestExten-%ld-%04d", rand, i % 10);
211 snprintf(test_channels[i]->uniqueid.unique_id, AST_MAX_UNIQUEID, "TestUniqueid-%ld-%04d-something", rand, i);
212 rc = CHANNELSTORAGE_API(storage_instance, insert, test_channels[i], 0, 1);
213 ast_test_validate_cleanup_custom(test, rc == 0, res, done, "Unable to insert channel %s\n", test_channels[i]->name);
214 }
215 end = ast_tvnow();
216 elapsed = ast_tvdiff_us(end, start);
217 i = CHANNELSTORAGE_API(storage_instance, active_channels);
218 ast_test_status_update(test, "%*s: %8ld\n", collen, "create channels", elapsed);
219 ast_test_validate_cleanup(test, i == CHANNEL_COUNT, res, done);
220
221 start = ast_tvnow();
222 for (i = 0; i < CHANNEL_COUNT; i++) {
223 sprintf(search1, "testchannel-%ld-%04d-something", rand, i);
224 mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_prefix_or_uniqueid, search1, 0);
225 ast_test_validate_cleanup(test, mock_channel, res, done);
226 ast_test_validate_cleanup(test, mock_channel == test_channels[i], res, done);
227 ast_test_validate_cleanup(test,
228 strcasecmp(ast_channel_name(mock_channel), search1) == 0, res, done);
229 ast_channel_unref(mock_channel);
230 }
231 end = ast_tvnow();
232 elapsed = ast_tvdiff_us(end, start);
233 ast_test_status_update(test, "%*s: %8ld\n", collen, "by name exact", elapsed);
234
235 start = ast_tvnow();
236 for (i = 0; i < CHANNEL_COUNT; i++) {
237 sprintf(search1, "TestUniqueid-%ld-%04d-something", rand, i);
238 mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_uniqueid, search1);
239 ast_test_validate_cleanup(test, mock_channel, res, done);
240 ast_channel_unref(mock_channel);
241 }
242 end = ast_tvnow();
243 elapsed = ast_tvdiff_us(end, start);
244 ast_test_status_update(test, "%*s: %8ld\n", collen, "by uniqueid exact", elapsed);
245
246 start = ast_tvnow();
247 for (i = 0; i < CHANNEL_COUNT; i++) {
248 sprintf(search1, "TestUniqueid-%ld-%04d-something", rand, i);
249 mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_prefix_or_uniqueid, search1, 0);
250 ast_test_validate_cleanup(test, mock_channel, res, done);
251 ast_channel_unref(mock_channel);
252 }
253 end = ast_tvnow();
254 elapsed = ast_tvdiff_us(end, start);
255 ast_test_status_update(test, "%*s: %8ld\n", collen, "by uniqueid via nm", elapsed);
256
257 start = ast_tvnow();
258 for (i = 0; i < CHANNEL_COUNT; i++) {
259 sprintf(search1, "TestChannel-%ld-%04d", rand, i);
260 mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_prefix_or_uniqueid, search1, strlen(search1));
261 ast_test_validate_cleanup(test, mock_channel, res, done);
262 ast_channel_unref(mock_channel);
263 }
264 end = ast_tvnow();
265 elapsed = ast_tvdiff_us(end, start);
266 ast_test_status_update(test, "%*s: %8ld\n", collen, "by name prefix", elapsed);
267
268 start = ast_tvnow();
269 for (i = 0; i < CHANNEL_COUNT; i++) {
270 sprintf(search1, "TestContext-%ld-%04d", rand, i % 100);
271 sprintf(search2, "TestExten-%ld-%04d", rand, i % 10);
272 mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_exten, search2, search1);
273 ast_test_validate_cleanup(test, mock_channel, res, done);
274 ast_channel_unref(mock_channel);
275 }
276 end = ast_tvnow();
277 elapsed = ast_tvdiff_us(end, start);
278 ast_test_status_update(test, "%*s: %8ld\n", collen, "by context/exten", elapsed);
279
280 i = 0;
281 start = ast_tvnow();
282 iter = CHANNELSTORAGE_API(storage_instance, iterator_all_new);
283 for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
284 ast_channel_unref(mock_channel)) {
285 i++;
286 }
287 CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
288 end = ast_tvnow();
289 elapsed = ast_tvdiff_us(end, start);
290 ast_test_status_update(test, "%*s: %8ld\n", collen, "iter all chan", elapsed);
291 ast_test_validate_cleanup_custom(test, i == CHANNEL_COUNT, res, done,
292 "Expected %d channels, got %d, in container: %d\n", CHANNEL_COUNT, i,
293 CHANNELSTORAGE_API(storage_instance, active_channels));
294
295 i = 0;
296 start = ast_tvnow();
297 sprintf(search1, "TestChannel-%ld-%03d", rand, (CHANNEL_COUNT - 11) / 10);
298 iter = CHANNELSTORAGE_API(storage_instance, iterator_by_name_new, search1, strlen(search1));
299 ast_test_validate_cleanup(test, iter != NULL, res, done);
300 for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
301 ast_channel_unref(mock_channel)) {
302 ast_test_validate_cleanup_custom(test, strncmp(search1,
303 ast_channel_name(mock_channel), strlen(search1)) == 0, res, done, "Expected %s got %s\n",
304 search1, ast_channel_name(mock_channel));
305 i++;
306 }
307 CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
308 end = ast_tvnow();
309 elapsed = ast_tvdiff_us(end, start);
310 ast_test_status_update(test, "%*s: %8ld\n", collen, "iter 10 partial name", elapsed);
311 ast_test_validate_cleanup_custom(test, i == 10, res, done,
312 "Expected %d channels, got %d, in container: %d\n", 10, i,
313 CHANNELSTORAGE_API(storage_instance, active_channels));
314
315 i = 0;
316 start = ast_tvnow();
317 sprintf(search1, "TestContext-%ld-%04d", rand, 50);
318 sprintf(search2, "TestExten-%ld-%04d", rand, 0);
319 iter = CHANNELSTORAGE_API(storage_instance, iterator_by_exten_new, search2, search1);
320 ast_test_validate_cleanup(test, iter != NULL, res, done);
321 for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
322 ast_channel_unref(mock_channel)) {
323 ast_test_validate_cleanup_custom(test,
324 (strcmp(search1, mock_channel->context) == 0 &&
325 strcmp(search2, mock_channel->exten) == 0), res, done, "Expected %s-%s got %s-%s\n",
326 search1, search2, mock_channel->context, mock_channel->exten);
327 i++;
328 }
329 CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
330 end = ast_tvnow();
331 elapsed = ast_tvdiff_us(end, start);
332 ast_test_status_update(test, "%*s: %8ld\n", collen, "iter context/exten", elapsed);
333 ast_test_validate_cleanup_custom(test, i == (CHANNEL_COUNT / 100), res, done,
334 "Expected %d channels, got %d, in container: %d\n", (CHANNEL_COUNT / 100), i,
335 CHANNEL_COUNT);
336
337done:
338 CHANNELSTORAGE_API(storage_instance, unlock);
339
340 start = ast_tvnow();
341 for (i = 0; i < CHANNEL_COUNT; i++) {
342 if (test_channels[i]) {
343 rc = CHANNELSTORAGE_API(storage_instance, remove, test_channels[i], 0);
344 ast_channel_unref(test_channels[i]);
345 test_channels[i] = NULL;
346 }
347 }
348 end = ast_tvnow();
349 elapsed = ast_tvdiff_us(end, start);
350 ast_test_status_update(test, "%*s: %8ld\n", collen, "del all channels", elapsed);
351 ast_test_validate_cleanup(test, i == CHANNEL_COUNT, res, done);
352 rc = CHANNELSTORAGE_API(storage_instance, active_channels);
353 ast_test_validate_cleanup_custom(test, rc == 0, res, final,
354 "There are still %d channels in the container\n", rc);
355
356 test_info->res = res;
357 return NULL;
358
359final:
360 iter = CHANNELSTORAGE_API(storage_instance, iterator_all_new);
361 for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
362 ast_channel_unref(mock_channel)) {
363 ast_test_status_update(test, "%p %s\n", mock_channel, ast_channel_name(mock_channel));
364 i++;
365 }
366 CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
367
368 test_info->res = res;
369 return NULL;
370}
371
372static enum ast_test_result_state test_storage(struct ast_test_info *info,
373 enum ast_test_command cmd, struct ast_test *test,
374 const char *storage_name, const char *summary)
375{
376 const struct ast_channelstorage_driver *storage_driver;
377 struct test_info ti = {
378 .test = test,
379 .storage_instance = NULL,
380 .res = AST_TEST_PASS,
381 };
382 pthread_t thread;
383 int rc = 0;
384
385 switch (cmd) {
386 case TEST_INIT:
387 info->name = storage_name;
388 info->category = "/main/channelstorage/";
389 info->summary = summary;
390 info->description = info->summary;
391 return AST_TEST_NOT_RUN;
392 case TEST_EXECUTE:
393 break;
394 }
395
396 storage_driver = ast_channelstorage_get_driver(info->name);
397 if (!storage_driver) {
398 ast_test_status_update(test, "Storage driver %s not registered\n", info->name);
399 return AST_TEST_NOT_RUN;
400 }
401 ti.storage_instance = ast_channelstorage_open(storage_driver, "channels_test");
402 ast_test_validate(test, ti.storage_instance, res);
403
404 rc = ast_pthread_create(&thread, NULL, test_storage_thread, &ti);
405 if (rc) {
406 ast_channelstorage_close(ti.storage_instance);
407 ast_test_status_update(test, "Failed to create thread: %s\n", strerror(rc));
408 return AST_TEST_FAIL;
409 }
410 pthread_join(thread, NULL);
411 ast_channelstorage_close(ti.storage_instance);
412
413 return ti.res;
414}
415
416#define DEFINE_STORAGE_TEST(_name) \
417AST_TEST_DEFINE(_name) \
418{ \
419 return test_storage(info, cmd, test, #_name, "Channel Storage test for " #_name); \
420}
421
422DEFINE_STORAGE_TEST(ao2_legacy)
423
424DEFINE_STORAGE_TEST(cpp_map_name_id)
425
426#define REGISTER_STORAGE_TEST(_name) \
427({ \
428 if (ast_channelstorage_get_driver(#_name)) { \
429 AST_TEST_REGISTER(_name); \
430 } \
431})
432#endif
433
434static void channelstorage_shutdown(void)
435{
436#ifdef TEST_FRAMEWORK
437 /* Unregistering a test that wasn't previously registered is safe */
438 AST_TEST_UNREGISTER(cpp_map_name_id);
439 AST_TEST_UNREGISTER(ao2_legacy);
440#endif
441}
442
444{
445#ifdef TEST_FRAMEWORK
446 /* Tests run in the reverse order registered */
447 REGISTER_STORAGE_TEST(cpp_map_name_id);
448 AST_TEST_REGISTER(ao2_legacy);
449#endif
451
452 return 0;
453}
454
pthread_t thread
Definition: app_sla.c:335
Asterisk main include file. File version handling, generic pbx functions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
const char * ast_channel_name(const struct ast_channel *chan)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3008
#define AST_MAX_UNIQUEID
Definition: channel.h:170
#define AST_MAX_CONTEXT
Definition: channel.h:135
const char * ast_channel_exten(const struct ast_channel *chan)
#define AST_MAX_EXTENSION
Definition: channel.h:134
struct ast_channelstorage_instance * ast_channelstorage_open(const struct ast_channelstorage_driver *storage_driver, const char *instance_name)
int ast_channelstorage_init(void)
int channelstorage_exten_cb(void *obj, void *arg, void *data, int flags)
int channelstorage_name_cb(void *obj, void *arg, void *data, int flags)
int channelstorage_uniqueid_cb(void *obj, void *arg, void *data, int flags)
int ast_channelstorage_register_driver(const struct ast_channelstorage_driver *driver_type)
static struct @336 storage_drivers
const struct ast_channelstorage_driver * ast_channelstorage_get_driver(const char *driver_name)
static void channelstorage_shutdown(void)
struct ast_channel * channelstorage_by_name_prefix_or_uniqueid(struct ast_channelstorage_instance *driver, const char *name, size_t name_len)
struct ast_channel * channelstorage_by_name_or_uniqueid(struct ast_channelstorage_instance *driver, const char *name)
void ast_channelstorage_close(struct ast_channelstorage_instance *storage_instance)
struct ast_channel * channelstorage_by_exten(struct ast_channelstorage_instance *driver, const char *exten, const char *context)
struct ast_channel * channelstorage_by_uniqueid(struct ast_channelstorage_instance *driver, const char *uniqueid)
#define CHANNELSTORAGE_API(_instance, _func,...)
static struct ast_channel * get_by_uniqueid(struct ast_channelstorage_instance *driver, const char *uniqueid)
static struct ast_channelstorage_driver driver_type
static struct ast_channel_iterator * iterator_by_exten_new(struct ast_channelstorage_instance *driver, const char *exten, const char *context)
static void close_instance(struct ast_channelstorage_instance *driver)
static struct ast_channel * get_by_name_prefix(struct ast_channelstorage_instance *driver, const char *name, size_t name_len)
static struct ast_channel * iterator_next(struct ast_channelstorage_instance *driver, struct ast_channel_iterator *i)
static int active_channels(struct ast_channelstorage_instance *driver)
returns number of active/allocated channels
static struct ast_channel_iterator * iterator_destroy(struct ast_channelstorage_instance *driver, struct ast_channel_iterator *i)
static struct ast_channel_iterator * iterator_by_name_new(struct ast_channelstorage_instance *driver, const char *name, size_t name_len)
static struct ast_channel * get_by_exten(struct ast_channelstorage_instance *driver, const char *exten, const char *context)
static struct ast_channel_iterator * iterator_all_new(struct ast_channelstorage_instance *driver)
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
char * end
Definition: eagi_proxy.c:73
static const char name[]
Definition: format_mp3.c:68
#define LOG_ERROR
#define remove
def info(msg)
Options provided by main asterisk program.
#define NULL
Definition: resample.c:96
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
char unique_id[AST_MAX_UNIQUEID]
Main Channel structure associated with a channel.
struct ast_channel_id uniqueid
char exten[AST_MAX_EXTENSION]
const char * data
char context[AST_MAX_CONTEXT]
struct ast_flags flags
struct ast_channelstorage_instance *(* open_instance)(const char *instance_name)
const int argc
Definition: cli.h:160
const char *const * argv
Definition: cli.h:161
Contains all the initialization information required to store a new test definition.
Definition: test.h:235
Test Framework API.
ast_test_command
Definition: test.h:199
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
int done
Definition: test_amihooks.c:48
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:87
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:621
long int ast_random(void)
Definition: utils.c:2348
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:620
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:267
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:691