/**
 * 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/spec.h>
#include <winpr/winpr.h>
#include <winpr/wtypes.h>
#include <winpr/platform.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _WIN32

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

typedef struct _WINPR_LIST_ENTRY WINPR_LIST_ENTRY;
typedef struct _WINPR_LIST_ENTRY* WINPR_PLIST_ENTRY;

struct _WINPR_LIST_ENTRY
{
		WINPR_PLIST_ENTRY Flink;
		WINPR_PLIST_ENTRY Blink;
};

typedef struct _WINPR_SINGLE_LIST_ENTRY WINPR_SINGLE_LIST_ENTRY;
typedef struct _WINPR_SINGLE_LIST_ENTRY* WINPR_PSINGLE_LIST_ENTRY;

struct _WINPR_SINGLE_LIST_ENTRY
{
		WINPR_PSINGLE_LIST_ENTRY Next;
};

typedef struct WINPR_LIST_ENTRY32
{
	DWORD Flink;
	DWORD Blink;
} WINPR_LIST_ENTRY32;
typedef WINPR_LIST_ENTRY32 *WINPR_PLIST_ENTRY32;

typedef struct WINPR_LIST_ENTRY64
{
	ULONGLONG Flink;
	ULONGLONG Blink;
} WINPR_LIST_ENTRY64;
typedef WINPR_LIST_ENTRY64 *WINPR_PLIST_ENTRY64;

#ifdef _WIN64

typedef struct _WINPR_SLIST_ENTRY *WINPR_PSLIST_ENTRY;
typedef struct DECLSPEC_ALIGN(16) _WINPR_SLIST_ENTRY
{
	    WINPR_PSLIST_ENTRY Next;
} WINPR_SLIST_ENTRY;

#else  /* _WIN64 */

#define WINPR_SLIST_ENTRY WINPR_SINGLE_LIST_ENTRY
#define _WINPR_SLIST_ENTRY _WINPR_SINGLE_LIST_ENTRY
#define WINPR_PSLIST_ENTRY WINPR_PSINGLE_LIST_ENTRY

#endif /* _WIN64 */

#ifdef _WIN64

typedef union DECLSPEC_ALIGN(16) _WINPR_SLIST_HEADER
{
	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, *WINPR_PSLIST_HEADER;

#else  /* _WIN64 */

typedef union _WINPR_SLIST_HEADER
{
	ULONGLONG Alignment;

	struct
	{
		WINPR_SLIST_ENTRY Next;
		WORD Depth;
		WORD Sequence;
	} DUMMYSTRUCTNAME;
} WINPR_SLIST_HEADER, *WINPR_PSLIST_HEADER;

#endif /* _WIN64 */

/* Singly-Linked List */

WINPR_API VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead);

WINPR_API WINPR_PSLIST_ENTRY InterlockedPushEntrySList(WINPR_PSLIST_HEADER ListHead, WINPR_PSLIST_ENTRY ListEntry);
WINPR_API WINPR_PSLIST_ENTRY InterlockedPushListSListEx(WINPR_PSLIST_HEADER ListHead, WINPR_PSLIST_ENTRY List, WINPR_PSLIST_ENTRY ListEnd, ULONG Count);
WINPR_API WINPR_PSLIST_ENTRY InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead);
WINPR_API WINPR_PSLIST_ENTRY InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead);

WINPR_API USHORT QueryDepthSList(WINPR_PSLIST_HEADER ListHead);

WINPR_API LONG InterlockedIncrement(LONG volatile *Addend);
WINPR_API LONG InterlockedDecrement(LONG volatile *Addend);

WINPR_API LONG InterlockedExchange(LONG volatile *Target, LONG Value);
WINPR_API LONG InterlockedExchangeAdd(LONG volatile *Addend, LONG Value);

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

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

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

#define WINPR_SINGLE_LIST_ENTRY SINGLE_LIST_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_SLIST_ENTRY _SLIST_ENTRY
#define WINPR_PSLIST_ENTRY PSLIST_ENTRY

#define WINPR_SLIST_HEADER SLIST_HEADER
#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_API LONGLONG InterlockedCompareExchange64(LONGLONG volatile *Destination, LONGLONG Exchange, LONGLONG Comperand);

#endif

/* Doubly-Linked List */

WINPR_API VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead);

WINPR_API BOOL IsListEmpty(const WINPR_LIST_ENTRY* ListHead);

WINPR_API BOOL RemoveEntryList(WINPR_PLIST_ENTRY Entry);

WINPR_API VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry);
WINPR_API WINPR_PLIST_ENTRY RemoveHeadList(WINPR_PLIST_ENTRY ListHead);

WINPR_API VOID InsertTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry);
WINPR_API WINPR_PLIST_ENTRY RemoveTailList(WINPR_PLIST_ENTRY ListHead);
WINPR_API VOID AppendTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY ListToAppend);

WINPR_API VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead, WINPR_PSINGLE_LIST_ENTRY Entry);
WINPR_API WINPR_PSINGLE_LIST_ENTRY PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead);

#ifdef __cplusplus
}
#endif

#endif /* WINPR_INTERLOCKED_H */

