/* SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later */

/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Netscape Portable Runtime library.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are 
 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
 * Rights Reserved.
 * 
 * Contributor(s):  Silicon Graphics, Inc.
 * 
 * Portions created by SGI are Copyright (C) 2000-2001 Silicon
 * Graphics, Inc.  All Rights Reserved.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */

/*
 * This file is derived directly from Netscape Communications Corporation,
 * and consists of extensive modifications made during the year(s) 1999-2000.
 */

#ifndef __ST_MD_H__
#define __ST_MD_H__

#if defined(ETIMEDOUT) && !defined(ETIME)
    #define ETIME ETIMEDOUT
#endif

#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
    #define MAP_ANON MAP_ANONYMOUS
#endif

#ifndef MAP_FAILED
    #define MAP_FAILED -1
#endif

/* We define the jmpbuf, because the system's is different in different OS */
typedef struct _st_jmp_buf {
    /*
     *   OS         CPU                  SIZE
     * Darwin   __amd64__/__x86_64__    long[8]
     * Darwin   __aarch64__             long[22]
     * Linux    __i386__                long[6]
     * Linux    __amd64__/__x86_64__    long[8]
     * Linux    __aarch64__             long[22]
     * Linux    __arm__                 long[16]
     * Linux    __mips__/__mips64       long[13]
     * Linux    __riscv                 long[14]
     * Linux    __loongarch64           long[12]
     * Cygwin64 __amd64__/__x86_64__    long[8]
     */
    long __jmpbuf[22];
} _st_jmp_buf_t[1];

extern int _st_md_cxt_save(_st_jmp_buf_t env);
extern void _st_md_cxt_restore(_st_jmp_buf_t env, int val);

/* Always use builtin setjmp/longjmp, use asm code. */
#define MD_USE_BUILTIN_SETJMP
#define MD_SETJMP(env) _st_md_cxt_save(env)
#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val)
#if defined(USE_LIBC_SETJMP)
#error The libc setjmp is not supported now
#endif

/*****************************************
 * Platform specifics
 */

#if defined (DARWIN)

    #define MD_USE_BSD_ANON_MMAP
    #define MD_ACCEPT_NB_INHERITED
    #define MD_HAVE_SOCKLEN_T

    #if defined(__amd64__) || defined(__x86_64__)
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[6]))
    #elif defined(__aarch64__)
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[13]))
    #else
        #error Unknown CPU architecture
    #endif
    
    #define MD_INIT_CONTEXT(_thread, _sp, _main) \
        ST_BEGIN_MACRO                             \
        if (MD_SETJMP((_thread)->context))         \
            _main();                                 \
        MD_GET_SP(_thread) = (long) (_sp);         \
        ST_END_MACRO

    #define MD_GET_UTIME()            \
        struct timeval tv;              \
        (void) gettimeofday(&tv, NULL); \
        return (tv.tv_sec * 1000000LL + tv.tv_usec)

#elif defined (LINUX)

    /*
     * These are properties of the linux kernel and are the same on every
     * flavor and architecture.
     */
    #define MD_USE_BSD_ANON_MMAP
    #define MD_ACCEPT_NB_NOT_INHERITED
    /*
     * Modern GNU/Linux is Posix.1g compliant.
     */
    #define MD_HAVE_SOCKLEN_T

    /*
     * All architectures and flavors of linux have the gettimeofday
     * function but if you know of a faster way, use it.
     */
    #define MD_GET_UTIME()            \
        struct timeval tv;              \
        (void) gettimeofday(&tv, NULL); \
        return (tv.tv_sec * 1000000LL + tv.tv_usec)

    #if defined(__i386__)
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[4]))
    #elif defined(__amd64__) || defined(__x86_64__)
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[6]))
    #elif defined(__aarch64__)
        /* https://github.com/ossrs/state-threads/issues/9 */
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[13]))
    #elif defined(__arm__)
        /* https://github.com/ossrs/state-threads/issues/1#issuecomment-244648573 */
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[8]))
    #elif defined(__mips64)
        /* https://github.com/ossrs/state-threads/issues/21 */
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0]))
    #elif defined(__mips__)
        /* https://github.com/ossrs/state-threads/issues/21 */
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0]))
    #elif defined(__riscv)
        /* https://github.com/ossrs/state-threads/pull/28 */
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0]))
    #elif defined(__loongarch64)
        /* https://github.com/ossrs/state-threads/issues/24 */
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[0]))
    #else
        #error "Unknown CPU architecture"
    #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */

    #define MD_INIT_CONTEXT(_thread, _sp, _main) \
        ST_BEGIN_MACRO                             \
        if (MD_SETJMP((_thread)->context))         \
            _main();                                 \
        MD_GET_SP(_thread) = (long) (_sp);         \
        ST_END_MACRO

#elif defined (CYGWIN64)

    // For CYGWIN64, build SRS on Windows.
    #define MD_USE_BSD_ANON_MMAP
    #define MD_ACCEPT_NB_INHERITED
    #define MD_HAVE_SOCKLEN_T

    #define MD_USE_BUILTIN_SETJMP

    #if defined(__amd64__) || defined(__x86_64__)
        #define MD_GET_SP(_t) *((long *)&((_t)->context[0].__jmpbuf[6]))
    #else
        #error Unknown CPU architecture
    #endif

    #define MD_INIT_CONTEXT(_thread, _sp, _main) \
        ST_BEGIN_MACRO                             \
        if (MD_SETJMP((_thread)->context))         \
            _main();                                 \
        MD_GET_SP(_thread) = (long) (_sp);         \
        ST_END_MACRO

    #define MD_GET_UTIME()            \
        struct timeval tv;              \
        (void) gettimeofday(&tv, NULL); \
        return (tv.tv_sec * 1000000LL + tv.tv_usec)

#else
    #error Unknown OS
#endif /* OS */

#ifndef MD_STACK_PAD_SIZE
    #define MD_STACK_PAD_SIZE 128
#endif

#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t)
    #define socklen_t int
#endif

#ifndef MD_CAP_STACK
    #define MD_CAP_STACK(var_addr)
#endif

#endif /* !__ST_MD_H__ */

