/*
 * Copyright 2015 - 2025 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include <ox/std/defines.hpp>
#include <ox/std/types.hpp>
#include <ox/std/memops.hpp>

OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)

#ifndef OX_USE_STDLIB

#define ox_inhibit_loop_to_libcall __attribute__((__optimize__("-fno-tree-loop-distribute-patterns")))

extern "C" {

void *ox_inhibit_loop_to_libcall memcpy(void *dest, const void *src, std::size_t size) {
	return ox::memcpy(dest, src, size);
}

void *ox_inhibit_loop_to_libcall memmove(void *dest, const void *src, std::size_t size) {
	return ox::memcpy(dest, src, size);
}

void *ox_inhibit_loop_to_libcall memset(void *ptr, int val, std::size_t size) {
	return ox::memset(ptr, val, size);
}


int ox_inhibit_loop_to_libcall memcmp(const void *ptr1, const void *ptr2, std::size_t size) noexcept {
	int retval = 0;
	auto block1 = reinterpret_cast<const uint8_t*>(ptr1);
	auto block2 = reinterpret_cast<const uint8_t*>(ptr2);
	for (std::size_t i = 0; i < size; i++) {
		if (block1[i] < block2[i]) {
			retval = -1;
			break;
		} else if (block1[i] > block2[i]) {
			retval = 1;
			break;
		}
	}
	return retval;
}

}

#undef ox_inhibit_loop_to_libcall

#endif

namespace ox {

void *memmove(void *dest, const void *src, std::size_t size) noexcept {
	if constexpr(!ox::defines::UseStdLib) {
		auto srcBuf = static_cast<const char*>(src);
		auto dstBuf = static_cast<char*>(dest);
		for (std::size_t i = 0; i < size; ++i) {
			dstBuf[i] = static_cast<char>(srcBuf[i]);
		}
		return dest;
	} else {
		return ::memmove(dest, src, size);
	}
}

void *memset(void *ptr, int val, std::size_t size) noexcept {
	if constexpr(!ox::defines::UseStdLib) {
		auto buf = static_cast<uint8_t*>(ptr);
		for (std::size_t i = 0; i < size; ++i) {
			buf[i] = static_cast<uint8_t>(val);
		}
		return ptr;
	} else {
		return ::memset(ptr, val, size);
	}
}

void *memcpy(void *dest, const void *src, std::size_t size) noexcept {
	if constexpr(!ox::defines::UseStdLib) {
		auto srcBuf = static_cast<const char*>(src);
		auto dstBuf = static_cast<char*>(dest);
		for (std::size_t i = 0; i < size; ++i) {
			dstBuf[i] = static_cast<char>(srcBuf[i]);
		}
		return dest;
	} else {
		return ::memcpy(dest, src, size);
	}
}

int memcmp(const void *ptr1, const void *ptr2, std::size_t size) noexcept {
	return ::memcmp(ptr1, ptr2, size);
}

}

OX_CLANG_NOWARN_END
