/*
 * Copyright 2008-2020 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements.
 *
 * 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 <aerospike/as_random.h>
#include <citrusleaf/cf_random.h>
#include <stddef.h>

/******************************************************************************
 * Static Variables
 *****************************************************************************/

static const uint8_t alphanum[] =
	"0123456789"
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	"abcdefghijklmnopqrstuvwxyz";

static int alphanum_len = sizeof(alphanum) - 1;

/******************************************************************************
 * Thread Local Variables
 *****************************************************************************/

__thread as_random as_rand;

/******************************************************************************
 * Functions
 *****************************************************************************/

void
as_random_init(as_random* random)
{
	// Keep this method in C file, so cf_random.h can stay a private header.
	// Initial seeds must be unique across all processes and threads.
	// Do not use a counter as a seed.
	random->seed0 = cf_get_rand64();
	random->seed1 = cf_get_rand64();
	random->initialized = true;
}

as_random*
as_random_instance()
{
	as_random* random = &as_rand;

	if (!random->initialized) {
		as_random_init(random);
	}
	return random;
}

void
as_random_next_bytes(as_random* random, uint8_t* bytes, uint32_t len)
{
	uint8_t* p = bytes;
	uint8_t* end = bytes + len;
	
	while (p + sizeof(uint64_t) <= end) {
		// Append full 8 bytes
		*(uint64_t*)p = as_random_next_uint64(random);
		p += sizeof(uint64_t);
	}

	if (p < end) {
		// Append partial bytes.
		uint8_t tmp[sizeof(uint64_t)];
		uint8_t* t = tmp;
		*(uint64_t*)t = as_random_next_uint64(random);
		
		while (p < end) {
			*p++ = *t++;
		}
	}
}

void
as_random_next_str(as_random* random, char* str, uint32_t len)
{
	uint8_t* p = (uint8_t*)str;
	as_random_next_bytes(random, p, len);
	
	for (uint32_t i = 0; i < len; i++) {
		p[i] = alphanum[p[i] % alphanum_len];
	}
	p[len] = 0;
}
