fastq.c
/***************************************************************************
 *  Library:
 *      #include <biolibc/fastq.h>
 *      -lbiolibc -lxtend
 *
 *  Description:
 *      Read a FASTQ record from a FILE stream.  Each record must begin
 *      with a description line (beginning with '@'), which is then
 *      followed by one or more lines of sequence data, a separator line
 *      beginning with '+', and a line of quality scores.  The end of the
 *      sequence is marked either by the next description line or EOF.
 *      If desc_len and seq_len are 0 (e.g. the structure is initialized
 *      with BL_FASTQ_INIT or bl_fastq_init(3), has been freed with
 *      bl_fastq_free(3), then memory is allocated for each line.
 *
 *      Otherwise, the existing allocated buffers are reused.  Hence, when
 *      reading many FASTQ records of the same length, only one allocation
 *      is needed.  In any case, the buffers are automatically enlarged if
 *      they become full and automatically trimmed to the actual data size
 *      after reading is complete.
 *
 *      Buffer memory should be freed as soon as possible by calling
 *      bl_fastq_free(3).
 *  
 *  Arguments:
 *      fastq_stream    FILE stream from which FASTQ data are read
 *      record          Pointer to a bl_fastq_t structure to receive data
 *
 *  Returns:
 *      BL_READ_OK upon successful read of description and sequence
 *      BL_READ_BAD_DATA if something is amiss with input format
 *      BL_READ_EOF if no more data are available
 *
 *  Examples:
 *      bl_fastq_t  rec = BL_FASTQ_INIT;
 *
 *      while ( bl_fastq_read(stdin, &rec) != BL_READ_EOF )
 *          bl_fastq_write(stdout, &rec, BL_FASTQ_LINE_UNLIMITED);
 *      bl_fastq_free(&rec);
 *
 *  See also:
 *      bl_fastq_write(3), bl_fastq_read(3), bl_fastq_write(3),
 *      bl_fastq_free(3)
 *
 *  History: 
 *  Date        Name        Modification
 *  2021-07-28  Jason Bacon Begin
 ***************************************************************************/
#include 
-lbiolibc -lxtend

Read a FASTQ record from a FILE stream.  Each record must begin
with a description line (beginning with '@'), which is then
followed by one or more lines of sequence data, a separator line
beginning with '+', and a line of quality scores.  The end of the
sequence is marked either by the next description line or EOF.
If desc_len and seq_len are 0 , has been freed with
bl_fastq_free(3), then memory is allocated for each line.

Otherwise, the existing allocated buffers are reused.  Hence, when
reading many FASTQ records of the same length, only one allocation
is needed.  In any case, the buffers are automatically enlarged if
they become full and automatically trimmed to the actual data size
after reading is complete.

Buffer memory should be freed as soon as possible by calling
bl_fastq_free(3).

fastq_stream    FILE stream from which FASTQ data are read
record          Pointer to a bl_fastq_t structure to receive data

BL_READ_OK upon successful read of description and sequence
BL_READ_BAD_DATA if something is amiss with input format
BL_READ_EOF if no more data are available







Date        Name        Modification
2021-07-28  Jason Bacon Begin
#include - lbiolibc - lxtend Read a FASTQ record from a FILE stream . Each record must begin with a description line ( beginning with ' @ ' ) which is then followed by one or more lines of sequence data a separator line beginning with ' + ' and a line of quality scores . The end of the sequence is marked either by the next description line or EOF . If desc_len and seq_len are 0 has been freed with bl_fastq_free ( 3 ) then memory is allocated for each line . Otherwise the existing allocated buffers are reused . Hence when reading many FASTQ records of the same length only one allocation is needed . In any case the buffers are automatically enlarged if they become full and automatically trimmed to the actual data size after reading is complete . Buffer memory should be freed as soon as possible by calling bl_fastq_free ( 3 ) . fastq_stream FILE stream from which FASTQ data are read record Pointer to a bl_fastq_t structure to receive data BL_READ_OK upon successful read of description and sequence BL_READ_BAD_DATA if something is amiss with input format BL_READ_EOF if no more data are available Date Name Modification 2021 - 07 - 28 Jason Bacon Begin
int     bl_fastq_read(bl_fastq_t *record, FILE *fastq_stream)
{
    int     ch,
	    last_ch;
    size_t  len;
    while ( ((ch = getc(fastq_stream)) == ';') && (ch != EOF) )
	while ( ((ch = getc(fastq_stream)) != '\n') && (ch != EOF) )
	    ;
    if ( ch == EOF )
	return BL_READ_EOF;
    if ( ch == '@' )    
    {
	ungetc(ch, fastq_stream);
	ch = dsv_read_field_malloc(fastq_stream, &record->desc,
			    &record->desc_array_size, "", &record->desc_len);
	if ( record->desc == NULL )
	{
	    fprintf(stderr, "bl_fastq_read(): Could not allocate desc.\n");
	    exit(EX_UNAVAILABLE);
	}
	if ( ch == EOF )
	{
	    fprintf(stderr, "bl_fastq_read(): Record truncated in desc %s.\n",
		    record->desc);
	    return BL_READ_TRUNCATED;
	}
	else if ( ch != '\n' )
	{
	    fprintf(stderr, "bl_fastq_read(): Bad data after desc %s\n", record->desc);
	    return BL_READ_BAD_DATA;
	}
	if ( record->seq_array_size == 0 )
	{
	    record->seq_array_size = 1024;
	    record->seq = xt_malloc(record->seq_array_size,
				    sizeof(*record->seq));
	    if ( record->seq == NULL )
	    {
		fprintf(stderr, "bl_fastq_read(): Could not allocate seq.\n");
		exit(EX_UNAVAILABLE);
	    }
	}
	len = 0;
	do
	{
	    if ( ch != '\n' )
		record->seq[len++] = ch;
	    if ( len == record->seq_array_size - 1 )
	    {
		record->seq_array_size *= 2;
		record->seq = xt_realloc(record->seq, record->seq_array_size,
		    sizeof(*record->seq));
		if ( record->seq == NULL )
		{
		    fprintf(stderr, "bl_fastq_read(): Could not reallocate seq.\n");
		    exit(EX_UNAVAILABLE);
		}
	    }
	    last_ch = ch;
	}   while ( ((ch = getc(fastq_stream)) != '+') && (ch != EOF) );
	record->seq[len] = '\0';
	record->seq_len = len;
	if ( last_ch != '\n' )
	    fprintf(stderr, "bl_fasta_read(): Missing newline at end of seq %s.\n",
		    record->qual);
	if ( record->seq_array_size != record->seq_len + 1 )
	{
	    record->seq_array_size = record->seq_len + 1;
	    record->seq = xt_realloc(record->seq, record->seq_array_size,
		sizeof(*record->seq));
	}
	if ( ch == EOF )
	{
	    fprintf(stderr, "bl_fastq_read(): Record truncated in seq %s.\n",
		    record->seq);
	    return BL_READ_TRUNCATED;
	}
	else if (ch != '+')
	{
	    fprintf(stderr, "bl_fasq_read(): Bad data after seq %s\n", record->seq);
	    return BL_READ_BAD_DATA;
	}
	ungetc(ch, fastq_stream);
	ch = dsv_read_field_malloc(fastq_stream, &record->plus,
			    &record->plus_array_size, "", &record->plus_len);
	if ( record->plus == NULL )
	{
	    fprintf(stderr, "bl_fastq_read(): Could not allocate plus.\n");
	    exit(EX_UNAVAILABLE);
	}
	if ( ch == EOF )
	{
	    fprintf(stderr, "bl_fastq_read(): Record truncated in plus %s.\n",
		    record->plus);
	    return BL_READ_TRUNCATED;
	}
	else if ( ch != '\n' )
	{
	    fprintf(stderr, "bl_fasq_read(): Bad data after plus %s\n",
		    record->plus);
	    return BL_READ_BAD_DATA;
	}
	if ( record->qual_array_size == 0 )
	{
	    record->qual_array_size = record->seq_array_size;
	    record->qual = xt_malloc(record->qual_array_size, sizeof(*record->qual));
	    if ( record->qual == NULL )
	    {
		fprintf(stderr, "bl_fastq_read(): Could not allocate qual.\n");
		exit(EX_UNAVAILABLE);
	    }
	}
	len = 0;
	do
	{
	    while ( ((ch = getc(fastq_stream)) != '\n') && (ch != EOF) )
	    {
		record->qual[len++] = ch;
		if ( len == record->qual_array_size - 1 )
		{
		    record->qual_array_size *= 2;
		    record->qual = xt_realloc(record->qual, record->qual_array_size,
			sizeof(*record->qual));
		    if ( record->qual == NULL )
		    {
			fprintf(stderr, "bl_fastq_read(): Could not reallocate qual.\n");
			exit(EX_UNAVAILABLE);
		    }
		}
	    }
	    last_ch = ch;
	}   while ( ((ch = getc(fastq_stream)) != '@') && (ch != EOF) );
	record->qual[len] = '\0';
	record->qual_len = len;
	if ( last_ch != '\n' )
	    fprintf(stderr, "bl_fasta_read(): Missing newline at end of qual %s.\n",
		    record->qual);
	if ( ch == '@' )
	    ungetc(ch, fastq_stream);
	return BL_READ_OK;
    }
    else
	return BL_READ_BAD_DATA;
}
int bl_fastq_read ( bl_fastq_t * record , FILE * fastq_stream ) { int ch , last_ch ; size_t len ; while ( ( ( ch = getc ( fastq_stream ) ) == ' ' ) && ( ch != EOF ) ) while ( ( ( ch = getc ( fastq_stream ) ) != ' \n ' ) && ( ch != EOF ) ) ; if ( ch == EOF ) return BL_READ_EOF ; if ( ch == ' ' ) { ungetc ( ch , fastq_stream ) ; ch = dsv_read_field_malloc ( fastq_stream , & record -> desc , & record -> desc_array_size , " " , & record -> desc_len ) ; if ( record -> desc == NULL ) { fprintf ( stderr , " \n " ) ; exit ( EX_UNAVAILABLE ) ; } if ( ch == EOF ) { fprintf ( stderr , " \n " , record -> desc ) ; return BL_READ_TRUNCATED ; } else if ( ch != ' \n ' ) { fprintf ( stderr , " \n " , record -> desc ) ; return BL_READ_BAD_DATA ; } if ( record -> seq_array_size == 0 ) { record -> seq_array_size = 1024 ; record -> seq = xt_malloc ( record -> seq_array_size , sizeof ( * record -> seq ) ) ; if ( record -> seq == NULL ) { fprintf ( stderr , " \n " ) ; exit ( EX_UNAVAILABLE ) ; } } len = 0 ; do { if ( ch != ' \n ' ) record -> seq [ len ++ ] = ch ; if ( len == record -> seq_array_size - 1 ) { record -> seq_array_size *= 2 ; record -> seq = xt_realloc ( record -> seq , record -> seq_array_size , sizeof ( * record -> seq ) ) ; if ( record -> seq == NULL ) { fprintf ( stderr , " \n " ) ; exit ( EX_UNAVAILABLE ) ; } } last_ch = ch ; } while ( ( ( ch = getc ( fastq_stream ) ) != ' ' ) && ( ch != EOF ) ) ; record -> seq [ len ] = ' \0 ' ; record -> seq_len = len ; if ( last_ch != ' \n ' ) fprintf ( stderr , " \n " , record -> qual ) ; if ( record -> seq_array_size != record -> seq_len + 1 ) { record -> seq_array_size = record -> seq_len + 1 ; record -> seq = xt_realloc ( record -> seq , record -> seq_array_size , sizeof ( * record -> seq ) ) ; } if ( ch == EOF ) { fprintf ( stderr , " \n " , record -> seq ) ; return BL_READ_TRUNCATED ; } else if ( ch != ' ' ) { fprintf ( stderr , " \n " , record -> seq ) ; return BL_READ_BAD_DATA ; } ungetc ( ch , fastq_stream ) ; ch = dsv_read_field_malloc ( fastq_stream , & record -> plus , & record -> plus_array_size , " " , & record -> plus_len ) ; if ( record -> plus == NULL ) { fprintf ( stderr , " \n " ) ; exit ( EX_UNAVAILABLE ) ; } if ( ch == EOF ) { fprintf ( stderr , " \n " , record -> plus ) ; return BL_READ_TRUNCATED ; } else if ( ch != ' \n ' ) { fprintf ( stderr , " \n " , record -> plus ) ; return BL_READ_BAD_DATA ; } if ( record -> qual_array_size == 0 ) { record -> qual_array_size = record -> seq_array_size ; record -> qual = xt_malloc ( record -> qual_array_size , sizeof ( * record -> qual ) ) ; if ( record -> qual == NULL ) { fprintf ( stderr , " \n " ) ; exit ( EX_UNAVAILABLE ) ; } } len = 0 ; do { while ( ( ( ch = getc ( fastq_stream ) ) != ' \n ' ) && ( ch != EOF ) ) { record -> qual [ len ++ ] = ch ; if ( len == record -> qual_array_size - 1 ) { record -> qual_array_size *= 2 ; record -> qual = xt_realloc ( record -> qual , record -> qual_array_size , sizeof ( * record -> qual ) ) ; if ( record -> qual == NULL ) { fprintf ( stderr , " \n " ) ; exit ( EX_UNAVAILABLE ) ; } } } last_ch = ch ; } while ( ( ( ch = getc ( fastq_stream ) ) != ' ' ) && ( ch != EOF ) ) ; record -> qual [ len ] = ' \0 ' ; record -> qual_len = len ; if ( last_ch != ' \n ' ) fprintf ( stderr , " \n " , record -> qual ) ; if ( ch == ' ' ) ungetc ( ch , fastq_stream ) ; return BL_READ_OK ; } else return BL_READ_BAD_DATA ; }
Library:
      #include <biolibc/fastq.h>
      -lbiolibc -lxtend
