/*
 * Based on John the Ripper and modified to integrate with aircrack
 *
 * 	John the Ripper copyright and license.
 *
 * John the Ripper password cracker,
 * Copyright (c) 1996-2013 by Solar Designer.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * As a special exception to the GNU General Public License terms,
 * permission is hereby granted to link the code of this program, with or
 * without modification, with any version of the OpenSSL library and/or any
 * version of unRAR, and to distribute such linked combinations.  You must
 * obey the GNU GPL in all respects for all of the code used other than
 * OpenSSL and unRAR.  If you modify this program, you may extend this
 * exception to your version of the program, but you are not obligated to
 * do so.  (In other words, you may release your derived work under pure
 * GNU GPL version 2 or later as published by the FSF.)
 *
 * (This exception from the GNU GPL is not required for the core tree of
 * John the Ripper, but arguably it is required for -jumbo.)
 *
 * 	Relaxed terms for certain components.
 *
 * In addition or alternatively to the license above, many components are
 * available to you under more relaxed terms (most commonly under cut-down
 * BSD license) as specified in the corresponding source files.
 *
 * For more information on John the Ripper licensing please visit:
 *
 * http://www.openwall.com/john/doc/LICENSE.shtml
 *
 * This file is part of John the Ripper password cracker,
 * Copyright (c) 1996-98,2003,2010-2012 by Solar Designer
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted.
 *
 * There's ABSOLUTELY NO WARRANTY, express or implied.
 */

/*
 * Memory allocation routines.
 */

#ifndef _MEMORY_H
#define _MEMORY_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <aircrack-ng/ce-wpa/arch.h>

#if __AVX512F__
#define SIMD_COEF_32 16
#define SIMD_COEF_64 8
#elif __AVX2__
#define SIMD_COEF_32 8
#define SIMD_COEF_64 4
#elif __SSE2__
#define SIMD_COEF_32 4
#define SIMD_COEF_64 2
#endif

/*
 * Standard alignments for mem_alloc_tiny().
 */
#define MEM_ALIGN_NONE 1
#define MEM_ALIGN_WORD ARCH_SIZE
/*
 * These are hopefully suitable guesses.  They are right for only a subset of
 * the architectures/CPUs we support, yet our use of them does not require that
 * they be entirely correct.
 */
#ifdef _MSC_VER
#define MEM_ALIGN_CACHE 64
#else
#define MEM_ALIGN_CACHE (ARCH_SIZE * 8)
#endif
#define MEM_ALIGN_PAGE 0x1000

/*
 * SIMD buffers need to be aligned to register size
 */
#if SIMD_COEF_32
#ifdef _MSC_VER
#define MEM_ALIGN_SIMD 16
#else
#define MEM_ALIGN_SIMD (SIMD_COEF_32 * 4)
#endif
#else
#define MEM_ALIGN_SIMD (16)
#endif

/*
 * Block size used by mem_alloc_tiny().
 */
#define MEM_ALLOC_SIZE 0x10000

/*
 * Use mem_alloc() instead of allocating a new block in mem_alloc_tiny()
 * if more than MEM_ALLOC_MAX_WASTE bytes would be lost.
 * This shouldn't be set too small, or mem_alloc_tiny() will keep calling
 * mem_alloc() for many allocations in a row, which might end up wasting even
 * more memory to malloc() overhead.
 */
#define MEM_ALLOC_MAX_WASTE 0xff

/*
 * Memory saving level, setting this high enough disables alignments (if the
 * architecture allows).
 */
extern unsigned int mem_saving_level;

/*
 * Allocates size bytes and returns a pointer to the allocated memory.
 * If an error occurs, the function does not return.
 */
extern void * mem_alloc_func(size_t size);
/*
 * this version same as mem_alloc, but initialized the memory
 * to NULL bytes, like CALLOC(3) function does
 */
extern void * mem_calloc_func(size_t count, size_t size);

#define mem_alloc(a) mem_alloc_func(a)
#define mem_calloc(a, b) mem_calloc_func(a, b)
#define mem_alloc_tiny(a, b) mem_alloc_tiny_func(a, b)
#define mem_calloc_tiny(a, b) mem_calloc_tiny_func(a, b)
#define mem_alloc_copy(a, b, c) mem_alloc_copy_func(a, b, c)
#define str_alloc_copy(a) str_alloc_copy_func(a)
#define mem_alloc_align(a, b) mem_alloc_align_func(a, b)
#define mem_calloc_align(a, b, c) mem_calloc_align_func(a, b, c)

/* These allow alignment and are wrappers to system-specific functions */
void * mem_alloc_align_func(size_t size, size_t align);

void * mem_calloc_align_func(size_t count, size_t size, size_t align);

