1*f7c14bbaSAndroid Build Coastguard Worker // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2*f7c14bbaSAndroid Build Coastguard Worker /* Copyright (c) 2021 Facebook */
3*f7c14bbaSAndroid Build Coastguard Worker #include <stdint.h>
4*f7c14bbaSAndroid Build Coastguard Worker #include <stdlib.h>
5*f7c14bbaSAndroid Build Coastguard Worker #include <stdio.h>
6*f7c14bbaSAndroid Build Coastguard Worker #include <errno.h>
7*f7c14bbaSAndroid Build Coastguard Worker #include <linux/err.h>
8*f7c14bbaSAndroid Build Coastguard Worker #include "hashmap.h"
9*f7c14bbaSAndroid Build Coastguard Worker #include "libbpf_internal.h"
10*f7c14bbaSAndroid Build Coastguard Worker #include "strset.h"
11*f7c14bbaSAndroid Build Coastguard Worker
12*f7c14bbaSAndroid Build Coastguard Worker struct strset {
13*f7c14bbaSAndroid Build Coastguard Worker void *strs_data;
14*f7c14bbaSAndroid Build Coastguard Worker size_t strs_data_len;
15*f7c14bbaSAndroid Build Coastguard Worker size_t strs_data_cap;
16*f7c14bbaSAndroid Build Coastguard Worker size_t strs_data_max_len;
17*f7c14bbaSAndroid Build Coastguard Worker
18*f7c14bbaSAndroid Build Coastguard Worker /* lookup index for each unique string in strings set */
19*f7c14bbaSAndroid Build Coastguard Worker struct hashmap *strs_hash;
20*f7c14bbaSAndroid Build Coastguard Worker };
21*f7c14bbaSAndroid Build Coastguard Worker
strset_hash_fn(long key,void * ctx)22*f7c14bbaSAndroid Build Coastguard Worker static size_t strset_hash_fn(long key, void *ctx)
23*f7c14bbaSAndroid Build Coastguard Worker {
24*f7c14bbaSAndroid Build Coastguard Worker const struct strset *s = ctx;
25*f7c14bbaSAndroid Build Coastguard Worker const char *str = s->strs_data + key;
26*f7c14bbaSAndroid Build Coastguard Worker
27*f7c14bbaSAndroid Build Coastguard Worker return str_hash(str);
28*f7c14bbaSAndroid Build Coastguard Worker }
29*f7c14bbaSAndroid Build Coastguard Worker
strset_equal_fn(long key1,long key2,void * ctx)30*f7c14bbaSAndroid Build Coastguard Worker static bool strset_equal_fn(long key1, long key2, void *ctx)
31*f7c14bbaSAndroid Build Coastguard Worker {
32*f7c14bbaSAndroid Build Coastguard Worker const struct strset *s = ctx;
33*f7c14bbaSAndroid Build Coastguard Worker const char *str1 = s->strs_data + key1;
34*f7c14bbaSAndroid Build Coastguard Worker const char *str2 = s->strs_data + key2;
35*f7c14bbaSAndroid Build Coastguard Worker
36*f7c14bbaSAndroid Build Coastguard Worker return strcmp(str1, str2) == 0;
37*f7c14bbaSAndroid Build Coastguard Worker }
38*f7c14bbaSAndroid Build Coastguard Worker
strset__new(size_t max_data_sz,const char * init_data,size_t init_data_sz)39*f7c14bbaSAndroid Build Coastguard Worker struct strset *strset__new(size_t max_data_sz, const char *init_data, size_t init_data_sz)
40*f7c14bbaSAndroid Build Coastguard Worker {
41*f7c14bbaSAndroid Build Coastguard Worker struct strset *set = calloc(1, sizeof(*set));
42*f7c14bbaSAndroid Build Coastguard Worker struct hashmap *hash;
43*f7c14bbaSAndroid Build Coastguard Worker int err = -ENOMEM;
44*f7c14bbaSAndroid Build Coastguard Worker
45*f7c14bbaSAndroid Build Coastguard Worker if (!set)
46*f7c14bbaSAndroid Build Coastguard Worker return ERR_PTR(-ENOMEM);
47*f7c14bbaSAndroid Build Coastguard Worker
48*f7c14bbaSAndroid Build Coastguard Worker hash = hashmap__new(strset_hash_fn, strset_equal_fn, set);
49*f7c14bbaSAndroid Build Coastguard Worker if (IS_ERR(hash))
50*f7c14bbaSAndroid Build Coastguard Worker goto err_out;
51*f7c14bbaSAndroid Build Coastguard Worker
52*f7c14bbaSAndroid Build Coastguard Worker set->strs_data_max_len = max_data_sz;
53*f7c14bbaSAndroid Build Coastguard Worker set->strs_hash = hash;
54*f7c14bbaSAndroid Build Coastguard Worker
55*f7c14bbaSAndroid Build Coastguard Worker if (init_data) {
56*f7c14bbaSAndroid Build Coastguard Worker long off;
57*f7c14bbaSAndroid Build Coastguard Worker
58*f7c14bbaSAndroid Build Coastguard Worker set->strs_data = malloc(init_data_sz);
59*f7c14bbaSAndroid Build Coastguard Worker if (!set->strs_data)
60*f7c14bbaSAndroid Build Coastguard Worker goto err_out;
61*f7c14bbaSAndroid Build Coastguard Worker
62*f7c14bbaSAndroid Build Coastguard Worker memcpy(set->strs_data, init_data, init_data_sz);
63*f7c14bbaSAndroid Build Coastguard Worker set->strs_data_len = init_data_sz;
64*f7c14bbaSAndroid Build Coastguard Worker set->strs_data_cap = init_data_sz;
65*f7c14bbaSAndroid Build Coastguard Worker
66*f7c14bbaSAndroid Build Coastguard Worker for (off = 0; off < set->strs_data_len; off += strlen(set->strs_data + off) + 1) {
67*f7c14bbaSAndroid Build Coastguard Worker /* hashmap__add() returns EEXIST if string with the same
68*f7c14bbaSAndroid Build Coastguard Worker * content already is in the hash map
69*f7c14bbaSAndroid Build Coastguard Worker */
70*f7c14bbaSAndroid Build Coastguard Worker err = hashmap__add(hash, off, off);
71*f7c14bbaSAndroid Build Coastguard Worker if (err == -EEXIST)
72*f7c14bbaSAndroid Build Coastguard Worker continue; /* duplicate */
73*f7c14bbaSAndroid Build Coastguard Worker if (err)
74*f7c14bbaSAndroid Build Coastguard Worker goto err_out;
75*f7c14bbaSAndroid Build Coastguard Worker }
76*f7c14bbaSAndroid Build Coastguard Worker }
77*f7c14bbaSAndroid Build Coastguard Worker
78*f7c14bbaSAndroid Build Coastguard Worker return set;
79*f7c14bbaSAndroid Build Coastguard Worker err_out:
80*f7c14bbaSAndroid Build Coastguard Worker strset__free(set);
81*f7c14bbaSAndroid Build Coastguard Worker return ERR_PTR(err);
82*f7c14bbaSAndroid Build Coastguard Worker }
83*f7c14bbaSAndroid Build Coastguard Worker
strset__free(struct strset * set)84*f7c14bbaSAndroid Build Coastguard Worker void strset__free(struct strset *set)
85*f7c14bbaSAndroid Build Coastguard Worker {
86*f7c14bbaSAndroid Build Coastguard Worker if (IS_ERR_OR_NULL(set))
87*f7c14bbaSAndroid Build Coastguard Worker return;
88*f7c14bbaSAndroid Build Coastguard Worker
89*f7c14bbaSAndroid Build Coastguard Worker hashmap__free(set->strs_hash);
90*f7c14bbaSAndroid Build Coastguard Worker free(set->strs_data);
91*f7c14bbaSAndroid Build Coastguard Worker free(set);
92*f7c14bbaSAndroid Build Coastguard Worker }
93*f7c14bbaSAndroid Build Coastguard Worker
strset__data_size(const struct strset * set)94*f7c14bbaSAndroid Build Coastguard Worker size_t strset__data_size(const struct strset *set)
95*f7c14bbaSAndroid Build Coastguard Worker {
96*f7c14bbaSAndroid Build Coastguard Worker return set->strs_data_len;
97*f7c14bbaSAndroid Build Coastguard Worker }
98*f7c14bbaSAndroid Build Coastguard Worker
strset__data(const struct strset * set)99*f7c14bbaSAndroid Build Coastguard Worker const char *strset__data(const struct strset *set)
100*f7c14bbaSAndroid Build Coastguard Worker {
101*f7c14bbaSAndroid Build Coastguard Worker return set->strs_data;
102*f7c14bbaSAndroid Build Coastguard Worker }
103*f7c14bbaSAndroid Build Coastguard Worker
strset_add_str_mem(struct strset * set,size_t add_sz)104*f7c14bbaSAndroid Build Coastguard Worker static void *strset_add_str_mem(struct strset *set, size_t add_sz)
105*f7c14bbaSAndroid Build Coastguard Worker {
106*f7c14bbaSAndroid Build Coastguard Worker return libbpf_add_mem(&set->strs_data, &set->strs_data_cap, 1,
107*f7c14bbaSAndroid Build Coastguard Worker set->strs_data_len, set->strs_data_max_len, add_sz);
108*f7c14bbaSAndroid Build Coastguard Worker }
109*f7c14bbaSAndroid Build Coastguard Worker
110*f7c14bbaSAndroid Build Coastguard Worker /* Find string offset that corresponds to a given string *s*.
111*f7c14bbaSAndroid Build Coastguard Worker * Returns:
112*f7c14bbaSAndroid Build Coastguard Worker * - >0 offset into string data, if string is found;
113*f7c14bbaSAndroid Build Coastguard Worker * - -ENOENT, if string is not in the string data;
114*f7c14bbaSAndroid Build Coastguard Worker * - <0, on any other error.
115*f7c14bbaSAndroid Build Coastguard Worker */
strset__find_str(struct strset * set,const char * s)116*f7c14bbaSAndroid Build Coastguard Worker int strset__find_str(struct strset *set, const char *s)
117*f7c14bbaSAndroid Build Coastguard Worker {
118*f7c14bbaSAndroid Build Coastguard Worker long old_off, new_off, len;
119*f7c14bbaSAndroid Build Coastguard Worker void *p;
120*f7c14bbaSAndroid Build Coastguard Worker
121*f7c14bbaSAndroid Build Coastguard Worker /* see strset__add_str() for why we do this */
122*f7c14bbaSAndroid Build Coastguard Worker len = strlen(s) + 1;
123*f7c14bbaSAndroid Build Coastguard Worker p = strset_add_str_mem(set, len);
124*f7c14bbaSAndroid Build Coastguard Worker if (!p)
125*f7c14bbaSAndroid Build Coastguard Worker return -ENOMEM;
126*f7c14bbaSAndroid Build Coastguard Worker
127*f7c14bbaSAndroid Build Coastguard Worker new_off = set->strs_data_len;
128*f7c14bbaSAndroid Build Coastguard Worker memcpy(p, s, len);
129*f7c14bbaSAndroid Build Coastguard Worker
130*f7c14bbaSAndroid Build Coastguard Worker if (hashmap__find(set->strs_hash, new_off, &old_off))
131*f7c14bbaSAndroid Build Coastguard Worker return old_off;
132*f7c14bbaSAndroid Build Coastguard Worker
133*f7c14bbaSAndroid Build Coastguard Worker return -ENOENT;
134*f7c14bbaSAndroid Build Coastguard Worker }
135*f7c14bbaSAndroid Build Coastguard Worker
136*f7c14bbaSAndroid Build Coastguard Worker /* Add a string s to the string data. If the string already exists, return its
137*f7c14bbaSAndroid Build Coastguard Worker * offset within string data.
138*f7c14bbaSAndroid Build Coastguard Worker * Returns:
139*f7c14bbaSAndroid Build Coastguard Worker * - > 0 offset into string data, on success;
140*f7c14bbaSAndroid Build Coastguard Worker * - < 0, on error.
141*f7c14bbaSAndroid Build Coastguard Worker */
strset__add_str(struct strset * set,const char * s)142*f7c14bbaSAndroid Build Coastguard Worker int strset__add_str(struct strset *set, const char *s)
143*f7c14bbaSAndroid Build Coastguard Worker {
144*f7c14bbaSAndroid Build Coastguard Worker long old_off, new_off, len;
145*f7c14bbaSAndroid Build Coastguard Worker void *p;
146*f7c14bbaSAndroid Build Coastguard Worker int err;
147*f7c14bbaSAndroid Build Coastguard Worker
148*f7c14bbaSAndroid Build Coastguard Worker /* Hashmap keys are always offsets within set->strs_data, so to even
149*f7c14bbaSAndroid Build Coastguard Worker * look up some string from the "outside", we need to first append it
150*f7c14bbaSAndroid Build Coastguard Worker * at the end, so that it can be addressed with an offset. Luckily,
151*f7c14bbaSAndroid Build Coastguard Worker * until set->strs_data_len is incremented, that string is just a piece
152*f7c14bbaSAndroid Build Coastguard Worker * of garbage for the rest of the code, so no harm, no foul. On the
153*f7c14bbaSAndroid Build Coastguard Worker * other hand, if the string is unique, it's already appended and
154*f7c14bbaSAndroid Build Coastguard Worker * ready to be used, only a simple set->strs_data_len increment away.
155*f7c14bbaSAndroid Build Coastguard Worker */
156*f7c14bbaSAndroid Build Coastguard Worker len = strlen(s) + 1;
157*f7c14bbaSAndroid Build Coastguard Worker p = strset_add_str_mem(set, len);
158*f7c14bbaSAndroid Build Coastguard Worker if (!p)
159*f7c14bbaSAndroid Build Coastguard Worker return -ENOMEM;
160*f7c14bbaSAndroid Build Coastguard Worker
161*f7c14bbaSAndroid Build Coastguard Worker new_off = set->strs_data_len;
162*f7c14bbaSAndroid Build Coastguard Worker memcpy(p, s, len);
163*f7c14bbaSAndroid Build Coastguard Worker
164*f7c14bbaSAndroid Build Coastguard Worker /* Now attempt to add the string, but only if the string with the same
165*f7c14bbaSAndroid Build Coastguard Worker * contents doesn't exist already (HASHMAP_ADD strategy). If such
166*f7c14bbaSAndroid Build Coastguard Worker * string exists, we'll get its offset in old_off (that's old_key).
167*f7c14bbaSAndroid Build Coastguard Worker */
168*f7c14bbaSAndroid Build Coastguard Worker err = hashmap__insert(set->strs_hash, new_off, new_off,
169*f7c14bbaSAndroid Build Coastguard Worker HASHMAP_ADD, &old_off, NULL);
170*f7c14bbaSAndroid Build Coastguard Worker if (err == -EEXIST)
171*f7c14bbaSAndroid Build Coastguard Worker return old_off; /* duplicated string, return existing offset */
172*f7c14bbaSAndroid Build Coastguard Worker if (err)
173*f7c14bbaSAndroid Build Coastguard Worker return err;
174*f7c14bbaSAndroid Build Coastguard Worker
175*f7c14bbaSAndroid Build Coastguard Worker set->strs_data_len += len; /* new unique string, adjust data length */
176*f7c14bbaSAndroid Build Coastguard Worker return new_off;
177*f7c14bbaSAndroid Build Coastguard Worker }
178