Library : #include <biolibc / fastq . h > - lbiolibc - lxtend
/* Skip comment lines */ /* Every record should begin with a '@' */ // Desc /*
	 *  Read description
	 */ /* Should not encounter EOF while reading description line */ /* Every description should be followed by at least one seq line */ /*
	 *  Read sequence lines.  May span multiple lines so can't use
	 *  dsv_read_field_malloc().
	 */ // Easily hold a short read sequence and minimize reallocs for long /* 
	 * Trim array.  realloc() can carry a significant cost, but it does
	 * not affect overall performance here, probably because I/O is
	 * the major bottleneck.
	 */ /* Should not encounter EOF while reading sequence lines */ /* Every sequence should be followed by a + separator line */ // Put '+' back so it's read into plus field /*
	 *  Read + separator
	 */ /* Should not encounter EOF while reading plus line */ /* Every plus should be followed by at least one qual line */ /*
	 *  Read quality string.  May span multiple lines so can't use
	 *  dsv_read_field_malloc().
	 */ // FIXME: This could be problematic with bad data where qual len // doesn't match seq len // Must match sequence len and no need to trim /* Read at least one full line, since '@' can be a quality score */ /*
	 *  This is where EOF should occur since we read past newlines
	 *  No need to trim since qual must be the same size as seq
	 */ // Put '@' back so it's read into next desc