[libc-commits] [libc] a72499e - [libc] Introduce asctime, asctime_r to LLVM libc

Raman Tenneti via libc-commits libc-commits at lists.llvm.org
Mon May 3 17:15:31 PDT 2021


Author: Raman Tenneti
Date: 2021-05-03T17:15:00-07:00
New Revision: a72499e47537c02a33d7c1c2e512d0b4a0ecbb89

URL: https://github.com/llvm/llvm-project/commit/a72499e47537c02a33d7c1c2e512d0b4a0ecbb89
DIFF: https://github.com/llvm/llvm-project/commit/a72499e47537c02a33d7c1c2e512d0b4a0ecbb89.diff

LOG: [libc] Introduce asctime, asctime_r to LLVM libc

[libc] Introduce asctime, asctime_r to LLVM libc

asctime and asctime_r share the same common code. They call asctime_internal
a static inline function.

asctime uses snprintf to return the string representation in a buffer.
It uses the following format (26 characters is the buffer size) as per
7.27.3.1 section in http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2478.pdf.
The buf parameter for asctime_r shall point to a buffer of at least 26 bytes.

snprintf(buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",...)

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D99686

Added: 
    libc/src/time/asctime.cpp
    libc/src/time/asctime.h
    libc/src/time/asctime_r.cpp
    libc/src/time/asctime_r.h
    libc/test/src/time/TmHelper.h
    libc/test/src/time/asctime_r_test.cpp
    libc/test/src/time/asctime_test.cpp

Modified: 
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/stdc.td
    libc/src/time/CMakeLists.txt
    libc/src/time/time_utils.h
    libc/test/src/time/CMakeLists.txt
    libc/test/src/time/mktime_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 36067fbbf22fe..c68888ab3c700 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -249,6 +249,8 @@ def TimeAPI : PublicAPI<"time.h"> {
   ];
 
   let Functions = [
+    "asctime",
+    "asctime_r",
     "gmtime",
     "gmtime_r",
     "mktime",

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 678cc460d28a3..764059d692127 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -181,6 +181,8 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.threads.thrd_join
 
     # time.h entrypoints
+    libc.src.time.asctime
+    libc.src.time.asctime_r
     libc.src.time.gmtime
     libc.src.time.gmtime_r
     libc.src.time.mktime

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index f771ad3249e51..b50d2ed44d73b 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -599,6 +599,19 @@ def StdC : StandardSpec<"stdc"> {
       ],
       [], // Enumerations
       [
+          FunctionSpec<
+              "asctime",
+              RetValSpec<CharPtr>,
+              [ArgSpec<StructTmPtr>]
+          >,
+          FunctionSpec<
+              "asctime_r",
+              RetValSpec<CharPtr>,
+              [
+                  ArgSpec<StructTmPtr>,
+                  ArgSpec<CharPtr>,
+              ]
+          >,
           FunctionSpec<
               "gmtime",
               RetValSpec<StructTmPtr>,

diff  --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 03343cf036c53..7e849cf0543ae 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -10,6 +10,28 @@ add_object_library(
     libc.src.errno.__errno_location
 )
 
+add_entrypoint_object(
+  asctime
+  SRCS
+    asctime.cpp
+  HDRS
+    asctime.h
+  DEPENDS
+    .time_utils
+    libc.include.time
+)
+
+add_entrypoint_object(
+  asctime_r
+  SRCS
+    asctime_r.cpp
+  HDRS
+    asctime_r.h
+  DEPENDS
+    .time_utils
+    libc.include.time
+)
+
 add_entrypoint_object(
   gmtime
   SRCS

diff  --git a/libc/src/time/asctime.cpp b/libc/src/time/asctime.cpp
new file mode 100644
index 0000000000000..cdf5391d29ebf
--- /dev/null
+++ b/libc/src/time/asctime.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of asctime function --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/time/asctime.h"
+#include "src/__support/common.h"
+#include "src/time/time_utils.h"
+
+namespace __llvm_libc {
+
+using __llvm_libc::time_utils::TimeConstants;
+
+LLVM_LIBC_FUNCTION(char *, asctime, (const struct tm *timeptr)) {
+  static char buffer[TimeConstants::AsctimeBufferSize];
+  return time_utils::asctime(timeptr, buffer, TimeConstants::AsctimeMaxBytes);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/time/asctime.h b/libc/src/time/asctime.h
new file mode 100644
index 0000000000000..9d75b40148afd
--- /dev/null
+++ b/libc/src/time/asctime.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of asctime ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_TIME_ASCTIME_H
+#define LLVM_LIBC_SRC_TIME_ASCTIME_H
+
+#include <time.h>
+
+namespace __llvm_libc {
+
+char *asctime(const struct tm *timeptr);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_TIME_ASCTIME_H
+
+#include "include/time.h"

diff  --git a/libc/src/time/asctime_r.cpp b/libc/src/time/asctime_r.cpp
new file mode 100644
index 0000000000000..fadc13e3b5a02
--- /dev/null
+++ b/libc/src/time/asctime_r.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of asctime_r function ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/time/asctime_r.h"
+#include "src/__support/common.h"
+#include "src/time/time_utils.h"
+
+namespace __llvm_libc {
+
+using __llvm_libc::time_utils::TimeConstants;
+
+LLVM_LIBC_FUNCTION(char *, asctime_r,
+                   (const struct tm *timeptr, char *buffer)) {
+  return time_utils::asctime(timeptr, buffer, TimeConstants::AsctimeMaxBytes);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/time/asctime_r.h b/libc/src/time/asctime_r.h
new file mode 100644
index 0000000000000..8521e782a509d
--- /dev/null
+++ b/libc/src/time/asctime_r.h
@@ -0,0 +1,22 @@
+//===-- Implementation header of asctime_r ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_TIME_ASCTIME_R_H
+#define LLVM_LIBC_SRC_TIME_ASCTIME_R_H
+
+#include <time.h>
+
+namespace __llvm_libc {
+
+char *asctime_r(const struct tm *timeptr, char *buffer);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_TIME_ASCTIME_R_H
+
+#include "include/time.h"

diff  --git a/libc/src/time/time_utils.h b/libc/src/time/time_utils.h
index cca03007084c9..c1c7735f49060 100644
--- a/libc/src/time/time_utils.h
+++ b/libc/src/time/time_utils.h
@@ -9,6 +9,8 @@
 #ifndef LLVM_LIBC_SRC_TIME_TIME_UTILS_H
 #define LLVM_LIBC_SRC_TIME_TIME_UTILS_H
 
+#include <stddef.h> // For size_t.
+
 #include "include/errno.h"
 
 #include "src/errno/llvmlibc_errno.h"
@@ -33,6 +35,14 @@ struct TimeConstants {
   static constexpr int NumberOfSecondsInLeapYear =
       (DaysPerNonLeapYear + 1) * SecondsPerDay;
 
+  // For asctime the behavior is undefined if struct tm's tm_wday or tm_mon are
+  // not within the normal ranges as defined in <time.h>, or if struct tm's
+  // tm_year exceeds {INT_MAX}-1990, or if the below asctime_internal algorithm
+  // would attempt to generate more than 26 bytes of output (including the
+  // terminating null).
+  static constexpr int AsctimeBufferSize = 256;
+  static constexpr int AsctimeMaxBytes = 26;
+
   /* 2000-03-01 (mod 400 year, immediately after feb29 */
   static constexpr int64_t SecondsUntil2000MarchFirst =
       (946684800LL + SecondsPerDay * (31 + 29));
@@ -63,6 +73,46 @@ static inline time_t OutOfRange() {
   return static_cast<time_t>(-1);
 }
 
+static inline void InvalidValue() { llvmlibc_errno = EINVAL; }
+
+static inline char *asctime(const struct tm *timeptr, char *buffer,
+                            size_t bufferLength) {
+  if (timeptr == nullptr || buffer == nullptr) {
+    InvalidValue();
+    return nullptr;
+  }
+  if (timeptr->tm_wday < 0 ||
+      timeptr->tm_wday > (TimeConstants::DaysPerWeek - 1)) {
+    InvalidValue();
+    return nullptr;
+  }
+  if (timeptr->tm_mon < 0 ||
+      timeptr->tm_mon > (TimeConstants::MonthsPerYear - 1)) {
+    InvalidValue();
+    return nullptr;
+  }
+
+  // TODO(rtenneti): i18n the following strings.
+  static const char *WeekDaysName[TimeConstants::DaysPerWeek] = {
+      "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+  static const char *MonthsName[TimeConstants::MonthsPerYear] = {
+      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+  int written_size = __builtin_snprintf(
+      buffer, bufferLength, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
+      WeekDaysName[timeptr->tm_wday], MonthsName[timeptr->tm_mon],
+      timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec,
+      TimeConstants::TimeYearBase + timeptr->tm_year);
+  if (written_size < 0)
+    return nullptr;
+  if (static_cast<size_t>(written_size) >= bufferLength) {
+    OutOfRange();
+    return nullptr;
+  }
+  return buffer;
+}
+
 static inline struct tm *gmtime_internal(const time_t *timer,
                                          struct tm *result) {
   int64_t seconds = *timer;

diff  --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index e3bf5e228c653..514a6faa57039 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -1,5 +1,31 @@
 add_libc_testsuite(libc_time_unittests)
 
+add_libc_unittest(
+  asctime
+  SUITE
+    libc_time_unittests
+  SRCS
+    asctime_test.cpp
+  HDRS
+    TmHelper.h
+    TmMatcher.h
+  DEPENDS
+    libc.src.time.asctime
+)
+
+add_libc_unittest(
+  asctime_r
+  SUITE
+    libc_time_unittests
+  SRCS
+    asctime_r_test.cpp
+  HDRS
+    TmHelper.h
+    TmMatcher.h
+  DEPENDS
+    libc.src.time.asctime_r
+)
+
 add_libc_unittest(
   gmtime
   SUITE
@@ -31,6 +57,7 @@ add_libc_unittest(
   SRCS
     mktime_test.cpp
   HDRS
+    TmHelper.h
     TmMatcher.h
   DEPENDS
     libc.src.time.mktime

diff  --git a/libc/test/src/time/TmHelper.h b/libc/test/src/time/TmHelper.h
new file mode 100644
index 0000000000000..6a0b3edac55ea
--- /dev/null
+++ b/libc/test/src/time/TmHelper.h
@@ -0,0 +1,42 @@
+//===---- TmHelper.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_SRC_TIME_TM_HELPER_H
+#define LLVM_LIBC_TEST_SRC_TIME_TM_HELPER_H
+
+#include <time.h>
+
+#include "src/time/time_utils.h"
+
+using __llvm_libc::time_utils::TimeConstants;
+
+namespace __llvm_libc {
+namespace tmhelper {
+namespace testing {
+
+// A helper function to initialize tm data structure.
+static inline void InitializeTmData(struct tm *tm_data, int year, int month,
+                                    int mday, int hour, int min, int sec,
+                                    int wday, int yday) {
+  struct tm temp = {.tm_sec = sec,
+                    .tm_min = min,
+                    .tm_hour = hour,
+                    .tm_mday = mday,
+                    .tm_mon = month - 1, // tm_mon starts with 0 for Jan
+                    // years since 1900
+                    .tm_year = year - TimeConstants::TimeYearBase,
+                    .tm_wday = wday,
+                    .tm_yday = yday};
+  *tm_data = temp;
+}
+
+} // namespace testing
+} // namespace tmhelper
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_TEST_SRC_TIME_TM_HELPER_H

diff  --git a/libc/test/src/time/asctime_r_test.cpp b/libc/test/src/time/asctime_r_test.cpp
new file mode 100644
index 0000000000000..66db4413d3d5d
--- /dev/null
+++ b/libc/test/src/time/asctime_r_test.cpp
@@ -0,0 +1,60 @@
+//===-- Unittests for asctime_r
+//--------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/time/asctime_r.h"
+#include "src/time/time_utils.h"
+#include "test/src/time/TmHelper.h"
+#include "utils/UnitTest/Test.h"
+
+using __llvm_libc::time_utils::TimeConstants;
+
+static inline char *call_asctime_r(struct tm *tm_data, int year, int month,
+                                   int mday, int hour, int min, int sec,
+                                   int wday, int yday, char *buffer) {
+  __llvm_libc::tmhelper::testing::InitializeTmData(tm_data, year, month, mday,
+                                                   hour, min, sec, wday, yday);
+  return __llvm_libc::asctime_r(tm_data, buffer);
+}
+
+// asctime and asctime_r share the same code and thus didn't repeat all the
+// tests from asctime. Added couple of validation tests.
+TEST(LlvmLibcAsctimeR, Nullptr) {
+  char *result;
+  result = __llvm_libc::asctime_r(nullptr, nullptr);
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+  ASSERT_STREQ(nullptr, result);
+
+  char buffer[TimeConstants::AsctimeBufferSize];
+  result = __llvm_libc::asctime_r(nullptr, buffer);
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+  ASSERT_STREQ(nullptr, result);
+
+  struct tm tm_data;
+  result = __llvm_libc::asctime_r(&tm_data, nullptr);
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+  ASSERT_STREQ(nullptr, result);
+}
+
+TEST(LlvmLibcAsctimeR, ValidDate) {
+  char buffer[TimeConstants::AsctimeBufferSize];
+  struct tm tm_data;
+  char *result;
+  // 1970-01-01 00:00:00. Test with a valid buffer size.
+  result = call_asctime_r(&tm_data,
+                          1970, // year
+                          1,    // month
+                          1,    // day
+                          0,    // hr
+                          0,    // min
+                          0,    // sec
+                          4,    // wday
+                          0,    // yday
+                          buffer);
+  ASSERT_STREQ("Thu Jan  1 00:00:00 1970\n", result);
+}

diff  --git a/libc/test/src/time/asctime_test.cpp b/libc/test/src/time/asctime_test.cpp
new file mode 100644
index 0000000000000..14201fd5a0de9
--- /dev/null
+++ b/libc/test/src/time/asctime_test.cpp
@@ -0,0 +1,215 @@
+//===-- Unittests for asctime ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/time/asctime.h"
+#include "test/src/time/TmHelper.h"
+#include "utils/UnitTest/Test.h"
+
+static inline char *call_asctime(struct tm *tm_data, int year, int month,
+                                 int mday, int hour, int min, int sec, int wday,
+                                 int yday) {
+  __llvm_libc::tmhelper::testing::InitializeTmData(tm_data, year, month, mday,
+                                                   hour, min, sec, wday, yday);
+  return __llvm_libc::asctime(tm_data);
+}
+
+TEST(LlvmLibcAsctime, Nullptr) {
+  char *result;
+  result = __llvm_libc::asctime(nullptr);
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+  ASSERT_STREQ(nullptr, result);
+}
+
+// Weekdays are in the range 0 to 6. Test passing invalid value in wday.
+TEST(LlvmLibcAsctime, InvalidWday) {
+  struct tm tm_data;
+  char *result;
+
+  // Test with wday = -1.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        1,    // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        -1,   // wday
+                        0);   // yday
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+
+  // Test with wday = 7.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        1,    // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        7,    // wday
+                        0);   // yday
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+}
+
+// Months are from January to December. Test passing invalid value in month.
+TEST(LlvmLibcAsctime, InvalidMonth) {
+  struct tm tm_data;
+  char *result;
+
+  // Test with month = 0.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        0,    // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        4,    // wday
+                        0);   // yday
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+
+  // Test with month = 13.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        13,   // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        4,    // wday
+                        0);   // yday
+  ASSERT_EQ(EINVAL, llvmlibc_errno);
+}
+
+TEST(LlvmLibcAsctime, ValidWeekdays) {
+  struct tm tm_data;
+  char *result;
+  // 1970-01-01 00:00:00.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        1,    // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        4,    // wday
+                        0);   // yday
+  ASSERT_STREQ("Thu Jan  1 00:00:00 1970\n", result);
+
+  // 1970-01-03 00:00:00.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        1,    // month
+                        3,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        6,    // wday
+                        0);   // yday
+  ASSERT_STREQ("Sat Jan  3 00:00:00 1970\n", result);
+
+  // 1970-01-04 00:00:00.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        1,    // month
+                        4,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        0,    // wday
+                        0);   // yday
+  ASSERT_STREQ("Sun Jan  4 00:00:00 1970\n", result);
+}
+
+TEST(LlvmLibcAsctime, ValidMonths) {
+  struct tm tm_data;
+  char *result;
+  // 1970-01-01 00:00:00.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        1,    // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        4,    // wday
+                        0);   // yday
+  ASSERT_STREQ("Thu Jan  1 00:00:00 1970\n", result);
+
+  // 1970-02-01 00:00:00.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        2,    // month
+                        1,    // day
+                        0,    // hr
+                        0,    // min
+                        0,    // sec
+                        0,    // wday
+                        0);   // yday
+  ASSERT_STREQ("Sun Feb  1 00:00:00 1970\n", result);
+
+  // 1970-12-31 23:59:59.
+  result = call_asctime(&tm_data,
+                        1970, // year
+                        12,   // month
+                        31,   // day
+                        23,   // hr
+                        59,   // min
+                        59,   // sec
+                        4,    // wday
+                        0);   // yday
+  ASSERT_STREQ("Thu Dec 31 23:59:59 1970\n", result);
+}
+
+TEST(LlvmLibcAsctime, EndOf32BitEpochYear) {
+  struct tm tm_data;
+  char *result;
+  // Test for maximum value of a signed 32-bit integer.
+  // Test implementation can encode time for Tue 19 January 2038 03:14:07 UTC.
+  result = call_asctime(&tm_data,
+                        2038, // year
+                        1,    // month
+                        19,   // day
+                        3,    // hr
+                        14,   // min
+                        7,    // sec
+                        2,    // wday
+                        7);   // yday
+  ASSERT_STREQ("Tue Jan 19 03:14:07 2038\n", result);
+}
+
+TEST(LlvmLibcAsctime, Max64BitYear) {
+  if (sizeof(time_t) == 4)
+    return;
+  // Mon Jan 1 12:50:50 2170 (200 years from 1970),
+  struct tm tm_data;
+  char *result;
+  result = call_asctime(&tm_data,
+                        2170, // year
+                        1,    // month
+                        1,    // day
+                        12,   // hr
+                        50,   // min
+                        50,   // sec
+                        1,    // wday
+                        50);  // yday
+  ASSERT_STREQ("Mon Jan  1 12:50:50 2170\n", result);
+
+  // Test for Tue Jan 1 12:50:50 in 2,147,483,647th year.
+  // This test would cause buffer overflow and thus asctime returns nullptr.
+  result = call_asctime(&tm_data,
+                        2147483647, // year
+                        1,          // month
+                        1,          // day
+                        12,         // hr
+                        50,         // min
+                        50,         // sec
+                        2,          // wday
+                        50);        // yday
+  ASSERT_EQ(EOVERFLOW, llvmlibc_errno);
+  ASSERT_STREQ(nullptr, result);
+}

diff  --git a/libc/test/src/time/mktime_test.cpp b/libc/test/src/time/mktime_test.cpp
index 93290d3ec0a4e..b9ea4ed64c0d1 100644
--- a/libc/test/src/time/mktime_test.cpp
+++ b/libc/test/src/time/mktime_test.cpp
@@ -9,6 +9,7 @@
 #include "src/time/mktime.h"
 #include "src/time/time_utils.h"
 #include "test/ErrnoSetterMatcher.h"
+#include "test/src/time/TmHelper.h"
 #include "test/src/time/TmMatcher.h"
 #include "utils/UnitTest/Test.h"
 
@@ -20,33 +21,18 @@ using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
 using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;
 using __llvm_libc::time_utils::TimeConstants;
 
-// A helper function to initialize tm data structure.
-static inline void initialize_tm_data(struct tm *tm_data, int year, int month,
-                                      int mday, int hour, int min, int sec,
-                                      int wday, int yday) {
-  struct tm temp = {.tm_sec = sec,
-                    .tm_min = min,
-                    .tm_hour = hour,
-                    .tm_mday = mday,
-                    .tm_mon = month - 1, // tm_mon starts with 0 for Jan
-                    // years since 1900
-                    .tm_year = year - TimeConstants::TimeYearBase,
-                    .tm_wday = wday,
-                    .tm_yday = yday};
-  *tm_data = temp;
-}
-
 static inline time_t call_mktime(struct tm *tm_data, int year, int month,
                                  int mday, int hour, int min, int sec, int wday,
                                  int yday) {
-  initialize_tm_data(tm_data, year, month, mday, hour, min, sec, wday, yday);
+  __llvm_libc::tmhelper::testing::InitializeTmData(tm_data, year, month, mday,
+                                                   hour, min, sec, wday, yday);
   return __llvm_libc::mktime(tm_data);
 }
 
 TEST(LlvmLibcMkTime, FailureSetsErrno) {
   struct tm tm_data;
-  initialize_tm_data(&tm_data, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, -1,
-                     0, 0);
+  __llvm_libc::tmhelper::testing::InitializeTmData(
+      &tm_data, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, -1, 0, 0);
   EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW));
 }
 


        


More information about the libc-commits mailing list