/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010-2015 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_fnv1.h"

#define PHP_FNV1_32_INIT ((uint32_t)0x811c9dc5)
#define PHP_FNV_32_PRIME ((uint32_t)0x01000193)

#define PHP_FNV1_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
#define PHP_FNV_64_PRIME ((uint64_t)0x100000001b3ULL)


namespace HPHP {
///////////////////////////////////////////////////////////////////////////////

typedef struct {
  uint32_t state;
} PHP_FNV132_CTX;

typedef struct {
  uint64_t state;
} PHP_FNV164_CTX;

hash_fnv132::hash_fnv132(bool a)
  : HashEngine(4, 4, sizeof(PHP_FNV132_CTX)), m_a(a) {
}

hash_fnv164::hash_fnv164(bool a)
  : HashEngine(8, 4, sizeof(PHP_FNV164_CTX)), m_a(a) {
}

void hash_fnv132::hash_init(void *context_) {
  PHP_FNV132_CTX *context = (PHP_FNV132_CTX*)context_;
  context->state = PHP_FNV1_32_INIT;
}

void hash_fnv164::hash_init(void *context_) {
  PHP_FNV164_CTX *context = (PHP_FNV164_CTX*)context_;
  context->state = PHP_FNV1_64_INIT;
}

void hash_fnv132::hash_update(void *context_, const unsigned char *input,
                             unsigned int len) {
  PHP_FNV132_CTX *context = (PHP_FNV132_CTX*)context_;
  const unsigned char *bp = (unsigned char *)input;   /* start of buffer */
  const unsigned char *be = bp + len;      /* beyond end of buffer */

  /*
   * FNV-1 hash each octet in the buffer
   */
  while (bp < be) {
    if (m_a == false) {
      /* multiply by the 32 bit FNV magic prime mod 2^32 */
      context->state *= PHP_FNV_32_PRIME;

      /* xor the bottom with the current octet */
      context->state ^= (uint32_t)*bp++;
    } else {
      /* xor the bottom with the current octet */
      context->state ^= (uint32_t)*bp++;

      /* multiply by the 32 bit FNV magic prime mod 2^32 */
      context->state *= PHP_FNV_32_PRIME;
    }
  }
}

void hash_fnv164::hash_update(void *context_, const unsigned char *input,
                             unsigned int len) {
  PHP_FNV164_CTX *context = (PHP_FNV164_CTX*)context_;
  unsigned char *bp = (unsigned char *)input;   /* start of buffer */
  unsigned char *be = bp + len;      /* beyond end of buffer */

  /*
   * FNV-1 hash each octet of the buffer
   */
  while (bp < be) {
    if (m_a == false) {
      /* multiply by the 64 bit FNV magic prime mod 2^64 */
      context->state *= PHP_FNV_64_PRIME;

      /* xor the bottom with the current octet */
      context->state ^= (uint64_t)*bp++;
    } else {
      /* xor the bottom with the current octet */
      context->state ^= (uint64_t)*bp++;

      /* multiply by the 64 bit FNV magic prime mod 2^64 */
      context->state *= PHP_FNV_64_PRIME;
    }
  }
}

void hash_fnv132::hash_final(unsigned char *digest, void *context_) {
  PHP_FNV132_CTX *context = (PHP_FNV132_CTX*)context_;
#ifdef WORDS_BIGENDIAN
    memcpy(digest, &context->state, 4);
#else
    int i = 0;
    unsigned char *c = (unsigned char *) &context->state;

    for (i = 0; i < 4; i++) {
        digest[i] = c[3 - i];
    }
#endif
}

void hash_fnv164::hash_final(unsigned char *digest, void *context_) {
  PHP_FNV164_CTX *context = (PHP_FNV164_CTX*)context_;
#ifdef WORDS_BIGENDIAN
    memcpy(digest, &context->state, 8);
#else
    int i = 0;
    unsigned char *c = (unsigned char *) &context->state;

    for (i = 0; i < 8; i++) {
        digest[i] = c[7 - i];
    }
#endif
}

///////////////////////////////////////////////////////////////////////////////
}
