#buf-reader #async-std #extension #file #prelude #chunks #predicate

async_buf_reader_utils

Adds additional functionality to the async-std crate for BufReader

1 unstable release

0.1.0 Dec 22, 2019

#2072 in Asynchronous

Apache-2.0/MIT

16KB
240 lines

async_buf_reader_utils

The purpose of this crate is to add additional functionality to the async-std crate for the async BufReader implementation.

To add the additional methods to async BufReader, simple add this to your given module:

use async_buf_reader_utils::prelude::*;

Methods

buf_reader.read_until_index_found(&mut predicate, &mut fillable).await

    fn read_until_index_found<'a, F>(
        &'a mut self,
        predicate_found_index: &'a mut F,
        buf: &'a mut Vec<u8>,
    ) -> ReadChunkUntilIndexFoundFuture<'a, Self, F> 

Example

The goal of this example is to load a text file and asynchronously find groups of characters leading up to the 4th comma within the given group.

so if the input text file is:

1,50,0,3,17,1,212,3,1,3,4,3,1,5

we want to be able to return chunks of:

> 1,50,0,3,
> 17,1,212,3,
> 1,3,4,3,
> 1,5

Here is the business logic to solve this problem with async read_until_index_found:

      fn main() {
        let _result: Result<(), String> = async_std::task::block_on(async {
        
            use async_buf_reader_utils::prelude::*;
            use async_std::{fs::File, io::BufReader};

            // Path to file you want to read asynchronously
            let str_path = "./data/file.txt";

            // Async load file (you can optionally use method with_capacity to limit how 
            // much async-std BufReader bytes are loaded while reading which is useful in memory sensative environments)
            let mut buf = BufReader::with_capacity(12, File::open(&str_path).await.expect("should have opened file"));
            
            let mut comma_count: usize = 0;
            let mut last_comma_idx = None;
            let mut fillable = vec![];

            // Our predicate closure is our business logic,
            // where we are trying to capture all characters up to
            // the 4th comma, remembering that a regex won't be as useful here
            // because our predicate will be called for every loaded chunk
            // of data returned by our async BufReader, so a desired match 
            // might stretch across two different strings provided to the predicate.

            // Once we've reached the 4th comma, return the index via Some(index) and reset
            // our counters, otherwise return None to indicate we are still searching.
            let mut predicate = |str_data: &str|->Option<usize>{
                // println!("in predicate {:?}", str_data);
                last_comma_idx = str_data.bytes().enumerate().find_map(|(i, x)| {
                    if x == b','{
                        comma_count += 1;
                    }
                    if comma_count == 4 {
                        Some(i)
                    }
                    else{
                        None
                    }
                });
                // println!("cur comma idx {:?}", last_comma_idx);
                if let Some(idx) = last_comma_idx {
                    last_comma_idx = None;
                    comma_count = 0;
                    Some(idx)
                }else{
                    None
                }
            };

            // Start our async while loop passing our predicate and buffer to read_until_index_found,
            // so our loop will continue returning the chunks of strings that are resulting
            // from the returned indices in our predicate.
            while let Ok(Some(_)) = buf.read_until_index_found(&mut predicate, &mut fillable).await {
                println!("in while await loop {:?}", str::from_utf8(&fillable).unwrap());
                fillable.clear();
            }
            Ok(())
        });
      }

Dependencies

~5–14MB
~182K SLoC