emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master bffceab 1/2: Add conversions to and from struct tim


From: Philipp Stephani
Subject: [Emacs-diffs] master bffceab 1/2: Add conversions to and from struct timespec to module interface.
Date: Wed, 24 Apr 2019 06:58:37 -0400 (EDT)

branch: master
commit bffceab6339fb4042588b893ef754c6264379e75
Author: Philipp Stephani <address@hidden>
Commit: Philipp Stephani <address@hidden>

    Add conversions to and from struct timespec to module interface.
    
    Time values are a fundamental data type, and such conversions are hard
    to implement within modules because of the various forms of time
    values in Emacs Lisp.  Adding dedicated conversion functions can
    significantly simplify module code dealing with times.
    
    This approach uses nanosecond precision.  While Emacs in theory has
    support for higher-precision time values, in practice most languages
    and standards, such as POSIX, C, Java, and Go, have settled on
    nanosecond-precision integers to represent time.
    
    * src/emacs-module.h.in: Add header for struct timespec.
    
    * src/module-env-27.h: Add module functions for time conversion.
    
    * src/emacs-module.c (module_extract_time, module_make_time): New
    functions.
    (initialize_environment): Use them.
    
    * test/data/emacs-module/mod-test.c (Fmod_test_add_nanosecond): New
    test function.
    (emacs_module_init): Define it.
    
    * test/src/emacs-module-tests.el (mod-test-add-nanosecond/valid)
    (mod-test-add-nanosecond/nil, mod-test-add-nanosecond/invalid): New
    unit tests.
    
    * doc/lispref/internals.texi (Module Values): Document time
    conversion functions.
---
 doc/lispref/internals.texi        | 44 +++++++++++++++++++++++++++++++++++++++
 etc/NEWS                          |  3 +++
 src/emacs-module.c                | 17 +++++++++++++++
 src/emacs-module.h.in             |  1 +
 src/module-env-27.h               |  6 ++++++
 test/data/emacs-module/mod-test.c | 13 ++++++++++++
 test/src/emacs-module-tests.el    | 28 +++++++++++++++++++++++++
 7 files changed, 112 insertions(+)

diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index 25892d4..0e7a133 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -1387,6 +1387,38 @@ This function returns the value of a Lisp float 
specified by
 @var{arg}, as a C @code{double} value.
 @end deftypefn
 
address@hidden Function struct timespec extract_time (emacs_env address@hidden, 
emacs_value @var{time})
+This function, which is available since Emacs 27, interprets
address@hidden as an Emacs Lisp time value and returns the corresponding
address@hidden timespec}.  @xref{Time of Day}.  @code{struct timespec}
+represents a timestamp with nanosecond precision.  It has the
+following members:
+
address@hidden @code
address@hidden time_t tv_sec
+Whole number of seconds.
address@hidden long tv_nsec
+Fractional seconds as number of nanoseconds, always less than one
+billion.
address@hidden table
+
address@hidden
address@hidden Time,,,libc}.
+
+If @var{time} has higher precision than nanoseconds, then this
+function truncates it to nanosecond precision.  This function signals
+an error if @var{time} (truncated to nanoseconds) cannot be
+represented by @code{struct timespec}.  For example, if @code{time_t}
+is a 32-bit integral type, then a @var{time} value of ten billion
+seconds would signal an error, but a @var{time} value of 600
+picoseconds would get truncated to zero.
+
+If you need to deal with time values that are not representable by
address@hidden timespec}, or if you want higher precision, call the Lisp
+function @code{encode-time} and work with its return value.
address@hidden Conversion}.
address@hidden deftypefn
+
 @deftypefn Function bool copy_string_contents (emacs_env address@hidden, 
emacs_value @var{arg}, char address@hidden, ptrdiff_t address@hidden)
 This function stores the UTF-8 encoded text of a Lisp string specified
 by @var{arg} in the array of @code{char} pointed by @var{buf}, which
@@ -1452,6 +1484,18 @@ This function takes a @code{double} argument @var{d} and 
returns the
 corresponding Emacs floating-point value.
 @end deftypefn
 
address@hidden Function emacs_value make_time (emacs_env address@hidden, struct 
timespec @var{time})
+This function, which is available since Emacs 27, takes a @code{struct
+timespec} argument @var{time} and returns the corresponding Emacs
+timestamp as a pair @code{(@var{ticks} . @var{hz})}.  @xref{Time of
+Day}.  The return value represents exactly the same timestamp as
address@hidden: all input values are representable, and there is never a
+loss of precision.  @address@hidden and
address@hidden@var{time}.tv_nsec} can be arbitrary values.  In particular,
+there's no requirement that @var{time} be normalized.  This means that
address@hidden@var{time}.tv_nsec} can be negative or larger than 999,999,999.
address@hidden deftypefn
+
 @deftypefn Function emacs_value make_string (emacs_env address@hidden, const 
char address@hidden, ptrdiff_t @var{strlen})
 This function creates an Emacs string from C text string pointed by
 @var{str} whose length in bytes, not including the terminating null
diff --git a/etc/NEWS b/etc/NEWS
index b13ab47..fc9b828 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1910,6 +1910,9 @@ returns a regexp that never matches anything, which is an 
identity for
 this operation.  Previously, the empty string was returned in this
 case.
 
