diff options
author | Ron Yorston <rmy@pobox.com> | 2021-01-29 13:22:48 +0000 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-02-02 14:16:38 +0100 |
commit | 59120c330330467c9e6aaec6d4ca8295baa653af (patch) | |
tree | d22982ca2eac6d933d9e68bb3aa81d3e9032e49b | |
parent | e8fe9f96356a6b19ec907ea30cffc829c539a7ff (diff) | |
download | busybox-w32-59120c330330467c9e6aaec6d4ca8295baa653af.tar.gz busybox-w32-59120c330330467c9e6aaec6d4ca8295baa653af.tar.bz2 busybox-w32-59120c330330467c9e6aaec6d4ca8295baa653af.zip |
libbb: code shrink and speed up find_applet_by_name()
find_applet_by_name() determines the appropriate range of applet
indices to check for the given name and performs a linear search in
applet_names[].
Revise the code so the index of the upper bound of the range, 'max',
isn't calculated. Instead check the value of the first non-matching
character to see if we've reached the end of the range.
This new code speeds up the time to find a valid applet name by 6%
and halves the time to detect that a given name is invalid. The
average time to detect an invalid name is now the same as for a valid
one.
function old new delta
find_applet_by_name 155 133 -22
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-22) Total: -22 bytes
Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | libbb/appletlib.c | 96 |
1 files changed, 17 insertions, 79 deletions
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 5f59f1273..542211255 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -176,7 +176,7 @@ void FAST_FUNC bb_show_usage(void) | |||
176 | 176 | ||
177 | int FAST_FUNC find_applet_by_name(const char *name) | 177 | int FAST_FUNC find_applet_by_name(const char *name) |
178 | { | 178 | { |
179 | unsigned i, max; | 179 | unsigned i; |
180 | int j; | 180 | int j; |
181 | const char *p; | 181 | const char *p; |
182 | 182 | ||
@@ -200,105 +200,43 @@ int FAST_FUNC find_applet_by_name(const char *name) | |||
200 | #endif | 200 | #endif |
201 | 201 | ||
202 | p = applet_names; | 202 | p = applet_names; |
203 | i = 0; | ||
204 | #if KNOWN_APPNAME_OFFSETS <= 0 | 203 | #if KNOWN_APPNAME_OFFSETS <= 0 |
205 | max = NUM_APPLETS; | 204 | i = 0; |
206 | #else | 205 | #else |
207 | max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS; | 206 | i = NUM_APPLETS * (KNOWN_APPNAME_OFFSETS - 1); |
208 | for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) { | 207 | for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) { |
209 | const char *pp = applet_names + applet_nameofs[j]; | 208 | const char *pp = applet_names + applet_nameofs[j]; |
210 | if (strcmp(name, pp) >= 0) { | 209 | if (strcmp(name, pp) >= 0) { |
211 | //bb_error_msg("name:'%s' >= pp:'%s'", name, pp); | 210 | //bb_error_msg("name:'%s' >= pp:'%s'", name, pp); |
212 | p = pp; | 211 | p = pp; |
213 | i = max - NUM_APPLETS; | ||
214 | break; | 212 | break; |
215 | } | 213 | } |
216 | max -= NUM_APPLETS; | 214 | i -= NUM_APPLETS; |
217 | } | 215 | } |
218 | max /= (unsigned)KNOWN_APPNAME_OFFSETS; | ||
219 | i /= (unsigned)KNOWN_APPNAME_OFFSETS; | 216 | i /= (unsigned)KNOWN_APPNAME_OFFSETS; |
220 | //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max); | 217 | //bb_error_msg("name:'%s' starting from:'%s' i:%u", name, p, i); |
221 | #endif | 218 | #endif |
222 | 219 | ||
223 | /* Open-coded linear search without strcmp/strlen calls for speed */ | 220 | /* Open-coded linear search without strcmp/strlen calls for speed */ |
224 | 221 | while (*p) { | |
225 | #if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/ | 222 | /* Do we see "name\0" at current position in applet_names? */ |
226 | /* skip "[\0" name, it's surely not it */ | 223 | for (j = 0; *p == name[j]; ++j) { |
227 | if (ENABLE_TEST && LONE_CHAR(p, '[')) | 224 | if (*p++ == '\0') { |
228 | i++, p += 2; | ||
229 | /* All remaining applet names in p[] are at least 2 chars long */ | ||
230 | /* name[] is also at least 2 chars long */ | ||
231 | |||
232 | n32 = (name[0] << 0) | (name[1] << 8) | (name[2] << 16); | ||
233 | while (i < max) { | ||
234 | uint32_t p32; | ||
235 | char ch; | ||
236 | |||
237 | /* Quickly check match of the first 3 bytes */ | ||
238 | move_from_unaligned32(p32, p); | ||
239 | p += 3; | ||
240 | if ((p32 & 0x00ffffff) != n32) { | ||
241 | /* Most likely case: 3 first bytes do not match */ | ||
242 | i++; | ||
243 | if ((p32 & 0x00ff0000) == '\0') | ||
244 | continue; // p[2] was NUL | ||
245 | p++; | ||
246 | if ((p32 & 0xff000000) == '\0') | ||
247 | continue; // p[3] was NUL | ||
248 | /* p[0..3] aren't matching and none is NUL, check the rest */ | ||
249 | while (*p++ != '\0') | ||
250 | continue; | ||
251 | continue; | ||
252 | } | ||
253 | |||
254 | /* Unlikely branch: first 3 bytes ([0..2]) match */ | ||
255 | if ((p32 & 0x00ff0000) == '\0') { | ||
256 | /* name is 2-byte long, it is full match */ | ||
257 | //bb_error_msg("found:'%s' i:%u", name, i); | ||
258 | return i; | ||
259 | } | ||
260 | /* Check remaining bytes [3..NUL] */ | ||
261 | ch = (p32 >> 24); | ||
262 | j = 3; | ||
263 | while (ch == name[j]) { | ||
264 | if (ch == '\0') { | ||
265 | //bb_error_msg("found:'%s' i:%u", name, i); | ||
266 | return i; | ||
267 | } | ||
268 | ch = *++p; | ||
269 | j++; | ||
270 | } | ||
271 | /* Not a match. Skip it, including NUL */ | ||
272 | while (ch != '\0') | ||
273 | ch = *++p; | ||
274 | p++; | ||
275 | i++; | ||
276 | } | ||
277 | return -1; | ||
278 | #else | ||
279 | while (i < max) { | ||
280 | char ch; | ||
281 | j = 0; | ||
282 | /* Do we see "name\0" in applet_names[p] position? */ | ||
283 | while ((ch = *p) == name[j]) { | ||
284 | if (ch == '\0') { | ||
285 | //bb_error_msg("found:'%s' i:%u", name, i); | 225 | //bb_error_msg("found:'%s' i:%u", name, i); |
286 | return i; /* yes */ | 226 | return i; /* yes */ |
287 | } | 227 | } |
288 | p++; | ||
289 | j++; | ||
290 | } | 228 | } |
291 | /* No. | 229 | /* No. Have we gone too far, alphabetically? */ |
292 | * p => 1st non-matching char in applet_names[], | 230 | if (*p > name[j]) { |
293 | * skip to and including NUL. | 231 | //bb_error_msg("break:'%s' i:%u", name, i); |
294 | */ | 232 | break; |
295 | while (ch != '\0') | 233 | } |
296 | ch = *++p; | 234 | /* No. Move to the start of the next applet name. */ |
297 | p++; | 235 | while (*p++ != '\0') |
236 | continue; | ||
298 | i++; | 237 | i++; |
299 | } | 238 | } |
300 | return -1; | 239 | return -1; |
301 | #endif | ||
302 | } | 240 | } |
303 | 241 | ||
304 | 242 | ||