X-Git-Url: https://git.draconx.ca/gitweb/dxcommon.git/blobdiff_plain/7ba459d508d5e3c08580e8ec6c3dbc7c1fedd289..e599119f0492b01f1f21a8cce8d695c314dab3b1:/scripts/gen-options.awk diff --git a/scripts/gen-options.awk b/scripts/gen-options.awk index 30b8202..80adcb4 100755 --- a/scripts/gen-options.awk +++ b/scripts/gen-options.awk @@ -1,6 +1,6 @@ #!/bin/awk -f # -# Copyright © 2021 Nick Bowler +# Copyright © 2021, 2023 Nick Bowler # # Generate definitions helpful when using getopt_long from an options # specification file. @@ -75,6 +75,29 @@ # # static const struct option lopts[] = { LOPTS_INITIALIZER, {0} }; # +# If none of the options have action specifications, then an alternate +# set of macros is also defined, which encode the struct option array +# into a more compact format that can be used to generate the full +# 'struct option' array at runtime: +# +# * the object-like macro LOPT_PACK_BITS expands to an integer constant +# expression, suitable for use in #if directives, that specifies the +# minimum number of bits required by the encoding. LOPT_PACK_BITS2 +# is the same, but rounded up to the next power of two greater than +# or equal to 8. +# +# * the object-like macro LOPTS_PACKED_INITIALIZER expands to a +# comma-separated sequence of integer constant expressions, suitable +# for initializing an array of integers. All values are less than +# 2^LOPT_PACK_BITS. +# +# * the function-like macro LOPT_UNPACK(opt, x), where opt is an +# lvalue of type 'struct option', and x is one of the array +# elements initialized by LOPTS_PACKED_INITIALIZER. This expands +# the encoded value and sets the name, has_arg and val members of +# opt appopriately. The caller should ensure that the flag member +# is set to zero. +# # The help text for an individual struct option element may be obtained by # the function # @@ -100,6 +123,7 @@ END { } BEGIN { + has_actions = 0 sopt_string = "" num_options = 0 lopt = "" @@ -130,7 +154,7 @@ $0 ~ /^-/ { } # Extract argument name - if (work ~ /^\[=[^\] \t]+\]/) { + if (work ~ /^\[=[^ \t]+\]/ && sub(/\]/, "&", work) == 1) { if (n = index(work, "]")) { arg = substr(work, 3, n-3) work = substr(work, n+1) @@ -150,6 +174,9 @@ $0 ~ /^-/ { # Extract action sub(/^[ \t]*/, "", work) if (!sopt && work ~ /^\([^, \t]+(,[ \t]*[^, \t]+)?\)/) { + # packed form is not possible w/ actions + has_actions = 1; + n = split(work, a, /,[ \t]*/) if (n == 2) { flag = substr(a[1], 2) ", " substr(a[2], 1, length(a[2])-1) @@ -188,7 +215,9 @@ lopt { sub(/^[ \t]*/, "") if (!$0) { next } - optionhelp[lopt] = (lopt in optionhelp ? optionhelp[lopt] "\n" : "") $0 + if (lopt in optionhelp) + $0 = "\n" $0; + optionhelp[lopt] = optionhelp[lopt] $0; } # Exit immediately on error @@ -221,9 +250,13 @@ END { print "\t" to_enum(opt), "= UCHAR_MAX+1 +", offsets[opt] sep } print "};" - print "#define lopt_str(x) (lopt_strings + (LOPT_ ## x - UCHAR_MAX - 1))\n" + print "#define lopt_str(x) (lopt_strings + (LOPT_ ## x - UCHAR_MAX - 1))" + + if (!has_actions) { + output_packed_macros() + } - print "#define LOPTS_INITIALIZER \\" + print "\n#define LOPTS_INITIALIZER \\" for (i = 0; i < count; i++) { opt = options[i] sep = (i+1 == count ? "" : ", \\") @@ -295,7 +328,7 @@ END { opt = options[opt] if (!(opt in optionhelp)) { print "\tARG_L10N_(PN_(\"" opt "\", \"\"))" - help_offsets[opt] = help_pos + help_offsets[opt] = help_pos - 1 } } @@ -315,6 +348,106 @@ END { print "}" } +# Emit the packed initializer macros. This is used as an array initializer +# that encodes the following information: +# +# - short option character offset +# - arg value (0, 1 or 2), and +# - long option string offset +# +# as a single integer value for each option, in as few bits as practical. +# +# Currently, this only works if none of the options use action specifications +# (as these would require encoding user-specified pointer expressions and +# arbitrary int values). +function output_packed_macros(i, tmp, accum, max, totalbits) +{ + print ""; + + # determine number of bits to encode offsets in SOPT_STRING + max = length(sopt_string); + totalbits = accum = 0; + for (i = 1; i <= max; i *= 2) { + accum++; + } + print "#define LOPT_SC_BITS " accum; + totalbits += accum; + + # determine number of bits to encode has_arg values + max = 0; + for (i in optionspec) { + tmp = optionspec[i]; sub(/,.*/, "", tmp); + if (tmp > max) + max = tmp; + } + accum = (max > 1 ? 2 : max > 0 ? 1 : 0); + print "#define LOPT_HA_BITS " accum; + totalbits += accum; + + # determine number of bits to encode offsets in lopt_strings + max = 0; + for (i in offsets) { + if (offsets[i] > max) + max = offsets[i]; + } + + accum = 0; + for (i = 1; i <= max; i *= 2) { + accum++; + } + print "#define LOPT_LS_BITS " accum; + totalbits += accum; + + print "#define LOPT_PACK_BITS " totalbits; + for (i = 8; i < totalbits; i *= 2) + ; + print "#define LOPT_PACK_BITS2 " i; + + # Now emit the packed initializer macro + print "\n#define LOPTS_PACKED_INITIALIZER \\"; + accum = ""; + for (i = 0; i < count; i++) { + if (accum) + print "\t" accum ", \\"; + + tmp = options[i]; + accum = "("offsets[tmp] "ul" "<>(LOPT_SC_BITS+LOPT_HA_BITS)))"; + + print "\n#define LOPT_UNPACK_ARG(x) \\"; + print "\t(((x)>>LOPT_SC_BITS)&((1ul<>(LOPT_SC_BITS+LOPT_HA_BITS)))"; + + print "\n#define LOPT_UNPACK(opt, x) do { \\"; + print "\t(opt).name = LOPT_UNPACK_NAME(x); \\" + print "\t(opt).has_arg = LOPT_UNPACK_ARG(x); \\" + print "\t(opt).val = LOPT_UNPACK_VAL(x); \\" + print "} while (0)"; +} + # bucketsort(dst, src) # # Sort the elements of src by descending string length,