Skip to content

Commit 2f06475

Browse files
committed
[WIP] Basic structures for strftime
1 parent 54a4965 commit 2f06475

File tree

11 files changed

+815
-0
lines changed

11 files changed

+815
-0
lines changed

libc/src/stdio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ add_entrypoint_object(
239239

240240
add_subdirectory(printf_core)
241241
add_subdirectory(scanf_core)
242+
add_subdirectory(strftime_core)
242243

243244
add_entrypoint_object(
244245
remove
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
add_header_library(
3+
core_structs
4+
HDRS
5+
core_structs.h
6+
DEPENDS
7+
libc.src.__support.CPP.string_view
8+
libc.src.__support.FPUtil.fp_bits
9+
)
10+
11+
add_header_library(
12+
parser
13+
HDRS
14+
parser.h
15+
DEPENDS
16+
.core_structs
17+
libc.src.__support.arg_list
18+
libc.src.__support.ctype_utils
19+
libc.src.__support.str_to_integer
20+
libc.src.__support.CPP.algorithm
21+
libc.src.__support.CPP.bit
22+
libc.src.__support.CPP.optional
23+
libc.src.__support.CPP.string_view
24+
libc.src.__support.CPP.type_traits
25+
libc.src.__support.common
26+
)
27+
28+
29+
add_object_library(
30+
converter
31+
SRCS
32+
converter.cpp
33+
HDRS
34+
converter.h
35+
DEPENDS
36+
.core_structs
37+
libc.src.stdio.printf_core.writer
38+
libc.src.__support.big_int
39+
libc.src.math.log10
40+
libc.src.__support.common
41+
libc.src.__support.CPP.limits
42+
libc.src.__support.CPP.span
43+
libc.src.__support.CPP.string_view
44+
libc.src.__support.float_to_string
45+
libc.src.__support.FPUtil.fenv_impl
46+
libc.src.__support.FPUtil.fp_bits
47+
libc.src.__support.FPUtil.rounding_mode
48+
libc.src.__support.integer_to_string
49+
libc.src.__support.libc_assert
50+
libc.src.__support.uint128
51+
libc.src.__support.StringUtil.error_to_string
52+
)
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//===-- Format specifier converter for printf -------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
10+
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
11+
12+
#include "src/__support/CPP/string.h"
13+
#include "src/__support/CPP/string_view.h"
14+
#include "src/__support/integer_to_string.h"
15+
#include "src/__support/macros/config.h"
16+
#include "src/math/log10.h"
17+
#include "src/stdio/printf_core/writer.h"
18+
#include "src/stdio/strftime_core/core_structs.h"
19+
#include "src/stdio/strftime_core/time_internal_def.h"
20+
#include <time.h>
21+
22+
namespace LIBC_NAMESPACE_DECL {
23+
namespace strftime_core {
24+
25+
namespace details {
26+
27+
LIBC_INLINE cpp::optional<cpp::string_view>
28+
num_to_strview(uintmax_t num, cpp::span<char> bufref) {
29+
return IntegerToString<uintmax_t>::format_to(bufref, num);
30+
}
31+
32+
template<int width>
33+
LIBC_INLINE int write_num(uintmax_t num,
34+
printf_core::Writer *writer) {
35+
cpp::array<char, width> buf;
36+
return writer->write(*num_to_strview(num, buf));
37+
}
38+
39+
template <int width, char padding>
40+
LIBC_INLINE int write_num_with_padding(uintmax_t num,
41+
printf_core::Writer *writer) {
42+
cpp::array<char, width> buf;
43+
auto digits = log10(num) + 1;
44+
auto padding_needed = width - digits;
45+
int char_written = 0;
46+
for (int _ = 0; _ < padding_needed; _++) {
47+
char_written += writer->write(padding);
48+
}
49+
char_written += writer->write(*num_to_strview(num, buf));
50+
return char_written;
51+
}
52+
53+
} // namespace details
54+
55+
LIBC_INLINE int convert_weekday(printf_core::Writer *writer,
56+
const FormatSection &to_conv) {
57+
return writer->write(day_names[to_conv.time->tm_wday]);
58+
}
59+
60+
LIBC_INLINE int convert_zero_padded_day_of_year(printf_core::Writer *writer,
61+
const FormatSection &to_conv) {
62+
return details::write_num_with_padding<3, '0'>(to_conv.time->tm_yday + 1, writer);
63+
}
64+
65+
LIBC_INLINE int convert_zero_padded_day_of_month(printf_core::Writer *writer,
66+
const FormatSection &to_conv) {
67+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_mday, writer);
68+
}
69+
70+
LIBC_INLINE int convert_space_padded_day_of_month(printf_core::Writer *writer,
71+
const FormatSection &to_conv) {
72+
return details::write_num_with_padding<2, ' '>(to_conv.time->tm_mday, writer);
73+
}
74+
75+
LIBC_INLINE int convert_decimal_weekday(printf_core::Writer *writer,
76+
const FormatSection &to_conv) {
77+
return details::write_num<1>(to_conv.time->tm_wday == 0 ? 7 : to_conv.time->tm_wday, writer);
78+
}
79+
80+
LIBC_INLINE int convert_decimal_weekday_iso(printf_core::Writer *writer,
81+
const FormatSection &to_conv) {
82+
return details::write_num<1>(to_conv.time->tm_wday, writer);
83+
}
84+
85+
LIBC_INLINE int convert_week_number_sunday(printf_core::Writer *writer,
86+
const FormatSection &to_conv) {
87+
int wday = to_conv.time->tm_wday;
88+
int yday = to_conv.time->tm_yday;
89+
int week = (yday - wday + 7) / 7;
90+
return details::write_num_with_padding<2, '0'>(week, writer);
91+
}
92+
93+
LIBC_INLINE int convert_week_number_monday(printf_core::Writer *writer,
94+
const FormatSection &to_conv) {
95+
int wday = (to_conv.time->tm_wday + 6) % 7;
96+
int yday = to_conv.time->tm_yday;
97+
int week = (yday - wday + 7) / 7;
98+
return details::write_num_with_padding<2, '0'>(week, writer);
99+
}
100+
101+
LIBC_INLINE int convert_full_month(printf_core::Writer *writer,
102+
const FormatSection &to_conv) {
103+
return writer->write(month_names[to_conv.time->tm_mon]);
104+
}
105+
106+
LIBC_INLINE int convert_abbreviated_month(printf_core::Writer *writer,
107+
const FormatSection &to_conv) {
108+
return writer->write(abbreviated_month_names[to_conv.time->tm_mon]);
109+
}
110+
111+
LIBC_INLINE int convert_zero_padded_month(printf_core::Writer *writer,
112+
const FormatSection &to_conv) {
113+
return details::write_num_with_padding<2, '0'>(to_conv.time->tm_mon + 1, writer);
114+
}
115+
116+
LIBC_INLINE int convert_full_year(printf_core::Writer *writer,
117+
const FormatSection &to_conv) {
118+
return details::write_num_with_padding<4, '0'>(to_conv.time->tm_year + 1900, writer);
119+
}
120+
121+
LIBC_INLINE int convert_two_digit_year(printf_core::Writer *writer,
122+
const FormatSection &to_conv) {
123+
return details::write_num_with_padding<2, '0'>((to_conv.time->tm_year + 1900) % 100, writer);
124+
}
125+
126+
LIBC_INLINE int convert_century(printf_core::Writer *writer,
127+
const FormatSection &to_conv) {
128+
return details::write_num_with_padding<2, '0'>((to_conv.time->tm_year + 1900) / 100, writer);
129+
}
130+
131+
LIBC_INLINE int convert_iso_year(printf_core::Writer *writer,
132+
const FormatSection &to_conv) {
133+
// TODO
134+
return details::write_num_with_padding<4, '0'>(to_conv.time->tm_year + 1900, writer);
135+
}
136+
137+
int convert(printf_core::Writer *writer, const FormatSection &to_conv) {
138+
if (!to_conv.has_conv)
139+
return writer->write(to_conv.raw_string);
140+
switch (to_conv.conv_name) {
141+
// day of the week
142+
case 'a':
143+
return convert_weekday(writer, to_conv);
144+
case 'w':
145+
return convert_decimal_weekday(writer, to_conv);
146+
case 'u':
147+
return convert_decimal_weekday_iso(writer, to_conv);
148+
// day of the year/ month
149+
case 'j':
150+
return convert_zero_padded_day_of_year(writer, to_conv);
151+
case 'd':
152+
return convert_zero_padded_day_of_month(writer, to_conv);
153+
case 'e':
154+
return convert_space_padded_day_of_month(writer, to_conv);
155+
// week
156+
case 'U':
157+
return convert_week_number_sunday(writer, to_conv);
158+
case 'W':
159+
return convert_week_number_monday(writer, to_conv);
160+
case 'V': // TODO: ISO 8061
161+
// month
162+
case 'B':
163+
return convert_full_month(writer, to_conv);
164+
case 'b':
165+
case 'h':
166+
return convert_abbreviated_month(writer, to_conv);
167+
case 'm':
168+
return convert_zero_padded_month(writer, to_conv);
169+
// year
170+
case 'Y': // Full year (e.g., 2024)
171+
return convert_full_year(writer, to_conv);
172+
case 'y': // Two-digit year (e.g., 24 for 2024)
173+
return convert_two_digit_year(writer, to_conv);
174+
case 'C': // Century (e.g., 20 for 2024)
175+
return convert_century(writer, to_conv);
176+
case 'G': // ISO 8601 year
177+
// TODO
178+
return convert_iso_year(writer, to_conv);
179+
180+
}
181+
return 0;
182+
}
183+
184+
} // namespace strftime_core
185+
} // namespace LIBC_NAMESPACE_DECL
186+
#endif
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===-- Format specifier converter for printf -------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
10+
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/stdio/strftime_core/core_structs.h"
14+
#include "src/stdio/printf_core/writer.h"
15+
16+
#include <stddef.h>
17+
18+
namespace LIBC_NAMESPACE_DECL {
19+
namespace strftime_core {
20+
21+
// convert will call a conversion function to convert the FormatSection into
22+
// its string representation, and then that will write the result to the
23+
// writer.
24+
int convert(printf_core::Writer *writer, const FormatSection &to_conv);
25+
26+
} // namespace strftime_core
27+
} // namespace LIBC_NAMESPACE_DECL
28+
29+
#endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===-- Core Structures for printf ------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CORE_STRUCTS_H
10+
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CORE_STRUCTS_H
11+
12+
#include "src/__support/macros/config.h"
13+
14+
#include "src/__support/CPP/string_view.h"
15+
#include "src/__support/CPP/type_traits.h"
16+
#include "src/__support/FPUtil/FPBits.h"
17+
18+
#include <inttypes.h>
19+
#include <stddef.h>
20+
21+
namespace LIBC_NAMESPACE_DECL {
22+
namespace strftime_core {
23+
24+
struct tm {
25+
int tm_sec; /* seconds after the minute [0-60] */
26+
int tm_min; /* minutes after the hour [0-59] */
27+
int tm_hour; /* hours since midnight [0-23] */
28+
int tm_mday; /* day of the month [1-31] */
29+
int tm_mon; /* months since January [0-11] */
30+
int tm_year; /* years since 1900 */
31+
int tm_wday; /* days since Sunday [0-6] */
32+
int tm_yday; /* days since January 1 [0-365] */
33+
int tm_isdst; /* Daylight Savings Time flag */
34+
long tm_gmtoff; /* offset from UTC in seconds */
35+
char *tm_zone; /* timezone abbreviation */
36+
};
37+
38+
struct FormatSection {
39+
bool has_conv {false};
40+
bool isE {false};
41+
bool isO {false};
42+
cpp::string_view raw_string;
43+
char conv_name;
44+
const struct tm *time;
45+
};
46+
47+
enum PrimaryType : uint8_t {
48+
Unknown = 0,
49+
Float = 1,
50+
Pointer = 2,
51+
Integer = 3,
52+
FixedPoint = 4,
53+
};
54+
55+
// TypeDesc stores the information about a type that is relevant to printf in
56+
// a relatively compact manner.
57+
struct TypeDesc {
58+
uint8_t size;
59+
PrimaryType primary_type;
60+
LIBC_INLINE constexpr bool operator==(const TypeDesc &other) const {
61+
return (size == other.size) && (primary_type == other.primary_type);
62+
}
63+
};
64+
65+
template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {
66+
if constexpr (cpp::is_same_v<T, void>) {
67+
return TypeDesc{0, PrimaryType::Unknown};
68+
} else {
69+
constexpr bool IS_POINTER = cpp::is_pointer_v<T>;
70+
constexpr bool IS_FLOAT = cpp::is_floating_point_v<T>;
71+
#ifdef LIBC_INTERNAL_STRFTIME_HAS_FIXED_POINT
72+
constexpr bool IS_FIXED_POINT = cpp::is_fixed_point_v<T>;
73+
#else
74+
constexpr bool IS_FIXED_POINT = false;
75+
#endif // LIBC_INTERNAL_STRFTIME_HAS_FIXED_POINT
76+
77+
return TypeDesc{sizeof(T), IS_POINTER ? PrimaryType::Pointer
78+
: IS_FLOAT ? PrimaryType::Float
79+
: IS_FIXED_POINT ? PrimaryType::FixedPoint
80+
: PrimaryType::Integer};
81+
}
82+
}
83+
84+
// This is the value to be returned by conversions when no error has occurred.
85+
constexpr int WRITE_OK = 0;
86+
// These are the printf return values for when an error has occurred. They are
87+
// all negative, and should be distinct.
88+
constexpr int FILE_WRITE_ERROR = -1;
89+
constexpr int FILE_STATUS_ERROR = -2;
90+
constexpr int NULLPTR_WRITE_ERROR = -3;
91+
constexpr int INT_CONVERSION_ERROR = -4;
92+
constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
93+
constexpr int ALLOCATION_ERROR = -6;
94+
} // namespace strftime_core
95+
} // namespace LIBC_NAMESPACE_DECL
96+
97+
#endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CORE_STRUCTS_H

0 commit comments

Comments
 (0)