text: /*
     +----------------------------------------------------------------------+
     | HipHop for PHP                                                       |
     +----------------------------------------------------------------------+
     | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com)  |
     | Copyright (c) 1997-2010 The PHP Group                                |
     +----------------------------------------------------------------------+
     | This source file is subject to version 3.01 of the PHP license,      |
     | that is bundled with this package in the file LICENSE, and is        |
     | available through the world-wide-web at the following url:           |
     | http://www.php.net/license/3_01.txt                                  |
     | If you did not receive a copy of the PHP license and are unable to   |
     | obtain it through the world-wide-web, please send a note to          |
     | license@php.net so we can mail you a copy immediately.               |
     +----------------------------------------------------------------------+
  */
  
  #include "hphp/runtime/ext/hash/hash_tiger.h"
  #include "hphp/runtime/ext/hash/php_hash_tiger_tables.h"
  
  #if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
  # if defined(__LITTLE_ENDIAN__)
  #  undef WORDS_BIGENDIAN
  # else
  #  if defined(__BIG_ENDIAN__)
  #   define WORDS_BIGENDIAN
  #  endif
  # endif
  #endif
  
  namespace HPHP {
  ///////////////////////////////////////////////////////////////////////////////
  
  typedef struct {
    uint64_t state[3];
    uint64_t passed;
    unsigned char passes:1;
    unsigned char length:7;
    union {
      unsigned char buffer[64];
      uint64_t      buffer_u64[8];
    };
  } PHP_TIGER_CTX;
  
  hash_tiger::hash_tiger(bool tiger3, int digest, bool invert /*= false */)
    : HashEngine(digest / 8, 64, sizeof(PHP_TIGER_CTX)),
      m_tiger3(tiger3), m_digest(digest), m_invert(invert) {
  }
  
  #define save_abc \
    aa = a;        \
    bb = b;        \
    cc = c;
  
  #define round(a,b,c,x,mul)                                              \
    c ^= x;                                                               \
    a -= t1[(unsigned char)(c)] ^                                         \
    t2[(unsigned char)(((unsigned int)(c))>>(2*8))] ^                     \
    t3[(unsigned char)((c)>>(4*8))] ^                                     \
    t4[(unsigned char)(((unsigned int)((c)>>(4*8)))>>(2*8))] ;            \
    b += t4[(unsigned char)(((unsigned int)(c))>>(1*8))] ^                \
    t3[(unsigned char)(((unsigned int)(c))>>(3*8))] ^                     \
    t2[(unsigned char)(((unsigned int)((c)>>(4*8)))>>(1*8))] ^            \
    t1[(unsigned char)(((unsigned int)((c)>>(4*8)))>>(3*8))];             \
    b *= mul;
  
  #define pass(a,b,c,mul)      \
    round(a,b,c,x0,mul);       \
    round(b,c,a,x1,mul);       \
    round(c,a,b,x2,mul);       \
    round(a,b,c,x3,mul);       \
    round(b,c,a,x4,mul);       \
    round(c,a,b,x5,mul);       \
    round(a,b,c,x6,mul);       \
    round(b,c,a,x7,mul);
  
  #define key_schedule                        \
    x0 -= x7 ^ L64(0xA5A5A5A5A5A5A5A5);       \
    x1 ^= x0;                                 \
    x2 += x1;                                 \
    x3 -= x2 ^ ((~x1)<<19);                   \
    x4 ^= x3;                                 \
    x5 += x4;                                 \
    x6 -= x5 ^ ((~x4)>>23);                   \
    x7 ^= x6;                                 \
    x0 += x7;                                 \
    x1 -= x0 ^ ((~x7)<<19);                   \
    x2 ^= x1;                                 \
    x3 += x2;                                 \
    x4 -= x3 ^ ((~x2)>>23);                   \
    x5 ^= x4;                                 \
    x6 += x5;                                 \
    x7 -= x6 ^ L64(0x0123456789ABCDEF);
  
  #define feedforward                             \
    a ^= aa;                                      \
    b -= bb;                                      \
    c += cc;
  
  #define compress(passes)                            \
    save_abc;                                         \
    pass(a,b,c,5);                                    \
    key_schedule;                                     \
    pass(c,a,b,7);                                    \
    key_schedule;                                     \
    pass(b,c,a,9);                                    \
    for(pass_no=0; pass_no<passes; pass_no++) {       \
      key_schedule;                                   \
      pass(a,b,c,9);                                  \
      tmpa=a; a=c; c=b; b=tmpa;                       \
    }                                                 \
    feedforward;
  
  #define split_ex(str)                               \
    x0=str[0]; x1=str[1]; x2=str[2]; x3=str[3];       \
    x4=str[4]; x5=str[5]; x6=str[6]; x7=str[7];
  #ifdef WORDS_BIGENDIAN
  # define split(str)                       \
    {                                             \
      int i;                                      \
      uint64_t tmp[8];                       \
                                                  \
      for (i = 0; i < 64; ++i) {                                          \
        ((unsigned char *) tmp)[i^7] = ((unsigned char *) str)[i];        \
      }                                                                   \
      split_ex(tmp);                                                      \
    }
  #else
  # define split split_ex
  #endif
  
  #define tiger_compress(passes, str, state)                              \
    {                                                                     \
      register uint64_t a, b, c, tmpa, x0, x1, x2, x3, x4, x5, x6, x7; \
      uint64_t aa, bb, cc;                                           \
      int pass_no;                                                        \
                                                                          \
      a = state[0];                                                       \
      b = state[1];                                                       \
      c = state[2];                                                       \
                                                                          \
      split(str);                                                         \
                                                                          \
      compress(passes);                                                   \
                                                                          \
      state[0] = a;                                                       \
      state[1] = b;                                                       \
      state[2] = c;                                                       \
    }
  
  static inline void TigerFinalize(PHP_TIGER_CTX *context) {
    context->passed += (uint64_t) context->length << 3;
  
    context->buffer[context->length++] = 0x1;
    if (context->length % 8) {
      memset(&context->buffer[context->length], 0, 8-context->length%8);
      context->length += 8-context->length%8;
    }
  
    if (context->length > 56) {
      memset(&context->buffer[context->length], 0, 64 - context->length);
      tiger_compress(context->passes, context->buffer_u64, context->state);
      memset(context->buffer, 0, 56);
    } else {
      memset(&context->buffer[context->length], 0, 56 - context->length);
    }
  
  #ifndef WORDS_BIGENDIAN
    memcpy(&context->buffer[56], &context->passed, sizeof(uint64_t));
  #else
    context->buffer[56] = (unsigned char) (context->passed & 0xff);
    context->buffer[57] = (unsigned char) ((context->passed >> 8) & 0xff);
    context->buffer[58] = (unsigned char) ((context->passed >> 16) & 0xff);
    context->buffer[59] = (unsigned char) ((context->passed >> 24) & 0xff);
    context->buffer[60] = (unsigned char) ((context->passed >> 32) & 0xff);
    context->buffer[61] = (unsigned char) ((context->passed >> 40) & 0xff);
    context->buffer[62] = (unsigned char) ((context->passed >> 48) & 0xff);
    context->buffer[63] = (unsigned char) ((context->passed >> 56) & 0xff);
  #endif
    tiger_compress(context->passes, context->buffer_u64, context->state);
  }
  
  void hash_tiger::hash_init(void *context_) {
    PHP_TIGER_CTX *context = (PHP_TIGER_CTX*)context_;
    memset(context, 0, sizeof(*context));
    if (!m_tiger3) {
      context->passes = 1;
    }
    context->state[0] = L64(0x0123456789ABCDEF);
    context->state[1] = L64(0xFEDCBA9876543210);
    context->state[2] = L64(0xF096A5B4C3B2E187);
  }
  
  void hash_tiger::hash_update(void *context_, const unsigned char *input,
                               unsigned int len) {
    PHP_TIGER_CTX *context = (PHP_TIGER_CTX*)context_;
  
    if (context->length + len < 64) {
      memcpy(&context->buffer[context->length], input, len);
      context->length += len;
    } else {
      size_t i = 0, r = (context->length + len) % 64;
  
      if (context->length) {
        i = 64 - context->length;
        memcpy(&context->buffer[context->length], input, i);
        tiger_compress(context->passes, context->buffer_u64, context->state);
        memset(context->buffer, 0, 64);
        context->passed += 512;
      }
  
      for (; i + 64 <= len; i += 64) {
        memcpy(context->buffer, &input[i], 64);
        tiger_compress(context->passes, context->buffer_u64, context->state);
        context->passed += 512;
      }
      memset(&context->buffer[r], 0, 64-r);
      memcpy(context->buffer, &input[i], r);
      context->length = r;
    }
  }
  
  void hash_tiger::hash_final(unsigned char *digest, void *context_) {
    PHP_TIGER_CTX *context = (PHP_TIGER_CTX*)context_;
  
    TigerFinalize(context);
  
    for(int i = 0; i < (m_digest >> 3); ++i) {
      auto b = i % 8;
      digest[i] = (unsigned char) ((context->state[i >> 3] >>
                                      ((m_invert ? (7 - b) : b) << 3))
                                      & 0xff);
    }
  
    memset(context, 0, sizeof(*context));
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  }
  