#include <string.h>

unsigned char   lower_ascii[ 0x100 ];

inline  void    build_lower_table()
{
    if( !lower_ascii[ 0xFF ] )
    {
        size_t i;
        for( i = 0; i < 'A'; ++i ) lower_ascii[i] = (unsigned char)i;
        for( ;     i <= 'Z'; ++i ) lower_ascii[i] = (unsigned char)i | 0x20;
        for( ;    i < 0x100; ++i ) lower_ascii[i] = (unsigned char)i;
    }
}

inline  size_t  has_zero_byte( const size_t n )
{
    const   size_t  finder = (size_t)0x0101010101010101ULL;
    const   size_t  masker = (size_t)0x8080808080808080ULL;
    return ( n - finder ) & ( ~n & masker );
}

inline  size_t  has_some_byte( const unsigned char* s, const size_t cs )
{
    return has_zero_byte( ( *(size_t*)s | (size_t)0x2020202020202020ULL ) ^ cs );
}

inline  char*   strstrcase( const unsigned char* src, const unsigned char* sub )
{
    unsigned char* s = (unsigned char*)src;
    unsigned char* r = (unsigned char*)sub;
    unsigned char* o = s;
    while( *s )
    {
        if( lower_ascii[*s] == lower_ascii[*r] )
        {
            s++;
            r++;
            if( !*r ) return (char*)o - 1;
        }
        else
        {
            s = o++;
            r = (unsigned char*)sub;
        }
    }
    return NULL;
}

inline  char*   strstrcase( const char* src, const char* sub )
{
    build_lower_table();
    if( !( (size_t)src | (size_t)sub ) ) return NULL;
    return strstrcase( (unsigned char*)src, (unsigned char*)sub );
}

inline  int     strdiff_except_ascii( const unsigned char* s1, const unsigned char* s2 )
{
    for( size_t i = 0; s2[i]; )
    {
        if( s1[i] & 0x80 )
        {
            if( *(short*)( s1 + i ) != *(short*)( s2 + i ) )   return 1;
            i += 2;
            continue;
        }
        i++;
    }
    return 0;
}

inline  char*   strstrcase_fast( const unsigned char* src, const unsigned char* sub )
{
    build_lower_table();
    if( !( (size_t)src | (size_t)sub ) ) return NULL;

    size_t* s               = (size_t*)src;
    unsigned char*  r       = (unsigned char*)sub + 1;
    const unsigned char c0  = lower_ascii[ sub[0] ];
    const unsigned char c0t = sub[0] | 0x20;
    const size_t    c0s     =
    #ifdef _WIN64
                              ( (size_t)c0t << 56 ) | ( (size_t)c0t << 48 ) |
                              ( (size_t)c0t << 40 ) | ( (size_t)c0t << 32 ) |
    #endif
                              ( (size_t)c0t << 24 ) | ( (size_t)c0t << 16 ) |
                              ( (size_t)c0t <<  8 ) | ( (size_t)c0t );

    if( !*r )
    {
        while( !has_zero_byte( *(size_t*)s ) )
        {
            if( has_some_byte( (unsigned char*)s, c0t ) )
            {
                unsigned char* ss = (unsigned char*)s;
                while( lower_ascii[ *ss++ ] != c0 );
                return (char*)ss - 1;
            }
            else s++;
        }
    }
    else
    {
        const unsigned char c1t = sub[1] | 0x20;
        const size_t        c1s =
        #ifdef _WIN64
                              ( (size_t)c1t << 56 ) | ( (size_t)c1t << 48 ) |
                              ( (size_t)c1t << 40 ) | ( (size_t)c1t << 32 ) |
        #endif
                              ( (size_t)c1t << 24 ) | ( (size_t)c1t << 16 ) |
                              ( (size_t)c1t <<  8 ) | ( (size_t)c1t );

        while( !has_zero_byte( *(size_t*)s ) )
        {
            if( has_some_byte( (unsigned char*)s, c0s ) &&
                has_some_byte( (unsigned char*)s + 1, c1s ) )
            {
                unsigned char* ss = (unsigned char*)s;
                s++;
                while( lower_ascii[ *ss++ ] != c0 );
                retry:
                for( int i = 0; lower_ascii[ ss[i] ] == lower_ascii[ r[i] ]; ++i )
                {
                    if( !ss[i] ) return NULL;
                    if( !r[i + 1] )
                    {
                        if( !strdiff_except_ascii( ss - 1, sub ) ) return (char*)ss - 1;
                        break;
                    }
                }
                do if( lower_ascii[*ss++] == c0 ) goto retry; while( ss < (unsigned char*)s );
            }
            else s++;
        }
    }
    return strstrcase( (unsigned char*)s, sub );
}

char*   strstrcase_fast( const char* src, const char* sub )
{
    return  strstrcase_fast( (unsigned char*)src, (unsigned char*)sub );
}

typedef unsigned long long  tick_t;

#ifdef _MSC_VER
    #include <intrin.h>
    #define clock()         __rdtsc()
#else
    #if 0
    inline unsigned long long rdtsc()
    {
        tick_t tick;
        __asm volatile ( ".byte 0x0f, 0x31" : "=A" ( tick ) );
        return tick;
    }
    #endif
#endif

#include <string.h>
#include <iostream>
using   namespace   std;

#define min( a, b ) ( (a) < (b)? (a): (b) )
#define update_min( a, b ) ( (a) = min( (a), (b) ) )

int main()
{
    // RDTSC recipie
                tick_t  elapsed0,   elapsed1,   elapsed2;
    volatile    size_t  sum0 = 0,   sum1 = 0,   sum2 = 0;

    // set to the max
    elapsed0    = elapsed1  = elapsed2  = -1;

    // parameters
    #define     LOOP_COUNT              10000
    #define     TEST_STRING_LENGTH      100000
    char*       src_string  = new char[TEST_STRING_LENGTH];
    char*       sub_string1 = "한글Hello World";
    char*       sub_string2 = "한글heLLo worLd";

    for( int i = 0; i < TEST_STRING_LENGTH - 1; ++i ) src_string[i] = rand() % 255 + 1;
    src_string[TEST_STRING_LENGTH - 1] = 0;
    strcpy( src_string + TEST_STRING_LENGTH - 100, sub_string1 );

    for( int i = 0; i < LOOP_COUNT; ++i )
    {
        tick_t  begin   = clock();
        tick_t  elapsed = clock() - begin;
        update_min( elapsed0, elapsed );
    }

    for( int i = 0; i < LOOP_COUNT; ++i )
    {
        tick_t  begin   = clock();
        sum1    = strcasestr( src_string, sub_string2 ) - src_string;
        tick_t  elapsed = clock() - begin;
        update_min( elapsed1, elapsed );
    }

    for( int i = 0; i < LOOP_COUNT; ++i )
    {
        tick_t  begin   = clock();
        sum2    = strstrcase_fast( src_string, sub_string2 ) - src_string;
        tick_t  elapsed = clock() - begin;
        update_min( elapsed2, elapsed );
    }

    cout << "RDTSC cost : " << elapsed0 << " clocks" << endl << endl;
    cout << sum1 << "     " << elapsed1 - elapsed0 << endl;
    cout << sum2 << "     " << elapsed2 - elapsed0 << endl;

    getchar();
    return 0;
}
