Commit 7f7abd9f authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Generate format tables at compile time in C++

Summary: [Folly] Generate format tables at compile time in C++, rather than as a separate custom build step in Python.

Reviewed By: ot

Differential Revision: D6829681

fbshipit-source-id: 160b5a7616b015cc37ff389adabfec095cf136ec
parent 4950f56d
......@@ -30,7 +30,6 @@ folly/m4/ltsugar.m4
folly/m4/ltversion.m4
folly/m4/lt~obsolete.m4
folly/generate_fingerprint_tables
folly/FormatTables.cpp
folly/EscapeTables.cpp
folly/GroupVarintTables.cpp
folly/FingerprintTables.cpp
......@@ -69,16 +69,6 @@ add_custom_command(
DEPENDS ${FOLLY_DIR}/build/generate_escape_tables.py
COMMENT "Generating the escape tables..." VERBATIM
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/folly/build/FormatTables.cpp
COMMAND
${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/folly/build
COMMAND
${PYTHON_EXECUTABLE} "${FOLLY_DIR}/build/generate_format_tables.py"
--install_dir ${CMAKE_CURRENT_BINARY_DIR}/folly/build
DEPENDS ${FOLLY_DIR}/build/generate_format_tables.py
COMMENT "Generating the format tables..." VERBATIM
)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/folly/build/GroupVarintTables.cpp"
COMMAND
......@@ -212,7 +202,6 @@ add_library(folly_base OBJECT
${files} ${hfiles}
${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h
${CMAKE_CURRENT_BINARY_DIR}/folly/build/EscapeTables.cpp
${CMAKE_CURRENT_BINARY_DIR}/folly/build/FormatTables.cpp
${CMAKE_CURRENT_BINARY_DIR}/folly/build/GroupVarintTables.cpp
)
if (BUILD_SHARED_LIBS)
......@@ -226,7 +215,6 @@ source_group("folly" FILES ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h)
source_group("folly\\build" FILES
${CMAKE_CURRENT_BINARY_DIR}/folly/build/EscapeTables.cpp
${CMAKE_CURRENT_BINARY_DIR}/folly/build/FingerprintTables.cpp
${CMAKE_CURRENT_BINARY_DIR}/folly/build/FormatTables.cpp
${CMAKE_CURRENT_BINARY_DIR}/folly/build/GroupVarintTables.cpp
)
......
......@@ -42,10 +42,10 @@ namespace detail {
// Updates the end of the buffer after the comma separators have been added.
void insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer);
extern const char formatHexUpper[256][2];
extern const char formatHexLower[256][2];
extern const char formatOctal[512][3];
extern const char formatBinary[256][8];
extern const std::array<std::array<char, 2>, 256> formatHexUpper;
extern const std::array<std::array<char, 2>, 256> formatHexLower;
extern const std::array<std::array<char, 3>, 512> formatOctal;
extern const std::array<std::array<char, 8>, 256> formatBinary;
const size_t kMaxHexLength = 2 * sizeof(uintmax_t);
const size_t kMaxOctalLength = 3 * sizeof(uintmax_t);
......@@ -61,8 +61,11 @@ const size_t kMaxBinaryLength = 8 * sizeof(uintmax_t);
* [buf+begin, buf+bufLen).
*/
template <class Uint>
size_t
uintToHex(char* buffer, size_t bufLen, Uint v, const char (&repr)[256][2]) {
size_t uintToHex(
char* buffer,
size_t bufLen,
Uint v,
std::array<std::array<char, 2>, 256> const& repr) {
// 'v >>= 7, v >>= 1' is no more than a work around to get rid of shift size
// warning when Uint = uint8_t (it's false as v >= 256 implies sizeof(v) > 1).
for (; !less_than<unsigned, 256>(v); v >>= 7, v >>= 1) {
......
......@@ -18,14 +18,75 @@
#include <folly/ConstexprMath.h>
#include <folly/CppAttributes.h>
#include <folly/container/Array.h>
#include <double-conversion/double-conversion.h>
namespace folly {
namespace detail {
extern const FormatArg::Align formatAlignTable[];
extern const FormatArg::Sign formatSignTable[];
// ctor for items in the align table
struct format_table_align_make_item {
static constexpr std::size_t size = 256;
constexpr FormatArg::Align operator()(std::size_t index) const {
// clang-format off
return
index == '<' ? FormatArg::Align::LEFT:
index == '>' ? FormatArg::Align::RIGHT :
index == '=' ? FormatArg::Align::PAD_AFTER_SIGN :
index == '^' ? FormatArg::Align::CENTER :
FormatArg::Align::INVALID;
// clang-format on
}
};
// ctor for items in the conv tables for representing parts of nonnegative
// integers into ascii digits of length Size, over a given base Base
template <std::size_t Base, std::size_t Size, bool Upper = false>
struct format_table_conv_make_item {
static_assert(Base <= 36, "Base is unrepresentable");
struct make_item {
std::size_t index{};
constexpr explicit make_item(std::size_t index_) : index(index_) {} // gcc49
constexpr char alpha(std::size_t ord) const {
return ord < 10 ? '0' + ord : (Upper ? 'A' : 'a') + (ord - 10);
}
constexpr char operator()(std::size_t offset) const {
return alpha(index / constexpr_pow(Base, Size - offset - 1) % Base);
}
};
constexpr std::array<char, Size> operator()(std::size_t index) const {
return make_array_with<Size>(make_item{index});
}
};
// ctor for items in the sign table
struct format_table_sign_make_item {
static constexpr std::size_t size = 256;
constexpr FormatArg::Sign operator()(std::size_t index) const {
// clang-format off
return
index == '+' ? FormatArg::Sign::PLUS_OR_MINUS :
index == '-' ? FormatArg::Sign::MINUS :
index == ' ' ? FormatArg::Sign::SPACE_OR_MINUS :
FormatArg::Sign::INVALID;
// clang-format on
}
};
// the tables
constexpr auto formatAlignTable =
make_array_with<256>(format_table_align_make_item{});
constexpr auto formatSignTable =
make_array_with<256>(format_table_sign_make_item{});
constexpr decltype(formatHexLower) formatHexLower =
make_array_with<256>(format_table_conv_make_item<16, 2, false>{});
constexpr decltype(formatHexUpper) formatHexUpper =
make_array_with<256>(format_table_conv_make_item<16, 2, true>{});
constexpr decltype(formatOctal) formatOctal =
make_array_with<512>(format_table_conv_make_item<8, 3>{});
constexpr decltype(formatBinary) formatBinary =
make_array_with<256>(format_table_conv_make_item<2, 8>{});
} // namespace detail
......
......@@ -62,7 +62,7 @@ struct FormatArg {
enum class Type {
INTEGER,
FLOAT,
OTHER
OTHER,
};
/**
* Validate the argument for the given type; throws on error.
......@@ -106,7 +106,7 @@ struct FormatArg {
RIGHT,
PAD_AFTER_SIGN,
CENTER,
INVALID
INVALID,
};
Align align;
......@@ -118,7 +118,7 @@ struct FormatArg {
PLUS_OR_MINUS,
MINUS,
SPACE_OR_MINUS,
INVALID
INVALID,
};
Sign sign;
......
......@@ -483,10 +483,6 @@ nobase_follyinclude_HEADERS = \
Utility.h \
Varint.h
FormatTables.cpp: build/generate_format_tables.py
$(PYTHON) build/generate_format_tables.py
CLEANFILES += FormatTables.cpp
EscapeTables.cpp: build/generate_escape_tables.py
$(PYTHON) build/generate_escape_tables.py
CLEANFILES += EscapeTables.cpp
......@@ -507,7 +503,6 @@ libfollybase_la_SOURCES = \
EscapeTables.cpp \
Format.cpp \
FormatArg.cpp \
FormatTables.cpp \
memory/MallctlHelper.cpp \
portability/BitsFunctexcept.cpp \
String.cpp \
......
#!/usr/bin/env python
#
# Generate Format tables
import os
from optparse import OptionParser
OUTPUT_FILE = "FormatTables.cpp"
def generate_table(f, type_name, name, map):
f.write("extern const {0} {1}[] = {{".format(type_name, name))
for i in range(0, 256):
if i % 2 == 0:
f.write("\n ")
f.write("{0}::{1}, ".format(type_name, map.get(chr(i), "INVALID")))
f.write("\n};\n\n")
def generate_conv_table(f, name, values):
values = list(values)
line = ''
for i, v in enumerate(values):
if i == 0:
f.write("extern const char {0}[{1}][{2}] = {{\n".format(
name, len(values), len(v)))
row = "{{{0}}}, ".format(", ".join("'{0}'".format(x) for x in v))
if len(line) + len(row) > 79:
f.write(line + "\n")
line = ''
line += row
if line:
f.write(line + "\n")
f.write("};\n\n")
def octal_values():
return (tuple("{0:03o}".format(x)) for x in range(512))
def hex_values(upper):
fmt = "{0:02X}" if upper else "{0:02x}"
return (tuple(fmt.format(x)) for x in range(256))
def binary_values():
return (tuple("{0:08b}".format(x)) for x in range(256))
def generate(f):
f.write("#include <folly/FormatArg.h>\n"
"\n"
"namespace folly {\n"
"namespace detail {\n"
"\n")
generate_table(
f, "FormatArg::Align", "formatAlignTable",
{"<": "LEFT", ">": "RIGHT", "=": "PAD_AFTER_SIGN", "^": "CENTER"})
generate_table(
f, "FormatArg::Sign", "formatSignTable",
{"+": "PLUS_OR_MINUS", "-": "MINUS", " ": "SPACE_OR_MINUS"})
generate_conv_table(f, "formatOctal", octal_values())
generate_conv_table(f, "formatHexLower", hex_values(False))
generate_conv_table(f, "formatHexUpper", hex_values(True))
generate_conv_table(f, "formatBinary", binary_values())
f.write("} // namespace detail\n"
"} // namespace folly\n")
def main():
parser = OptionParser()
parser.add_option("--install_dir", dest="install_dir", default=".",
help="write output to DIR", metavar="DIR")
parser.add_option("--fbcode_dir")
(options, args) = parser.parse_args()
f = open(os.path.join(options.install_dir, OUTPUT_FILE), "w")
generate(f)
f.close()
if __name__ == "__main__":
main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment