[Shootout-list] improved reversefile.c

Alan Post apost@recalcitrant.org
Wed, 28 Jul 2004 10:15:16 +0000 (UTC)


The following reversefile.c is 10% faster for me for N=20, on my PII
333 NetBSD laptop.  It is a translation of the clever ocaml solution,
which avoids copying around the input.

Much less pretty than the ocaml version, of course, because C doesn't
have builtin lists, tuples, pattern matching, type inference, and so
on.  :)

/* -*- mode: c -*-
 * from Alan Post <apost@recalcitrant.org>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

/*
 * Note that malloc(3) seems happier with chunks of 4096
 */
#define MAXREAD (4096 - sizeof( size_t ) - sizeof( void* ))

typedef struct buf_t { char d[MAXREAD];
                       size_t len;
                       struct buf_t *next;} buf_t;

static buf_t *read_lines( buf_t *tail )
{
    tail->next = NULL;

    buf_t *curr = tail;
    while ( 1 )
    {
        int nread = read( STDIN_FILENO, curr->d, MAXREAD );
        curr->len = nread;
        if ( nread < MAXREAD ) return curr;
        buf_t *head = (buf_t*) malloc( sizeof( buf_t ));
        head->next = curr;
        curr = head;
    }
}

#define WRITEOUT \
        do { \
                fwrite( pos, end - pos, 1, stdout ); \
                for (; loh != NULL; loh = loh->next ) \
                    fwrite( loh->d, loh->len, 1, stdout ); \
        } while (0)

int main(int argc, char *argv[]) {
    buf_t tail;
    buf_t *head = read_lines( &tail );

    buf_t *loh = NULL;
    buf_t *curr = head;

    while ( 1 )
    {
        char *buf = curr->d;
        char *end = buf + curr->len;
        char *pos = end;
        for (;; pos--)
        {
            if ( pos <= buf )
            {
                buf_t *new_curr = curr->next;

                if (new_curr == NULL )
                {
                    WRITEOUT;
                    return(EXIT_SUCCESS);
                }

                curr->len = end - buf;
                curr->next = loh;
                loh = curr;
                
                curr = new_curr;
                break;
            }
            if ( *(pos-1) == '\n' )
            {
                WRITEOUT;
                end = pos;
            }
        }
    }
    assert( NULL == "unreachable" );
    return(EXIT_FAILURE);
}