// use Unicode version API
#define _WINDOWS
#define _USRDLL
#define _UNICODE
#define UNICODE

// support Windows 7 or later
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif

#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>

// GCC のコンパイラ・オプションで -mssse3 をセットしないといけない
#include <immintrin.h>	// 全ての SIMD 命令セットを使用する場合インクルード

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifdef __cplusplus
extern "C" {
#endif

// DLLMain
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
/*
	switch (dwReason){
	case DLL_PROCESS_ATTACH:
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_PROCESS_DETACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	}
*/
	return TRUE;
}

// Exported funtions

void __stdcall gf16_ssse3_block32_altmap(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table)
{
	__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm7;

	// create mask for 16 entries
	xmm7 = _mm_set1_epi8(0x0F);		// 0x0F *16

	while (bsize != 0){
		xmm0 = _mm_load_si128((__m128i *)input);	// read source 32-bytes
		xmm1 = _mm_load_si128((__m128i *)input + 1);

		xmm4 = _mm_load_si128((__m128i *)table);	// load tables
		xmm5 = _mm_load_si128((__m128i *)table + 1);
		xmm3 = _mm_load_si128(&xmm0);	// copy source
		xmm0 = _mm_srli_epi16(xmm0, 4);	// prepare next 4-bit
		xmm3 = _mm_and_si128(xmm3, xmm7);	// src & 0x0F
		xmm0 = _mm_and_si128(xmm0, xmm7);	// (src >> 4) & 0x0F
		xmm4 = _mm_shuffle_epi8(xmm4, xmm3);	// table look-up
		xmm5 = _mm_shuffle_epi8(xmm5, xmm3);

		xmm2 = _mm_load_si128((__m128i *)table + 2);	// load tables
		xmm3 = _mm_load_si128((__m128i *)table + 3);
		xmm2 = _mm_shuffle_epi8(xmm2, xmm0);	// table look-up
		xmm3 = _mm_shuffle_epi8(xmm3, xmm0);
		xmm2 = _mm_xor_si128(xmm2, xmm4);	// combine result
		xmm3 = _mm_xor_si128(xmm3, xmm5);

		xmm4 = _mm_load_si128((__m128i *)table + 4);	// load tables
		xmm5 = _mm_load_si128((__m128i *)table + 5);
		xmm0 = _mm_load_si128(&xmm1);	// copy source
		xmm0 = _mm_srli_epi16(xmm0, 4);	// prepare next 4-bit
		xmm1 = _mm_and_si128(xmm1, xmm7);	// src & 0x0F
		xmm0 = _mm_and_si128(xmm0, xmm7);	// (src >> 4) & 0x0F
		xmm4 = _mm_shuffle_epi8(xmm4, xmm1);	// table look-up
		xmm5 = _mm_shuffle_epi8(xmm5, xmm1);
		xmm4 = _mm_xor_si128(xmm4, xmm2);	// combine result
		xmm5 = _mm_xor_si128(xmm5, xmm3);

		xmm2 = _mm_load_si128((__m128i *)table + 6);	// load tables
		xmm3 = _mm_load_si128((__m128i *)table + 7);
		xmm2 = _mm_shuffle_epi8(xmm2, xmm0);	// table look-up
		xmm3 = _mm_shuffle_epi8(xmm3, xmm0);

		xmm0 = _mm_load_si128((__m128i *)output);	// read dest 32-bytes
		xmm1 = _mm_load_si128((__m128i *)output + 1);
		xmm2 = _mm_xor_si128(xmm2, xmm4);	// combine result
		xmm3 = _mm_xor_si128(xmm3, xmm5);
		xmm0 = _mm_xor_si128(xmm0, xmm2);
		xmm1 = _mm_xor_si128(xmm1, xmm3);
		_mm_store_si128((__m128i *)output, xmm0);	// write dest 32-bytes
		_mm_store_si128((__m128i *)output + 1, xmm1);

		input += 32;
		output += 32;
		bsize -= 32;
	}
}

#ifdef __cplusplus
}
#endif
