Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thought Experiment : Turn 1% success into 99.9999% success

72 views
Skip to first unread message

Frederick Gotham

unread,
Mar 12, 2021, 8:25:48 AM3/12/21
to
Originally I was going to do this all in C++, but since Google Groups no longer allows posting to groups with a symbol in their name, and since I can't find a free Usenet NNTP server that allows posting, I'm gonna do this all in C. I'll keep the code as close to the ANSI/ISO Standard as I can except where I need a POSIX function for something.

Let's say we have a program that works 1% of the time. For this thought experiment, I'll choose a program that produces the SHA256 hashsum of the string passed to it as a command line argument.

So let's say that the program in question segfaults 33% of time, gives an incorrect hashsum 66% of the time, and gives the correct answer 1% of the time.

When the program gives an incorrect answer, the incorrect answer is extremely random. Since a SHA256 hashum is 256-Bit, and since we consider 128-Bit UUID's impossible to duplicate, it's safe to assume that this program will never give the same incorrect answer twice.

So I've taken the original non-buggy code for the SHA256 program, and I've renamed "main" to "real_main". Then I've written my own new 'main' as follows:

#include <assert.h> /* assert */
#include <time.h> /* time, timespec, clock_gettime */
#include <stdlib.h> /* rand, RAND_MAX */

/* Returns an integer in the range [0, n).
*
* Uses rand(), and so is affected-by/affects the same seed.
*/
int randint(int const n)
{
if ( (n - 1) == RAND_MAX )
{
return rand();
}
else
{
// Supporting larger values for n would requires an even more
// elaborate implementation that combines multiple calls to rand()
assert(n <= RAND_MAX);

// Chop off all of the values that would cause skew...
int end = RAND_MAX / n; // truncate skew
assert(end > 0);
end *= n;

// ... and ignore results from rand() that fall above that limit.
// (Worst case the loop condition should succeed 50% of the time,
// so we can expect to bail out of this loop pretty quickly.)
int r;
while ( (r = rand()) >= end );

return r % n;
}
}

int Get_Tick(void) /* Similar to GetTickCount in MS-Windows */
{
struct timespec ts;

clock_gettime(CLOCK_MONOTONIC, &ts);

return ts.tv_nsec;
}

int main(int const argc, char **const argv)
{
/* 1% of the time the correct answer is given
33% of the time the program segfaults
66% of the time the program gives the wrong answer */

unsigned i;

srand( time(NULL) ^ Get_Tick() );

int const num = randint(100);

if ( 0 == num ) return real_main(argc,argv);

if ( num < 33 ) *(int*)0 = 88; /* SEGFAULT */

unsigned const ints_per_digest = 32u / sizeof(int);

int digest[ints_per_digest];

for (i = 0u; i != ints_per_digest; ++i)
{
digest[i] = rand();
}

char unsigned const *const p = (char unsigned*)&digest;

for (i = 0u; i != 32u; ++i)
{
printf("%02x", p[i]);
}

printf("\n");
}

So we compile this C program to a binary file, and then we discard the source code for it. So now we somehow have to turn this 1% successful program into a 99.9999% successful program even though we only have the binary for it. Also let's say that we can't disassemble the machine code because the CPU is proprietary (and the instruction set is heavily obfuscated).

So here's what I'm thinking, in a few steps:
(1) Write a second helper program that invokes the SHA256 program
(2) If the SHA256 program segfaults then discard the output and run it again
(3) If the SHA256 program returns a hex string of a 32-Byte hashsum then save the hashsum in cache. Run the program several more times until the same hashsum is outputted a second time -- then take this to be the correct hashsum.

I haven't started coding the second helper program yet, but I've finished the buggy version of the SHA256 program.

Anyone got an thoughts on this?

Here's the buggy SHA256 program:

#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t, malloc, free */
#include <string.h> /* strlen */
#include <limits.h> /* CHAR_BIT, UINT_MAX, ULONG_MAX */