+** New module environment functions 'make_time' and 'extract_time' to
+convert between timespec structures and Emacs Lisp time values.
+
 
 * Changes in Emacs 27.1 on Non-Free Operating Systems
 
diff --git a/src/emacs-module.c b/src/emacs-module.c
index b812fdc..e46af30 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -77,6 +77,7 @@ To add a new module function, proceed as follows:
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 
 #include "lisp.h"
 #include "dynlib.h"
@@ -737,6 +738,20 @@ module_process_input (emacs_env *env)
   return emacs_process_input_continue;
 }
 
+static struct timespec
+module_extract_time (emacs_env *env, emacs_value value)
+{
+  MODULE_FUNCTION_BEGIN ((struct timespec) {0});
+  return lisp_time_argument (value_to_lisp (value));
+}
+
+static emacs_value
+module_make_time (emacs_env *env, struct timespec time)
+{
+  MODULE_FUNCTION_BEGIN (NULL);
+  return lisp_to_value (env, make_lisp_time (time));
+}
+
 
 /* Subroutines.  */
 
@@ -1140,6 +1155,8 @@ initialize_environment (emacs_env *env, struct 
emacs_env_private *priv)
   env->vec_size = module_vec_size;
   env->should_quit = module_should_quit;
   env->process_input = module_process_input;
+  env->extract_time = module_extract_time;
+  env->make_time = module_make_time;
   Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments);
   return env;
 }
diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in
index 009d158..bfbe226 100644
--- a/src/emacs-module.h.in
+++ b/src/emacs-module.h.in
@@ -22,6 +22,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <stdint.h>
 #include <stddef.h>
+#include <time.h>
 
 #ifndef __cplusplus
 #include <stdbool.h>
diff --git a/src/module-env-27.h b/src/module-env-27.h
index b491b60..e63843f 100644
--- a/src/module-env-27.h
+++ b/src/module-env-27.h
@@ -2,3 +2,9 @@
      function should quit.  */
   enum emacs_process_input_result (*process_input) (emacs_env *env)
     EMACS_ATTRIBUTE_NONNULL (1);
+
+  struct timespec (*extract_time) (emacs_env *env, emacs_value value)
+    EMACS_ATTRIBUTE_NONNULL (1);
+
+  emacs_value (*make_time) (emacs_env *env, struct timespec time)
+    EMACS_ATTRIBUTE_NONNULL (1);
diff --git a/test/data/emacs-module/mod-test.c 
b/test/data/emacs-module/mod-test.c
index a39e41a..dbdbfec 100644
--- a/test/data/emacs-module/mod-test.c
+++ b/test/data/emacs-module/mod-test.c
@@ -366,6 +366,18 @@ Fmod_test_sleep_until (emacs_env *env, ptrdiff_t nargs, 
emacs_value *args,
   return env->intern (env, "finished");
 }
 
+static emacs_value
+Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
+                          void *data)
+{
+  assert (nargs == 1);
+  struct timespec time = env->extract_time (env, args[0]);
+  assert (time.tv_nsec >= 0);
+  assert (time.tv_nsec < 2000000000);  /* possible leap second */
+  time.tv_nsec++;
+  return env->make_time (env, time);
+}
+
 /* Lisp utilities for easier readability (simple wrappers).  */
 
 /* Provide FEATURE to Emacs.  */
@@ -434,6 +446,7 @@ emacs_module_init (struct emacs_runtime *ert)
   DEFUN ("mod-test-invalid-finalizer", Fmod_test_invalid_finalizer, 0, 0,
          NULL, NULL);
   DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL);
+  DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, 
NULL);
 
 #undef DEFUN
 
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index 35aaaa6..eea4c61 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -310,4 +310,32 @@ Interactively, you can try hitting \\[keyboard-quit] to 
quit."
                       'finished))
         (quit)))))
 
+(ert-deftest mod-test-add-nanosecond/valid ()
+  (dolist (input (list
+                  ;; Some realistic examples.
+                  (current-time) (time-to-seconds)
+                  (encode-time 12 34 5 6 7 2019 t)
+                  ;; Various legacy timestamp forms.
+                  '(123 456) '(123 456 789) '(123 456 789 6000)
+                  ;; Corner case: this will result in a nanosecond
+                  ;; value of 1000000000 after addition.  The module
+                  ;; code should handle this correctly.
+                  '(123 65535 999999 999000)
+                  ;; Seconds since the epoch.
+                  123 123.45
+                  ;; New (TICKS . HZ) format.
+                  '(123456789 . 1000000000)))
+    (ert-info ((format "input: %s" input))
+      (should (time-equal-p (mod-test-add-nanosecond input)
+                            (time-add input '(0 0 0 1000)))))))
+
+(ert-deftest mod-test-add-nanosecond/nil ()
+  (should (<= (float-time (mod-test-add-nanosecond nil))
+              (+ (float-time) 1e-9))))
+
+(ert-deftest mod-test-add-nanosecond/invalid ()
+  (dolist (input '(1.0e+INF 1.0e-INF 0.0e+NaN (123) (123.45 6 7) "foo" [1 2]))
+    (ert-info ((format "input: %s" input))
+      (should-error (mod-test-add-nanosecond input)))))
+
 ;;; emacs-module-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]