/*
 * Frees memory allocated with mem_alloc() and sets the pointer to NULL.
 * Does nothing if the pointer is already NULL.
 */
#undef MEM_FREE

#ifdef _MSC_VER
#define strdup(a) strdup_MSVC(a)
char * strdup_MSVC(const char * str);
#define MEM_FREE(ptr)                                                          \
	{                                                                          \
		if ((ptr))                                                             \
		{                                                                      \
			_aligned_free((ptr));                                              \
			(ptr) = NULL;                                                      \
		}                                                                      \
	}

#else
#define MEM_FREE(ptr)                                                          \
	{                                                                          \
		if ((ptr))                                                             \
		{                                                                      \
			free((ptr));                                                       \
			(ptr) = NULL;                                                      \
		}                                                                      \
	}
#endif

/*
 * Similar to the above function, except the memory can't be freed.
 * This one is used to reduce the overhead.
 */
extern void * mem_alloc_tiny_func(size_t size, size_t align);

/*
 * this version same as mem_alloc_tiny, but initialized the memory
 * to NULL bytes, like CALLOC(3) function does
 */
extern void * mem_calloc_tiny_func(size_t size, size_t align);

/*
 * Uses mem_alloc_tiny() to allocate the memory, and copies src in there.
 */
extern void * mem_alloc_copy_func(void * src, size_t size, size_t align);

/*
 * Similar to the above function, but for ASCIIZ strings.
 */
extern char * str_alloc_copy_func(char * src);

/*
 * This will 'cleanup' the memory allocated by mem_alloc_tiny().  All
 * of that memory was 'blindly' allocated, and not freed up during
 * the run of john.  Now, it is 'cleaned' up.
 */
extern void cleanup_tiny_memory(void);

void dump_text(void * in, int len);
void dump_stuff(void * x, unsigned int size);
void dump_stuff_msg(const void * msg, void * x, unsigned int size);
void dump_stuff_noeol(void * x, unsigned int size);
void dump_stuff_msg_sepline(const void * msg, void * x, unsigned int size);
void dump_stuff_be(void * x, unsigned int size);
void dump_stuff_be_msg(const void * msg, void * x, unsigned int size);
void dump_stuff_be_noeol(void * x, unsigned int size);
void dump_stuff_be_msg_sepline(const void * msg, void * x, unsigned int size);

#if defined(SIMD_COEF_32) || defined(NT_X86_64) || defined(SIMD_PARA_MD5)      \
	|| defined(SIMD_PARA_MD4) || defined(SIMD_PARA_SHA1)
void dump_stuff_mmx(void * x, unsigned int size, unsigned int index);
void dump_stuff_mmx_noeol(void * x, unsigned int size, unsigned int index);
void dump_stuff_mmx_msg(const void * msg,
						void * buf,
						unsigned int size,
						unsigned int index);
void dump_stuff_mmx_msg_sepline(const void * msg,
								void * buf,
								unsigned int size,
								unsigned int index);
// for flat input, we do want to see SHA512 without byte swapping.
void dump_stuff_mmx64(void * buf, unsigned int size, unsigned int index);
void dump_stuff_mmx64_msg(const void * msg,
						  void * buf,
						  unsigned int size,
						  unsigned int index);
void dump_out_mmx(void * x, unsigned int size, unsigned int index);
void dump_out_mmx_noeol(void * x, unsigned int size, unsigned int index);
void dump_out_mmx_msg(const void * msg,
					  void * buf,
					  unsigned int size,
					  unsigned int index);
void dump_out_mmx_msg_sepline(const void * msg,
							  void * buf,
							  unsigned int size,
							  unsigned int index);
void dump_stuff_shammx(void * x, unsigned int size, unsigned int index);
void dump_stuff_shammx_msg(const void * msg,
						   void * buf,
						   unsigned int size,
						   unsigned int index);
void dump_out_shammx(void * x, unsigned int size, unsigned int index);
void dump_out_shammx_msg(const void * msg,
						 void * buf,
						 unsigned int size,
						 unsigned int index);
void dump_stuff_shammx64(void * x, unsigned int size, unsigned int index);
void dump_stuff_shammx64_msg(const void * msg,
							 void * buf,
							 unsigned int size,
							 unsigned int index);
void dump_out_shammx64(void * x, unsigned int size, unsigned int index);
void dump_out_shammx64_msg(const void * msg,
						   void * buf,
						   unsigned int size,
						   unsigned int index);
#endif

#if defined(SIMD_PARA_MD5)
// these functions help debug arrays of contiguous MD5 prepared PARA buffers.
// Seen in sunmd5 at the current time.
void dump_stuff_mpara_mmx(void * x, unsigned int size, unsigned int index);
void dump_stuff_mpara_mmx_noeol(void * x,
								unsigned int size,
								unsigned int index);
