/* Copyright 2016 Samsung Electronics Co., Ltd.
 *
 * 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 "scl/memory.h"

#if !defined(SCL_INSIDE_CPP) && defined(__cplusplus)
#	error .c file must be compiled only as C
#endif

/* TODO optimize */

#if SCL_USER_SCL_STD_FUNCTIONS || !SCL_STD_FUNCTIONS

	extern int scl_inner_use_clean_small(void *mem, scl_size_t size) {
		size /= sizeof(scl_umin_t);
		while (size > 0) {
			--size;
			((scl_umin_t *)mem)[size] = 0;
		}
		return 0;
	}

	extern int scl_inner_use_clean(void *mem, scl_size_t size) {
		typedef scl_item_t volatile item;
		scl_ptr_t source, align, tail, limit;
		scl_iter_t i;

		SCL_ASSERT((size_t)size >= 2 * sizeof(scl_item_t));

		source = (scl_ptr_t)mem;

		align = (source + (sizeof(item) - 1)) / sizeof(item) * sizeof(item);
		limit = source + size;

		tail = (limit - (sizeof(item) - 1)) / sizeof(item) * sizeof(item);
		SCL_ASSERT(tail >= align);
		source = (source + (sizeof(scl_umin_t) - 1))
		           / sizeof(scl_umin_t) * sizeof(scl_umin_t);
		while (source < align) {
			*(scl_umin_t *)source = 0;
			source += sizeof(scl_umin_t);
		}

		i = (scl_iter_t)((tail - align) / (sizeof(item) * 8));
		while (i > 0) {
			((item *)align)[0] = 0;
			((item *)align)[1] = 0;
			((item *)align)[2] = 0;
			((item *)align)[3] = 0;
			((item *)align)[4] = 0;
			((item *)align)[5] = 0;
			((item *)align)[6] = 0;
			((item *)align)[7] = 0;
			align += 8 * sizeof(item);
			--i;
		}
		while (align < tail) {
			*(item *)align = 0;
			align += sizeof(item);
		}

		while (tail < limit) {
			*(scl_umin_t *)tail = 0;
			tail += sizeof(scl_umin_t);
		}
		return 0;
	}

	extern int scl_inner_use_fill_small(void *mem, scl_int_t byte, scl_size_t size) {
		size /= sizeof(scl_umin_t);
		while (size > 0) {
			--size;
			((scl_umin_t *)mem)[size] = byte;
		}
		return 0;
	}

	extern int scl_inner_use_fill(void *mem, scl_int_t byte, scl_size_t size) {
		typedef scl_item_t volatile item;
		scl_ptr_t source, align, tail, limit;
		scl_iter_t i;
		scl_item_t fill;

		SCL_ASSERT((size_t)size >= 2 * sizeof(scl_item_t));

		source = (scl_ptr_t)mem;

		align = (source + (sizeof(item) - 1)) / sizeof(item) * sizeof(item);
		limit = source + size;

		tail = (limit - (sizeof(item) - 1)) / sizeof(item) * sizeof(item);
		SCL_ASSERT(tail >= align);
		source = (source + (sizeof(scl_umin_t) - 1))
		           / sizeof(scl_umin_t) * sizeof(scl_umin_t);
		while (source < align) {
			*(scl_umin_t *)source = byte;
			source += sizeof(scl_umin_t);
		}

		for (i = 0; i < sizeof(fill); ++i) {
			((scl_umin_t *)&fill)[i] = byte;
		}

		i = (scl_iter_t)((tail - align) / (sizeof(item) * 8));
		while (i > 0) {
			((item *)align)[0] = fill;
			((item *)align)[1] = fill;
			((item *)align)[2] = fill;
			((item *)align)[3] = fill;
			((item *)align)[4] = fill;
			((item *)align)[5] = fill;
			((item *)align)[6] = fill;
			((item *)align)[7] = fill;
			align += 8 * sizeof(item);
			--i;
		}
		while (align < tail) {
			*(item *)align = fill;
			align += sizeof(item);
		}

		while (tail < limit) {
			*(scl_umin_t *)tail = byte;
			tail += sizeof(scl_umin_t);
		}
		return 0;
	}


	extern int scl_inner_use_copy(scl_ptr_t dest, scl_ptr_t src, scl_size_t size) {
		while (size > 0) {
			size -= sizeof(scl_umin_t);
			((scl_umin_t *)dest)[size] = ((scl_umin_t const *)src)[size];
		}
		return 0;
	}

	extern int scl_inner_use_move_up(scl_ptr_t dest, scl_ptr_t src, scl_size_t size) {
		scl_iter_t i;
		size /= sizeof(scl_umin_t);
		i = 0;
		while (i < (size_t)size) {
			((scl_umin_t *)dest)[i] = ((scl_umin_t const *)src)[i];
			++i;
		}
		return 0;
	}

	extern int scl_inner_use_move_down(scl_ptr_t dest, scl_ptr_t src, scl_size_t size) {
		while (size > 0) {
			size -= sizeof(scl_umin_t);
			((scl_umin_t *)dest)[size] = ((scl_umin_t const *)src)[size];
		}
		return 0;
	}

#elif !defined(__GNUC__) || (__GNUC__ < 4)

	extern int scl_inner_use_clean(void *mem, scl_size_t size) {
		SCL_ABORT();
		return 0;
	}

	extern int scl_inner_use_copy(scl_ptr_t dest, scl_ptr_t src, scl_size_t size) {
		SCL_ABORT();
		return 0;
	}

	extern int scl_inner_use_move_up(scl_ptr_t dest, scl_ptr_t src, scl_size_t size) {
		SCL_ABORT();
		return 0;
	}

	extern int scl_inner_use_move_down(scl_ptr_t dest, scl_ptr_t src, scl_size_t size) {
		SCL_ABORT();
		return 0;
	}

#endif

extern int scl_inner_use_memcmp(void const *m1, void const *m2, scl_size_t n) {
	char unsigned volatile *u1, *u2;
	scl_size_t i;
	int cmp;

	u1 = (char unsigned volatile *)m1;
	u2 = (char unsigned volatile *)m2;
	i = 0;
	cmp = 1;
	do {
		cmp += (cmp % 2) * ((u1[i] > u2[i]) - (u1[i] < u2[i]));
		++i;
	} while (i < n);
	return cmp - 1;
}
