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 /libbb | |
| 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>
Diffstat (limited to 'libbb')
| -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 | ||
