Hash :
d5a91fa9
Author :
Date :
2025-04-04T16:38:16
xkbcomp: Use custom parsers instead of strtol* The use of `strtol*` functions was already restricted due to its slowness and its capacity to parse other stuff than digits (e.g. signs and spaces). There is also another *big* limitation: it requires a NULL-terminated string. This is incompatible with our functions that work on buffers, because we cannot guarantee this. This may lead to a memory violation if the last token is a number. We now roll out our own parsers, which are more efficients and compatible with buffers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
/*
* Copyright © 2025 Pierre Le Marre <dev@wismill.eu>
* SPDX-License-Identifier: MIT
*/
#include "config.h"
#include <fcntl.h>
#include <time.h>
#include <getopt.h>
#include <stdint.h>
#include "utils.h"
#include "utils-numbers.h"
#include "bench.h"
const double DEFAULT_STDEV = 0.05;
static void
usage(char **argv)
{
printf("Usage: %s [OPTIONS]\n"
"\n"
"Benchmark compilation of the given RMLVO\n"
"\n"
"Options:\n"
" --help\n"
" Print this help and exit\n"
" --stdev\n"
" Minimal relative standard deviation (percentage) to reach.\n"
" (default: %f)\n"
"\n",
argv[0], DEFAULT_STDEV * 100);
}
static void
print_stats(double stdev, unsigned int max_iterations,
struct bench_time *elapsed, struct bench *bench,
struct estimate *est)
{
struct bench_time total_elapsed;
bench_elapsed(bench, &total_elapsed);
fprintf(stderr,
"mean: %lld µs; stdev: %Lf%% (target: %f%%); "
"last run: parsed %u times in %ld.%06lds; "
"total time: %ld.%06lds\n", est->elapsed / 1000,
(long double) est->stdev * 100.0 / (long double) est->elapsed,
stdev * 100,
max_iterations, elapsed->seconds, elapsed->nanoseconds / 1000,
total_elapsed.seconds, total_elapsed.nanoseconds / 1000);
}
/* NOTE: Old parser, for comparison */
static bool
parse_keysym_hex(const char *s, uint32_t *out)
{
uint32_t result = 0;
unsigned int i;
for (i = 0; i < 8 && s[i] != '\0'; i++) {
result <<= 4;
if ('0' <= s[i] && s[i] <= '9')
result += s[i] - '0';
else if ('a' <= s[i] && s[i] <= 'f')
result += 10 + s[i] - 'a';
else if ('A' <= s[i] && s[i] <= 'F')
result += 10 + s[i] - 'A';
else
return false;
}
*out = result;
return s[i] == '\0' && i > 0;
}
int
main(int argc, char **argv)
{
struct bench bench;
struct bench_time elapsed;
struct estimate est;
int ret = 0;
double stdev = DEFAULT_STDEV;
enum options {
OPT_STDEV,
};
static struct option opts[] = {
{"help", no_argument, 0, 'h'},
{"stdev", required_argument, 0, OPT_STDEV},
{0, 0, 0, 0},
};
while (1) {
int c;
int option_index = 0;
c = getopt_long(argc, argv, "h", opts, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
usage(argv);
exit(EXIT_SUCCESS);
case OPT_STDEV:
stdev = atof(optarg) / 100;
if (stdev <= 0)
stdev = DEFAULT_STDEV;
break;
default:
usage(argv);
exit(EXIT_INVALID_USAGE);
}
}
FILE *file = fopen(__FILE__, "r");
assert(file);
size_t size = 0;
char *content = NULL;
map_file(file, &content, &size);
assert(content);
/*
* Some numbers for the parsers, do not delete.
*
* 0x0000000000000000 0x0000000000000002 0x0000000000000003
* 0x0000000000000001 0x00000000000000FE 0x00000000000001FE
* 0x000000000000000A 0x0000000000000200 0x0000000000000400
* 0x00000000000000FF 0x0000000000020000 0x0000000000040000
* 0x0000000000000100 0x0000000002000000 0x0000000004000000
* 0x0000000000001000 0x0000000200000000 0x0000000400000000
* 0x0000000000010000 0x0000020000000000 0x0000040000000000
* 0x0000000001000000 0x0002000000000000 0x0004000000000000
* 0x0000000100000000 0x0200000000000000 0x0400000000000000
* 0x0000010000000000 0x2000000000000000 0x4000000000000001
* 0x0001000000000000 0x4000000000000000 0x3FFFFFFFFFFFFFFF
* 0x0100000000000000 0x6FFFFFFFFFFFFFFF 0xA000000000000000
* 0x1000000000000000 0x9000000000000000 0xCFFFFFFFFFFFFFFF
* 0x7FFFFFFFFFFFFFFF 0xEFFFFFFFFFFFFFFF 0xD000000000000000
* 0x8000000000000000 0xF000000000000000 0xE000000000000000
* 0xFFFFFFFFFFFFFFFF 0x1A2B3C4D5E6F7089 0x0807060504030201
* 0x123456789ABCDEF0 0x89706F5E4D3C2B1A 0xF1E2D3C4B5A69788
* 0xFEDCBA9876543210 0x5A5A5A5A5A5A5A5A 0x6B6B6B6B6B6B6B6B
* 0xABABABABABABABAB 0xA5A5A5A5A5A5A5A5 0xB6B6B6B6B6B6B6B6
* 0xCDCDCDCDCDCDCDCD 0xC3D2E1F00F1E2D3C 0x1122334455667788
* 0x0123456789ABCDEF 0x3C2D1E0F0FE1D2C3 0x8877665544332211
* 0x9876543210FEDCBA 0x0000000080000000 0x0000000040000000
* 0x00000000FFFFFFFF 0x8000000000000000 0x4000000000000000
* 0xFFFFFFFF00000000 0x6666666666666666 0x7777777777777777
* 0x5555555555555555 0x9999999999999999 0x8888888888888888
* 0x0AAAAAAAAAAAAAAA 0x0000000200000002 0x0000000300000003
* 0x0000000100000001 0x4444444444444444 0x5F5F5F5F5F5F5F5F
* 0x1111111111111111 0xBBBBBBBBBBBBBBBB 0xC0C0C0C0C0C0C0C0
* 0x2222222222222222 0xCCCCCCCCCCCCCCCC 0xE1E1E1E1E1E1E1E1
* 0x3333333333333333 0xDDDDDDDDDDDDDDDD 0xF2F2F2F2F2F2F2F2
* 0x1A3F5C7E9D2B4A68 0x8E6D4C2B1A0F9E7D 0x3F9A8B7C6D5E4F2A
* 0x7B6C5D4E3F2A1B09 0x2D4E6F8A9C0B1D3E 0x5A4B3C2D1E0F9A8B
* 0x9E8D7C6B5A4F3E2D 0x1C3E5F7A9D0B2E4F 0x6D5E4F3A2B1C0D9E
* 0xA0B1C2D3E4F56789 0x3B4D5F6E7A8C9D0E 0x7E8F9A0B1C2D3E4F
* 0x2C3D4E5F6A7B8C9D 0x9A8B7C6D5E4F3A2B 0x1E2D3C4B5A6F7E8D
* 0x4D5E6F7A8B9C0D1E 0x3A2B1C0D9E8F7A6B 0x8C9D0E1F2A3B4C5D
* 0x5F6E7D8C9B0A1F2E 0x0A1B2C3D4E5F6A7B 0x9C0D1E2F3A4B5C6D
* 0x6A7B8C9D0E1F2A3B 0x2E3F4A5B6C7D8E9F 0x1D2C3B4A5F6E7D8C
* 0x7D8E9F0A1B2C3D4E 0x4B5C6D7E8F9A0B1C 0x0F1E2D3C4B5A6F7E
* 0x8B9C0D1E2F3A4B5C 0x5A6B7C8D9E0F1A2B 0x3C4D5E6F7A8B9C0D
* 0x9D0E1F2A3B4C5D6E 0x6B7C8D9E0F1A2B3C 0x2A3B4C5D6E7F8A9B
* 0x0E1F2A3B4C5D6E7F 0x7C8D9E0F1A2B3C4D 0x4A5B6C7D8E9F0A1B
* 0x1F2A3B4C5D6E7F8A 0x8D9E0F1A2B3C4D5E 0x5B6C7D8E9F0A1B2C
* 0x2B3C4D5E6F7A8B9C 0x9E0F1A2B3C4D5E6F 0x6C7D8E9F0A1B2C3D
* 0x0A1B2C3D4E5F6A7B 0x7E8F9A0B1C2D3E4F 0x3D4E5F6A7B8C9D0E
* 0x1B2C3D4E5F6A7B8C 0x8F9A0B1C2D3E4F5A 0x4E5F6A7B8C9D0E1F
* 0x3A4B5C6D7E8F9A0B 0x9F0A1B2C3D4E5F6A 0x5C6D7E8F9A0B1C2D
* 0x0B1C2D3E4F5A6B7C 0x7F8A9B0C1D2E3F4A 0x2D3E4F5A6B7C8D9E
* 0x1C2D3E4F5A6B7C8D 0x9A0B1C2D3E4F5A6B 0x4F5A6B7C8D9E0F1A
* 0x6D7E8F9A0B1C2D3E 0x0C1D2E3F4A5B6C7D 0x3B4C5D6E7F8A9B0C
* 0x8E9F0A1B2C3D4E5F 0x5D6E7F8A9B0C1D2E 0x1A2B3C4D5E6F7A8B
* 0x0D1E2F3A4B5C6D7E 0x7A8B9C0D1E2F3A4B 0x2E3F4A5B6C7D8E9F
* 0x9B0C1D2E3F4A5B6C 0x6E7F8A9B0C1D2E3F 0x0F1A2B3C4D5E6F7A
*/
volatile uint32_t __attribute__((unused)) dummy32 = 0;
volatile uint64_t __attribute__((unused)) dummy64 = 0;
unsigned int max_iterations;
printf("*** parse_hex_to_uint32_t ***\n");
bench_start2(&bench);
BENCH(stdev, max_iterations, elapsed, est,
for (size_t n = 0; n < size; n++) {
uint32_t val = 0;
parse_hex_to_uint32_t(content + n, 8, &val);
dummy32 += val;
}
);
bench_stop2(&bench);
print_stats(stdev, max_iterations, &elapsed, &bench, &est);
printf("*** parse_keysym_hex ***\n");
bench_start2(&bench);
BENCH(stdev, max_iterations, elapsed, est,
for (size_t n = 0; n < size; n++) {
uint32_t val = 0;
parse_keysym_hex(content + n, &val);
dummy32 += val;
}
);
bench_stop2(&bench);
print_stats(stdev, max_iterations, &elapsed, &bench, &est);
printf("*** parse_dec_to_uint64_t ***\n");
bench_start2(&bench);
BENCH(stdev, max_iterations, elapsed, est,
for (size_t n = 0; n < size; n++) {
uint64_t val = 0;
parse_dec_to_uint64_t(content + n, size - n, &val);
dummy64 += val;
}
);
bench_stop2(&bench);
print_stats(stdev, max_iterations, &elapsed, &bench, &est);
printf("*** strtol, base 10 ***\n");
bench_start2(&bench);
BENCH(stdev, max_iterations, elapsed, est,
for (size_t n = 0; n < size; n++) {
dummy64 += strtol(content + n, NULL, 10);
}
);
bench_stop2(&bench);
print_stats(stdev, max_iterations, &elapsed, &bench, &est);
printf("*** parse_hex_to_uint64_t ***\n");
bench_start2(&bench);
BENCH(stdev, max_iterations, elapsed, est,
for (size_t n = 0; n < size; n++) {
uint64_t val = 0;
parse_hex_to_uint64_t(content + n, size - n, &val);
dummy64 += val;
}
);
bench_stop2(&bench);
print_stats(stdev, max_iterations, &elapsed, &bench, &est);
printf("*** strtol, base 16 ***\n");
bench_start2(&bench);
BENCH(stdev, max_iterations, elapsed, est,
for (size_t n = 0; n < size; n++) {
dummy64 += strtol(content + n, NULL, 16);
}
);
bench_stop2(&bench);
print_stats(stdev, max_iterations, &elapsed, &bench, &est);
unmap_file(content, size);
fclose(file);
return ret;
}