//===-- Implementation of memmove -----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/string/memmove.h"

#include "src/__support/common.h"
#include "src/__support/integer_operations.h"
#include "src/string/memory_utils/memcpy_implementations.h"
#include <stddef.h> // size_t, ptrdiff_t

namespace __llvm_libc {

static inline void move_byte_forward(char *dest_m, const char *src_m,
                                     size_t count) {
  for (size_t offset = 0; count; --count, ++offset)
    dest_m[offset] = src_m[offset];
}

static inline void move_byte_backward(char *dest_m, const char *src_m,
                                      size_t count) {
  for (size_t offset = count - 1; count; --count, --offset)
    dest_m[offset] = src_m[offset];
}

LLVM_LIBC_FUNCTION(void *, memmove,
                   (void *dst, const void *src, size_t count)) {
  char *dest_c = reinterpret_cast<char *>(dst);
  const char *src_c = reinterpret_cast<const char *>(src);

  // If the distance between `src_c` and `dest_c` is equal to or greater
  // than `count` (integerAbs(src_c - dest_c) >= count), they would not overlap.
  // e.g.   greater     equal       overlapping
  //        [12345678]  [12345678]  [12345678]
  // src_c: [_ab_____]  [_ab_____]  [_ab_____]
  // dest_c:[_____yz_]  [___yz___]  [__yz____]

  // Call `memcpy` if `src_c` and `dest_c` do not overlap.
  if (__llvm_libc::integerAbs(src_c - dest_c) >=
      static_cast<ptrdiff_t>(count)) {
    inline_memcpy(dest_c, src_c, count);
    return dest_c;
  }

  // Overlapping cases.
  // If `dest_c` starts before `src_c` (dest_c < src_c), copy
  // forward(pointer add 1) from beginning to end.
  // If `dest_c` starts after `src_c` (dest_c > src_c), copy
  // backward(pointer add -1) from end to beginning.
  // If `dest_c` and `src_c` start at the same address (dest_c == src_c),
  // just return dest.
  // e.g.    forward      backward
  //                *->    <-*
  // src_c : [___abcde_]  [_abcde___]
  // dest_c: [_abc--___]  [___--cde_]

  // TODO: Optimize `move_byte_xxx(...)` functions.
  if (dest_c < src_c)
    move_byte_forward(dest_c, src_c, count);
  if (dest_c > src_c)
    move_byte_backward(dest_c, src_c, count);
  return dst;
}

} // namespace __llvm_libc
