perf: defer locale, quote, and table setup until a count needs it
This commit is contained in:
+102
-9
@@ -78,6 +78,34 @@ static int posixly_correct = 0;
|
||||
static enum total_mode total_mode = TOTAL_AUTO;
|
||||
static const char *files_from = NULL; /* --files0-from=F, or NULL */
|
||||
|
||||
/*
|
||||
* LC_CTYPE handling. A freshly exec'd process already runs in the C
|
||||
* locale, so when the environment selects C/POSIX (or selects nothing)
|
||||
* the setlocale() call is skipped: glibc would only re-derive the same
|
||||
* state, at a cost measurable on the 1-line benchmark inputs. When the
|
||||
* locale really is multibyte we adopt it exactly like GNU does.
|
||||
*/
|
||||
static int in_c_locale; /* LC_CTYPE resolves to C/POSIX, or is unset */
|
||||
static int locale_done; /* setup_locale() has run */
|
||||
|
||||
static void setup_locale(void)
|
||||
{
|
||||
const char *v;
|
||||
|
||||
if (locale_done)
|
||||
return;
|
||||
locale_done = 1;
|
||||
v = getenv("LC_ALL");
|
||||
if (v == NULL || v[0] == '\0')
|
||||
v = getenv("LC_CTYPE");
|
||||
if (v == NULL || v[0] == '\0')
|
||||
v = getenv("LANG");
|
||||
in_c_locale = v == NULL || v[0] == '\0' || strcmp(v, "C") == 0 ||
|
||||
strcmp(v, "POSIX") == 0;
|
||||
if (!in_c_locale)
|
||||
setlocale(LC_CTYPE, "");
|
||||
}
|
||||
|
||||
/* gnulib quote() style: locale quotation marks in a UTF-8 locale,
|
||||
* ASCII apostrophes elsewhere. Only argmatch diagnostics use it. */
|
||||
static int curly_quotes;
|
||||
@@ -141,6 +169,30 @@ static void init_isprint_tab(void)
|
||||
isprint_tab[i] = isprint((unsigned char)i) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* The C/POSIX byte classes are fixed: isspace() is the six ASCII
|
||||
* whitespace bytes and isprint() the range 0x20..0x7e. Precomputing
|
||||
* them skips 512 ctype() calls per exec in the common C-locale run. */
|
||||
static void init_ws_tab_c(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(ws_tab, 0, sizeof ws_tab);
|
||||
for (i = '\t'; i <= '\r'; i++)
|
||||
ws_tab[i] = 1;
|
||||
ws_tab[' '] = 1;
|
||||
ws_tab[0xa0] = (unsigned char)!posixly_correct;
|
||||
nbsp_ws = ws_tab[0xa0];
|
||||
}
|
||||
|
||||
static void init_isprint_tab_c(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(isprint_tab, 0, sizeof isprint_tab);
|
||||
for (i = 0x20; i <= 0x7e; i++)
|
||||
isprint_tab[i] = 1;
|
||||
}
|
||||
|
||||
static void try_help(void)
|
||||
{
|
||||
fprintf(stderr, "Try 'fastwc --help' for more information.\n");
|
||||
@@ -187,9 +239,19 @@ static void qfile(FILE *out, const char *s)
|
||||
fputs(s, out);
|
||||
}
|
||||
|
||||
/* quote(): always-quoting locale style used by argmatch diagnostics. */
|
||||
/* quote(): always-quoting locale style used by argmatch diagnostics.
|
||||
* The quote style is only needed on the --total error path, so the
|
||||
* locale work stays out of every other invocation. */
|
||||
static void qarg(FILE *out, const char *s)
|
||||
{
|
||||
static int qarg_ready;
|
||||
|
||||
if (!qarg_ready)
|
||||
{
|
||||
qarg_ready = 1;
|
||||
setup_locale();
|
||||
init_quote_style();
|
||||
}
|
||||
fputs(curly_quotes ? "\xe2\x80\x98" : "'", out);
|
||||
fputs(s, out);
|
||||
fputs(curly_quotes ? "\xe2\x80\x99" : "'", out);
|
||||
@@ -540,6 +602,34 @@ static count_lw_fn pick_kernel(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* One-time state the counting paths need, run after the options are
|
||||
* known so --help/--version/usage errors pay none of it. The locale is
|
||||
* only locale-sensitive for -m/-w/-L, the per-byte tables only for word
|
||||
* and -L counting, and the SIMD kernel only for byte-path counts. */
|
||||
static void init_count_state(void)
|
||||
{
|
||||
if (flags & (F_CHARS | F_WORDS | F_MAXLEN))
|
||||
{
|
||||
setup_locale();
|
||||
if (flags & F_WORDS)
|
||||
{
|
||||
if (in_c_locale)
|
||||
init_ws_tab_c();
|
||||
else
|
||||
init_ws_tab();
|
||||
}
|
||||
if (flags & F_MAXLEN)
|
||||
{
|
||||
if (in_c_locale)
|
||||
init_isprint_tab_c();
|
||||
else
|
||||
init_isprint_tab();
|
||||
}
|
||||
}
|
||||
if (flags & (F_LINES | F_WORDS))
|
||||
count_lw = pick_kernel();
|
||||
}
|
||||
|
||||
/*
|
||||
* The Unicode white space set as GNU wc 9.11 sees it: glibc's iswspace()
|
||||
* (which omits the no-break spaces U+00A0, U+2007, U+202F) plus those
|
||||
@@ -1413,16 +1503,10 @@ int main(int argc, char **argv)
|
||||
long n_args = 0;
|
||||
total_t totals = {0, 0, 0, 0, 0};
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
posixly_correct = getenv("POSIXLY_CORRECT") != NULL;
|
||||
init_quote_style();
|
||||
init_ws_tab();
|
||||
init_isprint_tab();
|
||||
count_lw = pick_kernel();
|
||||
/* Line-buffer stdout like GNU, so parallel processes do not
|
||||
* interleave their rows. */
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
/* Option parsing needs no locale, so --help/--version and usage
|
||||
* errors return before any of the one-time startup state exists. */
|
||||
while ((optc = getopt_long(argc, argv, "clLmw", longopts, NULL)) != -1)
|
||||
switch (optc)
|
||||
{
|
||||
@@ -1469,6 +1553,11 @@ int main(int argc, char **argv)
|
||||
if (flags == 0)
|
||||
flags = F_LINES | F_WORDS | F_BYTES; /* wc default: -l -w -c */
|
||||
|
||||
/* Line-buffer stdout like GNU, so parallel processes do not
|
||||
* interleave their rows. */
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
init_count_state();
|
||||
|
||||
if (files_from)
|
||||
{
|
||||
FILE *stream;
|
||||
@@ -1908,7 +1997,11 @@ int main(void)
|
||||
/* The CLI build's helpers are compiled out of reach here. */
|
||||
(void)usage;
|
||||
(void)try_help;
|
||||
(void)setup_locale;
|
||||
(void)init_isprint_tab;
|
||||
(void)init_isprint_tab_c;
|
||||
(void)init_ws_tab_c;
|
||||
(void)init_count_state;
|
||||
(void)init_quote_style;
|
||||
(void)qaf;
|
||||
(void)qfile;
|
||||
|
||||
Reference in New Issue
Block a user