/* Compiler for 16-Bit DOS hasn't got stdint.h */

#if CHAR_BIT == 8
typedef char unsigned u8;
#else
# error "Please tell me about your computer museum"
#endif

#if UINT_MAX == 4294967295UL
typedef unsigned u32;
#elif ULONG_MAX == 4294967295UL
typedef long unsigned u32;
#else
# error "Cannot find a 32-Bit integer type"
#endif

#define CHUNK_SIZE 64
#define TOTAL_LEN_LEN 8

/*
* ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
*/

/*
* Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
* When useful for clarification, portions of the pseudo-code are reproduced here too.
*/

/*
* Initialize array of round constants:
* (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
*/
static const u32 k[] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

struct buffer_state {
const u8 * p;
size_t len;
size_t total_len;
int single_one_delivered; /* bool */
int total_len_delivered; /* bool */
};

static u32 right_rot(u32 value, unsigned int count)
{
/*
* Defined behaviour in standard C for all count where 0 < count < 32,
* which is what we need here.
*/
return value >> count | value << (32 - count);
}

static void init_buf_state(struct buffer_state * state, const void * input, size_t len)
{
state->p = (u8 const *)input;
state->len = len;
state->total_len = len;
state->single_one_delivered = 0;
state->total_len_delivered = 0;
}

/* Return value: bool */
static int calc_chunk(u8 chunk[CHUNK_SIZE], struct buffer_state * state)
{
size_t space_in_chunk;

if (state->total_len_delivered) {
return 0;
}

if (state->len >= CHUNK_SIZE) {
memcpy(chunk, state->p, CHUNK_SIZE);
state->p += CHUNK_SIZE;
state->len -= CHUNK_SIZE;
return 1;
}

memcpy(chunk, state->p, state->len);
chunk += state->len;
space_in_chunk = CHUNK_SIZE - state->len;
state->p += state->len;
state->len = 0;

/* If we are here, space_in_chunk is one at minimum. */
if (!state->single_one_delivered) {
*chunk++ = 0x80;
space_in_chunk -= 1;
state->single_one_delivered = 1;
}

/*
* Now:
* - either there is enough space left for the total length, and we can conclude,
* - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
* In the latter case, we will conclude at the next invokation of this function.
*/
if (space_in_chunk >= TOTAL_LEN_LEN) {
const size_t left = space_in_chunk - TOTAL_LEN_LEN;
size_t len = state->total_len;
int i;
memset(chunk, 0x00, left);
chunk += left;

/* Storing of len * 8 as a big endian 64-bit without overflow. */
chunk[7] = (u8) (len << 3);
len >>= 5;
for (i = 6; i >= 0; i--) {
chunk[i] = (u8) len;
len >>= 8;
}
state->total_len_delivered = 1;
} else {
memset(chunk, 0x00, space_in_chunk);
}

return 1;
}

/*
* Limitations:
* - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
* for large data sizes.
* - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
* for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
* In particular, the len parameter is a number of bytes.
*/

static u32 const g_h[] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 };

