/*
 * Copyright (c) 2000-2024 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#pragma once

// check that we're being compiled from the unit-tests makefile and that UT_MODULE was found
#if defined(UT_MODULE) && (UT_MODULE == -1)
#error "UT_MODULE not defined, did you forget to add a `#define UT_MODULE <module>` in your test?"
#endif

#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdbool.h>
#include <mach/boolean.h>

// This file defines some function from libc that are used by the mocks.
// The testers are built with -nostdlibinc so the system headers folders (from the SDK) are not available when
// compiling these files.
// Having system headers folders available for the includes search would create conflicts when XNU code includes
// headers like string.h, sys/types.h etc' (which need to come from XNU).
// This is why it's not possible do add headers like <stdlib.h> here.
// Furthermore, even if we could have included system headers like stdlib.h, Having that and XNU headers in the same
// translation unit would have created conflicts with XNU-defined types which have the same name but a different type
// from the system includes definition.
// For example, every type in mach/mach_types.h is typedef'd as mach_port_t in the system headers.

// from stdlib.h
extern void *calloc(size_t count, size_t size);
extern void *aligned_alloc(size_t alignment, size_t size);
extern void free(void *ptr);
extern int rand(void);
extern void srand(unsigned seed);
extern __attribute__((noreturn)) void exit(int status);
extern __attribute__((noreturn)) void abort(void);
extern char * getenv(const char *name);
extern int atoi(const char *str);

extern void * calloc(size_t count, size_t size);
extern void free(void *ptr);
extern void * malloc(size_t size);
extern void *realloc(void *ptr, size_t size);

// from stdio.h
extern int vsnprintf(char * str, size_t size, const char * format, va_list ap);
extern int snprintf(char * str, size_t size, const char * format, ...);

// from string.h
extern void *memcpy(void *restrict dst, const void *restrict src, size_t n);
extern void *memset(void *b, int c, size_t len);
extern int strcmp(const char *s1, const char *s2);
extern char *strstr(const char *haystack, const char *needle);
extern char *strdup(const char *s1);

// from unistd.h
extern size_t write(int fildes, const void *buf, size_t nbyte);
#define STDOUT_FILENO 1

// from pthread.h
#if defined(__LP64__)
#define __PTHREAD_SIZE__                8176
#define __PTHREAD_ATTR_SIZE__           56
#define __PTHREAD_MUTEX_SIZE__          56
#define __PTHREAD_MUTEXATTR_SIZE__      8
#define __PTHREAD_COND_SIZE__           40
#define __PTHREAD_CONDATTR_SIZE__       8
#else // !__LP64__
#define __PTHREAD_SIZE__                4088
#define __PTHREAD_ATTR_SIZE__           36
#define __PTHREAD_MUTEX_SIZE__          40
#define __PTHREAD_MUTEXATTR_SIZE__      8
#define __PTHREAD_COND_SIZE__           24
#define __PTHREAD_CONDATTR_SIZE__       4
#endif
#if defined(__arm__) || defined(__arm64__)
#define PTHREAD_STACK_MIN              16384
#else
#define PTHREAD_STACK_MIN              8192
#endif

struct _opaque_pthread_attr_t {
	long __sig;
	char __opaque[__PTHREAD_ATTR_SIZE__];
};
struct _opaque_pthread_t {
	long __sig;
	void *__cleanup_stack;
	char __opaque[__PTHREAD_SIZE__];
};
struct _opaque_pthread_mutex_t {
	long __sig;
	char __opaque[__PTHREAD_MUTEX_SIZE__];
};
struct _opaque_pthread_mutexattr_t {
	long __sig;
	char __opaque[__PTHREAD_MUTEXATTR_SIZE__];
};
struct _opaque_pthread_cond_t {
	long __sig;
	char __opaque[__PTHREAD_COND_SIZE__];
};
struct _opaque_pthread_condattr_t {
	long __sig;
	char __opaque[__PTHREAD_CONDATTR_SIZE__];
};
typedef struct _opaque_pthread_attr_t __darwin_pthread_attr_t;
typedef struct _opaque_pthread_t *__darwin_pthread_t;
typedef unsigned long __darwin_pthread_key_t;
typedef struct _opaque_pthread_mutex_t __darwin_pthread_mutex_t;
typedef struct _opaque_pthread_mutexattr_t __darwin_pthread_mutexattr_t;
typedef struct _opaque_pthread_cond_t __darwin_pthread_cond_t;
typedef struct _opaque_pthread_condattr_t __darwin_pthread_condattr_t;

typedef __darwin_pthread_t pthread_t;
typedef __darwin_pthread_attr_t pthread_attr_t;
typedef __darwin_pthread_key_t pthread_key_t;
typedef __darwin_pthread_mutex_t pthread_mutex_t;
typedef __darwin_pthread_mutexattr_t pthread_mutexattr_t;
typedef __darwin_pthread_cond_t pthread_cond_t;
typedef __darwin_pthread_condattr_t pthread_condattr_t;

extern pthread_t pthread_self(void);
extern int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
extern int pthread_join(pthread_t thread, void **value_ptr);
extern int pthread_setspecific(pthread_key_t key, const void *value);
extern void *pthread_getspecific(pthread_key_t key);
extern int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
extern int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
extern int pthread_mutex_destroy(pthread_mutex_t *mutex);
extern int pthread_mutex_lock(pthread_mutex_t *mutex);
extern int pthread_mutex_trylock(pthread_mutex_t *mutex);
extern int pthread_mutex_unlock(pthread_mutex_t *mutex);
extern int pthread_mutexattr_init(pthread_mutexattr_t *attr);
extern int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
#define PTHREAD_MUTEX_RECURSIVE         2
extern int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
extern int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *mutex);
extern int pthread_cond_signal(pthread_cond_t *cond);
extern int pthread_cond_broadcast(pthread_cond_t *cond);
extern int pthread_cond_destroy(pthread_cond_t *cond);
extern size_t pthread_get_stacksize_np(pthread_t);
extern void* pthread_get_stackaddr_np(pthread_t);

// errno.h
#define EBUSY           16

extern int * __error(void);
#define errno (*__error())

// sysctl.h
extern int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);

// from setjmp.h
#if defined(__x86_64__)
#  define _JBLEN ((9 * 2) + 3 + 16)
#elif defined(__i386__)
#  define _JBLEN (18)
#elif defined(__arm__) && !defined(__ARM_ARCH_7K__)
#  define _JBLEN                (10 + 16 + 2)
#elif defined(__arm64__) || defined(__ARM_ARCH_7K__)
#  define _JBLEN                ((14 + 8 + 2) * 2)
#else
#  error Undefined platform for setjmp
#endif

typedef int jmp_buf[_JBLEN];

extern int setjmp(jmp_buf);
extern void longjmp(jmp_buf, int) __attribute__((__noreturn__));

// from time.h
#include <sys/time.h>
extern time_t time(time_t *tloc);