void dump_stuff_mpara_mmx_msg(const void * msg,
							  void * buf,
							  unsigned int size,
							  unsigned int index);
void dump_stuff_mpara_mmx_msg_sepline(const void * msg,
									  void * buf,
									  unsigned int size,
									  unsigned int index);
// a 'getter' to help debugging.  Returns a flat buffer, vs printing it out.
void getbuf_stuff_mpara_mmx(unsigned char * oBuf,
							void * buf,
							unsigned int size,
							unsigned int index);
#endif

/*
 * here, a stack buffer that is at least align-1 bytes LARGER than required, can
 * be
 * properly aligned to 'align' bytes. So:   char tmpbuf[256+15],
 * *aligned_buf=mem_align(tmpbuf,16);
 * will give you a stack buffer, aligned to 16 bytes.  There are bugs in some
 * compilers which cause
 * JTR_ALIGN(x) to fail properly (such as a bug in bitcoin OMP mode for linux32)
 * Switched to a define macro for performance.
 */
#define mem_align(a, b)                                                        \
	(void *) (((char *) (a))                                                   \
			  + (((b) -1) - (((size_t)((char *) (a)) - 1) & ((b) -1))))

/*
 * 32-bit endian-swap a memory buffer in place. Size is in octets (so should
 * be a multiple of 4). From now on, this function may be used on any arch.
 */
void alter_endianity(void * x, unsigned int size);

/* 32-bit endian-swap a memory buffer in place. Count is in 32-bit words */
void alter_endianity_w(void * x, unsigned int count);

/* 64-bit endian-swap a memory buffer in place. Count is in 64-bit words */
void alter_endianity_w64(void * x, unsigned int count);

#if ARCH_ALLOWS_UNALIGNED
// we can inline these, to always use JOHNSWAP/JOHNSWAP64
// NOTE, more portable to use #defines to inline, than the MAYBE_INLINE within
// header files.
#if (ARCH_LITTLE_ENDIAN == 0)
#define alter_endianity_to_BE(a, b)
#define alter_endianity_to_BE64(a, b)
#define alter_endianity_to_LE(ptr, word32_cnt)                                 \
	do                                                                         \
	{                                                                          \
		int i;                                                                 \
		for (i = 0; i < word32_cnt; i++)                                       \
			((ARCH_WORD_32 *) ptr)[i] = JOHNSWAP(((ARCH_WORD_32 *) ptr)[i]);   \
	} while (0)
#define alter_endianity_to_LE64(ptr, word64_cnt)                               \
	do                                                                         \
	{                                                                          \
		int i;                                                                 \
		for (i = 0; i < word64_cnt; i++)                                       \
			((ARCH_WORD_64 *) ptr)[i] = JOHNSWAP64(((ARCH_WORD_64 *) ptr)[i]); \
	} while (0)
#else
#define alter_endianity_to_LE(a, b)
#define alter_endianity_to_LE64(a, b)
#define alter_endianity_to_BE(ptr, word32_cnt)                                 \
	do                                                                         \
	{                                                                          \
		int i;                                                                 \
		for (i = 0; i < word32_cnt; i++)                                       \
			((ARCH_WORD_32 *) ptr)[i] = JOHNSWAP(((ARCH_WORD_32 *) ptr)[i]);   \
	} while (0)
#define alter_endianity_to_BE64(ptr, word64_cnt)                               \
	do                                                                         \
	{                                                                          \
		int i;                                                                 \
		for (i = 0; i < word64_cnt; i++)                                       \
			((ARCH_WORD_64 *) ptr)[i] = JOHNSWAP64(((ARCH_WORD_64 *) ptr)[i]); \
	} while (0)
#endif
#else
#if (ARCH_LITTLE_ENDIAN == 0)
#define alter_endianity_to_BE(a, b)
#define alter_endianity_to_LE(a, b)                                            \
	do                                                                         \
	{                                                                          \
		alter_endianity_w(a, b);                                               \
	} while (0)
#define alter_endianity_to_BE64(a, b)
#define alter_endianity_to_LE64(a, b)                                          \
	do                                                                         \
	{                                                                          \
		alter_endianity_w64(a, b);                                             \
	} while (0)
#else
#define alter_endianity_to_BE(a, b)                                            \
	do                                                                         \
	{                                                                          \
		alter_endianity_w(a, b);                                               \
	} while (0)
#define alter_endianity_to_LE(a, b)
#define alter_endianity_to_BE64(a, b)                                          \
	do                                                                         \
	{                                                                          \
		alter_endianity_w64(a, b);                                             \
	} while (0)
#define alter_endianity_to_LE64(a, b)
#endif
#endif

#endif
