/**
 * WinPR: Windows Portable Runtime
 * Interlocked Singly-Linked Lists
 *
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
 *
 * 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.
 */

#ifndef WINPR_INTERLOCKED_H
#define WINPR_INTERLOCKED_H

#include <winpr/config.h>
#include <winpr/spec.h>
#include <winpr/platform.h>
#include <winpr/winpr.h>
#include <winpr/wtypes.h>

#ifdef __cplusplus
extern "C"
{
#endif

#ifndef _WIN32

#if !defined(WITHOUT_WINPR_3x_DEPRECATED)

#ifndef CONTAINING_RECORD
#define CONTAINING_RECORD(address, type, field) \
	((type*)(((ULONG_PTR)address) - (ULONG_PTR)(&(((type*)0)->field))))
#endif /* CONTAINING_RECORD */

	WINPR_DEPRECATED_VAR("[since 3.27.0]", typedef struct S_WINPR_LIST_ENTRY WINPR_LIST_ENTRY);

	typedef struct S_WINPR_LIST_ENTRY* WINPR_PLIST_ENTRY;

	struct S_WINPR_LIST_ENTRY
	{
		WINPR_PLIST_ENTRY Flink;
		WINPR_PLIST_ENTRY Blink;
	};

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     typedef struct S_WINPR_SINGLE_LIST_ENTRY WINPR_SINGLE_LIST_ENTRY);

	typedef struct S_WINPR_SINGLE_LIST_ENTRY* WINPR_PSINGLE_LIST_ENTRY;

	struct S_WINPR_SINGLE_LIST_ENTRY
	{
		WINPR_PSINGLE_LIST_ENTRY Next;
	};

	WINPR_DEPRECATED_VAR(
	    "[since 3.27.0]", typedef struct WINPR_LIST_ENTRY32 {
		    DWORD Flink;
		    DWORD Blink;
	    } WINPR_LIST_ENTRY32);

	WINPR_DEPRECATED_VAR("[since 3.27.0]", typedef WINPR_LIST_ENTRY32* WINPR_PLIST_ENTRY32);

	WINPR_DEPRECATED_VAR(
	    "[since 3.27.0]", typedef struct WINPR_LIST_ENTRY64 {
		    ULONGLONG Flink;
		    ULONGLONG Blink;
	    } WINPR_LIST_ENTRY64);

	WINPR_DEPRECATED_VAR("[since 3.27.0]", typedef WINPR_LIST_ENTRY64* WINPR_PLIST_ENTRY64);

#ifdef _WIN64

	WINPR_DEPRECATED_VAR("[since 3.27.0]", typedef struct S_WINPR_SLIST_ENTRY* WINPR_PSLIST_ENTRY);

	WINPR_DEPRECATED_VAR(
	    "[since 3.27.0]", typedef struct DECLSPEC_ALIGN(16)
	                          S_WINPR_SLIST_ENTRY { WINPR_PSLIST_ENTRY Next; } WINPR_SLIST_ENTRY);

#else /* _WIN64 */

	WINPR_PRAGMA_DIAG_PUSH
	WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO

#define WINPR_SLIST_ENTRY WINPR_SINGLE_LIST_ENTRY
// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
#define _WINPR_SLIST_ENTRY _WINPR_SINGLE_LIST_ENTRY
#define WINPR_PSLIST_ENTRY WINPR_PSINGLE_LIST_ENTRY

	WINPR_PRAGMA_DIAG_POP

#endif /* _WIN64 */

#ifdef _WIN64
	WINPR_DEPRECATED_VAR(
	    "[since 3.27.0]", typedef union DECLSPEC_ALIGN(16) {
		    struct
		    {
			    ULONGLONG Alignment;
			    ULONGLONG Region;
		    } DUMMYSTRUCTNAME;

		    struct
		    {
			    ULONGLONG Depth : 16;
			    ULONGLONG Sequence : 9;
			    ULONGLONG NextEntry : 39;
			    ULONGLONG HeaderType : 1;
			    ULONGLONG Init : 1;
			    ULONGLONG Reserved : 59;
			    ULONGLONG Region : 3;
		    } Header8;

		    struct
		    {
			    ULONGLONG Depth : 16;
			    ULONGLONG Sequence : 48;
			    ULONGLONG HeaderType : 1;
			    ULONGLONG Reserved : 3;
			    ULONGLONG NextEntry : 60;
		    } HeaderX64;
	    } WINPR_SLIST_HEADER);

