/* 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 <limits.h>
#include <stddef.h>

int memset_scl_std_functions = 0;

#define SCL_STD_FUNCTIONS memset_scl_std_functions
#include "scl/memory.h"

#if defined(SCL_USE_ASSERT_H) && (SCL_USE_ASSERT_H == 0)
#	include <stdlib.h>
#	if defined(EXIT_FAILURE)
#		define SCL_ASSERT(assertion) { \
			if (!(assertion)) \
				exit(EXIT_FAILURE); \
		}
#	else
#		define SCL_ASSERT(assertion) { \
			if (!(assertion)) \
				exit(1); \
		}
#	endif
#else
#	include <assert.h>
#	define SCL_ASSERT(assertion) assert(assertion)
#endif

static unsigned long low, high;

static long sum = 0;

static void set_secret(void) {
	char buf[518];
	int i;

	low = (size_t)(buf + 256) % ULONG_MAX;
	high = (size_t)(buf + 256) / ULONG_MAX;

	buf[0] = 1;
	buf[sizeof(buf) / sizeof(buf[0]) - 1] = 2;

	buf[256] = 's';
	buf[257] = 'e';
	buf[258] = 'c';
	buf[259] = 'r';
	buf[260] = 'e';
	buf[261] = 't';
	for (i = 0; i < 6; ++i) {
		sum += buf[256 + i];
	}
	sum += buf[0] + buf[sizeof(buf) / sizeof(buf[0]) - 1];
#	if defined(CLEAN_MEMSET)
		memset(buf, 111, sizeof(buf));
#	elif !defined(CLEAN_NO)
		scl_memset(buf, 111, sizeof(buf));
#	endif
}

static void check_memset(void) {
	char buf[6];
	char *p;
	set_secret();
	p = (char *)(low + (size_t)high * ULONG_MAX);

	buf[0] = p[0];
	buf[1] = p[1];
	buf[2] = p[2];
	buf[3] = p[3];
	buf[4] = p[4];
	buf[5] = p[5];
	SCL_ASSERT(6 ==
	   (int)(buf[0] == 111)
	 + (int)(buf[1] == 111)
	 + (int)(buf[2] == 111)
	 + (int)(buf[3] == 111)
	 + (int)(buf[4] == 111)
	 + (int)(buf[5] == 111)
	);
}

static void check_integrity(void) {
	char unsigned buf[1024];
	unsigned volatile i, j, c;
	for (i = 0; i < (int)(sizeof(buf) / sizeof(buf[0])); ++i) {
		buf[i] = 0;
	}
	for (i = 0; i < (int)(sizeof(buf) / sizeof(buf[0])) - 1; ++i) {
		scl_memset(buf + ((sizeof(buf) / sizeof(buf[0]) - i) / 2) * sizeof(buf[0]), 1 + i % 255, i * sizeof(buf[0]));
		j = 0;
		while (j < (sizeof(buf) / sizeof(buf[0]) - i) / 2) {
			SCL_ASSERT(buf[j] == 0);
			++j;
		}
		c = 2 + i / 10;
		while (j < (unsigned)(sizeof(buf) / sizeof(buf[0]) - i) / 2 + i) {
			c -= (buf[j] == j);
			SCL_ASSERT(buf[j] == 1 + i % 255);
			++j;
		}
		while (j < (sizeof(buf) / sizeof(buf[0]))) {
			SCL_ASSERT(buf[j] == 0);
			++j;
		}
	}
}

static void do_tests(void) {
	check_memset();
	check_integrity();
}

#if defined(START_FUNC) && (START_FUNC == 1)
#	undef START_FUNC
#	define START_FUNC test_scl_memset
#endif

#if defined(START_FUNC)
	extern void START_FUNC(void) {
		do_tests();
		memset_scl_std_functions = 1 - memset_scl_std_functions;
		do_tests();
	}
#else
	extern int main(int argc, char const **argv) {
		do_tests();
		memset_scl_std_functions = 1 - memset_scl_std_functions;
		do_tests();
		return 0;
	}
#endif
