Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Extract internal set-of-strings datastructure APIs

Extract BTF logic for maintaining a set of strings data structure, used for
BTF strings section construction in writable mode, into separate re-usable
API. This data structure is going to be used by bpf_linker to maintains ELF
STRTAB section, which has the same layout as BTF strings section.

Suggested-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20210318194036.3521577-5-andrii@kernel.org

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
90d76d3e 3b029e06

+259 -195
+1 -1
tools/lib/bpf/Build
··· 1 1 libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \ 2 2 netlink.o bpf_prog_linfo.o libbpf_probes.o xsk.o hashmap.o \ 3 - btf_dump.o ringbuf.o 3 + btf_dump.o ringbuf.o strset.o
+61 -194
tools/lib/bpf/btf.c
··· 21 21 #include "libbpf.h" 22 22 #include "libbpf_internal.h" 23 23 #include "hashmap.h" 24 + #include "strset.h" 24 25 25 26 #define BTF_MAX_NR_TYPES 0x7fffffffU 26 27 #define BTF_MAX_STR_OFFSET 0x7fffffffU ··· 68 67 * | | | 69 68 * hdr | | 70 69 * types_data----+ | 71 - * strs_data------------------+ 70 + * strset__data(strs_set)-----+ 72 71 * 73 72 * +----------+---------+-----------+ 74 73 * | Header | Types | Strings | ··· 106 105 */ 107 106 int start_str_off; 108 107 108 + /* only one of strs_data or strs_set can be non-NULL, depending on 109 + * whether BTF is in a modifiable state (strs_set is used) or not 110 + * (strs_data points inside raw_data) 111 + */ 109 112 void *strs_data; 110 - size_t strs_data_cap; /* used size stored in hdr->str_len */ 111 - 112 - /* lookup index for each unique string in strings section */ 113 - struct hashmap *strs_hash; 113 + /* a set of unique strings */ 114 + struct strset *strs_set; 114 115 /* whether strings are already deduplicated */ 115 116 bool strs_deduped; 116 - /* extra indirection layer to make strings hashmap work with stable 117 - * string offsets and ability to transparently choose between 118 - * btf->strs_data or btf_dedup->strs_data as a source of strings. 119 - * This is used for BTF strings dedup to transfer deduplicated strings 120 - * data back to struct btf without re-building strings index. 121 - */ 122 - void **strs_data_ptr; 123 117 124 118 /* BTF object FD, if loaded into kernel */ 125 119 int fd; ··· 734 738 */ 735 739 free(btf->hdr); 736 740 free(btf->types_data); 737 - free(btf->strs_data); 741 + strset__free(btf->strs_set); 738 742 } 739 743 free(btf->raw_data); 740 744 free(btf->raw_data_swapped); ··· 1242 1246 btf->fd = fd; 1243 1247 } 1244 1248 1249 + static const void *btf_strs_data(const struct btf *btf) 1250 + { 1251 + return btf->strs_data ? btf->strs_data : strset__data(btf->strs_set); 1252 + } 1253 + 1245 1254 static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian) 1246 1255 { 1247 1256 struct btf_header *hdr = btf->hdr; ··· 1287 1286 } 1288 1287 p += hdr->type_len; 1289 1288 1290 - memcpy(p, btf->strs_data, hdr->str_len); 1289 + memcpy(p, btf_strs_data(btf), hdr->str_len); 1291 1290 p += hdr->str_len; 1292 1291 1293 1292 *size = data_sz; ··· 1321 1320 if (offset < btf->start_str_off) 1322 1321 return btf__str_by_offset(btf->base_btf, offset); 1323 1322 else if (offset - btf->start_str_off < btf->hdr->str_len) 1324 - return btf->strs_data + (offset - btf->start_str_off); 1323 + return btf_strs_data(btf) + (offset - btf->start_str_off); 1325 1324 else 1326 1325 return NULL; 1327 1326 } ··· 1475 1474 return 0; 1476 1475 } 1477 1476 1478 - static size_t strs_hash_fn(const void *key, void *ctx) 1479 - { 1480 - const struct btf *btf = ctx; 1481 - const char *strs = *btf->strs_data_ptr; 1482 - const char *str = strs + (long)key; 1483 - 1484 - return str_hash(str); 1485 - } 1486 - 1487 - static bool strs_hash_equal_fn(const void *key1, const void *key2, void *ctx) 1488 - { 1489 - const struct btf *btf = ctx; 1490 - const char *strs = *btf->strs_data_ptr; 1491 - const char *str1 = strs + (long)key1; 1492 - const char *str2 = strs + (long)key2; 1493 - 1494 - return strcmp(str1, str2) == 0; 1495 - } 1496 - 1497 1477 static void btf_invalidate_raw_data(struct btf *btf) 1498 1478 { 1499 1479 if (btf->raw_data) { ··· 1493 1511 */ 1494 1512 static int btf_ensure_modifiable(struct btf *btf) 1495 1513 { 1496 - void *hdr, *types, *strs, *strs_end, *s; 1497 - struct hashmap *hash = NULL; 1498 - long off; 1499 - int err; 1514 + void *hdr, *types; 1515 + struct strset *set = NULL; 1516 + int err = -ENOMEM; 1500 1517 1501 1518 if (btf_is_modifiable(btf)) { 1502 1519 /* any BTF modification invalidates raw_data */ ··· 1506 1525 /* split raw data into three memory regions */ 1507 1526 hdr = malloc(btf->hdr->hdr_len); 1508 1527 types = malloc(btf->hdr->type_len); 1509 - strs = malloc(btf->hdr->str_len); 1510 - if (!hdr || !types || !strs) 1528 + if (!hdr || !types) 1511 1529 goto err_out; 1512 1530 1513 1531 memcpy(hdr, btf->hdr, btf->hdr->hdr_len); 1514 1532 memcpy(types, btf->types_data, btf->hdr->type_len); 1515 - memcpy(strs, btf->strs_data, btf->hdr->str_len); 1516 - 1517 - /* make hashmap below use btf->strs_data as a source of strings */ 1518 - btf->strs_data_ptr = &btf->strs_data; 1519 1533 1520 1534 /* build lookup index for all strings */ 1521 - hash = hashmap__new(strs_hash_fn, strs_hash_equal_fn, btf); 1522 - if (IS_ERR(hash)) { 1523 - err = PTR_ERR(hash); 1524 - hash = NULL; 1535 + set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr->str_len); 1536 + if (IS_ERR(set)) { 1537 + err = PTR_ERR(set); 1525 1538 goto err_out; 1526 - } 1527 - 1528 - strs_end = strs + btf->hdr->str_len; 1529 - for (off = 0, s = strs; s < strs_end; off += strlen(s) + 1, s = strs + off) { 1530 - /* hashmap__add() returns EEXIST if string with the same 1531 - * content already is in the hash map 1532 - */ 1533 - err = hashmap__add(hash, (void *)off, (void *)off); 1534 - if (err == -EEXIST) 1535 - continue; /* duplicate */ 1536 - if (err) 1537 - goto err_out; 1538 1539 } 1539 1540 1540 1541 /* only when everything was successful, update internal state */ 1541 1542 btf->hdr = hdr; 1542 1543 btf->types_data = types; 1543 1544 btf->types_data_cap = btf->hdr->type_len; 1544 - btf->strs_data = strs; 1545 - btf->strs_data_cap = btf->hdr->str_len; 1546 - btf->strs_hash = hash; 1545 + btf->strs_data = NULL; 1546 + btf->strs_set = set; 1547 1547 /* if BTF was created from scratch, all strings are guaranteed to be 1548 1548 * unique and deduplicated 1549 1549 */ ··· 1539 1577 return 0; 1540 1578 1541 1579 err_out: 1542 - hashmap__free(hash); 1580 + strset__free(set); 1543 1581 free(hdr); 1544 1582 free(types); 1545 - free(strs); 1546 - return -ENOMEM; 1547 - } 1548 - 1549 - static void *btf_add_str_mem(struct btf *btf, size_t add_sz) 1550 - { 1551 - return libbpf_add_mem(&btf->strs_data, &btf->strs_data_cap, 1, 1552 - btf->hdr->str_len, BTF_MAX_STR_OFFSET, add_sz); 1583 + return err; 1553 1584 } 1554 1585 1555 1586 /* Find an offset in BTF string section that corresponds to a given string *s*. ··· 1553 1598 */ 1554 1599 int btf__find_str(struct btf *btf, const char *s) 1555 1600 { 1556 - long old_off, new_off, len; 1557 - void *p; 1601 + int off; 1558 1602 1559 1603 if (btf->base_btf) { 1560 - int ret; 1561 - 1562 - ret = btf__find_str(btf->base_btf, s); 1563 - if (ret != -ENOENT) 1564 - return ret; 1604 + off = btf__find_str(btf->base_btf, s); 1605 + if (off != -ENOENT) 1606 + return off; 1565 1607 } 1566 1608 1567 1609 /* BTF needs to be in a modifiable state to build string lookup index */ 1568 1610 if (btf_ensure_modifiable(btf)) 1569 1611 return -ENOMEM; 1570 1612 1571 - /* see btf__add_str() for why we do this */ 1572 - len = strlen(s) + 1; 1573 - p = btf_add_str_mem(btf, len); 1574 - if (!p) 1575 - return -ENOMEM; 1613 + off = strset__find_str(btf->strs_set, s); 1614 + if (off < 0) 1615 + return off; 1576 1616 1577 - new_off = btf->hdr->str_len; 1578 - memcpy(p, s, len); 1579 - 1580 - if (hashmap__find(btf->strs_hash, (void *)new_off, (void **)&old_off)) 1581 - return btf->start_str_off + old_off; 1582 - 1583 - return -ENOENT; 1617 + return btf->start_str_off + off; 1584 1618 } 1585 1619 1586 1620 /* Add a string s to the BTF string section. ··· 1579 1635 */ 1580 1636 int btf__add_str(struct btf *btf, const char *s) 1581 1637 { 1582 - long old_off, new_off, len; 1583 - void *p; 1584 - int err; 1638 + int off; 1585 1639 1586 1640 if (btf->base_btf) { 1587 - int ret; 1588 - 1589 - ret = btf__find_str(btf->base_btf, s); 1590 - if (ret != -ENOENT) 1591 - return ret; 1641 + off = btf__find_str(btf->base_btf, s); 1642 + if (off != -ENOENT) 1643 + return off; 1592 1644 } 1593 1645 1594 1646 if (btf_ensure_modifiable(btf)) 1595 1647 return -ENOMEM; 1596 1648 1597 - /* Hashmap keys are always offsets within btf->strs_data, so to even 1598 - * look up some string from the "outside", we need to first append it 1599 - * at the end, so that it can be addressed with an offset. Luckily, 1600 - * until btf->hdr->str_len is incremented, that string is just a piece 1601 - * of garbage for the rest of BTF code, so no harm, no foul. On the 1602 - * other hand, if the string is unique, it's already appended and 1603 - * ready to be used, only a simple btf->hdr->str_len increment away. 1604 - */ 1605 - len = strlen(s) + 1; 1606 - p = btf_add_str_mem(btf, len); 1607 - if (!p) 1608 - return -ENOMEM; 1649 + off = strset__add_str(btf->strs_set, s); 1650 + if (off < 0) 1651 + return off; 1609 1652 1610 - new_off = btf->hdr->str_len; 1611 - memcpy(p, s, len); 1653 + btf->hdr->str_len = strset__data_size(btf->strs_set); 1612 1654 1613 - /* Now attempt to add the string, but only if the string with the same 1614 - * contents doesn't exist already (HASHMAP_ADD strategy). If such 1615 - * string exists, we'll get its offset in old_off (that's old_key). 1616 - */ 1617 - err = hashmap__insert(btf->strs_hash, (void *)new_off, (void *)new_off, 1618 - HASHMAP_ADD, (const void **)&old_off, NULL); 1619 - if (err == -EEXIST) 1620 - return btf->start_str_off + old_off; /* duplicated string, return existing offset */ 1621 - if (err) 1622 - return err; 1623 - 1624 - btf->hdr->str_len += len; /* new unique string, adjust data length */ 1625 - return btf->start_str_off + new_off; 1655 + return btf->start_str_off + off; 1626 1656 } 1627 1657 1628 1658 static void *btf_add_type_mem(struct btf *btf, size_t add_sz) ··· 2934 3016 /* Various option modifying behavior of algorithm */ 2935 3017 struct btf_dedup_opts opts; 2936 3018 /* temporary strings deduplication state */ 2937 - void *strs_data; 2938 - size_t strs_cap; 2939 - size_t strs_len; 2940 - struct hashmap* strs_hash; 3019 + struct strset *strs_set; 2941 3020 }; 2942 3021 2943 3022 static long hash_combine(long h, long value) ··· 3100 3185 { 3101 3186 struct btf_dedup *d = ctx; 3102 3187 __u32 str_off = *str_off_ptr; 3103 - long old_off, new_off, len; 3104 3188 const char *s; 3105 - void *p; 3106 - int err; 3189 + int off, err; 3107 3190 3108 3191 /* don't touch empty string or string in main BTF */ 3109 3192 if (str_off == 0 || str_off < d->btf->start_str_off) ··· 3118 3205 return err; 3119 3206 } 3120 3207 3121 - len = strlen(s) + 1; 3208 + off = strset__add_str(d->strs_set, s); 3209 + if (off < 0) 3210 + return off; 3122 3211 3123 - new_off = d->strs_len; 3124 - p = libbpf_add_mem(&d->strs_data, &d->strs_cap, 1, new_off, BTF_MAX_STR_OFFSET, len); 3125 - if (!p) 3126 - return -ENOMEM; 3127 - 3128 - memcpy(p, s, len); 3129 - 3130 - /* Now attempt to add the string, but only if the string with the same 3131 - * contents doesn't exist already (HASHMAP_ADD strategy). If such 3132 - * string exists, we'll get its offset in old_off (that's old_key). 3133 - */ 3134 - err = hashmap__insert(d->strs_hash, (void *)new_off, (void *)new_off, 3135 - HASHMAP_ADD, (const void **)&old_off, NULL); 3136 - if (err == -EEXIST) { 3137 - *str_off_ptr = d->btf->start_str_off + old_off; 3138 - } else if (err) { 3139 - return err; 3140 - } else { 3141 - *str_off_ptr = d->btf->start_str_off + new_off; 3142 - d->strs_len += len; 3143 - } 3212 + *str_off_ptr = d->btf->start_str_off + off; 3144 3213 return 0; 3145 3214 } 3146 3215 ··· 3139 3244 */ 3140 3245 static int btf_dedup_strings(struct btf_dedup *d) 3141 3246 { 3142 - char *s; 3143 3247 int err; 3144 3248 3145 3249 if (d->btf->strs_deduped) 3146 3250 return 0; 3147 3251 3148 - /* temporarily switch to use btf_dedup's strs_data for strings for hash 3149 - * functions; later we'll just transfer hashmap to struct btf as is, 3150 - * along the strs_data 3151 - */ 3152 - d->btf->strs_data_ptr = &d->strs_data; 3153 - 3154 - d->strs_hash = hashmap__new(strs_hash_fn, strs_hash_equal_fn, d->btf); 3155 - if (IS_ERR(d->strs_hash)) { 3156 - err = PTR_ERR(d->strs_hash); 3157 - d->strs_hash = NULL; 3252 + d->strs_set = strset__new(BTF_MAX_STR_OFFSET, NULL, 0); 3253 + if (IS_ERR(d->strs_set)) { 3254 + err = PTR_ERR(d->strs_set); 3158 3255 goto err_out; 3159 3256 } 3160 3257 3161 3258 if (!d->btf->base_btf) { 3162 - s = libbpf_add_mem(&d->strs_data, &d->strs_cap, 1, d->strs_len, BTF_MAX_STR_OFFSET, 1); 3163 - if (!s) 3164 - return -ENOMEM; 3165 - /* initial empty string */ 3166 - s[0] = 0; 3167 - d->strs_len = 1; 3168 - 3169 3259 /* insert empty string; we won't be looking it up during strings 3170 3260 * dedup, but it's good to have it for generic BTF string lookups 3171 3261 */ 3172 - err = hashmap__insert(d->strs_hash, (void *)0, (void *)0, 3173 - HASHMAP_ADD, NULL, NULL); 3174 - if (err) 3262 + err = strset__add_str(d->strs_set, ""); 3263 + if (err < 0) 3175 3264 goto err_out; 3176 3265 } 3177 3266 ··· 3165 3286 goto err_out; 3166 3287 3167 3288 /* replace BTF string data and hash with deduped ones */ 3168 - free(d->btf->strs_data); 3169 - hashmap__free(d->btf->strs_hash); 3170 - d->btf->strs_data = d->strs_data; 3171 - d->btf->strs_data_cap = d->strs_cap; 3172 - d->btf->hdr->str_len = d->strs_len; 3173 - d->btf->strs_hash = d->strs_hash; 3174 - /* now point strs_data_ptr back to btf->strs_data */ 3175 - d->btf->strs_data_ptr = &d->btf->strs_data; 3176 - 3177 - d->strs_data = d->strs_hash = NULL; 3178 - d->strs_len = d->strs_cap = 0; 3289 + strset__free(d->btf->strs_set); 3290 + d->btf->hdr->str_len = strset__data_size(d->strs_set); 3291 + d->btf->strs_set = d->strs_set; 3292 + d->strs_set = NULL; 3179 3293 d->btf->strs_deduped = true; 3180 3294 return 0; 3181 3295 3182 3296 err_out: 3183 - free(d->strs_data); 3184 - hashmap__free(d->strs_hash); 3185 - d->strs_data = d->strs_hash = NULL; 3186 - d->strs_len = d->strs_cap = 0; 3187 - 3188 - /* restore strings pointer for existing d->btf->strs_hash back */ 3189 - d->btf->strs_data_ptr = &d->strs_data; 3297 + strset__free(d->strs_set); 3298 + d->strs_set = NULL; 3190 3299 3191 3300 return err; 3192 3301 }
+176
tools/lib/bpf/strset.c
··· 1 + // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 + /* Copyright (c) 2021 Facebook */ 3 + #include <stdint.h> 4 + #include <stdlib.h> 5 + #include <stdio.h> 6 + #include <errno.h> 7 + #include <linux/err.h> 8 + #include "hashmap.h" 9 + #include "libbpf_internal.h" 10 + #include "strset.h" 11 + 12 + struct strset { 13 + void *strs_data; 14 + size_t strs_data_len; 15 + size_t strs_data_cap; 16 + size_t strs_data_max_len; 17 + 18 + /* lookup index for each unique string in strings set */ 19 + struct hashmap *strs_hash; 20 + }; 21 + 22 + static size_t strset_hash_fn(const void *key, void *ctx) 23 + { 24 + const struct strset *s = ctx; 25 + const char *str = s->strs_data + (long)key; 26 + 27 + return str_hash(str); 28 + } 29 + 30 + static bool strset_equal_fn(const void *key1, const void *key2, void *ctx) 31 + { 32 + const struct strset *s = ctx; 33 + const char *str1 = s->strs_data + (long)key1; 34 + const char *str2 = s->strs_data + (long)key2; 35 + 36 + return strcmp(str1, str2) == 0; 37 + } 38 + 39 + struct strset *strset__new(size_t max_data_sz, const char *init_data, size_t init_data_sz) 40 + { 41 + struct strset *set = calloc(1, sizeof(*set)); 42 + struct hashmap *hash; 43 + int err = -ENOMEM; 44 + 45 + if (!set) 46 + return ERR_PTR(-ENOMEM); 47 + 48 + hash = hashmap__new(strset_hash_fn, strset_equal_fn, set); 49 + if (IS_ERR(hash)) 50 + goto err_out; 51 + 52 + set->strs_data_max_len = max_data_sz; 53 + set->strs_hash = hash; 54 + 55 + if (init_data) { 56 + long off; 57 + 58 + set->strs_data = malloc(init_data_sz); 59 + if (!set->strs_data) 60 + goto err_out; 61 + 62 + memcpy(set->strs_data, init_data, init_data_sz); 63 + set->strs_data_len = init_data_sz; 64 + set->strs_data_cap = init_data_sz; 65 + 66 + for (off = 0; off < set->strs_data_len; off += strlen(set->strs_data + off) + 1) { 67 + /* hashmap__add() returns EEXIST if string with the same 68 + * content already is in the hash map 69 + */ 70 + err = hashmap__add(hash, (void *)off, (void *)off); 71 + if (err == -EEXIST) 72 + continue; /* duplicate */ 73 + if (err) 74 + goto err_out; 75 + } 76 + } 77 + 78 + return set; 79 + err_out: 80 + strset__free(set); 81 + return ERR_PTR(err); 82 + } 83 + 84 + void strset__free(struct strset *set) 85 + { 86 + if (IS_ERR_OR_NULL(set)) 87 + return; 88 + 89 + hashmap__free(set->strs_hash); 90 + free(set->strs_data); 91 + } 92 + 93 + size_t strset__data_size(const struct strset *set) 94 + { 95 + return set->strs_data_len; 96 + } 97 + 98 + const char *strset__data(const struct strset *set) 99 + { 100 + return set->strs_data; 101 + } 102 + 103 + static void *strset_add_str_mem(struct strset *set, size_t add_sz) 104 + { 105 + return libbpf_add_mem(&set->strs_data, &set->strs_data_cap, 1, 106 + set->strs_data_len, set->strs_data_max_len, add_sz); 107 + } 108 + 109 + /* Find string offset that corresponds to a given string *s*. 110 + * Returns: 111 + * - >0 offset into string data, if string is found; 112 + * - -ENOENT, if string is not in the string data; 113 + * - <0, on any other error. 114 + */ 115 + int strset__find_str(struct strset *set, const char *s) 116 + { 117 + long old_off, new_off, len; 118 + void *p; 119 + 120 + /* see strset__add_str() for why we do this */ 121 + len = strlen(s) + 1; 122 + p = strset_add_str_mem(set, len); 123 + if (!p) 124 + return -ENOMEM; 125 + 126 + new_off = set->strs_data_len; 127 + memcpy(p, s, len); 128 + 129 + if (hashmap__find(set->strs_hash, (void *)new_off, (void **)&old_off)) 130 + return old_off; 131 + 132 + return -ENOENT; 133 + } 134 + 135 + /* Add a string s to the string data. If the string already exists, return its 136 + * offset within string data. 137 + * Returns: 138 + * - > 0 offset into string data, on success; 139 + * - < 0, on error. 140 + */ 141 + int strset__add_str(struct strset *set, const char *s) 142 + { 143 + long old_off, new_off, len; 144 + void *p; 145 + int err; 146 + 147 + /* Hashmap keys are always offsets within set->strs_data, so to even 148 + * look up some string from the "outside", we need to first append it 149 + * at the end, so that it can be addressed with an offset. Luckily, 150 + * until set->strs_data_len is incremented, that string is just a piece 151 + * of garbage for the rest of the code, so no harm, no foul. On the 152 + * other hand, if the string is unique, it's already appended and 153 + * ready to be used, only a simple set->strs_data_len increment away. 154 + */ 155 + len = strlen(s) + 1; 156 + p = strset_add_str_mem(set, len); 157 + if (!p) 158 + return -ENOMEM; 159 + 160 + new_off = set->strs_data_len; 161 + memcpy(p, s, len); 162 + 163 + /* Now attempt to add the string, but only if the string with the same 164 + * contents doesn't exist already (HASHMAP_ADD strategy). If such 165 + * string exists, we'll get its offset in old_off (that's old_key). 166 + */ 167 + err = hashmap__insert(set->strs_hash, (void *)new_off, (void *)new_off, 168 + HASHMAP_ADD, (const void **)&old_off, NULL); 169 + if (err == -EEXIST) 170 + return old_off; /* duplicated string, return existing offset */ 171 + if (err) 172 + return err; 173 + 174 + set->strs_data_len += len; /* new unique string, adjust data length */ 175 + return new_off; 176 + }
+21
tools/lib/bpf/strset.h
··· 1 + /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 + 3 + /* Copyright (c) 2021 Facebook */ 4 + #ifndef __LIBBPF_STRSET_H 5 + #define __LIBBPF_STRSET_H 6 + 7 + #include <stdbool.h> 8 + #include <stddef.h> 9 + 10 + struct strset; 11 + 12 + struct strset *strset__new(size_t max_data_sz, const char *init_data, size_t init_data_sz); 13 + void strset__free(struct strset *set); 14 + 15 + const char *strset__data(const struct strset *set); 16 + size_t strset__data_size(const struct strset *set); 17 + 18 + int strset__find_str(struct strset *set, const char *s); 19 + int strset__add_str(struct strset *set, const char *s); 20 + 21 + #endif /* __LIBBPF_STRSET_H */