<commit_before>/*
  Copyright 2014 <NAME>  <<EMAIL>>

  This file is part of php_posixclocks and 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.
 */


/*
 * Includes
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_posixclocks.h"
#include "posixclocks_ver.h"


/*
 * Macros
 */

#define BILLION_D   1000000000.0
#define BILLION_LD  1000000000.
#define FLOOR_TO_CLOCKRES -1
#define CONSTFLAGS  CONST_CS | CONST_PERSISTENT

#define STR(str) #str

#define INTLEN(val) \
    (size_t) ((val >= 0 && val < 10) ? 1 : floor(log10(abs(val))) + (val < 0 ? 2 : 1))

#define TIMESPEC_TO_DOUBLE(ts) (ts.tv_sec + (ts.tv_nsec / BILLION_D))

#define TIMESPEC_TO_LDOUBLE(ts) (ts.tv_sec + (ts.tv_nsec / BILLION_LD))

#define DEFINE_CLOCK(id) \
    REGISTER_LONG_CONSTANT(STR(PSXCLK_C_ ## id), CLOCK_ ## id, CONSTFLAGS)

typedef enum {
    TIMESPEC = 10,
    FLOAT = 20,
    STRING = 30
} ReturnType;


/*
 * Static globals
 */

static int le_posixclocks;

static char * timespec_to_string(struct timespec const * ts_p)
{
    long decimal = ts_p->tv_nsec;
    size_t result_sz;
    char * result_p;

    /* Remove trailing zeros for decimal string representation */
    while (decimal % 10 == 0) {
        decimal /= 10;
    }

    result_sz = (sizeof (char) * (INTLEN(ts_p->tv_sec) + 1 + INTLEN(decimal))) + 1;

    result_p = emalloc(result_sz);
    if (!result_p) {
        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to allocate memory [%s] (%s)", __func__, strerror(errno));
        return NULL;
    }

    snprintf(result_p, result_sz, "%jd.%ld", (intmax_t) ts_p->tv_sec, decimal);

    return result_p;
}

static zval * timespec_to_zval(struct timespec const * ts_p)
{
    long nsec = ts_p->tv_nsec;
    zval * obj_p;

    MAKE_STD_ZVAL(obj_p);
    object_init(obj_p);

    // TODO: Check value against LONG_MAX instead?
    if (sizeof (ts_p->tv_sec) <= SIZEOF_LONG) {
        add_property_long(obj_p, "tv_sec", ts_p->tv_sec);
    } else {
        char secstr[(size_t) (INTLEN(ts_p->tv_sec) + 1)];
        snprintf(secstr, sizeof (secstr), "%d", ts_p->tv_sec);
        add_property_string(obj_p, "tv_sec", secstr, 0);
    }

    add_property_long(obj_p, "tv_nsec", nsec);

    return obj_p;
}

/*
 * PHP callbacks
 */

PHP_MINIT_FUNCTION(posixclocks)
{
    REGISTER_LONG_CONSTANT("PSXCLK_AS_TIMESPEC", TIMESPEC, CONSTFLAGS);
    REGISTER_LONG_CONSTANT("PSXCLK_AS_FLOAT", FLOAT, CONSTFLAGS);
    REGISTER_LONG_CONSTANT("PSXCLK_AS_STRING", STRING, CONSTFLAGS);
    REGISTER_LONG_CONSTANT("PSXCLK_FLOOR_TO_CLOCKRES", FLOOR_TO_CLOCKRES, CONSTFLAGS);

    DEFINE_CLOCK(REALTIME);

#    ifdef CLOCK_MONOTONIC
    DEFINE_CLOCK(MONOTONIC);
#    endif

#    ifdef CLOCK_PROCESS_CPUTIME_ID
    DEFINE_CLOCK(PROCESS_CPUTIME_ID);
#    endif

#    ifdef CLOCK_THREAD_CPUTIME_ID
    DEFINE_CLOCK(THREAD_CPUTIME_ID);
#    endif

#    ifdef CLOCK_MONOTONIC_RAW
    DEFINE_CLOCK(MONOTONIC_RAW);
#    endif

#    ifdef CLOCK_REALTIME_COARSE
    DEFINE_CLOCK(REALTIME_COARSE);
#    endif

#    ifdef CLOCK_MONOTONIC_COARSE
    DEFINE_CLOCK(MONOTONIC_COARSE);
#    endif

#    ifdef CLOCK_BOOTTIME
    DEFINE_CLOCK(BOOTTIME);
#    endif

    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(posixclocks)
{
    return SUCCESS;
}

PHP_MINFO_FUNCTION(posixclocks)
{
    char precision[50];
    struct timespec clock_res;

#    define PRINTINFO_SUPPORTED(clock_id)                                \
    clock_getres(CLOCK_ ## clock_id, &clock_res);                      \
    snprintf(precision, 50, "%.0le", TIMESPEC_TO_LDOUBLE(clock_res));  \
    php_info_print_table_row(3, #clock_id, "Yes", precision)

#    define PRINTINFO_UNSUPPORTED(clock_id) \<commit_msg>Fix phpinfo error and TIMESPEC_TO_LDOUBLE macro<commit_after>/*
  Copyright 2014 <NAME>  <<EMAIL>>

  This file is part of php_posixclocks and 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.
 */


/*
 * Includes
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_posixclocks.h"
#include "posixclocks_ver.h"


/*
 * Macros
 */

#define BILLION_D   1000000000.0
#define BILLION_LD  1000000000.0L
#define FLOOR_TO_CLOCKRES -1
#define CONSTFLAGS  CONST_CS | CONST_PERSISTENT

#define STR(str) #str

#define INTLEN(val) \
    (size_t) ((val >= 0 && val < 10) ? 1 : floor(log10(abs(val))) + (val < 0 ? 2 : 1))

#define TIMESPEC_TO_DOUBLE(ts) (ts.tv_sec + (ts.tv_nsec / BILLION_D))

#define TIMESPEC_TO_LDOUBLE(ts) (ts.tv_sec + (ts.tv_nsec / BILLION_LD))

#define DEFINE_CLOCK(id) \
    REGISTER_LONG_CONSTANT(STR(PSXCLK_C_ ## id), CLOCK_ ## id, CONSTFLAGS)

typedef enum {
    TIMESPEC = 10,
    FLOAT = 20,
    STRING = 30
} ReturnType;


/*
 * Static globals
 */

static int le_posixclocks;

static char * timespec_to_string(struct timespec const * ts_p)
{
    long decimal = ts_p->tv_nsec;
    size_t result_sz;
    char * result_p;

    /* Remove trailing zeros for decimal string representation */
    while (decimal % 10 == 0) {
        decimal /= 10;
    }

    result_sz = (sizeof (char) * (INTLEN(ts_p->tv_sec) + 1 + INTLEN(decimal))) + 1;

    result_p = emalloc(result_sz);
    if (!result_p) {
        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to allocate memory [%s] (%s)", __func__, strerror(errno));
        return NULL;
    }

    snprintf(result_p, result_sz, "%jd.%ld", (intmax_t) ts_p->tv_sec, decimal);

    return result_p;
}

static zval * timespec_to_zval(struct timespec const * ts_p)
{
    long nsec = ts_p->tv_nsec;
    zval * obj_p;

    MAKE_STD_ZVAL(obj_p);
    object_init(obj_p);

    // TODO: Check value against LONG_MAX instead?
    if (sizeof (ts_p->tv_sec) <= SIZEOF_LONG) {
        add_property_long(obj_p, "tv_sec", ts_p->tv_sec);
    } else {
        char secstr[(size_t) (INTLEN(ts_p->tv_sec) + 1)];
        snprintf(secstr, sizeof (secstr), "%d", ts_p->tv_sec);
        add_property_string(obj_p, "tv_sec", secstr, 0);
    }

    add_property_long(obj_p, "tv_nsec", nsec);

    return obj_p;
}

/*
 * PHP callbacks
 */

PHP_MINIT_FUNCTION(posixclocks)
{
    REGISTER_LONG_CONSTANT("PSXCLK_AS_TIMESPEC", TIMESPEC, CONSTFLAGS);
    REGISTER_LONG_CONSTANT("PSXCLK_AS_FLOAT", FLOAT, CONSTFLAGS);
    REGISTER_LONG_CONSTANT("PSXCLK_AS_STRING", STRING, CONSTFLAGS);
    REGISTER_LONG_CONSTANT("PSXCLK_FLOOR_TO_CLOCKRES", FLOOR_TO_CLOCKRES, CONSTFLAGS);

    DEFINE_CLOCK(REALTIME);

#    ifdef CLOCK_MONOTONIC
    DEFINE_CLOCK(MONOTONIC);
#    endif

#    ifdef CLOCK_PROCESS_CPUTIME_ID
    DEFINE_CLOCK(PROCESS_CPUTIME_ID);
#    endif

#    ifdef CLOCK_THREAD_CPUTIME_ID
    DEFINE_CLOCK(THREAD_CPUTIME_ID);
#    endif

#    ifdef CLOCK_MONOTONIC_RAW
    DEFINE_CLOCK(MONOTONIC_RAW);
#    endif

#    ifdef CLOCK_REALTIME_COARSE
    DEFINE_CLOCK(REALTIME_COARSE);
#    endif

#    ifdef CLOCK_MONOTONIC_COARSE
    DEFINE_CLOCK(MONOTONIC_COARSE);
#    endif

#    ifdef CLOCK_BOOTTIME
    DEFINE_CLOCK(BOOTTIME);
#    endif

    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(posixclocks)
{
    return SUCCESS;
}

PHP_MINFO_FUNCTION(posixclocks)
{
    char precision[50];
    struct timespec clock_res;

#    define PRINTINFO_SUPPORTED(clock_id)                              \
    clock_getres(CLOCK_ ## clock_id, &clock_res);                      \
    snprintf(precision, 50, "%.0Le", TIMESPEC_TO_LDOUBLE(clock_res));  \
    php_info_print_table_row(3, #clock_id, "Yes", precision)

#    define PRINTINFO_UNSUPPORTED(clock_id) \
{'fraction_non_alphanumeric': 0.10987449680322046, 'fraction_numerical': 0.014326308311626805, 'mean_word_length': 3.5857763300760044, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '53052', 'n_tokens_mistral': 3374, 'n_tokens_neox': 3146, 'n_words': 685}