void calc_sha_256(u8 hash[32], const void * input, size_t len)
{
/*
* Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
* Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
* Note 3: The compression function uses 8 working variables, a through h
* Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
* and when parsing message block data from bytes to words, for example,
* the first word of the input message "abc" after padding is 0x61626380
*/

/*
* Initialize hash values:
* (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
*/
u32 h[8];
unsigned i, j;

/* 512-bit chunks is what we will operate on. */
u8 chunk[64];

struct buffer_state state;

memcpy(h, g_h, sizeof h);
init_buf_state(&state, input, len);

while (calc_chunk(chunk, &state)) {
u32 ah[8];

const u8 *p = chunk;

/* Initialize working variables to current hash value: */
for (i = 0; i < 8; i++)
ah[i] = h[i];

/* Compression function main loop: */
for (i = 0; i < 4; i++) {
/*
* The w-array is really w[64], but since we only need
* 16 of them at a time, we save stack by calculating
* 16 at a time.
*
* This optimization was not there initially and the
* rest of the comments about w[64] are kept in their
* initial state.
*/

/*
* create a 64-entry message schedule array w[0..63] of 32-bit words
* (The initial values in w[0..63] don't matter, so many implementations zero them here)
* copy chunk into first 16 words w[0..15] of the message schedule array
*/
u32 w[16];

for (j = 0; j < 16; j++) {
u32 s1, ch, temp1, s0, maj, temp2;

if (i == 0) {
w[j] = (u32) p[0] << 24 | (u32) p[1] << 16 |
(u32) p[2] << 8 | (u32) p[3];
p += 4;
} else {
/* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
const u32 s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^ (w[(j + 1) & 0xf] >> 3);
const u32 s1 = right_rot(w[(j + 14) & 0xf], 17) ^ right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10);
w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1;
}

s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
temp2 = s0 + maj;

ah[7] = ah[6];
ah[6] = ah[5];
ah[5] = ah[4];
ah[4] = ah[3] + temp1;
ah[3] = ah[2];
ah[2] = ah[1];
ah[1] = ah[0];
ah[0] = temp1 + temp2;
}
}

/* Add the compressed chunk to the current hash value: */
for (i = 0; i < 8; i++)
h[i] += ah[i];
}

/* Produce the final hash value (big-endian): */
for (i = 0, j = 0; i < 8; i++)
{
hash[j++] = (u8) (h[i] >> 24);
hash[j++] = (u8) (h[i] >> 16);
hash[j++] = (u8) (h[i] >> 8);
hash[j++] = (u8) h[i];
}
}

int real_main(int const argc, char **const argv)
{
char *full_str_to_process = 0;
size_t total_input_len = 0u;
unsigned i;

u8 digest[32u];

if ( argc < 2 ) return 1;

for (i = 1u; i != argc; ++i) total_input_len += strlen(argv[i]);

total_input_len += argc - 2; /* For a space between words */

/* In next line:
Add +1 for redundant space at end (which will be deleted)
Add +1 for terminating null char
*/
full_str_to_process = malloc(total_input_len + 1u + 1u);

if ( !full_str_to_process ) return 1;

*full_str_to_process = '\0';

for (i = 1u; i != argc; ++i)
{
strcat(full_str_to_process, argv[i]);
strcat(full_str_to_process, " ");
}

full_str_to_process[total_input_len] = '\0';

calc_sha_256(digest, full_str_to_process, total_input_len);

for (i = 0u; i != 32u; ++i)
printf("%02x", digest[i]);

printf("\n");

return 0;
}

#include <assert.h> /* assert */
#include <time.h> /* time clock*/
#include <stdlib.h> /* rand, RAND_MAX */

/* Returns an integer in the range [0, n).
*
* Uses rand(), and so is affected-by/affects the same seed.
*/
int randint(int const n)
{
if ( (n - 1) == RAND_MAX )
{
return rand();
}
else
{
// Supporting larger values for n would requires an even more
// elaborate implementation that combines multiple calls to rand()
assert(n <= RAND_MAX);

// Chop off all of the values that would cause skew...
int end = RAND_MAX / n; // truncate skew
assert(end > 0);
end *= n;

// ... and ignore results from rand() that fall above that limit.
// (Worst case the loop condition should succeed 50% of the time,
// so we can expect to bail out of this loop pretty quickly.)
int r;
while ( (r = rand()) >= end );

return r % n;
}
}

int Get_Tick(void) /* Similar to GetTickCount in MS-Windows */
{
struct timespec ts;

clock_gettime(CLOCK_MONOTONIC, &ts);

return ts.tv_nsec;
}

int main(int const argc, char **const argv)
{
/* 1% of the time the correct answer is given
33% of the time the program segfaults
66% of the time the program gives the wrong answer */

unsigned i;

srand( time(NULL) ^ Get_Tick() );

int const num = randint(100);

if ( 0 == num ) return real_main(argc,argv);

if ( num < 33 ) *(int*)0 = 88; /* SEGFAULT */

unsigned const ints_per_digest = 32u / sizeof(int);

int digest[ints_per_digest];

for (i = 0u; i != ints_per_digest; ++i)
{
digest[i] = rand();
}

char unsigned const *const p = (char unsigned*)&digest;

for (i = 0u; i != 32u; ++i)
{
printf("%02x", p[i]);
}

printf("\n");
}

Frederick Gotham

unread,
Mar 12, 2021, 8:30:33 AM3/12/21
to
In my previous post I even went to the bother of replacing all tab characters with four spaces, but the individual(s) who upgraded Google Groups decided that instead of letting me post to comp.lang.c++, they would let me post to comp.lang.c without proper identation.

Lew Pitcher

unread,
Mar 12, 2021, 11:00:56 AM3/12/21
to
On Fri, 12 Mar 2021 05:25:39 -0800, Frederick Gotham wrote:

> Originally I was going to do this all in C++, but since Google Groups no
> longer allows posting to groups with a symbol in their name,

Easily solved: use https://groups.google.com/g/comp.lang.c%2b%2b


> and since I
> can't find a free Usenet NNTP server that allows posting,

Again, easily solved: you can (with registration, but no payment) use
http://www.eternal-september.org/
"Even a stopped clock is correct twice a day".

The problem with "random" faults is that they are random, with no
guarantee that the results will be consistent. Or even consistently
inconsistent.

There's no guarantee that the buggy code /won't/ return the same /
incorrect/ results multiple times in a row, making your results-caching-
and-comparison workaround moot.

This seems like a truly wasteful and way to circumvent preventable errors.

[snip]

--
Lew Pitcher
"In Skills, We Trust"

Anton Shepelev

unread,
Mar 12, 2021, 11:16:07 AM3/12/21
to
Lew Pitcher:

> Again, easily solved: you can (with registration, but no
> payment) use http://www.eternal-september.org/

Or without either: aioe.org .

--
() ascii ribbon campaign - against html e-mail
/\ http://preview.tinyurl.com/qcy6mjc [archived]

Lew Pitcher

unread,
Mar 12, 2021, 11:20:57 AM3/12/21
to
On Fri, 12 Mar 2021 19:15:56 +0300, Anton Shepelev wrote:

> Lew Pitcher:
>
>> Again, easily solved: you can (with registration, but no payment) use
>> http://www.eternal-september.org/
>
> Or without either: aioe.org .

Good to know

Keith Thompson

unread,
Mar 12, 2021, 11:42:03 AM3/12/21
to
The indentation looked correct to me.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

anti...@math.uni.wroc.pl

unread,
Mar 12, 2021, 1:54:32 PM3/12/21
to
Frederick Gotham <cauldwel...@gmail.com> wrote:
>
> Let's say we have a program that works 1% of the time. For this thought experiment, I'll choose a program that produces the SHA256 hashsum of the string passed to it as a command line argument.
>
> So let's say that the program in question segfaults 33% of time, gives an incorrect hashsum 66% of the time, and gives the correct answer 1% of the time.
>
> When the program gives an incorrect answer, the incorrect answer is extremely random. Since a SHA256 hashum is 256-Bit, and since we consider 128-Bit UUID's impossible to duplicate, it's safe to assume that this program will never give the same incorrect answer twice.
<snip many details>
> Anyone got an thoughts on this?

I wonder what is your motivation for such problem? There are
so called Monte Carlo algorthms, where by nature of algorithm
result is only probably correct. But other details in your
setup look unrealistic. One of problems solvable by Monte
Carlo methods is deciding if "simple" function given by
a black box computing values is 0. I used quoutes around
simple because I mean that function belongs to some
restricted class like polynomial. But the actual formula
can be extremaly large (and black box may be huge/tricky).
Anyway, Monte Carlo approach is as follows: get random
point, evaluate black box at that point. If you get
nozero value, then you know that function is nonzero.
If you get zero, then function probaly is zero (there
are resonably precise estimate of probability for
polynomials). However, nonzero function may have zeros,
and you may find one by accident.

If you want eliminate bugs, things are more tricky. If
you have a few programs computin the same thing natural
approach is to run all of them and then take majority
vote. There are real world examples were majority
was wrong... In fact, even independently developed
programs tend to repeat the same bugs.

--
Waldek Hebisch

John McCue

unread,
Mar 12, 2021, 4:18:52 PM3/12/21
to
Frederick Gotham <cauldwel...@gmail.com> wrote:
> Originally I was going to do this all in C++

<snip>

As noted by Lew Pitcher, you can get around this,
also please see:

http://www.mugsy.org/asa_faq/getting_along/usenet.shtml

for proper post formatting

Thanks

Frederick Gotham

unread,
Mar 13, 2021, 6:58:04 AM3/13/21
to
On Friday, March 12, 2021 at 4:00:56 PM UTC, Lew Pitcher wrote:

> "Even a stopped clock is correct twice a day".


That isn't similar to the problem I posed in my thought experiment.

A clock has 12 hours, each containing 60 minutes. So the probability
of a clock's hands being at the correct hour and minute is 1 in 720.

Contrast this with a 128-Bit UUID which has 3.4 x 10^38 different values.
We mortal humans believe this probability to be so slim that a UUID
cannot be duplicated by random generation.


> The problem with "random" faults is that they are random, with no
> guarantee that the results will be consistent. Or even consistently
> inconsistent.


In my thought experiment, the hashsum is 256-Bit, with 1.2 x 10^77 possible values.
Guessing a 256-bit number correctly is like guessing two UUID's correctly in a row.

It isn't difficult to get this kind of entropy in a computer malfunction.
For example I work in a building with something like 300 or 400 computers
on a large network. If all of these computers were to exchange the last bit of
their GetTickCount over the network then I would have about 300 or 400
unpredictable bits to crash my program with a random result.

Or even if they didn't exchange the LSB of GetTickCount, I could just use
LSB's from latency of packets.


> There's no guarantee that the buggy code /won't/ return the same /
> incorrect/ results multiple times in a row, making your results-caching-
> and-comparison workaround moot.


It's not difficult to get 256 random bits from an internal network, or perhaps even from
the internal state of a personal computer.

In my thought experiment, the same incorrect hashsum will not be outputted a second
tme before our sun has burned out.


> This seems like a truly wasteful and way to circumvent preventable errors.


I intend to use the solution to this thought experiment as a general approach
to making unreliable systems very reliable.

Frederick Gotham

unread,
Mar 13, 2021, 5:08:52 PM3/13/21
to
I wrote earlier:
> I haven't started coding the second helper program yet, but I've finished the buggy version of the SHA256 program.

I've finished writing the helper program that invokes the buggy SHA256 program.

Here's how the helper program works: pipe - fork - dup2 - execv

I'm not sure if Google Groups will ruin my indentation but if it does then download the original raw ASCII source file from here:
http://virjacode.com/experimental3/helper.c

I have not optimised the helper program; for example I could make the ring buffer store raw binary digests (i.e. 32 bytes) instead of ASCII digests (i.e. 64 bytes), and I could keep track of the capacity of the ring buffer to avoid unnecessary comparisons.

Here is the helper program that invokes the buggy program, I've replaced all tabs with four spaces but Google might get creative with my four spaces, I've tested it on my x86_64 Ubuntu 18.04 machine and it works:

#define NDEBUG

#ifdef NDEBUG
int const _DeBuG_ = 0;
#else
int const _DeBuG_ = 1;
#endif

#include <string.h> /* memcpy, memcmp */
#include <stdio.h> /* printf */

#include <sys/types.h> /* fork */
#include <unistd.h> /* pipe, dup2, fork, read, close, STDIN_FILENO, STDOUT_FILENO */
#include <sys/wait.h> /* wait */

#define READ_END (0)
#define WRITE_END (1)

#define LEN_DIGEST (32u) /* The digest of the SHA256 hash function is 32 bytes long */
#define ASCII_LEN_DIGEST (2u*LEN_DIGEST)
char g_buf[ASCII_LEN_DIGEST + 2u]; /* +2 for \n\0 -- The SHA256 program prints output with a new line at the end */

int Get_Hashsum(char **const argv)
{
pid_t pid;
int link[2u];

if ( -1 == pipe(link) ) return 0;

_DeBuG_ && puts("Fork");
switch ( pid = fork() )
{
case 0: /* Child */

dup2(link[WRITE_END], STDOUT_FILENO);
close(link[READ_END]);
close(link[WRITE_END]);

_DeBuG_ && puts("execv");
execv("./sha256", argv);

_DeBuG_ && puts("FAILURE of execv");
return 0;

case -1: /* Error in fork'ing */

_DeBuG_ && puts("Fork Failure");
return 0;

default: /* Parent */
{
int retval;

int const total_bytes_needed = ASCII_LEN_DIGEST+1u; /* +1 for '\n' */

int last_count = 0;

close(link[WRITE_END]);

while ( 1 )
{
int const nbytes = read(link[READ_END], g_buf + last_count, total_bytes_needed - last_count);

_DeBuG_ && printf("Read from pipe results = %i\n", nbytes);

if ( total_bytes_needed == nbytes + last_count )
{
retval = 1;
break;
}
else if ( 0 == nbytes ) /* If the other program crashed */
{
retval = 0;
break;
}
else
{
if ( nbytes > 0 ) last_count += nbytes;
}
}

_DeBuG_ && puts("Wait for any child processes to die");
wait(NULL);

return retval;
}
}
}

#define CAPACITY_OF_RING_BUFFER (1024u)

int Append_To_Ring_Cache_And_Check_For_Duplicate(char const *const hashsum)
{
unsigned i;

static char g_circular_buffer[CAPACITY_OF_RING_BUFFER][ASCII_LEN_DIGEST] = {0};
static char (*g_pos)[ASCII_LEN_DIGEST] = &g_circular_buffer[0u];

for (i = 0; i != CAPACITY_OF_RING_BUFFER; ++i)
{
if ( &g_circular_buffer[i] == g_pos ) continue;

if ( 0 == memcmp(hashsum, g_circular_buffer[i], ASCII_LEN_DIGEST) )
{
_DeBuG_ && printf("Digests are identical when i = %u\n",i);
return 1;
}
}

memcpy(*g_pos, hashsum, ASCII_LEN_DIGEST); /* hashsum might have a '\n' at the end before the '\0' */
(*g_pos)[ASCII_LEN_DIGEST] = '\0';

++g_pos;

if ( g_pos == &g_circular_buffer[CAPACITY_OF_RING_BUFFER] )
{
g_pos = &g_circular_buffer[0u];
}

return 0;
}

void Print_Digest(char const *const p)
{
/* If it was pure binary

unsigned i;

for (i = 0u; i != 32u; ++i)
{
printf("%02x", p[i]);
}

printf("\n");
*/

printf("%s", p); /* It already has a new line on the end */
}

int main(int const argc, char **const argv) /* argv[argc] is always a null pointer */
{
while ( 1 )
{
if ( Get_Hashsum(argv) )
{
if ( Append_To_Ring_Cache_And_Check_For_Duplicate(g_buf) )
{
Print_Digest(g_buf);
return 0;
}
}
}
}





Frederick Gotham

unread,
Mar 13, 2021, 5:16:40 PM3/13/21
to
On Saturday, March 13, 2021 at 10:08:52 PM UTC, Frederick Gotham wrote:

> Here's how the helper program works: pipe - fork - dup2 - execv

By the way if you're wondering why I just didn't use 'popen', well it's because 'popen' starts a new shell. I don't want the overhead of a starting a new shell and I don't need a new shell anyway.

0 new messages