/*
* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
* Jonathan Schleifer <js@heap.zone>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#include "objfw-defs.h"
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "platform.h"
#ifdef OF_OBJFW_RUNTIME
# import "runtime.h"
#endif
#ifdef OF_APPLE_RUNTIME
# import <objc/objc.h>
# import <objc/runtime.h>
# import <objc/message.h>
#endif
#if defined(__GNUC__)
# define restrict __restrict__
#elif __STDC_VERSION__ < 199901L
# define restrict
#endif
#if __STDC_VERSION__ >= 201112L && !defined(static_assert)
/* C11 compiler, but old libc */
# define static_assert _Static_assert
#endif
#if defined(OF_HAVE__THREAD_LOCAL)
# define OF_HAVE_COMPILER_TLS
# ifdef OF_HAVE_THREADS_H
# include <threads.h>
# else
# define thread_local _Thread_local
# endif
#elif defined(OF_HAVE___THREAD)
# define OF_HAVE_COMPILER_TLS
# define thread_local __thread
#endif
#ifdef __GNUC__
# define OF_INLINE inline __attribute__((__always_inline__))
# define OF_LIKELY(cond) (__builtin_expect(!!(cond), 1))
# define OF_UNLIKELY(cond) (__builtin_expect(!!(cond), 0))
# define OF_CONST_FUNC __attribute__((__const__))
# define OF_NO_RETURN_FUNC __attribute__((__noreturn__))
#else
# define OF_INLINE inline
# define OF_LIKELY(cond) cond
# define OF_UNLIKELY(cond) cond
# define OF_CONST_FUNC
# define OF_NO_RETURN_FUNC
#endif
#ifdef OF_BIG_ENDIAN
# define OF_BYTE_ORDER_NATIVE OF_BYTE_ORDER_BIG_ENDIAN
#else
# define OF_BYTE_ORDER_NATIVE OF_BYTE_ORDER_LITTLE_ENDIAN
#endif
#if __STDC_VERSION__ >= 201112L && defined(OF_HAVE_MAX_ALIGN_T)
# define OF_BIGGEST_ALIGNMENT _Alignof(max_align_t)
#else
# ifdef __BIGGEST_ALIGNMENT__
# define OF_BIGGEST_ALIGNMENT __BIGGEST_ALIGNMENT__
# else
# /* Hopefully no arch needs more than 16 byte alignment */
# define OF_BIGGEST_ALIGNMENT 16
# endif
#endif
#ifdef __GNUC__
# define __GCC_VERSION__ (__GNUC__ * 100 + __GNUC_MINOR__)
#else
# define __GCC_VERSION__ 0
#endif
#ifndef __has_feature
# define __has_feature(x) 0
#endif
#ifndef __has_attribute
# define __has_attribute(x) 0
#endif
#if __has_feature(objc_bool)
# undef YES
# define YES __objc_yes
# undef NO
# define NO __objc_no
# ifndef __cplusplus
# undef true
# define true ((bool)1)
# undef false
# define false ((bool)0)
# endif
#endif
#if !__has_feature(objc_instancetype)
# define instancetype id
#endif
#if __has_feature(blocks)
# define OF_HAVE_BLOCKS
#endif
#if __has_feature(objc_arc)
# define OF_RETURNS_RETAINED __attribute__((__ns_returns_retained__))
# define OF_RETURNS_NOT_RETAINED __attribute__((__ns_returns_not_retained__))
# define OF_RETURNS_INNER_POINTER \
__attribute__((__objc_returns_inner_pointer__))
# define OF_CONSUMED __attribute__((__ns_consumed__))
# define OF_WEAK_UNAVAILABLE __attribute__((__objc_arc_weak_unavailable__))
#else
# define OF_RETURNS_RETAINED
# define OF_RETURNS_NOT_RETAINED
# define OF_RETURNS_INNER_POINTER
# define OF_CONSUMED
# define OF_WEAK_UNAVAILABLE
/*
* undef them first, as new Clang versions have these as built-in defines even
* when ARC is disabled.
*/
# undef __unsafe_unretained
# undef __bridge
# undef __autoreleasing
# define __unsafe_unretained
# define __bridge
# define __autoreleasing
#endif
#if __has_feature(objc_generics)
# define OF_HAVE_GENERICS
# define OF_GENERIC(...) <__VA_ARGS__>
#else
# define OF_GENERIC(...)
#endif
#if __has_feature(nullability)
# define OF_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
# define OF_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
# define OF_NULLABLE_PROPERTY(...) (__VA_ARGS__, nullable)
#else
# define OF_ASSUME_NONNULL_BEGIN
# define OF_ASSUME_NONNULL_END
# define _Nonnull
# define _Nullable
# define _Null_unspecified
# define OF_NULLABLE_PROPERTY
# define nonnull
# define nullable
#endif
#if __has_feature(objc_kindof)
# define OF_KINDOF(cls) __kindof cls
#else
# define OF_KINDOF(cls) id
#endif
#if defined(__clang__) || __GCC_VERSION__ >= 405
# define OF_UNREACHABLE __builtin_unreachable();
#else
# define OF_UNREACHABLE abort();
#endif
#if defined(__clang__) || __GCC_VERSION__ >= 406
# define OF_SENTINEL __attribute__((__sentinel__))
# define OF_NO_RETURN __attribute__((__noreturn__))
#else
# define OF_SENTINEL
# define OF_NO_RETURN
#endif
#if __has_attribute(__objc_requires_super__)
# define OF_REQUIRES_SUPER __attribute__((__objc_requires_super__))
#else
# define OF_REQUIRES_SUPER
#endif
#if __has_attribute(__objc_root_class__)
# define OF_ROOT_CLASS __attribute__((__objc_root_class__))
#else
# define OF_ROOT_CLASS
#endif
#if __has_attribute(__objc_subclassing_restricted__)
# define OF_SUBCLASSING_RESTRICTED \
__attribute__((__objc_subclassing_restricted__))
#else
# define OF_SUBCLASSING_RESTRICTED
#endif
#ifdef __GNUC__
# ifdef OF_X86_64
# define OF_X86_64_ASM
# endif
# ifdef OF_X86
# define OF_X86_ASM
# endif
# ifdef OF_POWERPC
# define OF_POWERPC_ASM
# endif
# ifdef OF_ARM64
# define OF_ARM64_ASM
# endif
# ifdef OF_ARM
# define OF_ARM_ASM
# endif
# ifdef OF_ARMV7
# define OF_ARMV7_ASM
# endif
# ifdef OF_ARMV6
# define OF_ARMV6_ASM
# endif
# ifdef OF_MIPS64
# define OF_MIPS64_ASM
# endif
# ifdef OF_MIPS
# define OF_MIPS_ASM
# endif
#endif
#ifdef OF_APPLE_RUNTIME
# if defined(OF_X86_64) || defined(OF_X86) || defined(OF_ARM64) || \
defined(OF_ARM) || defined(OF_POWERPC)
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
# endif
#else
# if defined(OF_ELF)
# if defined(OF_X86_64) || defined(OF_X86) || \
defined(OF_ARM64) || defined(OF_ARM) || defined(OF_POWERPC) || \
defined(OF_MIPS)
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
# if __OBJFW_RUNTIME_ABI__ >= 800
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
# endif
# endif
# elif defined(OF_MACH_O)
# if defined(OF_X86_64)
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
# if __OBJFW_RUNTIME_ABI__ >= 800
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
# endif
# endif
# elif defined(OF_WINDOWS)
# if defined(OF_X86_64) || defined(OF_X86)
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR
# if __OBJFW_RUNTIME_ABI__ >= 800
# define OF_HAVE_FORWARDING_TARGET_FOR_SELECTOR_STRET
# endif
# endif
# endif
#endif
#define OF_RETAIN_COUNT_MAX UINT_MAX
#define OF_NOT_FOUND SIZE_MAX
#if !defined(OF_WINDOWS) && !defined(OF_MSDOS)
# define OF_PATH_DELIMITER '/'
# define OF_PATH_DELIMITER_STRING @"/"
# define OF_IS_PATH_DELIMITER(c) (c == '/')
#else
# define OF_PATH_DELIMITER '\\'
# define OF_PATH_DELIMITER_STRING @"\\"
# define OF_IS_PATH_DELIMITER(c) (c == '\\' || c == '/')
#endif
#define OF_PATH_CURRENT_DIRECTORY @"."
#define OF_PATH_PARENT_DIRECTORY @".."
#define OF_ENSURE(cond) \
do { \
if (!(cond)) { \
fprintf(stderr, "Failed to ensure condition " \
"in " __FILE__ ":%d:\n" #cond "\n", \
__LINE__); \
abort(); \
} \
} while (0)
#define OF_UNRECOGNIZED_SELECTOR of_method_not_found(self, _cmd);
#if __has_feature(objc_arc)
# define OF_INVALID_INIT_METHOD \
[self doesNotRecognizeSelector: _cmd]; \
\
abort();
#else
# define OF_INVALID_INIT_METHOD \
@try { \
[self doesNotRecognizeSelector: _cmd]; \
} @catch (id e) { \
[self release]; \
@throw e; \
} \
\
abort();
#endif
#ifdef __clang__
# define OF_DEALLOC_UNSUPPORTED \
[self doesNotRecognizeSelector: _cmd]; \
\
abort(); \
\
_Pragma("clang diagnostic push ignore \"-Wunreachable-code\""); \
[super dealloc]; /* Get rid of a stupid warning */ \
_Pragma("clang diagnostic pop");
#else
# define OF_DEALLOC_UNSUPPORTED \
[self doesNotRecognizeSelector: _cmd]; \
\
abort(); \
\
[super dealloc]; /* Get rid of a stupid warning */
#endif
static OF_INLINE uint16_t OF_CONST_FUNC
OF_BSWAP16_CONST(uint16_t i)
{
return (i & UINT16_C(0xFF00)) >> 8 |
(i & UINT16_C(0x00FF)) << 8;
}
static OF_INLINE uint32_t OF_CONST_FUNC
OF_BSWAP32_CONST(uint32_t i)
{
return (i & UINT32_C(0xFF000000)) >> 24 |
(i & UINT32_C(0x00FF0000)) >> 8 |
(i & UINT32_C(0x0000FF00)) << 8 |
(i & UINT32_C(0x000000FF)) << 24;
}
static OF_INLINE uint64_t OF_CONST_FUNC
OF_BSWAP64_CONST(uint64_t i)
{
return (i & UINT64_C(0xFF00000000000000)) >> 56 |
(i & UINT64_C(0x00FF000000000000)) >> 40 |
(i & UINT64_C(0x0000FF0000000000)) >> 24 |
(i & UINT64_C(0x000000FF00000000)) >> 8 |
(i & UINT64_C(0x00000000FF000000)) << 8 |
(i & UINT64_C(0x0000000000FF0000)) << 24 |
(i & UINT64_C(0x000000000000FF00)) << 40 |
(i & UINT64_C(0x00000000000000FF)) << 56;
}
static OF_INLINE uint16_t OF_CONST_FUNC
OF_BSWAP16_NONCONST(uint16_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP16)
return __builtin_bswap16(i);
#elif defined(OF_X86_64_ASM) || defined(OF_X86_ASM)
__asm__ (
"xchgb %h0, %b0"
: "=Q"(i)
: "0"(i)
);
#elif defined(OF_POWERPC_ASM)
__asm__ (
"lhbrx %0, 0, %1"
: "=r"(i)
: "r"(&i), "m"(i)
);
#elif defined(OF_ARMV6_ASM)
__asm__ (
"rev16 %0, %0"
: "=r"(i)
: "0"(i)
);
#else
i = (i & UINT16_C(0xFF00)) >> 8 |
(i & UINT16_C(0x00FF)) << 8;
#endif
return i;
}
static OF_INLINE uint32_t OF_CONST_FUNC
OF_BSWAP32_NONCONST(uint32_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP32)
return __builtin_bswap32(i);
#elif defined(OF_X86_64_ASM) || defined(OF_X86_ASM)
__asm__ (
"bswap %0"
: "=q"(i)
: "0"(i)
);
#elif defined(OF_POWERPC_ASM)
__asm__ (
"lwbrx %0, 0, %1"
: "=r"(i)
: "r"(&i), "m"(i)
);
#elif defined(OF_ARMV6_ASM)
__asm__ (
"rev %0, %0"
: "=r"(i)
: "0"(i)
);
#else
i = (i & UINT32_C(0xFF000000)) >> 24 |
(i & UINT32_C(0x00FF0000)) >> 8 |
(i & UINT32_C(0x0000FF00)) << 8 |
(i & UINT32_C(0x000000FF)) << 24;
#endif
return i;
}
static OF_INLINE uint64_t OF_CONST_FUNC
OF_BSWAP64_NONCONST(uint64_t i)
{
#if defined(OF_HAVE_BUILTIN_BSWAP64)
return __builtin_bswap64(i);
#elif defined(OF_X86_64_ASM)
__asm__ (
"bswap %0"
: "=r"(i)
: "0"(i)
);
#elif defined(OF_X86_ASM)
__asm__ (
"bswap %%eax\n\t"
"bswap %%edx\n\t"
"xchgl %%eax, %%edx"
: "=A"(i)
: "0"(i)
);
#else
i = (uint64_t)OF_BSWAP32_NONCONST((uint32_t)(i & 0xFFFFFFFF)) << 32 |
OF_BSWAP32_NONCONST((uint32_t)(i >> 32));
#endif
return i;
}
#ifdef __GNUC__
# define OF_BSWAP16(i) \
(__builtin_constant_p(i) ? OF_BSWAP16_CONST(i) : OF_BSWAP16_NONCONST(i))
# define OF_BSWAP32(i) \
(__builtin_constant_p(i) ? OF_BSWAP32_CONST(i) : OF_BSWAP32_NONCONST(i))
# define OF_BSWAP64(i) \
(__builtin_constant_p(i) ? OF_BSWAP64_CONST(i) : OF_BSWAP64_NONCONST(i))
#else
# define OF_BSWAP16(i) OF_BSWAP16_CONST(i)
# define OF_BSWAP32(i) OF_BSWAP32_CONST(i)
# define OF_BSWAP64(i) OF_BSWAP64_CONST(i)
#endif
static OF_INLINE float OF_CONST_FUNC
OF_BSWAP_FLOAT(float f)
{
union {
float f;
uint32_t i;
} u;
u.f = f;
u.i = OF_BSWAP32(u.i);
return u.f;
}
static OF_INLINE double OF_CONST_FUNC
OF_BSWAP_DOUBLE(double d)
{
union {
double d;
uint64_t i;
} u;
u.d = d;
u.i = OF_BSWAP64(u.i);
return u.d;
}
#ifdef OF_BIG_ENDIAN
# define OF_BSWAP16_IF_BE(i) OF_BSWAP16(i)
# define OF_BSWAP32_IF_BE(i) OF_BSWAP32(i)
# define OF_BSWAP64_IF_BE(i) OF_BSWAP64(i)
# define OF_BSWAP16_IF_LE(i) (i)
# define OF_BSWAP32_IF_LE(i) (i)
# define OF_BSWAP64_IF_LE(i) (i)
#else
# define OF_BSWAP16_IF_BE(i) (i)
# define OF_BSWAP32_IF_BE(i) (i)
# define OF_BSWAP64_IF_BE(i) (i)
# define OF_BSWAP16_IF_LE(i) OF_BSWAP16(i)
# define OF_BSWAP32_IF_LE(i) OF_BSWAP32(i)
# define OF_BSWAP64_IF_LE(i) OF_BSWAP64(i)
#endif
#ifdef OF_FLOAT_BIG_ENDIAN
# define OF_BSWAP_FLOAT_IF_BE(i) OF_BSWAP_FLOAT(i)
# define OF_BSWAP_DOUBLE_IF_BE(i) OF_BSWAP_DOUBLE(i)
# define OF_BSWAP_FLOAT_IF_LE(i) (i)
# define OF_BSWAP_DOUBLE_IF_LE(i) (i)
#else
# define OF_BSWAP_FLOAT_IF_BE(i) (i)
# define OF_BSWAP_DOUBLE_IF_BE(i) (i)
# define OF_BSWAP_FLOAT_IF_LE(i) OF_BSWAP_FLOAT(i)
# define OF_BSWAP_DOUBLE_IF_LE(i) OF_BSWAP_DOUBLE(i)
#endif
#define OF_ROL(value, bits) \
(((bits) % (sizeof(value) * 8)) > 0 \
? ((value) << ((bits) % (sizeof(value) * 8))) | \
((value) >> (sizeof(value) * 8 - ((bits) % (sizeof(value) * 8)))) \
: (value))
#define OF_ROR(value, bits) \
(((bits) % (sizeof(value) * 8)) > 0 \
? ((value) >> ((bits) % (sizeof(value) * 8))) | \
((value) << (sizeof(value) * 8 - ((bits) % (sizeof(value) * 8)))) \
: (value))
#define OF_ROUND_UP_POW2(pow2, value) (((value) + (pow2) - 1) & ~((pow2) - 1))
#define OF_HASH_INIT(hash) hash = of_hash_seed;
#define OF_HASH_ADD(hash, byte) \
{ \
hash += (uint8_t)(byte); \
hash += (hash << 10); \
hash ^= (hash >> 6); \
}
#define OF_HASH_FINALIZE(hash) \
{ \
hash += (hash << 3); \
hash ^= (hash >> 11); \
hash += (hash << 15); \
}
#define OF_HASH_ADD_HASH(hash, other) \
{ \
uint32_t otherCopy = other; \
OF_HASH_ADD(hash, (otherCopy >> 24) & 0xFF); \
OF_HASH_ADD(hash, (otherCopy >> 16) & 0xFF); \
OF_HASH_ADD(hash, (otherCopy >> 8) & 0xFF); \
OF_HASH_ADD(hash, otherCopy & 0xFF); \
}
static OF_INLINE bool
of_bitset_isset(uint8_t *storage, size_t index)
{
return storage[index / 8] & (1 << (index % 8));
}
static OF_INLINE void
of_bitset_set(uint8_t *storage, size_t index)
{
storage[index / 8] |= (1 << (index % 8));
}
static OF_INLINE void
of_bitset_clear(uint8_t *storage, size_t index)
{
storage[index / 8] &= ~(1 << (index % 8));
}
static OF_INLINE char*
of_strdup(const char *string)
{
char *copy;
size_t length = strlen(string);
if ((copy = (char*)malloc(length + 1)) == NULL)
return NULL;
memcpy(copy, string, length + 1);
return copy;
}
static OF_INLINE void
of_explicit_memset(void *buffer_, int character, size_t length)
{
volatile unsigned char *buffer = buffer_;
while (buffer < (unsigned char*)buffer_ + length)
*buffer++ = character;
}
static OF_INLINE bool
of_ascii_isalpha(char c)
{
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
}
static OF_INLINE bool
of_ascii_isalnum(char c)
{
return (of_ascii_isalpha(c) || (c >= '0' && c <= '9'));
}
static OF_INLINE char
of_ascii_toupper(char c)
{
return (c >= 'a' && c <= 'z' ? 'A' + (c - 'a') : c);
}
static OF_INLINE char
of_ascii_tolower(char c)
{
return (c >= 'A' && c <= 'Z' ? 'a' + (c - 'A') : c);
}