use strict; use warnings; use Inline 'C'; use constant ZERO_START => 0; # digit-sequence allows 0 start use constant NON_ZERO_START => 1; # digit-sequence must not start with a leading 0 msg_subst_seed((time() + $$) % 32000); # just for tests, not a good idea where security matters do { my $x = msg_subst("3x: ({\\d4})x({\\d1})x({\\d10})", NON_ZERO_START); print "[$x]\n"; }; print msg_subst("empty: ({\\d0})\n", ZERO_START); print msg_subst("$_-dig: ({\\d$_})\n", ZERO_START) for (1..10); print msg_subst("2x : ({\\d3}) x ({\\d3})\n", NON_ZERO_START); print msg_subst("huge: ({\\d99})\n", ZERO_START); print msg_subst("err: ({\\d100})\n", ZERO_START); print msg_subst("err: ({\\d-10})\n", ZERO_START); print msg_subst('overflow: {\d99}{\d99}{\d99}{\d99}{\d99}{\d99}{\d99}{\d99}{\d99}{\d99}{\d99}'."\n", ZERO_START); __END__ __C__ #include /* see: http://stackoverflow.com/questions/1167253/implementation-of-rand */ static uint32_t z1 = 12345, z2 = 12345, z3 = 12345, z4 = 12345; /* seed */ void msg_subst_seed(int seed) { /* TODO: needs improvement */ if (seed < 0) seed = -seed; if (!seed) seed = 1; z1 = seed; /* z1: positive and >= 1 */ } uint32_t lfsr113_Bits (void) { uint32_t b; b = ((z1 << 6) ^ z1) >> 13; z1 = ((z1 & 4294967294U) << 18) ^ b; b = ((z2 << 2) ^ z2) >> 27; z2 = ((z2 & 4294967288U) << 2) ^ b; b = ((z3 << 13) ^ z3) >> 21; z3 = ((z3 & 4294967280U) << 7) ^ b; b = ((z4 << 3) ^ z4) >> 12; z4 = ((z4 & 4294967168U) << 13) ^ b; return (z1 ^ z2 ^ z3 ^ z4); } /* see.. until here */ char get_digit() {/* iterator to get an ASCII digit in range '0'..'9', the naive way... */ static int count = 0; static uint32_t drawn_rand; char digit; if (!count) { drawn_rand = lfsr113_Bits(); count = 8; } digit = (char) ('0' + drawn_rand % 10); drawn_rand /= 10; count--; return digit; } SV * msg_subst(char* msg, int mode) { static char buf[1024]; /* keep the result smaller than this, increase the static buffer, or use malloc() */ char *to = buf; char *from = msg; while(*from && (to-buf) <= 1000) { /* for simplicity, we allow {\d0} .. {\d99} only */ if (*from == '{' && from[1] == '\\' && from[2] == 'd' && isdigit( from[3] )) { int i; int count = from[3] - '0'; int consumed = 4; if ( isdigit(from[4]) ) { count = count * 10 + from[4] - '0'; consumed++; } if ( from[consumed] == '}' ) { /* found a valid {\d##} sequence */ if (count && mode == 1) { count--; *to = get_digit(); while (*to == '0') { *to = get_digit(); } /* no leading zeros wanted */ to++; } for (i=0; i= 1000 ) strcpy( to, "... *OVERFLOW*!\n"); return newSVpv(buf, strlen(buf)); } /* further improvements: - profile / this alternative "can" be faster - other/better random generator or lookup-table () - parse pattern once (if pattern stays constant for a sufficiently long period of time) - more than 99 digits - Modes: HEX, ASCII, SET(...), RANGE(...) - tweak "use inline C;" - pre-compute messages if possible - return a list of results - find more buffer overflows ;-) - find mem-leaks? - set $! - ... */