// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "util/misc/reinterpret_bytes.h"

#include <string.h>

#include <algorithm>

#include "base/logging.h"

namespace crashpad {
namespace internal {

bool ReinterpretBytesImpl(const char* data,
                          size_t data_size,
                          char* dest,
                          size_t dest_size) {
  // Verify that any unused bytes from data are zero.
  // The unused bytes are at the start of the data buffer for big-endian and the
  // end of the buffer for little-endian.
  if (dest_size < data_size) {
    auto extra_bytes = data;
#if defined(ARCH_CPU_LITTLE_ENDIAN)
    extra_bytes += dest_size;
#endif  // ARCH_CPU_LITTLE_ENDIAN

    uint64_t zero = 0;
    size_t extra_bytes_size = data_size - dest_size;
    while (extra_bytes_size > 0) {
      size_t to_check = std::min(extra_bytes_size, sizeof(zero));
      if (memcmp(extra_bytes, &zero, to_check) != 0) {
        LOG(ERROR) << "information loss";
        return false;
      }
      extra_bytes += to_check;
      extra_bytes_size -= to_check;
    }
  }

  // Zero out the destination, in case it is larger than data.
  memset(dest, 0, dest_size);

#if defined(ARCH_CPU_LITTLE_ENDIAN)
  // Copy a prefix of data to a prefix of dest for little-endian
  memcpy(dest, data, std::min(dest_size, data_size));
#else
  // or the suffix of data to the suffix of dest for big-endian
  if (data_size >= dest_size) {
    memcpy(dest, data + data_size - dest_size, dest_size);
  } else {
    memcpy(dest + dest_size - data_size, data, data_size);
  }
#endif  // ARCH_CPU_LITTLE_ENDIAN
  return true;
}

}  // namespace internal
}  // namespace crashpad