diff options
Diffstat (limited to 'refs.c')
-rw-r--r-- | refs.c | 150 |
1 files changed, 135 insertions, 15 deletions
@@ -24,6 +24,25 @@ static unsigned char refname_disposition[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4 }; +static int check_refname_component_trailer(const char *cp, const char *refname, int flags) +{ + if (cp == refname) + return 0; /* Component has zero length. */ + if (refname[0] == '.') { + if (!(flags & REFNAME_DOT_COMPONENT)) + return -1; /* Component starts with '.'. */ + /* + * Even if leading dots are allowed, don't allow "." + * as a component (".." is prevented by a rule above). + */ + if (refname[1] == '\0') + return -1; /* Component equals ".". */ + } + if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5)) + return -1; /* Refname ends with ".lock". */ + return cp - refname; +} + /* * Try to read one refname component from the front of refname. * Return the length of the component found, or -1 if the component is @@ -37,7 +56,7 @@ static unsigned char refname_disposition[256] = { * - it ends with ".lock" * - it contains a "\" (backslash) */ -static int check_refname_component(const char *refname, int flags) +static int check_refname_component_bytewise(const char *refname, int flags) { const char *cp; char last = '\0'; @@ -47,7 +66,7 @@ static int check_refname_component(const char *refname, int flags) unsigned char disp = refname_disposition[ch]; switch (disp) { case 1: - goto out; + return check_refname_component_trailer(cp, refname, flags); case 2: if (last == '.') return -1; /* Refname contains "..". */ @@ -61,24 +80,125 @@ static int check_refname_component(const char *refname, int flags) } last = ch; } -out: - if (cp == refname) - return 0; /* Component has zero length. */ - if (refname[0] == '.') { - if (!(flags & REFNAME_DOT_COMPONENT)) - return -1; /* Component starts with '.'. */ +} + +#if defined(NO_SSE42) || !(defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +#define check_refname_component check_refname_component_bytewise +#else +#define SSE_VECTOR_BYTES 16 + +/* Vectorized version of check_refname_component */ +static int check_refname_component_sse42(const char *refname, int flags) +{ + const __m128i *refname_vec = (__m128i*) refname; + + /* Character ranges for characters forbidden in refs; see above */ + static const __v16qi bad = { + 0x01, 0x20, 0x7e, 0x7f, 0x5e, 0x5e, 0x3a, 0x3a, + 0x5b, 0x5c, 0x2a, 0x2a, 0x3f, 0x3f, 0x3f, 0x3f}; + + static const __v16qi nonslashes = { + '\001', '/' -1, '/' + 1, 0xff, + }; + + static const __v16qi dotdot = {'.', '.', 0}; + static const __v16qi atcurly = {'@', '{', 0}; + + const __m128i *vp; + const char *cp = (const char *)refname_vec; + + int dotdotpos = SSE_VECTOR_BYTES, atcurlypos = SSE_VECTOR_BYTES; + for (vp = refname_vec; ; vp++) { + __m128i tmp; + int endpos; + /* - * Even if leading dots are allowed, don't allow "." - * as a component (".." is prevented by a rule above). + * Handle case of forbidden substrings .. and @{ crossing + * sixteen-byte boundaries */ - if (refname[1] == '\0') - return -1; /* Component equals ".". */ + if (dotdotpos == 15 && *cp == '.') + return -1; + + if (atcurlypos == 15 && *cp == '{') + return -1; + + if (((uintptr_t) vp % PAGE_SIZE) > PAGE_SIZE - SSE_VECTOR_BYTES) + /* + * End-of-page; fall back to slow method for + * this entire component. + */ + return check_refname_component_bytewise(refname, flags); + + tmp = _mm_lddqu_si128(vp); + + /* + * Find slashes or end-of-string. The double-negative + * (negative-polarity search for non-slashes) is + * necessary so that \0 will also be counted. + */ + endpos = _mm_cmpistri((__m128i) nonslashes, tmp, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (_mm_cmpestrc((__m128i) bad, SSE_VECTOR_BYTES, tmp, endpos, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES)) + return -1; + + dotdotpos = _mm_cmpestri((__m128i) dotdot, 2, tmp, endpos, + _SIDD_UBYTE_OPS | + _SIDD_CMP_EQUAL_ORDERED); + if (dotdotpos < 15) + return -1; + + atcurlypos = _mm_cmpestri((__m128i) atcurly, 2, tmp, endpos, + _SIDD_UBYTE_OPS | + _SIDD_CMP_EQUAL_ORDERED); + if (atcurlypos < 15) + return -1; + + if (endpos < SSE_VECTOR_BYTES) { + cp = ((const char*) vp) + endpos; + break; + } + cp = (const char*) vp + SSE_VECTOR_BYTES; } - if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5)) - return -1; /* Refname ends with ".lock". */ - return cp - refname; + return check_refname_component_trailer(cp, refname, flags); +} + +static void* find_check_refname_component(void) +{ + if (processor_supports_sse42()) + return check_refname_component_sse42; + else + return check_refname_component_bytewise; } +#ifdef __clang__ + +static int check_refname_component_autodetect(const char *refname, int flags); + +static int (*check_refname_component)(const char *, int) = check_refname_component_autodetect; + +/* + * On the first run of check_refname_component, autodetect the correct + * version for this CPU. This is necessary because Clang doesn't support + * ifunc. + */ +static int check_refname_component_autodetect(const char *refname, int flags) +{ + check_refname_component = find_check_refname_component(); + return check_refname_component(refname, flags); +} + +#else + +static int check_refname_component(const char *refname, int flags) + __attribute__ ((ifunc ("find_check_refname_component"))); + +#endif /* __clang__ */ + +#endif /* NO_SSE42 */ + int check_refname_format(const char *refname, int flags) { int component_len, component_count = 0; |