#else /* _WIN64 */

	WINPR_DEPRECATED_VAR(
	    "[since 3.27.0]", typedef union {
		    ULONGLONG Alignment;

		    struct
		    {
			    WINPR_SLIST_ENTRY Next;
			    WORD Depth;
			    WORD Sequence;
		    } DUMMYSTRUCTNAME;
	    } WINPR_SLIST_HEADER);
#endif /* _WIN64 */

	typedef WINPR_SLIST_HEADER* WINPR_PSLIST_HEADER;

	/* Singly-Linked List */

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_API VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_API WINPR_PSLIST_ENTRY InterlockedPushEntrySList(
	                         WINPR_PSLIST_HEADER ListHead, WINPR_PSLIST_ENTRY ListEntry));

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_API WINPR_PSLIST_ENTRY InterlockedPushListSListEx(
	                         WINPR_PSLIST_HEADER ListHead, WINPR_PSLIST_ENTRY List,
	                         WINPR_PSLIST_ENTRY ListEnd, ULONG Count));

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_ATTR_NODISCARD WINPR_API WINPR_PSLIST_ENTRY
	                         InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API WINPR_PSLIST_ENTRY
	                                           InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API USHORT
	                                           QueryDepthSList(WINPR_PSLIST_HEADER ListHead));
#endif /* WITHOUT_WINPR_3x_DEPRECATED */

	WINPR_API LONG InterlockedIncrement(LONG volatile* Addend);

	WINPR_API LONG InterlockedDecrement(LONG volatile* Addend);

	WINPR_ATTR_NODISCARD
	WINPR_API LONG InterlockedExchange(LONG volatile* Target, LONG Value);

	WINPR_ATTR_NODISCARD
	WINPR_API LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value);

	WINPR_ATTR_NODISCARD
	WINPR_API LONG InterlockedCompareExchange(LONG volatile* Destination, LONG Exchange,
	                                          LONG Comperand);

	WINPR_ATTR_NODISCARD
	WINPR_API PVOID InterlockedCompareExchangePointer(PVOID volatile* Destination, PVOID Exchange,
	                                                  PVOID Comperand);

#else /* _WIN32 */
#define WINPR_LIST_ENTRY LIST_ENTRY
#define WINPR_PLIST_ENTRY PLIST_ENTRY

#define WINPR_SINGLE_LIST_ENTRY SINGLE_LIST_ENTRY
#define WINPR_PSINGLE_LIST_ENTRY PSINGLE_LIST_ENTRY

#define WINPR_SLIST_ENTRY SLIST_ENTRY
#define WINPR_PSLIST_ENTRY PSLIST_ENTRY

#define WINPR_SLIST_HEADER SLIST_HEADER
#define WINPR_PSLIST_HEADER PSLIST_HEADER

#endif /* _WIN32 */

#if (!defined(_WIN32) || \
     (defined(_WIN32) && (_WIN32_WINNT < 0x0502) && !defined(InterlockedCompareExchange64)))
#define WINPR_INTERLOCKED_COMPARE_EXCHANGE64 1
#endif

#ifdef WINPR_INTERLOCKED_COMPARE_EXCHANGE64

	WINPR_ATTR_NODISCARD
	WINPR_API LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination,
	                                                LONGLONG Exchange, LONGLONG Comperand);

#endif

#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
	/* Doubly-Linked List */
	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_API VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API BOOL
	                                           IsListEmpty(const WINPR_LIST_ENTRY* ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API BOOL
	                                           RemoveEntryList(WINPR_PLIST_ENTRY Entry));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_API VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead,
	                                                                     WINPR_PLIST_ENTRY Entry));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API WINPR_PLIST_ENTRY
	                                           RemoveHeadList(WINPR_PLIST_ENTRY ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_API VOID InsertTailList(WINPR_PLIST_ENTRY ListHead,
	                                                                     WINPR_PLIST_ENTRY Entry));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API WINPR_PLIST_ENTRY
	                                           RemoveTailList(WINPR_PLIST_ENTRY ListHead));

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_API VOID AppendTailList(WINPR_PLIST_ENTRY ListHead,
	                                                   WINPR_PLIST_ENTRY ListToAppend));

	WINPR_DEPRECATED_VAR("[since 3.27.0]",
	                     WINPR_API VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead,
	                                                  WINPR_PSINGLE_LIST_ENTRY Entry));

	WINPR_DEPRECATED_VAR("[since 3.27.0]", WINPR_ATTR_NODISCARD WINPR_API WINPR_PSINGLE_LIST_ENTRY
	                                           PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead));
#endif /* WITHOUT_WINPR_3x_DEPRECATED */

#ifdef __cplusplus
}
#endif

#endif /* WINPR_INTERLOCKED_H */
