/* 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 clean_scl_std_functions = 0;

#define SCL_STD_FUNCTIONS clean_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, 0, sizeof(buf));
#	elif !defined(CLEAN_NO)
		scl_clean(buf, sizeof(buf));
#	endif
}

static void check_clean(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(0 ==
	   (int)(buf[0] == 's')
	 + (int)(buf[1] == 'e')
	 + (int)(buf[2] == 'c')
	 + (int)(buf[3] == 'r')
	 + (int)(buf[4] == 'e')
	 + (int)(buf[5] == 't')
	);
}

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] = (char unsigned)(1 + i % 255);
	}
	for (i = 0; i < (int)(sizeof(buf) / sizeof(buf[0])) - 1; ++i) {
		scl_clean(buf + ((sizeof(buf) / sizeof(buf[0]) - i) / 2) * sizeof(buf[0]), i * sizeof(buf[0]));
		j = 0;
		while (j < (sizeof(buf) / sizeof(buf[0]) - i) / 2) {
			SCL_ASSERT(buf[j] == (1 + j % 255));
			++j;
		}
		c = 2 + i / 10;
		while (j < (unsigned)(sizeof(buf) / sizeof(buf[0]) - i) / 2 + i) {
			c -= (buf[j] == j);
			SCL_ASSERT(c > 0);
			++j;
		}
		while (j < (sizeof(buf) / sizeof(buf[0]))) {
			SCL_ASSERT(buf[j] == (1 + j % 255));
			++j;
		}
	}
}

static void do_tests(void) {
	check_clean();
	check_integrity();
}

#if defined(START_FUNC) && (START_FUNC == 1)
#	undef START_FUNC
#	define START_FUNC test_clean
#endif

#if defined(START_FUNC)
	extern void START_FUNC(void) {
		do_tests();
		clean_scl_std_functions = 1 - clean_scl_std_functions;
		do_tests();
	}
#else
	extern int main(int argc, char const **argv) {
		do_tests();
		clean_scl_std_functions = 1 - clean_scl_std_functions;
		do_tests();
		return 0;
	}
#endif
