// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software 
 * Foundation.  See file COPYING.
 * 
 */
#ifndef CEPH_INLINE_MEMORY_H
#define CEPH_INLINE_MEMORY_H

#if defined(__GNUC__)

// optimize for the common case, which is very small copies
static inline void *maybe_inline_memcpy(void *dest, const void *src, size_t l,
				       size_t inline_len)
  __attribute__((always_inline));

void *maybe_inline_memcpy(void *dest, const void *src, size_t l,
			 size_t inline_len)
{
  if (l > inline_len) {
    return memcpy(dest, src, l);
  }
  switch (l) {
  case 8:
    return __builtin_memcpy(dest, src, 8);
  case 4:
    return __builtin_memcpy(dest, src, 4);
  case 3:
    return __builtin_memcpy(dest, src, 3);
  case 2:
    return __builtin_memcpy(dest, src, 2);
  case 1:
    return __builtin_memcpy(dest, src, 1);
  default:
    int cursor = 0;
    while (l >= sizeof(uint64_t)) {
      __builtin_memcpy((char*)dest + cursor, (char*)src + cursor,
		       sizeof(uint64_t));
      cursor += sizeof(uint64_t);
      l -= sizeof(uint64_t);
    }
    while (l >= sizeof(uint32_t)) {
      __builtin_memcpy((char*)dest + cursor, (char*)src + cursor,
		       sizeof(uint32_t));
      cursor += sizeof(uint32_t);
      l -= sizeof(uint32_t);
    }
    while (l > 0) {
      *((char*)dest + cursor) = *((char*)src + cursor);
      cursor++;
      l--;
    }
  }
  return dest;
}

#else

#define maybe_inline_memcpy(d, s, l, x) memcpy(d, s, l)

#endif


#if defined(__GNUC__) && defined(__x86_64__)

typedef unsigned uint128_t __attribute__ ((mode (TI)));

static inline bool mem_is_zero(const char *data, size_t len)
  __attribute__((always_inline));

bool mem_is_zero(const char *data, size_t len)
{
  // we do have XMM registers in x86-64, so if we need to check at least
  // 16 bytes, make use of them
  if (len / sizeof(uint128_t) > 0) {
    // align data pointer to 16 bytes, otherwise it'll segfault due to bug
    // in (at least some) GCC versions (using MOVAPS instead of MOVUPS).
    // check up to 15 first bytes while at it.
    while (((unsigned long long)data) & 15) {
      if (*(uint8_t*)data != 0) {
	return false;
      }
      data += sizeof(uint8_t);
      --len;
    }

    const char* data_start = data;
    const char* max128 = data + (len / sizeof(uint128_t))*sizeof(uint128_t);

    while (data < max128) {
      if (*(uint128_t*)data != 0) {
	return false;
      }
      data += sizeof(uint128_t);
    }
    len -= (data - data_start);
  }

  const char* max = data + len;
  const char* max32 = data + (len / sizeof(uint32_t))*sizeof(uint32_t);
  while (data < max32) {
    if (*(uint32_t*)data != 0) {
      return false;
    }
    data += sizeof(uint32_t);
  }
  while (data < max) {
    if (*(uint8_t*)data != 0) {
      return false;
    }
    data += sizeof(uint8_t);
  }
  return true;
}

#else  // gcc and x86_64

static inline bool mem_is_zero(const char *data, size_t len) {
  const char *end = data + len;
  const char* end64 = data + (len / sizeof(uint64_t))*sizeof(uint64_t);

  while (data < end64) {
    if (*(uint64_t*)data != 0) {
      return false;
    }
    data += sizeof(uint64_t);
  }

  while (data < end) {
    if (*data != 0) {
      return false;
    }
    ++data;
  }
  return true;
}

#endif  // !x86_64

#endif
