1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 /* Copyright (c) 2021 Facebook */
9 #include "libbpf_internal.h"
16 size_t strs_data_max_len
;
18 /* lookup index for each unique string in strings set */
19 struct hashmap
*strs_hash
;
22 static size_t strset_hash_fn(long key
, void *ctx
)
24 const struct strset
*s
= ctx
;
25 const char *str
= s
->strs_data
+ key
;
30 static bool strset_equal_fn(long key1
, long key2
, void *ctx
)
32 const struct strset
*s
= ctx
;
33 const char *str1
= s
->strs_data
+ key1
;
34 const char *str2
= s
->strs_data
+ key2
;
36 return strcmp(str1
, str2
) == 0;
39 struct strset
*strset__new(size_t max_data_sz
, const char *init_data
, size_t init_data_sz
)
41 struct strset
*set
= calloc(1, sizeof(*set
));
46 return ERR_PTR(-ENOMEM
);
48 hash
= hashmap__new(strset_hash_fn
, strset_equal_fn
, set
);
52 set
->strs_data_max_len
= max_data_sz
;
53 set
->strs_hash
= hash
;
58 set
->strs_data
= malloc(init_data_sz
);
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
;
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
70 err
= hashmap__add(hash
, off
, off
);
72 continue; /* duplicate */
84 void strset__free(struct strset
*set
)
86 if (IS_ERR_OR_NULL(set
))
89 hashmap__free(set
->strs_hash
);
94 size_t strset__data_size(const struct strset
*set
)
96 return set
->strs_data_len
;
99 const char *strset__data(const struct strset
*set
)
101 return set
->strs_data
;
104 static void *strset_add_str_mem(struct strset
*set
, size_t add_sz
)
106 return libbpf_add_mem(&set
->strs_data
, &set
->strs_data_cap
, 1,
107 set
->strs_data_len
, set
->strs_data_max_len
, add_sz
);
110 /* Find string offset that corresponds to a given string *s*.
112 * - >0 offset into string data, if string is found;
113 * - -ENOENT, if string is not in the string data;
114 * - <0, on any other error.
116 int strset__find_str(struct strset
*set
, const char *s
)
118 long old_off
, new_off
, len
;
121 /* see strset__add_str() for why we do this */
123 p
= strset_add_str_mem(set
, len
);
127 new_off
= set
->strs_data_len
;
130 if (hashmap__find(set
->strs_hash
, new_off
, &old_off
))
136 /* Add a string s to the string data. If the string already exists, return its
137 * offset within string data.
139 * - > 0 offset into string data, on success;
142 int strset__add_str(struct strset
*set
, const char *s
)
144 long old_off
, new_off
, len
;
148 /* Hashmap keys are always offsets within set->strs_data, so to even
149 * look up some string from the "outside", we need to first append it
150 * at the end, so that it can be addressed with an offset. Luckily,
151 * until set->strs_data_len is incremented, that string is just a piece
152 * of garbage for the rest of the code, so no harm, no foul. On the
153 * other hand, if the string is unique, it's already appended and
154 * ready to be used, only a simple set->strs_data_len increment away.
157 p
= strset_add_str_mem(set
, len
);
161 new_off
= set
->strs_data_len
;
164 /* Now attempt to add the string, but only if the string with the same
165 * contents doesn't exist already (HASHMAP_ADD strategy). If such
166 * string exists, we'll get its offset in old_off (that's old_key).
168 err
= hashmap__insert(set
->strs_hash
, new_off
, new_off
,
169 HASHMAP_ADD
, &old_off
, NULL
);
171 return old_off
; /* duplicated string, return existing offset */
175 set
->strs_data_len
+= len
; /* new unique string, adjust data length */