作った RFC 1321 (MD5) のプログラムの改良を試みる

「[MD5: RFC 1321 を実装してみる](https://qiita.com/ikiuo/items/6f6d223dfea51a4ed7aa)」にある C++ プログラムを弄って改良を試みます。


<details><summary>オリジナル・プログラム (テスト部分は改良版と同じものに変更)</summary><div>

```c++:md5bs_orig.cpp
//
// ヘッダ用
//

#include <cstddef>
#include <cstdint>

/*
 * MD5ByteSum
 */

class MD5ByteSum
{
public:
    typedef uint32_t State[4];
    typedef uint32_t Block[16];
    typedef uint8_t Digest[16];
    typedef char HexDigest[33];

protected:
    static const uint8_t padding[64];

    State state;
    uint32_t bytes;
    Block input;
    bool active;

public:
    MD5ByteSum();
    MD5ByteSum(const void *message, size_t size);

    void digest(Digest &out);
    void hexdigest(HexDigest &out);

    void initialize();
    void update(const void *message, size_t size);
    void finalize();

protected:
    void update_state();
};

/*
 * MD5ByteSum - inline
 */

inline MD5ByteSum::MD5ByteSum()
{
    initialize();
}

//
// コンパイル用
//

/*
 * MD5ByteSum::State - update 16-Word block
 */

inline static uint32_t ROL(uint32_t x, int s)
{
    return ((x << s) | (x >> (32 - s)));
}

inline static uint32_t get_block_index(uint32_t index)
{
    /*
     * [y|y|x|x|x|x]
     *  (index * 1 + 0) & 15
     *  (index * 5 + 1) & 15
     *  (index * 3 + 5) & 15
     *  (index * 7 + 0) & 15
     */
    uint32_t x = index & 0x0f;
    uint32_t y = index & 0x30;
    uint32_t y1 = index & 0x10;
    uint32_t y2 = index & 0x20;
    uint32_t xmul = (y1 >> 2) | (y2 >> 4) | 1;
    uint32_t offs = (0x0510 >> (y >> 2)) & 7;
    return (x * xmul + offs) & 15;
}

inline static int get_rotate_bits(uint32_t index)
{
    /*
     * [y|y|.|.|x|x]
     *  7 12 17 22 : 3 8 13 18 : 3 3 3 3 : 3 3 3 2
     *  5  9 14 20 : 1 5 10 16 : 1 0 0 1 : 1 0 0 0
     *  4 11 16 23 : 0 7 12 19 : 0 2 2 4 : 0 2 2 3
     *  6 10 15 21 : 2 6 11 17 : 2 1 1 2 : 2 1 1 1
     *              -4          -(x*5)    -(x==3)
     *
     *  11 11 11 10 : 10_11_11_11 = bf
     *  01 00 00 00 : 00_00_00_01 = 01
     *  00 10 10 11 : 11_10_10_00 = e8
     *  10 01 01 01 : 01_01_01_10 = 56
     */
    uint32_t x = index & 0x03;
    uint32_t y = index & 0x30;
    uint32_t p = x | (y >> 2);
    uint32_t x0 = (0x56e801bf >> (p << 1)) & 3;
    uint32_t x3 = (x + 1) >> 2; // (x == 3) ? 1 : 0;
    uint32_t x5 = x * 5;
    return x0 + x3 + x5 + 4;
}

inline static uint32_t get_state_index(uint32_t index, uint32_t element)
{
    /*
     *  0 1 2 3
     *  3 0 1 2
     *  2 3 0 1
     *  1 2 3 0
     */
    return ((index * 3) + element) & 3;
}

inline static uint32_t md5bs_state_func(uint32_t index, uint32_t x, uint32_t y, uint32_t z)
{
    switch ((index >> 4) & 3)
    {
    case 0: return (y & x) | (z & ~x);
    case 1: return (x & z) | (y & ~z);
    case 2: return x ^ y ^ z;
    case 3: return y ^ (x | ~z);
    }
    return 0;
}

inline static void md5bs_state_update(uint32_t index, uint32_t *S, const MD5ByteSum::Block &X, uint32_t T)
{
    uint32_t a = get_state_index(index, 0);
    uint32_t b = get_state_index(index, 1);
    uint32_t c = get_state_index(index, 2);
    uint32_t d = get_state_index(index, 3);
    uint32_t x = get_block_index(index);
    uint32_t r = get_rotate_bits(index);

    S[a] = S[b] + ROL(X[x] + T + S[a] + md5bs_state_func(index, S[b], S[c], S[d]), r);
}

inline static void md5bs_state_update(uint32_t index, uint32_t *S,
                                      const MD5ByteSum::Block &X,
                                      uint32_t T1, uint32_t T2,
                                      uint32_t T3, uint32_t T4)
{
    md5bs_state_update(index + 0, S, X, T1);
    md5bs_state_update(index + 1, S, X, T2);
    md5bs_state_update(index + 2, S, X, T3);
    md5bs_state_update(index + 3, S, X, T4);
}

void MD5ByteSum::update_state()
{
    State p;

    p[0] = state[0];
    p[1] = state[1];
    p[2] = state[2];
    p[3] = state[3];

    // Round 1.
    md5bs_state_update( 0 * 4, state, input, 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee);
    md5bs_state_update( 1 * 4, state, input, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501);
    md5bs_state_update( 2 * 4, state, input, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be);
    md5bs_state_update( 3 * 4, state, input, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821);

    // Round 2.
    md5bs_state_update( 4 * 4, state, input, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa);
    md5bs_state_update( 5 * 4, state, input, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8);
    md5bs_state_update( 6 * 4, state, input, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed);
    md5bs_state_update( 7 * 4, state, input, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a);

    // Round 3.
    md5bs_state_update( 8 * 4, state, input, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c);
    md5bs_state_update( 9 * 4, state, input, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70);
    md5bs_state_update(10 * 4, state, input, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05);
    md5bs_state_update(11 * 4, state, input, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665);

    // Round 4.
    md5bs_state_update(12 * 4, state, input, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039);
    md5bs_state_update(13 * 4, state, input, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1);
    md5bs_state_update(14 * 4, state, input, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1);
    md5bs_state_update(15 * 4, state, input, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391);

    state[0] += p[0];
    state[1] += p[1];
    state[2] += p[2];
    state[3] += p[3];
}

/*
 * MD5ByteSum
 */

const uint8_t MD5ByteSum::padding[64] = { 0x80 };

MD5ByteSum::MD5ByteSum(const void *message, size_t size)
{
    initialize();
    update(message, size);
    // finalize();
}

void MD5ByteSum::initialize()
{
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
    bytes = 0;
    for (int i = 0; i < 16; i++)
        input[i] = 0;
    active = true;
}

void MD5ByteSum::finalize()
{
    if (!active)
        return;

    uint32_t l = bytes;
    uint32_t i = l & 0x3f;
    uint32_t n = ((i >= 56) ? 64 : 0) + 56 - i;

    update(padding, n);

    input[14] = l << 3;
    input[15] = l >> 29;
    update_state();

    active = false;
}

void MD5ByteSum::update(const void *message, size_t size)
{
    if (!active)
        initialize();

    const uint8_t *d = reinterpret_cast<const uint8_t *>(message);
    unsigned b = (bytes & 3) << 3;
    unsigned i = (bytes >> 2) & 0x0f;

    bytes += size;
    while (size-- > 0)
    {
        input[i] |= (*d++) << b;

        b = (b + 8) & 0x1f;
        if (b == 0)
        {
            i = (i + 1) & 0x0f;
            if (i == 0)
                update_state();
            input[i] = 0;
        }
    }
}

void MD5ByteSum::digest(Digest &out)
{
    finalize();

    for (int p = 0; p < 4; p++)
    {
        uint32_t s = state[p];

        for (int q = 0; q < 4; q++, s >>= 8)
            out[p * 4 + q] = uint8_t(s);
    }
}

void MD5ByteSum::hexdigest(HexDigest &out)
{
    finalize();

    for (int p = 0; p < 4; p++)
    {
        uint32_t s = state[p];

        for (unsigned q = 0; q < 4; q++, s >>= 8)
        {
            char h = (s >> 4) & 0x0f;
            char l = (s >> 0) & 0x0f;
            out[p * 8 + q * 2 + 0] = h + ((h < 10) ? '0' : ('a' - 10));
            out[p * 8 + q * 2 + 1] = l + ((l < 10) ? '0' : ('a' - 10));
        }
    }
    out[32] = 0;
}

//
// テスト用
//

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cinttypes>
#include <time.h>

inline uint64_t RDTSC()
{
#if defined(_MSC_VER) ||   \
    (defined(__clang__) && \
     (defined(__amd64) ||  \
      defined(__i386) ||   \
      defined(__x86) ||    \
      defined(__x86_64)))

    return __rdtsc();

#elif defined(__GNUC__) && \
    (defined(__amd64) ||   \
     defined(__i386) ||    \
     defined(__x86) ||     \
     defined(__x86_64))

    uint32_t eax, edx;
    __asm__ volatile ("RDTSC" : "=a" (eax), "=d" (edx));
    return ((uint64_t)edx << 32) | eax;

#else

    timespec c;
    clock_gettime(CLOCK_REALTIME, &c);
    return ((uint64_t)c.tv_sec * 1000 * 1000 * 1000 + c.tv_nsec);

#endif
}

static void prepare_message(uint8_t *d, size_t dlen, const uint8_t *s, size_t slen)
{
    size_t clen = slen;
    if (clen > dlen)
        clen = dlen;
    std::memcpy(d, s, clen);
    d += clen;
    dlen -= clen;
    if (dlen != 0)
        prepare_message(d, dlen, s, slen + clen);
}

static int string_message(const char *message, const char *digest = NULL)
{
    size_t msglen = strlen(message);
    MD5ByteSum md5(message, msglen);
    MD5ByteSum::HexDigest res;

    md5.hexdigest(res);
    printf("MD5 (\"%s\") = %s\n", message, res);
    if (!digest)
        return 0;
    if (!strcmp(digest, res))
        return 0;
    printf("BUG: %s != %s\n", digest, res);
    return 2;
}

static int file_message(const char *path)
{
    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    size_t size = 1024 * 1024;
    void *buffer = malloc(size);

    if (!buffer)
    {
        printf("not enough memory.");
        return 2;
    }

    bool success = true;
    FILE *fp = fopen(path, "rb");

    if (!fp)
    {
        printf("open error: %s\n", path);
        return 2;
    }
    while (!feof(fp))
    {
        size_t rdsz = fread(buffer, 1, size, fp);
        if (ferror(fp))
        {
            success = false;
            break;
        }
        if (!rdsz)
            break;
        md5.update(buffer, rdsz);
    }
    fclose(fp);
    free(buffer);
    if (!success)
    {
        printf("read error: %s\n", path);
        return 2;
    }

    md5.hexdigest(res);
    printf("MD5 (%s) = %s\n", path, res);

    return 0;
}

static int benchmark(const char *message)
{
    if (!message)
        message = "benchmark";
    int rval = string_message(message);
    if (rval)
        return rval;

    size_t msglen = strlen(message);
    size_t blocks = (4 * 1024 * 1024);
    size_t bufsize = sizeof(MD5ByteSum::Block) * blocks;
    uint8_t *buffer = (uint8_t *)malloc(bufsize);
    if (!buffer)
    {
        puts("not enough memory.");
        return 2;
    }
    if (msglen)
    {
        uint8_t *d = buffer;
        std::memcpy(d, message, msglen);
        prepare_message(d + msglen, bufsize - msglen, buffer, msglen);
    }
    else
    {
        std::memset(buffer, -1, bufsize);
    }

    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    uint64_t s_tsc = RDTSC();
    {
        const uint8_t *p = buffer;
        size_t s = bufsize;

#define STEP(n) md5.update(p, n); p += n; s -= n;
        // 動作テスト用に分解(計測への影響は極小)
        STEP(17);     // 0
        STEP(17);     // 17
        STEP(63);     // 34
        STEP(31+128); // 97
        STEP(s);
    }
    uint64_t e_tsc = RDTSC();
    uint64_t d_tsc = e_tsc - s_tsc;
    uint64_t ave_i = d_tsc / blocks;
    uint64_t ave_f = (100 * (d_tsc % blocks) + (blocks / 2)) / blocks;

    md5.hexdigest(res);
    printf("TEST digest: %s\n", res);
    // printf("timer start  : %" PRIu64 "\n", s_tsc);
    // printf("timer end    : %" PRIu64 "\n", e_tsc);
    printf("timer count  : %" PRIu64 "\n", d_tsc);
    printf("block average: %" PRIu64 ".%02" PRIu64 "\n", ave_i, ave_f);

    free(buffer);
    return 0;
}

static int test()
{
    static const char *tab[] = {
        "d41d8cd98f00b204e9800998ecf8427e", "",
        "0cc175b9c0f1b6a831c399e269772661", "a",
        "900150983cd24fb0d6963f7d28e17f72", "abc",
        "f96b697d7cb7938d525a2f31aaf161d0", "message digest",
        "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz",
        "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "ad76b175d22a98a4e3bc2ad72affc53e", "01234567890123456789012345678901234567890123456789012345678",
        NULL, NULL,
    };
    for (int i = 0; tab[i] != NULL; i += 2)
    {
        int res = string_message(tab[i + 1], tab[i + 0]);
        if (res != 0)
            return res;
    }
    return 0;
}

static const char *program;
static int usage()
{
    printf("Usage:\n"
           "  %s -b [message]\n"
           "  %s -f file\n"
           "  %s message\n",
           program, program, program);
    return 1;
}

int main(int argc, char **argv)
{
    program = argv[0];
    if (argc < 2)
        return usage();
    if (!strcmp(argv[1], "-b"))
    {
        if (argc < 4)
            return benchmark(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-f"))
    {
        if (argc == 3)
            return file_message(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-t"))
        return test();
    if (argc == 2)
        return string_message(argv[1]);
    return usage();
}
```

</div></details>

# 改良の試み

主に、メモリへのアクセスのパターンの変更と SIMD レジスタの使用ですが、できるだけコンパイラの最適化に依存します。

<details><summary>改良を試みたプログラム</summary><div>

```c++:md5bs.cpp
///////////////////////////////////////////////////////////////////////////////
//
// ヘッダ ファイル用
//
///////////////////////////////////////////////////////////////////////////////

#include <cstddef>
#include <cstdint>

/*
 * alignas(n) : バイト境界指定
 *   x86/x64 では alignas(16) で XMM レジスタの活用を期待する
 */

#if (__cplusplus < 201103L) && !defined(alignas)
#  if defined(__GNUC__)
#    define alignas(n)  __attribute__((__aligned__(n)))
#  elif defined(_MSC_VER)
#    define alignas(n)  __declspec(align(n))
#  else
#    define alignas(n)  /**/
#  endif
#endif

/*
 * MD5ByteSum クラス
 *   アプリケーションは、コンストラクタ,update,digest,hexdigest の呼び出しだけでよい
 *   initialize, finalize は必要に応じて自動的に呼び出される
 */
class MD5ByteSum
{
public:
    typedef uint32_t State[4];
    typedef uint32_t Block[16];
    typedef uint8_t Digest[16];
    typedef char HexDigest[33];

protected:
    alignas(64) Block input;
    alignas(64) Block adbuf[4];
    alignas(16) State state;
    uint32_t bytes;
    bool active;

public:
    MD5ByteSum();
    MD5ByteSum(const void *message, size_t size);

    void update(const void *message, size_t size);
    void digest(Digest &out);
    void hexdigest(HexDigest &out);

    void initialize();
    void finalize();

protected:
    void update_state();
    void update_state(const void *block);
};

/*
 * MD5ByteSum (inline)
 */

// コンストラクタ
inline MD5ByteSum::MD5ByteSum()
{
    initialize();
}

// ダイジェスト値(state)の更新:
//   入力するブロックは input メンバー変数を使う
inline void MD5ByteSum::update_state()
{
    update_state(input);
}

///////////////////////////////////////////////////////////////////////////////
//
// コンパイル ファイル用
//
///////////////////////////////////////////////////////////////////////////////

#include <cstring>

// ベクトル型の代用
namespace // anonymous
{
    template <typename T, int N>
    struct md5bs_vector
    {
        typedef md5bs_vector<T,N> self_type;

        T value[N];

        T &operator [](int n)
            { return value[n]; }
        T operator [](int n) const
            { return value[n]; }
        self_type operator +(const self_type &rhs) const
            {
                self_type r;

                for (int i = 0; i < N; i++)
                    r[i] = value[i] + rhs[i];
                return r;
            }
    };
}

/*
 * MD5BS_ISA_ARM : ARM 命令セット
 * MD5BS_ISA_X86 : x86 命令セット
 */
#if defined(__arm__)
#  define MD5BS_ISA_ARM  1
#endif
#if (defined(__amd64)  || \
     defined(__i386)   || \
     defined(__x86)    || \
     defined(__x86_64))
#  define MD5BS_ISA_X86  1
#endif

#ifndef MD5BS_ISA_ARM
#  define MD5BS_ISA_ARM  0
#endif
#ifndef MD5BS_ISA_X86
#  define MD5BS_ISA_X86  0
#endif

/*
 * __dummy_access    : コンパイラの最適化抑制用
 * __force_inline    : 強制インライン展開のお呪い
 * __no_inline       : インライン展開抑制のお呪い
 * md5bs_clear_cache : キャッシュのクリア
 * md5bs_prefetch    : キャッシュへの先読み
 */
#if defined(__GNUC__)
#  define __force_inline  __attribute__((always_inline)) inline
#  define __no_inline  __attribute__((noinline))
#  define md5bs_clear_cache(s,e)  __builtin___clear_cache((char*)(s), (char*)(e))
#  define md5bs_prefetch(p)  __builtin_prefetch(p)
#elif defined(_MSC_VER)
#  define __force_inline  __forceinline
#  define __no_inline     __noinline
#endif

#if !defined(__dummy_access)
#  if defined(__GNUC__)
#    define __dummy_access(m)  __asm__ volatile ("#dummy %0":/**/:"m" (m))
#  else
#    define __dummy_access(m)
#  endif
#endif
#if !defined(__force_inline)
#  define __force_inline  inline
#endif
#if !defined(__no_inline)
#  define __no_inline
#endif
#if !defined(md5bs_clear_cache)
#  define md5bs_clear_cache(s,e)  /*NOOP*/
#endif
#if !defined(md5bs_prefetch)
#  define md5bs_prefetch(p)  /*NOOP*/
#endif

/*
 * MD5BS_BIG_ENDIAN        : ビッグ・エンディアン
 * MD5BS_ADDRESS_ALIGN     : バイト境界アクセスが必要
 * MD5BS_CACHE_LINE_SIZE   : キャッシュのライン・サイズ
 * MD5BS_BLOCK_PREPARE     : int(abs(sin(i))*(1<<32)) の加算済みブロックを用意
 * MD5BS_BLOCK_X_ORDER     : 加算済みブロックの配列を T の並びから X の並びに変更
 * MD5BS_HAS_SIMD          : SIMD 機能を明示的に使用
 * MD5BS_SIMD_REGISTER     : コア・レジスタの代わりに SIMD レジスタを使用
 *       良 : 使用出来るレジスタが増えてスタック・フレームへのアクセスが減る場合がある
 *       悪 : SIMD 命令がコア・レジスタの命令よりスループットが遅い場合がある
 * MD5BS_SIMD_REGISTER_128 : 128ビット・レジスタの有無
 * MD5BS_SIMD_REGISTER_256 : 256ビット・レジスタの有無
 */

/*
 * ARM
 */

#if MD5BS_ISA_ARM
#  if !defined(MD5BS_CACHE_LINE_SIZE)
#    define MD5BS_CACHE_LINE_SIZE  32
#  endif
#  if defined(__ARM_NEON__) && !defined(MD5BS_HAS_SIMD)
#    define MD5BS_HAS_SIMD  1
#    define MD5BS_ISA_ARM_NEON  1
#    define MD5BS_BLOCK_PREPARE_ARM  0

#    include <arm_neon.h>

typedef uint32x2_t md5bs_simd2_t;
typedef uint32x4_t md5bs_simd4_t;
typedef md5bs_simd2_t md5bs_alt_uint32_t;

#  endif
#  if !defined(MD5BS_BLOCK_PREPARE_ARM)
#    define MD5BS_BLOCK_PREPARE_ARM  0
#  endif
#  if !defined(MD5BS_BLOCK_PREPARE)
#    define MD5BS_BLOCK_PREPARE  MD5BS_BLOCK_PREPARE_ARM
#  endif
#endif

/*
 * x86
 */

#if MD5BS_ISA_X86
#  define MD5BS_BIG_ENDIAN  0
#  if defined(__SSE4_1__) && !defined(MD5BS_HAS_SIMD)
#    define MD5BS_HAS_SIMD  1
#    define MD5BS_ISA_X86_SSE4_1  1
#    if !defined(MD5BS_CACHE_LINE_SIZE)
#      define MD5BS_CACHE_LINE_SIZE  64
#    endif
#    define MD5BS_BLOCK_PREPARE_X86  1
#    if !defined(MD5BS_BLOCK_X_ORDER)
#      define MD5BS_BLOCK_X_ORDER  1
#    endif
#    if defined(__clang__) && !defined(MD5BS_SIMD_REGISTER)
#      define MD5BS_SIMD_REGISTER  1
#    endif

#    include <immintrin.h>

typedef __v2si md5bs_simd2_t;
typedef __v4su md5bs_simd4_t;
typedef md5bs_simd4_t md5bs_alt_uint32_t;

#    if defined(__AVX__)
#      define MD5BS_SIMD_REGISTER_256 1

typedef __v8su md5bs_simd8_t;

#    endif

#  elif defined(__clang__)
#    define MD5BS_BLOCK_PREPARE_X86  1
#  endif
#  if !defined(MD5BS_BLOCK_PREPARE_X86)
#    define MD5BS_BLOCK_PREPARE_X86  0
#  endif
#  if !defined(MD5BS_BLOCK_PREPARE)
#    define MD5BS_BLOCK_PREPARE  MD5BS_BLOCK_PREPARE_X86
#  endif
#endif

/*
 * 未定義の解消
 */

#ifndef MD5BS_BIG_ENDIAN
#  if (defined(__BYTE_ORDER__) && \
       (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#    define MD5BS_BIG_ENDIAN  1
#  else
#    define MD5BS_BIG_ENDIAN  0
#  endif
#endif
#ifndef MD5BS_ADDRESS_ALIGN
#  define MD5BS_ADDRESS_ALIGN  0
#endif
#ifndef MD5BS_CACHE_LINE_SIZE
#  define MD5BS_CACHE_LINE_SIZE  0
#endif
#ifndef MD5BS_BLOCK_PREPARE
#  define MD5BS_BLOCK_PREPARE  0
#endif
#ifndef MD5BS_BLOCK_X_ORDER
#  define MD5BS_BLOCK_X_ORDER  0
#endif

#ifndef MD5BS_HAS_SIMD
#  define MD5BS_HAS_SIMD  0
#endif
#ifndef MD5BS_SIMD_REGISTER
#  define MD5BS_SIMD_REGISTER  0
#elif !defined(MD5BS_SIMD_REGISTER_128)
#  define MD5BS_SIMD_REGISTER_128 1
#endif
#ifndef MD5BS_SIMD_REGISTER_128
#  define MD5BS_SIMD_REGISTER_128 0
#endif
#ifndef MD5BS_SIMD_REGISTER_256
#  define MD5BS_SIMD_REGISTER_256 0
#endif

#if !MD5BS_HAS_SIMD
typedef md5bs_vector<uint32_t,2> md5bs_simd2_t;
typedef md5bs_vector<uint32_t,4> md5bs_simd4_t;
typedef uint32_t md5bs_uint32_t;
#elif !MD5BS_SIMD_REGISTER /*  && MD5BS_HAS_SIMD */
typedef uint32_t md5bs_uint32_t;
#else /* MD5BS_HAS_SIMD && MD5BS_SIMD_REGISTER */
typedef md5bs_alt_uint32_t md5bs_uint32_t;
#endif
#if !MD5BS_SIMD_REGISTER_256
typedef md5bs_vector<md5bs_simd4_t,2> md5bs_simd8_t;
#endif

// 32ビットをリトル・エンディアンとして読み込む関数型
typedef uint32_t (*md5bs_load32)(const uint32_t &, bool);

// ポインタ型を uint8_t * 型に変換
__force_inline
static uint8_t *md5bs_uint8_p(void *p)
{
    return reinterpret_cast<uint8_t *>(p);
}

// const ポインタ型を const uint8_t * 型に変換
__force_inline
static const uint8_t *md5bs_uint8_p(const void *p)
{
    return reinterpret_cast<const uint8_t *>(p);
}

// const ポインタ型を const uint32_t * 型に変換
__force_inline
static const uint32_t *md5bs_uint32_p(const void *p)
{
    return reinterpret_cast<const uint32_t *>(p);
}

// ポインタ型を md5bs_simd4_t * 型に変換
__force_inline
static md5bs_simd4_t *md5bs_simd4_p(void *p)
{
    return reinterpret_cast<md5bs_simd4_t *>(p);
}

// const ポインタ型を const md5bs_simd4_t * 型に変換
__force_inline
static const md5bs_simd4_t *md5bs_simd4_p(const void *p)
{
    return reinterpret_cast<const md5bs_simd4_t *>(p);
}

// ポインタ型を md5bs_simd8_t * 型に変換
__force_inline
static md5bs_simd8_t *md5bs_simd8_p(void *p)
{
    return reinterpret_cast<md5bs_simd8_t *>(p);
}

// const ポインタ型を const md5bs_simd4_t * 型に変換
__force_inline
static const md5bs_simd8_t *md5bs_simd8_p(const void *p)
{
    return reinterpret_cast<const md5bs_simd8_t *>(p);
}

// ポインタ型を intptr_t 型に変換
__force_inline
static intptr_t md5bs_intptr(const void *p)
{
    return reinterpret_cast<intptr_t>(p);
}

// ポインタのアドレスが4バイト境界ならば true
__force_inline
static bool md5bs_is_align4(const void *p)
{
    return ((md5bs_intptr(p) & 3) == 0);
}

// ビッグ・エンディアン構成の CPU 用
//   リトル・エンディアン形式の32ビット整数を取り出す
//   aligned が true の場合は x のアドレスが4バイト境界ではない
//   [最適化によるバイト・オーダー変換命令の使用を期待する]
__force_inline
static uint32_t md5bs_load32be(const uint32_t &x, bool aligned)
{
    uint32_t b0, b1, b2, b3;

    if (aligned)
    {
        b3 = (x >>  0) & 0xff;
        b2 = (x >>  8) & 0xff;
        b1 = (x >> 16) & 0xff;
        b0 = (x >> 24) & 0xff;
    }
    else
    {
        const uint8_t *p = md5bs_uint8_p(&x);

        b0 = p[0];
        b1 = p[1];
        b2 = p[2];
        b3 = p[3];
    }
    return ((b3 << 24) | (b2 << 16) | (b1 << 8) | (b0 << 0));
}

// リトル・エンディアン構成の CPU 用
//   リトル・エンディアン形式の32ビット整数を取り出す
//   [最適化による非境界アクセス命令の使用を期待する]
__force_inline
static uint32_t md5bs_load32le(const uint32_t &x, bool aligned)
{
    if (aligned || !MD5BS_ADDRESS_ALIGN)
        return x;

    const uint8_t *p = md5bs_uint8_p(&x);
    uint32_t b0 = p[0];
    uint32_t b1 = p[1];
    uint32_t b2 = p[2];
    uint32_t b3 = p[3];

    return ((b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24));
}

// 32ビットをリトル・エンディアンとして読み込む関数を得る
__force_inline
static md5bs_load32 md5bs_load32_func(bool endian)
{
    return endian ? md5bs_load32be : md5bs_load32le;
}

/*
 * SIMD 向け
 */

#if MD5BS_HAS_SIMD
#  if MD5BS_ISA_ARM

//
// ARM
//

namespace // anonymous
{
    template <typename T>
    struct vfp_cast_type
    {
        template <typename U>
        __force_inline
        static T from(U s)
        {
#if MD5BS_BIG_ENDIAN
#error "TODO"
#endif
            union
            {
                T d;
                U s;
            } u;

            u.s = s;
            return u.d;
        }
    };

    template <>
    struct vfp_cast_type<uint32_t>
    {
        __force_inline
        static uint32_t from(uint32_t s)
            { return s; }

        template <typename U>
        __force_inline
        static uint32_t from(U s)
            { return s[0]; }
    };

} // anonymous

template <typename dT, typename sT>
__force_inline
static dT vfp_cast(sT s)
{
    return vfp_cast_type<dT>::from(s);
}

template <typename T>
__force_inline
static md5bs_uint32_t md5bs_uint32(T x)
{
    return vfp_cast<md5bs_uint32_t>(x);
}

template <typename T>
__force_inline
static md5bs_simd4_t md5bs_simd(T x)
{
    return vfp_cast<md5bs_simd4_t>(x);
}

#    if MD5BS_SIMD_REGISTER /* ARM */

__force_inline
static uint64x1_t md5bs_vshl_n_s64(uint64x1_t x, int s)
{
    switch (s)
    {
#define MD5BS_VSHL_N_S64_CASE_1(n) case (n): return vshl_n_s64(x, (n))
#define MD5BS_VSHL_N_S64_CASE_2(n)      \
        MD5BS_VSHL_N_S64_CASE_1(n + 0); \
        MD5BS_VSHL_N_S64_CASE_1(n + 1)
#define MD5BS_VSHL_N_S64_CASE_4(n)      \
        MD5BS_VSHL_N_S64_CASE_2(n + 0); \
        MD5BS_VSHL_N_S64_CASE_2(n + 2)
#define MD5BS_VSHL_N_S64_CASE_8(n)      \
        MD5BS_VSHL_N_S64_CASE_4(n + 0); \
        MD5BS_VSHL_N_S64_CASE_4(n + 4)
#define MD5BS_VSHL_N_S64_CASE_16(n)     \
        MD5BS_VSHL_N_S64_CASE_8(n + 0); \
        MD5BS_VSHL_N_S64_CASE_8(n + 8)
#define MD5BS_VSHL_N_S64_CASE_32(n)      \
        MD5BS_VSHL_N_S64_CASE_16(n + 0); \
        MD5BS_VSHL_N_S64_CASE_16(n + 16)

        MD5BS_VSHL_N_S64_CASE_32(0);
        MD5BS_VSHL_N_S64_CASE_32(32);
    }
    return (uint64x1_t){0};
}

__force_inline
static md5bs_simd4_t md5bs_simd_add(const md5bs_simd4_t &a, const md5bs_simd4_t &b)
{
    return vaddq_u32(a, b);
}

__force_inline
static md5bs_alt_uint32_t md5bs_rotate(md5bs_alt_uint32_t x, int s)
{
    x[1] = x[0];
    x = vfp_cast<uint32x2_t>(md5bs_vshl_n_s64(vfp_cast<uint64x1_t>(x), s));
    x[0] = x[1];
    return x;
}

__force_inline
static md5bs_uint32_t md5bs_simd_read(md5bs_simd4_t q, int n)
{
    return vfp_cast<md5bs_uint32_t>(q[n]);
}

__force_inline
static md5bs_simd4_t md5bs_simd_write(md5bs_simd4_t &p, int n, md5bs_uint32_t v)
{
    md5bs_simd4_t r = p;

    r[n] = vfp_cast<uint32_t>(v);
    return r;
}

#    endif
#  elif MD5BS_ISA_X86 /* x86 */

//
// x86
//

// uint32/xmm, xmm/xmm 型の変換
namespace // anonymous
{
    template <typename T>
    struct xmm_cast_type
    {
        __force_inline
        static T from(uint32_t s)
            { return (T)_mm_cvtsi32_si128(s); }
        template <typename U>
        __force_inline
        static T from(U s)
            { return (T)s; }
    };

    template <>
    struct xmm_cast_type<uint32_t>
    {
        __force_inline
        static uint32_t from(uint32_t s)
            { return s; }

        template <typename U>
        __force_inline
        static uint32_t from(U s)
            { return (uint32_t)_mm_cvtsi128_si32(s); }
    };

} // anonymous

template <typename dT, typename sT>
__force_inline
static dT xmm_cast(sT s)
{
    return xmm_cast_type<dT>::from(s);
}

template <typename T>
__force_inline
static __m128 md5bs_m128(T v)
{
    return xmm_cast<__m128>(v);
}

template <typename T>
__force_inline
static __m128i md5bs_m128i(T v)
{
    return xmm_cast<__m128i>(v);
}

template <typename T>
__force_inline
static md5bs_uint32_t md5bs_uint32(T x)
{
    return xmm_cast<md5bs_uint32_t>(x);
}

template <typename T>
__force_inline
static md5bs_simd4_t md5bs_simd(T x)
{
    return xmm_cast<md5bs_simd4_t>(x);
}

#    if MD5BS_SIMD_REGISTER /* x86 */

// xmm レジスタ型へのアクセス
__force_inline
static md5bs_uint32_t md5bs_simd_read(md5bs_simd4_t q, int n)
{
    __m128i p = md5bs_m128i(q);

    switch ((n & 3))
    {
    case 0: return md5bs_uint32(_mm_shuffle_epi32(p, 0x00));
    case 1: return md5bs_uint32(_mm_shuffle_epi32(p, 0x55));
    case 2: return md5bs_uint32(_mm_shuffle_epi32(p, 0xaa));
    case 3: return md5bs_uint32(_mm_shuffle_epi32(p, 0xff));
    }
    return md5bs_uint32(q ^ q); /* 警告抑制用：意味は無い */
    // return 0; // はエラーになる
}

__force_inline
static md5bs_simd4_t md5bs_simd_write(md5bs_simd4_t q, int n, md5bs_simd4_t w)
{
    __m128 p = md5bs_m128(q);
    __m128 v = md5bs_m128(w);

    switch ((n & 3))
    {
    case 0: return md5bs_simd(_mm_insert_ps(p, v, 0x00));
    case 1: return md5bs_simd(_mm_insert_ps(p, v, 0x10));
    case 2: return md5bs_simd(_mm_insert_ps(p, v, 0x20));
    case 3: return md5bs_simd(_mm_insert_ps(p, v, 0x30));
    }
    return (w ^ w); /* 警告抑制用：意味は無い */
    // return 0; // はエラーになる
}

// xmm を使った加算
__force_inline
static md5bs_simd4_t md5bs_simd_add(md5bs_simd4_t a, md5bs_simd4_t b)
{
    return a + b;
}

// xmm を使ったローテート
__force_inline
static md5bs_alt_uint32_t md5bs_rotate(md5bs_alt_uint32_t x, int s)
{
    return md5bs_simd(_mm_srli_epi64(_mm_shuffle_epi32(md5bs_m128i(x), 0), (32 - s)));
}

#    else  /* MD5BS_SIMD_REGISTER */
#    endif /* MD5BS_SIMD_REGISTER */

// メモリ・アクセス
__force_inline
static md5bs_simd4_t md5bs_simd_loadu(const md5bs_simd4_t *p)
{
    return md5bs_simd(_mm_lddqu_si128((const __m128i *)p));
}

#    if MD5BS_SIMD_REGISTER_256 // YMM

template <typename T>
__force_inline
static __m256 md5bs_m256(T v)
{
    return xmm_cast<__m256>(v);
}

template <typename T>
__force_inline
static __m256i md5bs_m256i(T v)
{
    return xmm_cast<__m256i>(v);
}

template <typename T>
__force_inline
static md5bs_simd8_t md5bs_simd8(T x)
{
    return xmm_cast<md5bs_simd8_t>(x);
}

__force_inline
static md5bs_simd8_t md5bs_simd_loadu(const md5bs_simd8_t *p)
{
    return md5bs_simd8(_mm256_lddqu_si256((const __m256i *)p));
}

#    endif /* MD5BS_SIMD_REGISTER_256 */
#  else /* MD5BS_ISA_??? */
#    error "unknown ISA"
#  endif /* MD5BS_ISA_??? */
#else /* !MD5BS_HAS_SIMD */

// C

#  if MD5BS_SIMD_REGISTER /*****/

__force_inline
static md5bs_simd4_t md5bs_simd(uint32_t x)
{
    md5bs_simd4_t r;

    r[0] = x;
    r[1] = 0;
    r[2] = 0;
    r[3] = 0;
    return r;
}

__force_inline
static md5bs_uint32_t md5bs_simd_read(const md5bs_simd4_t &p, int n)
{
    return p[n];
}

__force_inline
static md5bs_simd4_t md5bs_simd_write(md5bs_simd4_t &p, int n, uint32_t v)
{
    md5bs_simd4_t r = p;

    r[n] = v;
    return r;
}

#  endif /* MD5BS_SIMD_REGISTER */
#endif /* !MD5BS_HAS_SIMD */

namespace // anonymous
{

    template <typename T>
    __force_inline
    static T md5bs_simd_loadu(const T *p)
    {
        return *p;
    }

    template <typename T>
    __force_inline
    static void md5bs_simd_store(T *p, const T &v)
    {
        *p = v;
    }


} // anonymous

#if !MD5BS_HAS_SIMD || !MD5BS_SIMD_REGISTER

__force_inline
static md5bs_uint32_t md5bs_uint32(uint32_t x)
{
    return x;
}

__force_inline
static md5bs_simd4_t md5bs_simd_add(md5bs_simd4_t a, md5bs_simd4_t b)
{
    return a + b;
}

__force_inline
static uint32_t md5bs_rotate(uint32_t x, int s)
{
    return ((x << s) | (x >> (32 - s)));
}

#endif

/*****************************************
 *
 *     ブロックの計算
 *
 *****************************************/

// T[n] の値
static const uint32_t md5bs_block_t_order_table[64] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,

    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,

    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,

    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};

// X[i] に合わせた T[n] の値
static const uint32_t md5bs_block_x_order_table[64] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,  //  0,  1,  2,  3
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,  //  4,  5,  6,  7
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,  //  8,  9, 10, 11
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,  // 12, 13, 14, 15

    0xe9b6c7aa, 0xf61e2562, 0xfcefa3f8, 0xf4d50d87,  //  1,  6, 11,  0
    0xe7d3fbc8, 0xd62f105d, 0xc040b340, 0x676f02d9,  //  5, 10, 15,  4
    0x455a14ed, 0x21e1cde6, 0x02441453, 0x265e5a51,  //  9, 14,  3,  8
    0x8d2a4c8a, 0xa9e3e905, 0xc33707d6, 0xd8a1e681,  // 13,  2,  7, 12

    0xeaa127fa, 0xa4beea44, 0xc4ac5665, 0xd4ef3085,  //  5,  8, 11, 14
    0x4bdecfa9, 0xfffa3942, 0x04881d05, 0xf6bb4b60,  //  1,  4,  7, 10
    0x8771f681, 0xd9d4d039, 0xbebfbc70, 0x6d9d6122,  // 13,  0,  3,  6
    0xe6db99e5, 0x289b7ec6, 0xfde5380c, 0x1fa27cf8,  //  9, 12, 15,  2

    0xf4292244, 0x85845dd1, 0x2ad7d2bb, 0x8f0ccc92,  //  0,  7, 14,  5
    0xf7537e82, 0xfc93a039, 0xa3014314, 0x432aff97,  // 12,  3, 10,  1
    0x6fa87e4f, 0xeb86d391, 0xffeff47d, 0xbd3af235,  //  8, 15,  6, 13
    0x655b59c3, 0x4e0811a1, 0xab9423a7, 0xfe2ce6e0,  //  4, 11,  2,  9
};

// 4バイト境界にないブロック・データを取得する
__force_inline
static void md5bs_block_load(MD5ByteSum::Block &out,
                             const uint32_t *inp,
                             bool endian)
{
    md5bs_load32 load = md5bs_load32_func(endian);

    for (int i = 0; i < 16; i++)
        out[i] = load(inp[i], false);
}

// 1ブロックの先読み(Dキャッシュ)を試みる
__force_inline
static void md5bs_block_prefetch(const void *block)
{
    const uint8_t *p = md5bs_uint8_p(block);

    if (MD5BS_CACHE_LINE_SIZE >= 16)
        for (int o = 0; o < 64; o += MD5BS_CACHE_LINE_SIZE)
            md5bs_prefetch(p + o);
}

/*
 * get_block_index()
 * get_rotate_bits()
 */
#if 0

/* テーブル参照方式 */

// 計算順番(インデックス)とブロックのワード番号の表
static const uint8_t md5bs_block_index_table[64] = {
    0, 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
    1, 6, 11,  0,  5, 10, 15,  4,  9, 14,  3,  8, 13,  2,  7, 12,
    5, 8, 11, 14,  1,  4,  7, 10, 13,  0,  3,  6,  9, 12, 15,  2,
    0, 7, 14,  5, 12,  3, 10,  1,  8, 15,  6, 13,  4, 11,  2,  9,
};

// 計算順番(インデックス)からブロックのワード番号の取得する
__force_inline
static uint32_t get_block_index(uint32_t index)
{
    return md5bs_block_index_table[index];
}

// 計算順番(インデックス)とローテート・ビット数の表
static const uint8_t md5bs_rotate_bits_table[64] = {
    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
    5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
};

// 計算順番(インデックス)からローテート・ビット数を取得する
__force_inline
static int get_rotate_bits(uint32_t index)
{
    return md5bs_rotate_bits_table[index];
}

#else

/* 計算方式 */

// 計算順番(インデックス)からブロックのワード番号の取得する
//     0..15: (index * 1 + 0) & 15
//    16..31: (index * 5 + 1) & 15
//    32..47: (index * 3 + 5) & 15
//    48..63: (index * 7 + 0) & 15
//
__force_inline
static uint32_t get_block_index(uint32_t index)
{
    uint32_t x = index & 0x0f;
    uint32_t y = index & 0x30;
    uint32_t y1 = index & 0x10;
    uint32_t y2 = index & 0x20;
    uint32_t xmul = (y1 >> 2) | (y2 >> 4) | 1;  // {1,5,3,7}[y >> 4]
    uint32_t offs = (0x0510 >> (y >> 2)) & 7;   // {0,1,5,0}[y >> 4]
    return (x * xmul + offs) & 15;
}

// 計算順番(インデックス)からローテート・ビット数を取得する
//   {
//     7, 12, 17, 22,
//     5,  9, 14, 20,
//     4, 11, 16, 23,
//     6, 10, 15, 21,
//   } = {
//     4+(0*5)+0+[3], 4+(1*5)+0+[3], 4+(2*5)+0+[3], 4+(3*5)+1+[2],
//     4+(0*5)+0+[1], 4+(1*5)+0+[0], 4+(2*5)+0+[0], 4+(3*5)+1+[0],
//     4+(0*5)+0+[0], 4+(1*5)+0+[2], 4+(2*5)+0+[2], 4+(3*5)+1+[3],
//     4+(0*5)+0+[2], 4+(1*5)+0+[1], 4+(2*5)+0+[1], 4+(3*5)+1+[1],
//   }
//   ここから[n]を抜き出して
//   {
//     3, 3, 3, 2, // |2|3|3|3| = 10_11_11_11 = 0xbf
//     1, 0, 0, 0, // |0|0|0|1| = 00_00_00_01 = 0x01
//     0, 2, 2, 3, // |3|2|2|0| = 11_10_10_00 = 0xe8
//     2, 1, 1, 1, // |1|1|1|2| = 01_01_01_10 = 0x56
//   }
//
__force_inline
static int get_rotate_bits(uint32_t index)
{
    uint32_t x = index & 0x03;
    uint32_t y = index & 0x30;
    uint32_t p = x | (y >> 2);
    uint32_t x3 = (x + 1) >> 2;    // (x == 3) ? 1 : 0;
    uint32_t x0 = (0x56e801bf >> (p << 1)) & 3;
    return 4 + (x * 5) + x3 + x0;  // 4+(x*5)+x3+[x0]
}

#endif

// 計算順番(インデックス)によって異なる計算をする
template <typename T>
__force_inline
static T md5bs_state_func(uint32_t index, T x, T y, T z)
{
    switch ((index >> 4) & 3)
    {
    case 0: return (y & x) | (z & ~x); /* index: 0x00 - 0x0F */
    case 1: return (x & z) | (y & ~z); /* index: 0x10 - 0x1F */
    case 2: return x ^ y ^ z;          /* index: 0x20 - 0x2F */
    case 3: return y ^ (x | ~z);       /* index: 0x30 - 0x3F */
    }
    return (x ^ x); /* 警告抑制用：意味はない */
}

// ダイジェスト値(A,B,C,D)の更新準備: 計算1回(indexでは4個)分
__force_inline
static void md5bs_update_prepare4(uint32_t i,
                                  const uint32_t *X,
                                  const uint32_t *T,
                                  uint32_t *XT,
                                  bool aligned,
                                  bool endian,
                                  bool order)
{
    md5bs_load32 load = md5bs_load32_func(endian);
    uint32_t j = i & 0x0f;
    uint32_t x0 = order ? (j + 0) : get_block_index(i + 0);
    uint32_t x1 = order ? (j + 1) : get_block_index(i + 1);
    uint32_t x2 = order ? (j + 2) : get_block_index(i + 2);
    uint32_t x3 = order ? (j + 3) : get_block_index(i + 3);
    uint32_t *xt = XT;

    xt[j + 0] = T[i + 0] + load(X[x0], aligned);
    xt[j + 1] = T[i + 1] + load(X[x1], aligned);
    xt[j + 2] = T[i + 2] + load(X[x2], aligned);
    xt[j + 3] = T[i + 3] + load(X[x3], aligned);

    __dummy_access(*xt);
}

// ダイジェスト値(A,B,C,D)の更新準備: 計算4回(indexでは16個)分
__force_inline
static void md5bs_update_prepare16(uint32_t i,
                                   const uint32_t *X,
                                   const uint32_t *T,
                                   uint32_t *XT,
                                   bool aligned,
                                   bool endian,
                                   bool order)
{
    if (MD5BS_SIMD_REGISTER && aligned && order && !endian)
    {
        const md5bs_simd4_t *x = md5bs_simd4_p(X);
        const md5bs_simd4_t *t = md5bs_simd4_p(T + i);
        md5bs_simd4_t *xt = md5bs_simd4_p(XT);

        xt[0] = md5bs_simd_add(t[0], md5bs_simd_loadu(x + 0));
        xt[1] = md5bs_simd_add(t[1], md5bs_simd_loadu(x + 1));
        xt[2] = md5bs_simd_add(t[2], md5bs_simd_loadu(x + 2));
        xt[3] = md5bs_simd_add(t[3], md5bs_simd_loadu(x + 3));

        __dummy_access(xt[0]);
        __dummy_access(xt[1]);
        __dummy_access(xt[2]);
        __dummy_access(xt[3]);
    }
    else
    {
        md5bs_update_prepare4(i + 0 * 4, X, T, XT, aligned, endian, order);
        md5bs_update_prepare4(i + 1 * 4, X, T, XT, aligned, endian, order);
        md5bs_update_prepare4(i + 2 * 4, X, T, XT, aligned, endian, order);
        md5bs_update_prepare4(i + 3 * 4, X, T, XT, aligned, endian, order);
    }
}

// ダイジェスト値(A,B,C,D)の更新準備: 計算16回(indexでは64個)分
__force_inline
static void md5bs_update_prepare64(const uint32_t *X,
                                   const uint32_t *T,
                                   MD5ByteSum::Block *XT)
{
    const md5bs_simd8_t *wx = md5bs_simd8_p(X);
    const md5bs_simd8_t *wt = md5bs_simd8_p(T);
    md5bs_simd8_t *wxt = md5bs_simd8_p(XT);
    md5bs_simd8_t x0 = md5bs_simd_loadu(wx + 0);
    md5bs_simd8_t x1 = md5bs_simd_loadu(wx + 1);

    for (int i = 0; i < 4; i++)
    {
        int j = i * 2;

        wxt[j + 0] = x0 + wt[j + 0];
        wxt[j + 1] = x1 + wt[j + 1];

        __dummy_access(wxt[j + 0]);
        __dummy_access(wxt[j + 1]);
    }
}

// ダイジェスト値(A,B,C,D)の更新: 計算1個分
//   md5bs_update_prepare4 で用意したブロックを使う
__force_inline
static md5bs_uint32_t md5bs_update_execute1(uint32_t i,
                                            md5bs_uint32_t xt,
                                            md5bs_uint32_t a,
                                            md5bs_uint32_t b,
                                            md5bs_uint32_t c,
                                            md5bs_uint32_t d,
                                            int r)
{
    return b + md5bs_rotate(xt + a + md5bs_state_func(i, b, c, d), r);
}

// ダイジェスト値(A,B,C,D)の更新: 計算4個分
//   md5bs_update_prepare4 で用意したブロックを使う
__force_inline
static void md5bs_update_execute4(uint32_t i,
                                  const uint32_t *xt,
                                  md5bs_uint32_t &a,
                                  md5bs_uint32_t &b,
                                  md5bs_uint32_t &c,
                                  md5bs_uint32_t &d,
                                  bool order)
{
    uint32_t j = (i & 0x0f);
    uint32_t b0 = order ? get_block_index(i + 0) : (j + 0);
    uint32_t b1 = order ? get_block_index(i + 1) : (j + 1);
    uint32_t b2 = order ? get_block_index(i + 2) : (j + 2);
    uint32_t b3 = order ? get_block_index(i + 3) : (j + 3);

    int r0 = get_rotate_bits(i + 0);
    int r1 = get_rotate_bits(i + 1);
    int r2 = get_rotate_bits(i + 2);
    int r3 = get_rotate_bits(i + 3);

    md5bs_uint32_t xt0 = md5bs_uint32(xt[b0]);
    md5bs_uint32_t xt1 = md5bs_uint32(xt[b1]);
    md5bs_uint32_t xt2 = md5bs_uint32(xt[b2]);
    md5bs_uint32_t xt3 = md5bs_uint32(xt[b3]);

    a = md5bs_update_execute1(i + 0, xt0, a, b, c, d, r0);
    d = md5bs_update_execute1(i + 1, xt1, d, a, b, c, r1);
    c = md5bs_update_execute1(i + 2, xt2, c, d, a, b, r2);
    b = md5bs_update_execute1(i + 3, xt3, b, c, d, a, r3);
}

// ダイジェスト値(A,B,C,D)の更新: 計算16個分
__force_inline
static void md5bs_update_execute16(uint32_t i,
                                   const uint32_t *xt,
                                   md5bs_uint32_t &a,
                                   md5bs_uint32_t &b,
                                   md5bs_uint32_t &c,
                                   md5bs_uint32_t &d,
                                   bool order)
{
    md5bs_update_execute4(i + 0 * 4, xt, a, b, c, d, order);
    md5bs_update_execute4(i + 1 * 4, xt, a, b, c, d, order);
    md5bs_update_execute4(i + 2 * 4, xt, a, b, c, d, order);
    md5bs_update_execute4(i + 3 * 4, xt, a, b, c, d, order);
}

// ダイジェスト値(A,B,C,D)の更新: 計算1回分
__force_inline
static md5bs_uint32_t md5bs_update_step1(uint32_t i,
                                         md5bs_uint32_t X,
                                         md5bs_uint32_t T,
                                         md5bs_uint32_t a,
                                         md5bs_uint32_t b,
                                         md5bs_uint32_t c,
                                         md5bs_uint32_t d,
                                         int r)
{
    return b + md5bs_rotate(X + T + a + md5bs_state_func(i, b, c, d), r);
}

// ダイジェスト値(A,B,C,D)の更新: 計算4回分
__force_inline
static void md5bs_update_step4(uint32_t i,
                               const uint32_t *X,
                               const uint32_t *T,
                               md5bs_uint32_t &a,
                               md5bs_uint32_t &b,
                               md5bs_uint32_t &c,
                               md5bs_uint32_t &d,
                               bool aligned,
                               bool endian)
{
    md5bs_load32 load = md5bs_load32_func(endian);

    uint32_t b0 = get_block_index(i + 0);
    uint32_t b1 = get_block_index(i + 1);
    uint32_t b2 = get_block_index(i + 2);
    uint32_t b3 = get_block_index(i + 3);

    int r0 = get_rotate_bits(i + 0);
    int r1 = get_rotate_bits(i + 1);
    int r2 = get_rotate_bits(i + 2);
    int r3 = get_rotate_bits(i + 3);

    md5bs_uint32_t t0 = md5bs_uint32(T[i + 0]);
    md5bs_uint32_t t1 = md5bs_uint32(T[i + 1]);
    md5bs_uint32_t t2 = md5bs_uint32(T[i + 2]);
    md5bs_uint32_t t3 = md5bs_uint32(T[i + 3]);

    md5bs_uint32_t x0 = md5bs_uint32(load(X[b0], aligned));
    md5bs_uint32_t x1 = md5bs_uint32(load(X[b1], aligned));
    md5bs_uint32_t x2 = md5bs_uint32(load(X[b2], aligned));
    md5bs_uint32_t x3 = md5bs_uint32(load(X[b3], aligned));

    a = md5bs_update_step1(i + 0, x0, t0, a, b, c, d, r0);
    d = md5bs_update_step1(i + 1, x1, t1, d, a, b, c, r1);
    c = md5bs_update_step1(i + 2, x2, t2, c, d, a, b, r2);
    b = md5bs_update_step1(i + 3, x3, t3, b, c, d, a, r3);
}

// ダイジェスト値(A,B,C,D)の更新: 計算16回分
__force_inline
static void md5bs_update_step16(uint32_t i,
                                const uint32_t *X,
                                const uint32_t *T,
                                md5bs_uint32_t &a,
                                md5bs_uint32_t &b,
                                md5bs_uint32_t &c,
                                md5bs_uint32_t &d,
                                bool aligned,
                                bool endian)
{
    md5bs_update_step4(i + 0 * 4, X, T, a, b, c, d, aligned, endian);
    md5bs_update_step4(i + 1 * 4, X, T, a, b, c, d, aligned, endian);
    md5bs_update_step4(i + 2 * 4, X, T, a, b, c, d, aligned, endian);
    md5bs_update_step4(i + 3 * 4, X, T, a, b, c, d, aligned, endian);
}

// uint32_t 型用: state から a, b, c, d を取得する
__force_inline
static void md5bs_get_state(const MD5ByteSum::State &state,
                            md5bs_uint32_t &a,
                            md5bs_uint32_t &b,
                            md5bs_uint32_t &c,
                            md5bs_uint32_t &d)
{
#if !MD5BS_SIMD_REGISTER

    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];

#else

    const md5bs_simd4_t &p = *reinterpret_cast<const md5bs_simd4_t *>(&state);

    a = md5bs_simd_read(p, 0);
    b = md5bs_simd_read(p, 1);
    c = md5bs_simd_read(p, 2);
    d = md5bs_simd_read(p, 3);

#endif
}

// uint32_t 型用: state に a, b, c, d を加算する
__force_inline
static void md5bs_add_state(MD5ByteSum::State &state,
                            md5bs_uint32_t a,
                            md5bs_uint32_t b,
                            md5bs_uint32_t c,
                            md5bs_uint32_t d)
{
#if !MD5BS_SIMD_REGISTER

    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;

#else

    md5bs_simd4_t &s = *reinterpret_cast<md5bs_simd4_t *>(&state);
    md5bs_simd4_t t;

    t = md5bs_simd(a);
    t = md5bs_simd_write(t, 1, b);
    t = md5bs_simd_write(t, 2, c);
    t = md5bs_simd_write(t, 3, d);
    s = s + t;

#endif
}

// ダイジェスト値(state)の更新:
//   入力 block は任意のアドレスで、16ワードのデータがある
__force_inline
static void md5bs_state_update(MD5ByteSum::State &state,
                               MD5ByteSum::Block &input,
                               MD5ByteSum::Block xt[4],
                               const void *block,
                               bool aligned,
                               bool endian)
{
    const uint32_t *t = md5bs_block_t_order_table;
    const uint32_t *x = md5bs_uint32_p(block);
    md5bs_uint32_t a, b, c, d;

    md5bs_get_state(state, a, b, c, d);

    if (!MD5BS_BLOCK_PREPARE)
    {
        md5bs_update_step16(0 * 16, x, t, a, b, c, d, aligned, endian);
        md5bs_update_step16(1 * 16, x, t, a, b, c, d, aligned, endian);
        md5bs_update_step16(2 * 16, x, t, a, b, c, d, aligned, endian);
        md5bs_update_step16(3 * 16, x, t, a, b, c, d, aligned, endian);
    }
    else
    {
        // 1ブロック分の (xt[k] = T[i] + X[j]) を準備する版
        bool order = MD5BS_BLOCK_X_ORDER;

        if (!aligned && MD5BS_ADDRESS_ALIGN)
        {
            md5bs_block_load(input, x, endian);
            aligned = true;
            endian = false;
            x = input;
        }
        if (order)
            t = md5bs_block_x_order_table;

        if (MD5BS_SIMD_REGISTER_256 && aligned && order && !endian)
            md5bs_update_prepare64(x, t, xt);
        else
        {
            md5bs_update_prepare16(0 * 16, x, t, xt[0], aligned, endian, order);
            md5bs_update_prepare16(1 * 16, x, t, xt[1], aligned, endian, order);
            md5bs_update_prepare16(2 * 16, x, t, xt[2], aligned, endian, order);
            md5bs_update_prepare16(3 * 16, x, t, xt[3], aligned, endian, order);
        }

        md5bs_update_execute16(0 * 16, xt[0], a, b, c, d, order);
        md5bs_update_execute16(1 * 16, xt[1], a, b, c, d, order);
        md5bs_update_execute16(2 * 16, xt[2], a, b, c, d, order);
        md5bs_update_execute16(3 * 16, xt[3], a, b, c, d, order);
    }

    md5bs_add_state(state, a, b, c, d);
}

// 複数ブロックによるダイジェスト値(state)の更新:
//   入力 block は任意のアドレスで、16ワードのデータが count 個ある
template <bool aligned>
__force_inline
static void md5bs_state_update(MD5ByteSum::State &state,
                               MD5ByteSum::Block &input,
                               MD5ByteSum::Block adbuf[4],
                               const void *block,
                               unsigned count,
                               bool endian)
{
    const uint8_t *p = md5bs_uint8_p(block);

    if (MD5BS_CACHE_LINE_SIZE < 16)
    {
        do
        {
            md5bs_state_update(state, input, adbuf, p, aligned, endian);
            p += 64;
        }
        while (--count != 0);
    }
    else
    {
        md5bs_block_prefetch(p);
        while (--count > 0)
        {
            md5bs_block_prefetch(p + 64);
            md5bs_state_update(state, input, adbuf, p, aligned, endian);
            p += 64;
        }
        md5bs_state_update(state, input, adbuf, p, aligned, endian);
    }
}

// メッセージを入力ブロック（バッファ）にコピーする
//   s から n バイト(n>0)を、input のバイト換算 i の位置へ
__force_inline
static void md5bs_setinp(uint32_t *input, const void *message, unsigned i, uint32_t n, bool endian)
{
    uint8_t *d = md5bs_uint8_p(input);
    const uint8_t *s = md5bs_uint8_p(message);

    if (!endian)
    {
        std::memcpy(d + i, s, n);
        return;
    }

    while ((i & 3) != 0)
    {
        d[i++ ^ 3] = *s++;
        if (--n == 0)
            return;
    }
    for (uint32_t c = n >> 2; c > 0; c--, i += 4, s += 4)
        input[i >> 2] = md5bs_load32be(*md5bs_uint32_p(s), false);
    n &= 3;
    if (n == 0)
        return;
    while (n-- > 0)
        d[i++ ^ 3] = *s++;
}

// ワードの8ビット以上(iによる)を0クリアする(リトル・エンディアン版)
//   p = MD5ByteSum::input, i = MD5ByteSum::bytes
__force_inline
static uint32_t md5bs_set0le(uint8_t *p, uint32_t i)
{
    { if ((i & 3) == 0) return i; } p[i++] = 0;
    { if ((i & 3) == 0) return i; } p[i++] = 0;
    { if ((i & 3) == 0) return i; } p[i++] = 0;
    return i;
}

// ワードの8ビット以上(iによる)を0クリアする(ビッグ・エンディアン版)
//   p = MD5ByteSum::input, i = MD5ByteSum::bytes
__force_inline
static uint8_t md5bs_set0be(uint8_t *p, uint32_t i)
{
    { if ((i & 3) == 0) return i; } p[i++ ^ 3] = 0;
    { if ((i & 3) == 0) return i; } p[i++ ^ 3] = 0;
    { if ((i & 3) == 0) return i; } p[i++ ^ 3] = 0;
    return i;
}

/*****************************************
 *
 *    MD5ByteSum クラス
 *
 *****************************************/

// コンストラクタ
MD5ByteSum::MD5ByteSum(const void *message, size_t size)
{
    initialize();
    update(message, size);
}

// メンバー変数の初期化
void MD5ByteSum::initialize()
{
    std::memset(input, 0, sizeof(input));
    std::memset(adbuf, 0, sizeof(adbuf));
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
    bytes = 0;
    active = true;
}

// ダイジェスト値(state)の更新:
//   入力するブロックは任意のアドレスで 64 バイトのメッセージがある
void MD5ByteSum::update_state(const void *block)
{
    md5bs_state_update(state, input, adbuf, block, true, false);
}

// ダイジェスト値の確定処理
void MD5ByteSum::finalize()
{
    if (!active)
        return;

    uint32_t i = bytes & 0x3f;
    uint8_t *p = md5bs_uint8_p(input);

    if (!MD5BS_BIG_ENDIAN)
    {
        p[i++] = 0x80;
        if (i > 56)
        {
            if (i < 64)
            {
                i = md5bs_set0le(p, i);
                if (i == 60)
                    input[15] = 0;
            }
            update_state();
            i = 0;
        }
    }
    else
    {
        p[i++ ^ 3] = 0x80;
        i = md5bs_set0be(p, i);
        if (i >= 60)
        {
            if (i == 60)
                input[15] = 0;
            update_state();
            i = 0;
        }
    }
    std::memset(p + i, 0, (56 - i));
    input[14] = bytes << 3;
    input[15] = bytes >> 29;
    update_state();

    active = false;
}

// メッセージによるダイジェスト値の更新
void MD5ByteSum::update(const void *message, size_t size)
{
    if (!active)
        initialize();

    bool endian = MD5BS_BIG_ENDIAN;
    const uint8_t *p = md5bs_uint8_p(message);
    unsigned i = bytes & 0x3f;
    unsigned r;
    size_t s;

    for (;;)
    {
        if (size == 0)
            return;

        if ((i != 0) || (size < 64))
        {
            s = size + i;
            if (s >= 64)
                s = (64 - i);
            if (s > size)
                s = size;
            r = unsigned(s);

            md5bs_setinp(input, p, i, r, endian);
            bytes += r;
            size -= r;
            p += r;

            i = bytes & 0x3f;
            if (i == 0)
                update_state();
            continue;
        }

        r = unsigned(size & ~0x3f);
        bytes += r;
        size -= r;

        r >>= 6;
        if (md5bs_is_align4(p) || !MD5BS_ADDRESS_ALIGN)
            md5bs_state_update<true>(state, input, adbuf, p, r, endian);
        else
            md5bs_state_update<false>(state, input, adbuf, p, r, endian);
    }
}

// ダイジェスト値の確定と取得
void MD5ByteSum::digest(Digest &out)
{
    finalize();

    if (!MD5BS_BIG_ENDIAN)
    {
        std::memcpy(out, &state, 16);
    }
    else
    {
        uint8_t *d = out;
        const uint32_t *s = state;

        for (uint32_t i = 0; i < 4; i++)
            for (uint32_t w = *s++, j = 0; j < 4; j++, w >>= 8)
                *d++ = uint8_t(w);
    }
}

// ダイジェスト値の確定と16進数文字列による取得(NULL終端文字列)
void MD5ByteSum::hexdigest(HexDigest &out)
{
    finalize();

    for (int p = 0; p < 4; p++)
    {
        uint32_t s = state[p];

        for (unsigned q = 0; q < 4; q++, s >>= 8)
        {
            char h = (s >> 4) & 0x0f;
            char l = (s >> 0) & 0x0f;
            out[p * 8 + q * 2 + 0] = h + ((h < 10) ? '0' : ('a' - 10));
            out[p * 8 + q * 2 + 1] = l + ((l < 10) ? '0' : ('a' - 10));
        }
    }
    out[32] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//
// テスト用
//
///////////////////////////////////////////////////////////////////////////////

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cinttypes>
#include <time.h>

inline uint64_t RDTSC()
{
#if defined(_MSC_VER) ||   \
    (defined(__clang__) && \
     (defined(__amd64) ||  \
      defined(__i386) ||   \
      defined(__x86) ||    \
      defined(__x86_64)))

    return __rdtsc();

#elif defined(__GNUC__) && \
    (defined(__amd64) ||   \
     defined(__i386) ||    \
     defined(__x86) ||     \
     defined(__x86_64))

    uint32_t eax, edx;
    __asm__ volatile ("RDTSC" : "=a" (eax), "=d" (edx));
    return ((uint64_t)edx << 32) | eax;

#else

    timespec c;
    clock_gettime(CLOCK_REALTIME, &c);
    return ((uint64_t)c.tv_sec * 1000 * 1000 * 1000 + c.tv_nsec);

#endif
}

static void prepare_message(uint8_t *d, size_t dlen, const uint8_t *s, size_t slen)
{
    size_t clen = slen;
    if (clen > dlen)
        clen = dlen;
    std::memcpy(d, s, clen);
    d += clen;
    dlen -= clen;
    if (dlen != 0)
        prepare_message(d, dlen, s, slen + clen);
}

static int string_message(const char *message, const char *digest = NULL)
{
    size_t msglen = strlen(message);
    MD5ByteSum md5(message, msglen);
    MD5ByteSum::HexDigest res;

    md5.hexdigest(res);
    printf("MD5 (\"%s\") = %s\n", message, res);
    if (!digest)
        return 0;
    if (!strcmp(digest, res))
        return 0;
    printf("BUG: %s != %s\n", digest, res);
    return 2;
}

static int file_message(const char *path)
{
    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    size_t size = 1024 * 1024;
    void *buffer = malloc(size);

    if (!buffer)
    {
        printf("not enough memory.");
        return 2;
    }

    bool success = true;
    FILE *fp = fopen(path, "rb");

    if (!fp)
    {
        printf("open error: %s\n", path);
        return 2;
    }
    while (!feof(fp))
    {
        size_t rdsz = fread(buffer, 1, size, fp);
        if (ferror(fp))
        {
            success = false;
            break;
        }
        if (!rdsz)
            break;
        md5.update(buffer, rdsz);
    }
    fclose(fp);
    free(buffer);
    if (!success)
    {
        printf("read error: %s\n", path);
        return 2;
    }

    md5.hexdigest(res);
    printf("MD5 (%s) = %s\n", path, res);

    return 0;
}

static int benchmark(const char *message)
{
    if (!message)
        message = "benchmark";
    int rval = string_message(message);
    if (rval)
        return rval;

    size_t msglen = strlen(message);
    size_t blocks = (4 * 1024 * 1024);
    size_t bufsize = sizeof(MD5ByteSum::Block) * blocks;
    uint8_t *buffer = (uint8_t *)malloc(bufsize);
    if (!buffer)
    {
        puts("not enough memory.");
        return 2;
    }
    if (msglen)
    {
        uint8_t *d = buffer;
        std::memcpy(d, message, msglen);
        prepare_message(d + msglen, bufsize - msglen, buffer, msglen);
    }
    else
    {
        std::memset(buffer, -1, bufsize);
    }

    MD5ByteSum md5;
    MD5ByteSum::HexDigest res;

    uint64_t s_tsc = RDTSC();
    {
        const uint8_t *p = buffer;
        size_t s = bufsize;

#define STEP(n) md5.update(p, n); p += n; s -= n;
        // 動作テスト用に分解(計測への影響は極小)
        STEP(17);     // 0
        STEP(17);     // 17
        STEP(63);     // 34
        STEP(31+128); // 97
        STEP(s);
    }
    uint64_t e_tsc = RDTSC();
    uint64_t d_tsc = e_tsc - s_tsc;
    uint64_t ave_i = d_tsc / blocks;
    uint64_t ave_f = (100 * (d_tsc % blocks) + (blocks / 2)) / blocks;

    md5.hexdigest(res);
    printf("TEST digest: %s\n", res);
    // printf("timer start  : %" PRIu64 "\n", s_tsc);
    // printf("timer end    : %" PRIu64 "\n", e_tsc);
    printf("timer count  : %" PRIu64 "\n", d_tsc);
    printf("block average: %" PRIu64 ".%02" PRIu64 "\n", ave_i, ave_f);

    free(buffer);
    return 0;
}

static int test()
{
    static const char *tab[] = {
        "d41d8cd98f00b204e9800998ecf8427e", "",
        "0cc175b9c0f1b6a831c399e269772661", "a",
        "900150983cd24fb0d6963f7d28e17f72", "abc",
        "f96b697d7cb7938d525a2f31aaf161d0", "message digest",
        "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz",
        "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
        "ad76b175d22a98a4e3bc2ad72affc53e", "01234567890123456789012345678901234567890123456789012345678",
        NULL, NULL,
    };
    for (int i = 0; tab[i] != NULL; i += 2)
    {
        int res = string_message(tab[i + 1], tab[i + 0]);
        if (res != 0)
            return res;
    }
    return 0;
}

static const char *program;
static int usage()
{
    printf("Usage:\n"
           "  %s -b [message]\n"
           "  %s -f file\n"
           "  %s message\n",
           program, program, program);
    return 1;
}

int main(int argc, char **argv)
{
    program = argv[0];
    if (argc < 2)
        return usage();
    if (!strcmp(argv[1], "-b"))
    {
        if (argc < 4)
            return benchmark(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-f"))
    {
        if (argc == 3)
            return file_message(argv[2]);
        return usage();
    }
    if (!strcmp(argv[1], "-t"))
        return test();
    if (argc == 2)
        return string_message(argv[1]);
    return usage();
}
```

</div></details>

**※ビッグ・エンディアン機での動作確認はしていません**

<details><summary>Makefile (GNU make 用)</summary><div>

```Makefile:Makefile
# -*- Makefile -*-
####

-include local-variable.mk

####

DATETIME     := $(shell date +%Y%m%d-%H%M%S)
MACHINE      := $(shell uname -m)

####

ifeq "$(MACHINE)" "armv7l"
CPU_TYPE      = arm
else ifeq "$(MACHINE)" "i686"
CPU_TYPE      = x86
else ifeq "$(MACHINE)" "x86"
CPU_TYPE      = x86
else ifeq "$(MACHINE)" "x86_64"
CPU_TYPE      = x86
else
CPU_TYPE      = none
endif

####

CLANGXX      ?= clang++
GNUGXX       ?= g++

ifeq "$(COMPILER)" "clang"
CXX          = $(CLANGXX)
else ifeq "$(COMPILER)" "gcc"
CXX          = $(GNUGXX)
endif

ifndef COMPILER
flag_CLANG    = $(shell (c++ -v 2>&1 | grep -i clang > /dev/null) && echo clang)
flag_GCC      = $(shell (c++ -v 2>&1 | grep '^gcc' > /dev/null) && echo gcc)
COMPILER      = $(strip $(flag_CLANG) $(flag_GCC))
endif

####

ifeq "$(CPU_TYPE)" "arm"
CPU_ARCHS     = arm neon
else ifeq "$(CPU_TYPE)" "x86"
CPU_ARCHS     = x86 sse avx
endif
ifndef CPU_ARCHS
CPU_ARCHS     = host
endif

CARCH_host    = $(NULL)

CARCH_arm     = $(NULL) # -march=armv6
CARCH_neon    = -march=armv7 -mfpu=neon

CARCH_x86     = -mno-sse
CARCH_sse     = -msse4.2
CARCH_avx     = -mavx2

CMTUNE_arm    =
CMTUNE_x86    = -mtune=native

OPT_ARCH_host = host
OPT_ARCH_arm  = core
OPT_ARCH_x86  = gpr
OPT_ARCH_sse  = sse
OPT_ARCH_avx  = avx
OPT_ARCH_neon = neon
OPT_XARCH     = $(OPT_ARCH_$(OPT_ARCH))

OPT_stdc      = -DMD5BS_HAS_SIMD=0
OPT_core      = -DMD5BS_SIMD_REGISTER=0
OPT_simd      = -DMD5BS_SIMD_REGISTER=1
OPT_direct    = -DMD5BS_BLOCK_PREPARE=0
OPT_preadd    = -DMD5BS_BLOCK_PREPARE=1
OPT_torder    = $(OPT_preadd) -DMD5BS_BLOCK_X_ORDER=0
OPT_xorder    = $(OPT_preadd) -DMD5BS_BLOCK_X_ORDER=1

ifndef PREFETCH
PREFETCH      = default
endif
PREFETCH_off  = -DMD5BS_CACHE_LINE_SIZE=0
PREFETCH_on   = -DMD5BS_CACHE_LINE_SIZE=64

CARCH         = $(CARCH_$(OPT_ARCH)) $(CMTUNE_$(CPU_TYPE))
CDEFS         = $(OPT_$(OPT_SIMD)) $(OPT_$(OPT_BLOCK)) $(PREFETCH_$(PREFETCH))

ifndef COPTS
COPTS         = -O3
endif
ifndef CWARNS
CWARNS        = -W -Wall -Wno-ignored-attributes
endif
CFLAGS        = $(CARCH) $(COPTS) $(CWARNS) $(CSTD)

CXXARCH       = $(CARCH)
CXXDEFS       = $(CDEFS)
CXXOPTS       = $(COPTS)
CXXWARNS      = $(CWARNS)
CXXFLAGS      = $(patsubst %,%,$(CXXARCH) $(CXXOPTS) $(CXXWARNS) $(CXXSTD) $(CXXDEFS))

####

BUILDOPT      = COMPILER=$(COMPILER) PREFETCH=$(PREFETCH)

TEST_BINARY  ?= *

####

SUBDIRS       = asm bin log

####

all:		$(COMPILER)

gcc:
		$(MAKE) COMPILER=gcc build
clang:
		$(MAKE) COMPILER=clang build

build:		build-orig build-curr

build-orig:
		@(for arch in $(CPU_ARCHS);\
		do\
		  $(MAKE) COMPILER=$(COMPILER) OPT_ARCH=$$arch build-orig-exec;\
		done)
build-orig-exec: asm bin
		$(CXX) $(CXXFLAGS) md5bs_orig.cpp -o bin/$(COMPILER)-$(OPT_XARCH)-md5bs_orig
		$(CXX) $(CXXFLAGS) md5bs_orig.cpp -o asm/$(COMPILER)-$(OPT_XARCH)-md5bs_orig.s -S

build-new:
		@(for arch in $(CPU_ARCHS);\
		do\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=stdc OPT_BLOCK=direct OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=stdc OPT_BLOCK=torder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=stdc OPT_BLOCK=xorder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=core OPT_BLOCK=direct OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=core OPT_BLOCK=torder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=core OPT_BLOCK=xorder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=simd OPT_BLOCK=direct OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=simd OPT_BLOCK=torder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		  $(MAKE) $(BUILDOPT) OPT_SIMD=simd OPT_BLOCK=xorder OPT_ARCH=$$arch build-new-exec || exit $$?;\
		done)

build-new-exec: asm bin
		$(CXX) $(CXXFLAGS) md5bs.cpp -o bin/$(COMPILER)-$(OPT_XARCH)-md5bs-$(OPT_SIMD)-$(OPT_BLOCK)
		$(CXX) $(CXXFLAGS) md5bs.cpp -o asm/$(COMPILER)-$(OPT_XARCH)-md5bs-$(OPT_SIMD)-$(OPT_BLOCK).s -S

bench:		log
		@(cd bin;\
		for f in *;\
		do\
		  [ -x $$f ] || continue;\
		  echo "********" $$f -t "*******";\
		  for n in `seq 5`; do ./$$f -b | tee -a ../log/$(DATETIME)-$$f.log; done;\
		done)

####

check:
		@(cd bin;\
		for f in $(TEST_BINARY);\
		do\
		  [ -x $$f ] || continue;\
		  echo $$f -t;\
		  ./$$f -t || exit $$?;\
		done)

####

clean:
		rm -f *.[os] *.log *~
		rm -rf $(SUBDIRS)
clean-output:
		rm -rf asm bin
clean-log:
		rm -rf log

$(SUBDIRS) $(VMSHAREDIR):
		mkdir $@

####

-include local-rule.mk
```

**コンパイルと実行**

```console:
$ make clean gcc clang check bench
```

</div></details>


## コンパイル・オプション

改良版は、以下の3通り

| 略号 | コンパイル・オプション | 内容 |
|:-:|:--|:--|
| D | -DMD5BS_BLOCK_PREPARE=0 | メッセージの記憶域を直接参照する |
| T | -DMD5BS_BLOCK_PREPARE=1<br/>-DMD5BS_BLOCK_X_ORDER=0 | ブロックの計算開始時に X[j] + T[i] を i 順に用意 |
| X | -DMD5BS_BLOCK_PREPARE=1<br/> -DMD5BS_BLOCK_X_ORDER=1 | ブロックの計算開始時に X[j] + T[i] を j 順に用意 |

を、それぞれ以下の3通り

| 略号 | コンパイル・オプション | 内容 |
|:-:|:--|:--|
| N | -DMD5BS_HAS_SIMD=0 | 明示的に SIMD は使用しない |
| B | -DMD5BS_SIMD_REGISTER=0 | ブロックの計算開始時を除いて<br/>明示的に SIMD は使用しない |
| S | -DMD5BS_SIMD_REGISTER=1 | 明示的に SIMD を使用する |

で、9通りのビルド・パターンがあります。

実行時のコマンド・ラインは

```console:
$ ./md5bs_orig -b
$ ./md5bs -b
```

として、 5回実行し、出力の "block average" を比較します。計測は、(1<<22)個のブロック全体の処理時間を測り、(1<<22)で割っています。

### ARM (RaspberryPI 3B+)

改良版の9通りに対して、命令セット(ISA)の2通り

| 略号 | コンパイル・オプション | 内容 |
|:-:|:--|:--|
| CORE | | デフォルトの最適化 |
| NEON | -march=armv7 -mfpu=neon | NEON を使用した最適化 |

で18通りのビルド・パターンを2つのコンパイラ

| コンパイラ |
|:--|
| Clang version 12.0.1 |
| GCC version 11.2.0 |

で行い、これを RaspberryPI 3B+ の RaspberryPI OS 10.10で実行します。全部で36通りです。

`CORE,B:S` の組み合わせでは SIMD の代替を用意しています。


### x86/x86_64

改良版の9通りに対して、命令セット(ISA)の3通り

| 略号 | コンパイル・オプション | 内容 |
|:-:|:--|:--|
| GPR | -mno-sse -mtune=native -O3 | SSE なしで最適化 |
| SSE | -msse4.2 -mtune=native  -O3 | SSE4.2 を使用した最適化 |
| AVX | -mavx2 -mtune=native -O3 | AVX2 を使用した最適化 |

で27通りのビルド・パターンを2つのコンパイラ

| コンパイラ |
|:--|
| Clang version 14.0.0 |
| GCC version 11.2.0 |

で行い、これを2つの実行環境

| CPU | Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz |
|:-:|:--|
| x86 | Debian 10.10 (i386) |
| x86_64 | Debian 10.10 (amd64) |

で実行します。全部で108通りです。

`GRP,B:S` の組み合わせでは SIMD の代替を用意しています。

## 実行結果

### ARM (RaspberryPI 3B+)

計測は clock_gettime を使用しているので、概ね ナノ秒(ns)になります。 RaspberryPI 3B+ は 1.4GHz 動作らしいので、1.4 倍で CPU サイクル数に相当します。

<details><summary>実行結果一覧（長いので折りたたみ）</summary><div>

オリジナル(md5bs_orig)の結果

| | ISA | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | CORE | 1227.88 | 1200.95 | 1227.22 | 1201.41 | 1230.51 | 1217.59 |
| GCC   | NEON | 776.77 | 770.64 | 776.64 | 772.85 | 775.67 | 774.51 |
| Clang | CORE | 967.45 | 958.84 | 966.88 | 962.38 | 966.46 | 964.40 |
| Clang | NEON | 967.45 | 966.36 | 952.18 | 967.03 | 951.40 | 960.88 |


改良版(md5bs)の結果

| | ISA | | | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | CORE | N | D | 332.38 | 331.38 | 332.54 | 331.93 | 332.57 | 332.16 |
| GCC   | CORE | N | T | 557.36 | 556.45 | 557.38 | 556.19 | 557.34 | 556.94 |
| GCC   | CORE | N | X | 559.18 | 559.18 | 558.75 | 556.10 | 558.76 | 558.39 |
| GCC   | CORE | B | D | 329.78 | 330.20 | 329.44 | 330.73 | 329.83 | 330.00 |
| GCC   | CORE | B | T | 553.59 | 557.48 | 553.72 | 556.88 | 553.79 | 555.09 |
| GCC   | CORE | B | X | 558.58 | 555.80 | 558.90 | 555.55 | 560.23 | 557.81 |
| GCC   | CORE | S | D | 336.25 | 336.46 | 336.26 | 336.23 | 332.90 | 335.62 |
| GCC   | CORE | S | T | 561.28 | 558.27 | 559.14 | 558.24 | 559.11 | 559.21 |
| GCC   | CORE | S | X | 559.67 | 561.68 | 560.02 | 562.40 | 556.93 | 560.14 |
| GCC   | NEON | N | D | 307.95 | 308.03 | 306.86 | 308.61 | 306.68 | 307.63 |
| GCC   | NEON | N | T | 520.61 | 525.04 | 521.11 | 524.06 | 521.23 | 522.41 |
| GCC   | NEON | N | X | 537.60 | 533.93 | 537.28 | 533.89 | 537.65 | 536.07 |
| GCC   | NEON | B | D | 307.20 | 305.58 | 306.40 | 306.27 | 306.41 | 306.37 |
| GCC   | NEON | B | T | 521.12 | 520.38 | 520.92 | 521.05 | 521.51 | 521.00 |
| GCC   | NEON | B | X | 534.80 | 538.76 | 534.50 | 538.05 | 534.09 | 536.04 |
| GCC   | NEON | S | D | 1046.08 | 1051.76 | 1052.14 | 1043.85 | 1045.98 | 1047.96 |
| GCC   | NEON | S | T | 1115.89 | 1119.64 | 1120.17 | 1125.41 | 1119.80 | 1120.18 |
| GCC   | NEON | S | X | 1020.62 | 1026.44 | 1021.06 | 1020.98 | 1025.80 | 1022.98 |
| Clang | CORE | N | D | 335.22 | 334.81 | 333.06 | 334.76 | 334.14 | 334.40 |
| Clang | CORE | N | T | 483.41 | 482.87 | 486.73 | 483.20 | 482.90 | 483.82 |
| Clang | CORE | N | X | 486.73 | 480.72 | 486.96 | 483.17 | 486.81 | 484.88 |
| Clang | CORE | B | D | 332.63 | 335.06 | 332.51 | 333.11 | 332.67 | 333.20 |
| Clang | CORE | B | T | 484.56 | 480.44 | 480.38 | 480.34 | 483.46 | 481.84 |
| Clang | CORE | B | X | 480.33 | 480.22 | 483.21 | 480.17 | 483.16 | 481.42 |
| Clang | CORE | S | D | 975.93 | 979.16 | 976.14 | 979.95 | 976.03 | 977.44 |
| Clang | CORE | S | T | 1373.85 | 1364.28 | 1373.68 | 1363.78 | 1363.86 | 1367.89 |
| Clang | CORE | S | X | 1204.28 | 1204.10 | 1203.96 | 1203.96 | 1203.88 | 1204.04 |
| Clang | NEON | N | D | 335.14 | 332.84 | 335.12 | 333.05 | 333.53 | 333.94 |
| Clang | NEON | N | T | 483.33 | 480.61 | 483.12 | 480.93 | 480.45 | 481.69 |
| Clang | NEON | N | X | 480.11 | 482.80 | 480.26 | 483.17 | 480.51 | 481.37 |
| Clang | NEON | B | D | 332.51 | 337.50 | 332.95 | 334.56 | 333.03 | 334.11 |
| Clang | NEON | B | T | 483.19 | 486.70 | 480.40 | 486.63 | 481.29 | 483.64 |
| Clang | NEON | B | X | 483.14 | 486.69 | 483.13 | 486.77 | 483.01 | 484.55 |
| Clang | NEON | S | D | 975.90 | 979.21 | 976.13 | 978.92 | 976.49 | 977.33 |
| Clang | NEON | S | T | 1373.42 | 1364.28 | 1373.26 | 1364.35 | 1364.02 | 1367.87 |
| Clang | NEON | S | X | 1208.53 | 1203.87 | 1209.26 | 1203.23 | 1204.65 | 1205.91 |

</div></details>

**改良版(md5bs)の平均**

| &nbsp;<br>ISA | GCC<br>CORE | GCC<br>NEON | Clang<br>CORE | Clang<br>NEON |
|:-:|--:|--:|--:|--:|
| N:D | 332.16 |  307.63 |  334.40 |  333.94 |
| N:T | 556.94 |  522.41 |  483.82 |  481.69 |
| N:X | 558.39 |  536.07 |  484.88 |  481.37 |
| B:D | 330.00 |  306.37 |  333.20 |  334.11 |
| B:T | 555.09 |  521.00 |  481.84 |  483.64 |
| B:X | 557.81 |  536.04 |  481.42 |  484.55 |
| S:D | 335.62 | 1047.96 |  977.44 |  977.33 |
| S:T | 559.21 | 1120.18 | 1367.89 | 1367.87 |
| S:X | 560.14 | 1022.98 | 1204.04 | 1205.91 |

GCC では NEON,B:D の場合が 306.37 で、Clang では CORE,B:D の場合が 333.20 で最小になりました。

<details><summary>平均でソートした表</summary><div>

| コンパイラ | 種類 | 平均 | コンパイラ | 種類 | 平均 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | NEON,B:D | 306.37  | Clang | CORE,B:D | 333.20  |
| GCC   | NEON,N:D | 307.63  | Clang | NEON,N:D | 333.94  |
| GCC   | CORE,B:D | 330.00  | Clang | NEON,B:D | 334.11  |
| GCC   | CORE,N:D | 332.16  | Clang | CORE,N:D | 334.40  |
| GCC   | CORE,S:D | 335.62  | Clang | NEON,N:X | 481.37  |
| GCC   | NEON,B:T | 521.00  | Clang | CORE,B:X | 481.42  |
| GCC   | NEON,N:T | 522.41  | Clang | NEON,N:T | 481.69  |
| GCC   | NEON,B:X | 536.04  | Clang | CORE,B:T | 481.84  |
| GCC   | NEON,N:X | 536.07  | Clang | NEON,B:T | 483.64  |
| GCC   | CORE,B:T | 555.09  | Clang | CORE,N:T | 483.82  |
| GCC   | CORE,N:T | 556.94  | Clang | NEON,B:X | 484.55  |
| GCC   | CORE,B:X | 557.81  | Clang | CORE,N:X | 484.88  |
| GCC   | CORE,N:X | 558.39  | Clang | NEON,S:D | 977.33  |
| GCC   | CORE,S:T | 559.21  | Clang | CORE,S:D | 977.44  |
| GCC   | CORE,S:X | 560.14  | Clang | CORE,S:X | 1204.04 |
| GCC   | NEON,S:X | 1022.98 | Clang | NEON,S:X | 1205.91 |
| GCC   | NEON,S:D | 1047.96 | Clang | NEON,S:T | 1367.87 |
| GCC   | NEON,S:T | 1120.18 | Clang | CORE,S:T | 1367.89 |

</div></details>

### x86/x86_64

x86 では、RDTSC を使用しているので、概ね CPU サイクル数になります。

<details><summary>実行結果一覧（長いので折りたたみ）</summary><div>

オリジナル(md5bs_orig:x86)の結果

| | ISA | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | GPR | 407.97 | 398.59 | 401.21 | 398.68 | 398.12 | 400.91 |
| GCC   | SSE | 400.45 | 398.58 | 398.89 | 397.89 | 399.72 | 399.11 |
| GCC   | AVX | 428.43 | 426.58 | 426.93 | 428.53 | 427.38 | 427.57 |
| Clang | GPR | 457.11 | 458.43 | 455.37 | 459.71 | 455.57 | 457.24 |
| Clang | SSE | 473.62 | 473.85 | 473.85 | 472.85 | 473.78 | 473.59 |
| Clang | AVX | 477.69 | 477.86 | 476.72 | 480.49 | 474.50 | 477.45 |

オリジナル(md5bs_orig:x86_64)の結果

| | ISA | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | GPR | 425.67 | 424.85 | 425.23 | 426.17 | 423.01 | 424.99 |
| GCC   | SSE | 437.09 | 436.23 | 437.59 | 435.42 | 437.42 | 436.75 |
| GCC   | AVX | 433.30 | 438.24 | 436.30 | 435.33 | 436.27 | 435.89 |
| Clang | GPR | 456.02 | 457.86 | 461.90 | 457.10 | 458.76 | 458.33 |
| Clang | SSE | 461.80 | 463.15 | 462.28 | 461.82 | 463.53 | 462.52 |
| Clang | AVX | 461.90 | 463.72 | 463.61 | 463.22 | 466.09 | 463.71 |

改良版(md5bs:x86)の結果

| | ISA | | | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | GPR | N | D | 262.77 | 262.95 | 261.93 | 262.42 | 263.05 | 262.62 |
| GCC   | GPR | N | T | 320.92 | 321.77 | 321.22 | 322.16 | 321.53 | 321.52 |
| GCC   | GPR | N | X | 321.04 | 321.28 | 320.62 | 321.84 | 320.76 | 321.11 |
| GCC   | GPR | B | D | 263.50 | 262.13 | 262.35 | 263.10 | 262.20 | 262.66 |
| GCC   | GPR | B | T | 322.77 | 320.79 | 321.15 | 322.33 | 321.55 | 321.72 |
| GCC   | GPR | B | X | 322.08 | 321.94 | 322.91 | 320.76 | 321.66 | 321.87 |
| GCC   | GPR | S | D | 258.92 | 258.82 | 259.55 | 259.22 | 258.74 | 259.05 |
| GCC   | GPR | S | T | 321.87 | 320.09 | 321.68 | 320.41 | 321.60 | 321.13 |
| GCC   | GPR | S | X | 322.83 | 324.13 | 322.73 | 322.48 | 322.83 | 323.00 |
| GCC   | SSE | N | D | 261.86 | 262.34 | 263.79 | 261.92 | 263.59 | 262.70 |
| GCC   | SSE | N | T | 320.71 | 321.19 | 321.97 | 322.05 | 322.58 | 321.70 |
| GCC   | SSE | N | X | 321.64 | 322.28 | 322.42 | 321.63 | 322.08 | 322.01 |
| GCC   | SSE | B | D | 260.70 | 259.84 | 259.08 | 260.29 | 258.94 | 259.77 |
| GCC   | SSE | B | T | 320.81 | 320.24 | 320.50 | 321.37 | 320.63 | 320.71 |
| GCC   | SSE | B | X | 321.34 | 320.51 | 322.11 | 320.81 | 321.82 | 321.32 |
| GCC   | SSE | S | D | 348.15 | 348.05 | 348.71 | 348.29 | 348.63 | 348.37 |
| GCC   | SSE | S | T | 370.97 | 372.03 | 371.33 | 371.69 | 373.95 | 371.99 |
| GCC   | SSE | S | X | 301.67 | 301.40 | 302.90 | 301.58 | 302.58 | 302.03 |
| GCC   | AVX | N | D | 261.82 | 262.97 | 261.65 | 261.86 | 263.56 | 262.37 |
| GCC   | AVX | N | T | 322.11 | 321.10 | 321.81 | 324.40 | 322.59 | 322.40 |
| GCC   | AVX | N | X | 322.12 | 321.82 | 321.52 | 321.89 | 320.47 | 321.56 |
| GCC   | AVX | B | D | 259.20 | 260.34 | 259.10 | 259.12 | 260.41 | 259.63 |
| GCC   | AVX | B | T | 320.30 | 321.56 | 320.68 | 320.10 | 321.58 | 320.84 |
| GCC   | AVX | B | X | 259.58 | 259.81 | 259.04 | 258.07 | 259.83 | 259.27 |
| GCC   | AVX | S | D | 348.34 | 349.27 | 348.80 | 350.05 | 348.10 | 348.91 |
| GCC   | AVX | S | T | 372.02 | 371.80 | 372.16 | 371.16 | 372.35 | 371.90 |
| GCC   | AVX | S | X | 313.90 | 314.35 | 312.90 | 314.03 | 314.38 | 313.91 |
| Clang | GPR | N | D | 306.05 | 308.56 | 305.89 | 306.34 | 307.45 | 306.86 |
| Clang | GPR | N | T | 295.40 | 296.06 | 293.19 | 295.77 | 294.45 | 294.97 |
| Clang | GPR | N | X | 296.26 | 295.02 | 294.07 | 295.97 | 294.53 | 295.17 |
| Clang | GPR | B | D | 306.62 | 308.30 | 305.83 | 306.35 | 307.51 | 306.92 |
| Clang | GPR | B | T | 293.67 | 296.13 | 294.20 | 295.70 | 295.32 | 295.00 |
| Clang | GPR | B | X | 294.21 | 296.56 | 296.30 | 296.33 | 293.29 | 295.34 |
| Clang | GPR | S | D | 305.90 | 306.56 | 305.36 | 305.58 | 306.09 | 305.90 |
| Clang | GPR | S | T | 294.87 | 293.45 | 293.75 | 295.20 | 297.43 | 294.94 |
| Clang | GPR | S | X | 339.56 | 342.89 | 336.89 | 342.88 | 338.08 | 340.06 |
| Clang | SSE | N | D | 305.96 | 305.61 | 306.30 | 304.84 | 305.31 | 305.60 |
| Clang | SSE | N | T | 293.79 | 294.15 | 294.43 | 294.62 | 295.37 | 294.47 |
| Clang | SSE | N | X | 294.13 | 293.57 | 294.68 | 293.91 | 294.85 | 294.23 |
| Clang | SSE | B | D | 313.45 | 326.08 | 304.71 | 302.66 | 303.99 | 310.18 |
| Clang | SSE | B | T | 305.73 | 308.47 | 306.43 | 306.55 | 307.09 | 306.85 |
| Clang | SSE | B | X | 306.76 | 306.84 | 306.66 | 306.61 | 305.85 | 306.54 |
| Clang | SSE | S | D | 286.51 | 287.99 | 287.31 | 288.10 | 287.17 | 287.42 |
| Clang | SSE | S | T | 302.26 | 300.22 | 299.80 | 301.23 | 300.24 | 300.75 |
| Clang | SSE | S | X | 281.74 | 280.64 | 280.77 | 281.87 | 280.55 | 281.11 |
| Clang | AVX | N | D | 307.69 | 308.49 | 305.87 | 308.33 | 307.04 | 307.48 |
| Clang | AVX | N | T | 298.05 | 298.38 | 296.24 | 296.35 | 294.17 | 296.64 |
| Clang | AVX | N | X | 295.33 | 294.32 | 297.56 | 294.17 | 295.85 | 295.45 |
| Clang | AVX | B | D | 305.28 | 303.31 | 304.80 | 303.21 | 305.33 | 304.39 |
| Clang | AVX | B | T | 309.11 | 306.97 | 312.09 | 307.02 | 306.03 | 308.24 |
| Clang | AVX | B | X | 252.19 | 251.88 | 252.52 | 252.22 | 251.21 | 252.00 |
| Clang | AVX | S | D | 294.33 | 295.06 | 294.19 | 293.54 | 291.73 | 293.77 |
| Clang | AVX | S | T | 299.37 | 299.94 | 302.42 | 300.17 | 299.48 | 300.28 |
| Clang | AVX | S | X | 281.69 | 283.01 | 281.51 | 282.62 | 282.68 | 282.30 |

改良版(md5bs:x86_64)の結果

| | ISA | | | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 |
|:--|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | GPR | N | D | 256.96 | 257.21 | 256.06 | 257.59 | 255.54 | 256.67 |
| GCC   | GPR | N | T | 357.57 | 356.19 | 358.66 | 354.65 | 359.62 | 357.34 |
| GCC   | GPR | N | X | 352.60 | 355.81 | 353.47 | 355.36 | 352.42 | 353.93 |
| GCC   | GPR | B | D | 256.31 | 256.41 | 258.40 | 255.47 | 256.45 | 256.61 |
| GCC   | GPR | B | T | 357.87 | 356.56 | 358.40 | 354.31 | 356.23 | 356.67 |
| GCC   | GPR | B | X | 354.08 | 354.31 | 353.27 | 353.20 | 352.54 | 353.48 |
| GCC   | GPR | S | D | 255.50 | 258.72 | 255.21 | 257.10 | 255.14 | 256.33 |
| GCC   | GPR | S | T | 354.59 | 356.76 | 355.84 | 357.69 | 356.14 | 356.20 |
| GCC   | GPR | S | X | 367.23 | 363.54 | 362.16 | 361.69 | 363.25 | 363.57 |
| GCC   | SSE | N | D | 257.35 | 259.63 | 257.12 | 257.03 | 255.62 | 257.35 |
| GCC   | SSE | N | T | 314.19 | 314.64 | 313.42 | 313.90 | 313.86 | 314.00 |
| GCC   | SSE | N | X | 312.98 | 313.91 | 311.79 | 314.15 | 312.18 | 313.00 |
| GCC   | SSE | B | D | 263.13 | 265.71 | 263.71 | 264.56 | 261.81 | 263.78 |
| GCC   | SSE | B | T | 310.23 | 313.00 | 313.28 | 312.44 | 311.76 | 312.14 |
| GCC   | SSE | B | X | 308.33 | 311.29 | 308.94 | 312.98 | 310.15 | 310.34 |
| GCC   | SSE | S | D | 345.95 | 342.75 | 346.63 | 344.33 | 347.49 | 345.43 |
| GCC   | SSE | S | T | 357.23 | 358.31 | 358.05 | 357.48 | 360.34 | 358.28 |
| GCC   | SSE | S | X | 302.82 | 304.24 | 302.93 | 302.95 | 303.91 | 303.37 |
| GCC   | AVX | N | D | 257.54 | 255.75 | 253.70 | 256.45 | 255.62 | 255.81 |
| GCC   | AVX | N | T | 313.38 | 308.89 | 313.26 | 312.11 | 311.77 | 311.88 |
| GCC   | AVX | N | X | 313.57 | 312.99 | 314.11 | 312.09 | 313.88 | 313.33 |
| GCC   | AVX | B | D | 263.60 | 262.46 | 264.83 | 262.05 | 264.58 | 263.50 |
| GCC   | AVX | B | T | 310.24 | 311.77 | 312.41 | 311.48 | 312.30 | 311.64 |
| GCC   | AVX | B | X | 263.60 | 263.59 | 265.41 | 265.45 | 265.01 | 264.61 |
| GCC   | AVX | S | D | 350.30 | 345.10 | 341.74 | 344.86 | 345.25 | 345.45 |
| GCC   | AVX | S | T | 358.64 | 358.57 | 358.21 | 359.84 | 358.64 | 358.78 |
| GCC   | AVX | S | X | 313.22 | 311.52 | 313.15 | 312.47 | 309.78 | 312.03 |
| Clang | GPR | N | D | 300.57 | 302.18 | 300.31 | 300.31 | 302.04 | 301.08 |
| Clang | GPR | N | T | 278.92 | 280.39 | 278.86 | 278.66 | 282.17 | 279.80 |
| Clang | GPR | N | X | 278.86 | 279.28 | 277.39 | 278.68 | 280.04 | 278.85 |
| Clang | GPR | B | D | 300.46 | 301.16 | 301.44 | 299.97 | 299.21 | 300.45 |
| Clang | GPR | B | T | 279.32 | 280.46 | 278.16 | 278.72 | 279.91 | 279.31 |
| Clang | GPR | B | X | 279.80 | 280.98 | 281.75 | 279.37 | 280.72 | 280.52 |
| Clang | GPR | S | D | 299.30 | 300.89 | 300.16 | 299.37 | 301.26 | 300.20 |
| Clang | GPR | S | T | 279.73 | 281.44 | 279.93 | 280.17 | 281.48 | 280.55 |
| Clang | GPR | S | X | 291.58 | 293.09 | 291.58 | 292.20 | 292.52 | 292.19 |
| Clang | SSE | N | D | 300.35 | 301.89 | 300.53 | 300.02 | 301.63 | 300.88 |
| Clang | SSE | N | T | 278.76 | 281.00 | 279.40 | 279.15 | 279.41 | 279.54 |
| Clang | SSE | N | X | 278.96 | 280.83 | 279.18 | 281.02 | 280.06 | 280.01 |
| Clang | SSE | B | D | 301.03 | 300.04 | 299.74 | 300.78 | 302.69 | 300.86 |
| Clang | SSE | B | T | 279.05 | 280.84 | 279.97 | 279.25 | 280.33 | 279.89 |
| Clang | SSE | B | X | 278.90 | 281.16 | 277.63 | 279.39 | 281.28 | 279.67 |
| Clang | SSE | S | D | 293.28 | 292.23 | 290.10 | 293.19 | 292.92 | 292.34 |
| Clang | SSE | S | T | 299.61 | 302.07 | 307.42 | 300.91 | 301.28 | 302.26 |
| Clang | SSE | S | X | 281.94 | 282.91 | 281.32 | 279.91 | 283.43 | 281.90 |
| Clang | AVX | N | D | 299.80 | 301.19 | 300.86 | 301.99 | 300.21 | 300.81 |
| Clang | AVX | N | T | 278.06 | 278.10 | 280.44 | 279.53 | 278.99 | 279.02 |
| Clang | AVX | N | X | 278.85 | 280.27 | 278.93 | 279.24 | 280.24 | 279.51 |
| Clang | AVX | B | D | 301.25 | 302.76 | 301.16 | 301.76 | 300.56 | 301.50 |
| Clang | AVX | B | T | 280.73 | 279.43 | 279.48 | 280.34 | 279.65 | 279.93 |
| Clang | AVX | B | X | 251.00 | 251.88 | 249.67 | 251.93 | 250.70 | 251.04 |
| Clang | AVX | S | D | 288.59 | 289.64 | 288.52 | 288.43 | 288.12 | 288.66 |
| Clang | AVX | S | T | 295.95 | 298.04 | 296.20 | 297.59 | 295.22 | 296.60 |
| Clang | AVX | S | X | 280.93 | 282.61 | 281.30 | 282.59 | 280.97 | 281.68 |

</div></details>

**改良版(md5bs:x86)の平均**

| &nbsp;<br>ISA | GCC<br>GPR | GCC<br>SSE | GCC<br>AVX | Clang<br>GPR | Clang<br>SSE | Clang<br>AVX |
|:-:|--:|--:|--:|--:|--:|--:|
| N:D | 262.62 | 262.70 | 262.37 | 306.86 | 305.60 | 307.48 |
| N:T | 321.52 | 321.70 | 322.40 | 294.97 | 294.47 | 296.64 |
| N:X | 321.11 | 322.01 | 321.56 | 295.17 | 294.23 | 295.45 |
| B:D | 262.66 | 259.77 | 259.63 | 306.92 | 310.18 | 304.39 |
| B:T | 321.72 | 320.71 | 320.84 | 295.00 | 306.85 | 308.24 |
| B:X | 321.87 | 321.32 | 259.27 | 295.34 | 306.54 | 252.00 |
| S:D | 259.05 | 348.37 | 348.91 | 305.90 | 287.42 | 293.77 |
| S:T | 321.13 | 371.99 | 371.90 | 294.94 | 300.75 | 300.28 |
| S:X | 323.00 | 302.03 | 313.91 | 340.06 | 281.11 | 282.30 |

GCC では GPR,S:D の場合が 259.05 で、Clang では AVX,B:X の場合が 252.00 で最小になりました。

<details><summary>平均でソートした表</summary><div>

| コンパイラ | 種類 | 平均 | コンパイラ | 種類 | 平均 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | GPR,S:D | 259.05 | Clang | AVX,B:X | 252.00 |
| GCC   | AVX,B:X | 259.27 | Clang | SSE,S:X | 281.11 |
| GCC   | AVX,B:D | 259.63 | Clang | AVX,S:X | 282.30 |
| GCC   | SSE,B:D | 259.77 | Clang | SSE,S:D | 287.42 |
| GCC   | AVX,N:D | 262.37 | Clang | AVX,S:D | 293.77 |
| GCC   | GPR,N:D | 262.62 | Clang | SSE,N:X | 294.23 |
| GCC   | GPR,B:D | 262.66 | Clang | SSE,N:T | 294.47 |
| GCC   | SSE,N:D | 262.70 | Clang | GPR,S:T | 294.94 |
| GCC   | SSE,S:X | 302.03 | Clang | GPR,N:T | 294.97 |
| GCC   | AVX,S:X | 313.91 | Clang | GPR,B:T | 295.00 |
| GCC   | SSE,B:T | 320.71 | Clang | GPR,N:X | 295.17 |
| GCC   | AVX,B:T | 320.84 | Clang | GPR,B:X | 295.34 |
| GCC   | GPR,N:X | 321.11 | Clang | AVX,N:X | 295.45 |
| GCC   | GPR,S:T | 321.13 | Clang | AVX,N:T | 296.64 |
| GCC   | SSE,B:X | 321.32 | Clang | AVX,S:T | 300.28 |
| GCC   | GPR,N:T | 321.52 | Clang | SSE,S:T | 300.75 |
| GCC   | AVX,N:X | 321.56 | Clang | AVX,B:D | 304.39 |
| GCC   | SSE,N:T | 321.70 | Clang | SSE,N:D | 305.60 |
| GCC   | GPR,B:T | 321.72 | Clang | GPR,S:D | 305.90 |
| GCC   | GPR,B:X | 321.87 | Clang | SSE,B:X | 306.54 |
| GCC   | SSE,N:X | 322.01 | Clang | SSE,B:T | 306.85 |
| GCC   | AVX,N:T | 322.40 | Clang | GPR,N:D | 306.86 |
| GCC   | GPR,S:X | 323.00 | Clang | GPR,B:D | 306.92 |
| GCC   | SSE,S:D | 348.37 | Clang | AVX,N:D | 307.48 |
| GCC   | AVX,S:D | 348.91 | Clang | AVX,B:T | 308.24 |
| GCC   | AVX,S:T | 371.90 | Clang | SSE,B:D | 310.18 |
| GCC   | SSE,S:T | 371.99 | Clang | GPR,S:X | 340.06 |

</div></details>

**改良版(md5bs:x86_64)の平均**

| &nbsp;<br>ISA | GCC<br>GPR | GCC<br>SSE | GCC<br>AVX | Clang<br>GPR | Clang<br>SSE | Clang<br>AVX |
|:-:|--:|--:|--:|--:|--:|--:|
| N:D | 256.67 | 257.35 | 255.81 | 301.08 | 300.88 | 300.81 |
| N:T | 357.34 | 314.00 | 311.88 | 279.80 | 279.54 | 279.02 |
| N:X | 353.93 | 313.00 | 313.33 | 278.85 | 280.01 | 279.51 |
| B:D | 256.61 | 263.78 | 263.50 | 300.45 | 300.86 | 301.50 |
| B:T | 356.67 | 312.14 | 311.64 | 279.31 | 279.89 | 279.93 |
| B:X | 353.48 | 310.34 | 264.61 | 280.52 | 279.67 | 251.04 |
| S:D | 256.33 | 345.43 | 345.45 | 300.20 | 292.34 | 288.66 |
| S:T | 356.20 | 358.28 | 358.78 | 280.55 | 302.26 | 296.60 |
| S:X | 363.57 | 303.37 | 312.03 | 292.19 | 281.90 | 281.68 |

GCC では AVX,N:D の場合が 255.81 で、Clang では AVX,B:X の場合が 251.04 で最小になりました。

<details><summary>平均でソートした表</summary><div>

| コンパイラ | 種類 | 平均 | コンパイラ | 種類 | 平均 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| GCC   | AVX,N:D | 255.81 | Clang | AVX,B:X | 251.04 |
| GCC   | GPR,S:D | 256.33 | Clang | GPR,N:X | 278.85 |
| GCC   | GPR,B:D | 256.61 | Clang | AVX,N:T | 279.02 |
| GCC   | GPR,N:D | 256.67 | Clang | GPR,B:T | 279.31 |
| GCC   | SSE,N:D | 257.35 | Clang | AVX,N:X | 279.51 |
| GCC   | AVX,B:D | 263.50 | Clang | SSE,N:T | 279.54 |
| GCC   | SSE,B:D | 263.78 | Clang | SSE,B:X | 279.67 |
| GCC   | AVX,B:X | 264.61 | Clang | GPR,N:T | 279.80 |
| GCC   | SSE,S:X | 303.37 | Clang | SSE,B:T | 279.89 |
| GCC   | SSE,B:X | 310.34 | Clang | AVX,B:T | 279.93 |
| GCC   | AVX,B:T | 311.64 | Clang | SSE,N:X | 280.01 |
| GCC   | AVX,N:T | 311.88 | Clang | GPR,B:X | 280.52 |
| GCC   | AVX,S:X | 312.03 | Clang | GPR,S:T | 280.55 |
| GCC   | SSE,B:T | 312.14 | Clang | AVX,S:X | 281.68 |
| GCC   | SSE,N:X | 313.00 | Clang | SSE,S:X | 281.90 |
| GCC   | AVX,N:X | 313.33 | Clang | AVX,S:D | 288.66 |
| GCC   | SSE,N:T | 314.00 | Clang | GPR,S:X | 292.19 |
| GCC   | SSE,S:D | 345.43 | Clang | SSE,S:D | 292.34 |
| GCC   | AVX,S:D | 345.45 | Clang | AVX,S:T | 296.60 |
| GCC   | GPR,B:X | 353.48 | Clang | GPR,S:D | 300.20 |
| GCC   | GPR,N:X | 353.93 | Clang | GPR,B:D | 300.45 |
| GCC   | GPR,S:T | 356.20 | Clang | AVX,N:D | 300.81 |
| GCC   | GPR,B:T | 356.67 | Clang | SSE,B:D | 300.86 |
| GCC   | GPR,N:T | 357.34 | Clang | SSE,N:D | 300.88 |
| GCC   | SSE,S:T | 358.28 | Clang | GPR,N:D | 301.08 |
| GCC   | AVX,S:T | 358.78 | Clang | AVX,B:D | 301.50 |
| GCC   | GPR,S:X | 363.57 | Clang | SSE,S:T | 302.26 |

</div></details>

---

手元の環境では、このような結果になりましたが、同一バイナリでも動作環境によって異なるので、使用する環境での確認が必要です。