summaryrefslogtreecommitdiffstats
path: root/lib
AgeCommit message (Expand)Author
2021-01-20Fetcher: Update client and add testSean Molenaar
2021-01-18Migration: Don't use unsigned for pubdateSean Molenaar
2021-01-18Controllers: Fetch feed after creatingSean Molenaar
2021-01-17fix typo in string template valueanoy
2021-01-17fix TypeError caused by type conversion in controlleranoy
2021-01-15fix TypeError in ItemMapperanoy
2021-01-13fix opened state of folders is not restoredanoy
2021-01-12Fix psalm issuesSean Molenaar
2021-01-12Fix getUnreadOrStarredTypeSean Molenaar
2021-01-11remove deprecated colon prefix for parametersanoy
2021-01-08autofix round2Benjamin Brahmer
2021-01-08fix the testBenjamin Brahmer
2021-01-08fixes done by psalmBenjamin Brahmer
2020-12-30Improve itemmapper wordingSean Molenaar
2020-12-30Remove PHPunit integration testsSean Molenaar
2020-12-23fix FeedMapper find exceptionanoy
2020-12-17Fix mapper->find and empty user sessionsSean Molenaar
2020-12-15Remove LastModified-based cursor when updating feedsKevin Decherf
2020-12-08Fix alias usage in v2 mapperSean Molenaar
2020-12-08Refactor User ID fetching and fix non-specific cleanupSean Molenaar
2020-12-08Remove usage of old Folder codeSean Molenaar
2020-11-19Return expected Value for Symfony Commandswaffshappen
2020-11-11fix second where overrides previous conditionanoy
2020-11-10Unremove but deprecate User APISean Molenaar
2020-11-10Fix unread count and related issuesSean Molenaar
2020-11-07remove deprecated YouTube playlist APIanoy
2020-11-03Controllers: Use v2 servicesSean Molenaar
2020-11-03fix delete apiBenjamin Brahmer
2020-11-03Add migration with foreign keysSean Molenaar
2020-10-29Merge branch 'master' into fix/dont_overload_getidBenjamin Brahmer
2020-10-29Command: Add unittestsSean Molenaar
2020-10-28DB: stop overloading getIDSean Molenaar
2020-10-12Allow titles to be nullSean Molenaar
2020-10-09Fix minor issues, prepare for foreign keys and check feedsSean Molenaar
2020-10-09Update feeds of Explore page, curate instead of just allowing everything, to ...Jan C. Borchardt
2020-10-09fix multiple results for guid_hashanoy
2020-10-09Add Type Info to EntitiesTilo Spannagel
2020-10-08Update feeds.en.jsonSturtz Network
2020-10-05Fix Application class loading in configSean Molenaar
2020-10-05Define microtime as stringSean Molenaar
2020-10-04update only relevant item fieldsanoy
2020-10-04trim whitespaces in item titlesanoy
2020-10-04Fix 'news:updater:after-update' commandGioele Falcetti
2020-10-03remove doubled executeanoy
2020-10-03DB: Fix mime checkingSean Molenaar
2020-10-03fix matching any stringBenjamin Brahmer
2020-10-03remove loggerParamsBenjamin Brahmer
2020-10-03Fix various 15.0 bugsSean Molenaar
2020-10-02fix invalid UserId when logged outanoy
2020-10-02fix incorrect cron recommendationanoy
n107' href='#n107'>107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
use std::io;
use std::fmt;
use std::cmp;

use super::*;

/// A `BufferedReader` that duplicates the underlying `BufferedReader`
/// without consuming any of the data.
///
/// Note: this will likely cause the underlying stream to buffer as
/// much data as you read.  Thus, it should only be used for peeking
/// at the underlying `BufferedReader`.
pub struct BufferedReaderDup<R: BufferedReader<C>, C> {
    reader: R,

    // The number of bytes that have been consumed.
    cursor: usize,

    // The user settable cookie.
    cookie: C,
}

impl<R: BufferedReader<C>, C> fmt::Debug for BufferedReaderDup<R, C> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("BufferedReaderDup")
            .field("reader", &self.reader)
            .field("cursor", &self.cursor)
            .finish()
    }
}

impl<R: BufferedReader<()>> BufferedReaderDup<R, ()> {
    /// Instantiate a new memory-based reader.  `buffer` contains the
    /// reader's contents.
    pub fn new(reader: R) -> Self {
        Self::with_cookie(reader, ())
    }
}

impl<R: BufferedReader<C>, C> BufferedReaderDup<R, C> {
    /// Like `new()`, but sets a cookie, which can be retrieved using
    /// the `cookie_ref` and `cookie_mut` methods, and set using
    /// the `cookie_set` method.
    pub fn with_cookie(reader: R, cookie: C) -> Self {
        BufferedReaderDup {
            reader: reader,
            cursor: 0,
            cookie: cookie,
        }
    }

    /// Returns the number of bytes that have been consumed by this
    /// reader.
    pub fn total_out(&self) -> usize {
        return self.cursor;
    }
}

impl<R: BufferedReader<C>, C> io::Read for BufferedReaderDup<R, C> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
        let data = self.reader.data(self.cursor + buf.len())?;
        assert!(data.len() >= self.cursor);
        let data = &data[self.cursor..];

        let amount = cmp::min(buf.len(), data.len());
        buf.copy_from_slice(&data[..amount]);

        self.cursor += amount;

        Ok(amount)
    }
}

impl<R: BufferedReader<C>, C> BufferedReader<C> for BufferedReaderDup<R, C> {
    fn buffer(&self) -> &[u8] {
        let data = self.reader.buffer();
        assert!(data.len() >= self.cursor);
        &data[self.cursor..]
    }

    fn data(&mut self, amount: usize) -> Result<&[u8], io::Error> {
        let data = self.reader.data(self.cursor + amount)?;
        assert!(data.len() >= self.cursor);
        Ok(&data[self.cursor..])
    }

    fn consume(&mut self, amount: usize) -> &[u8] {
        let data = self.reader.buffer();
        assert!(data.len() >= self.cursor + amount);
        let data = &data[self.cursor..];
        self.cursor += amount;
        data
    }

    fn data_consume(&mut self, amount: usize) -> Result<&[u8], io::Error> {
        let data = self.reader.data(self.cursor + amount)?;
        assert!(data.len() >= self.cursor);
        let data = &data[self.cursor..];
        self.cursor += cmp::min(data.len(), amount);
        Ok(data)
    }

    fn data_consume_hard(&mut self, amount: usize) -> Result<&[u8], io::Error> {
        let data = self.reader.data_hard(self.cursor + amount)?;
        assert!(data.len() >= self.cursor + amount);
        let data = &data[self.cursor..];
        self.cursor += amount;
        Ok(data)
    }

    fn get_mut(&mut self) -> Option<&mut BufferedReader<C>> {
        Some(&mut self.reader)
    }

    fn get_ref(&self) -> Option<&BufferedReader<C>> {
        Some(&self.reader)
    }

    fn into_inner<'b>(self: Box<Self>) -> Option<Box<BufferedReader<C> + 'b>>
            where Self: 'b {
        Some(Box::new(self.reader))
    }

    fn cookie_set(&mut self, cookie: C) -> C {
        use std::mem;

        mem::replace(&mut self.cookie, cookie)
    }

    fn cookie_ref(&self) -> &C {
        &self.cookie
    }

    fn cookie_mut(&mut self) -> &mut C {
        &mut self.cookie
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn buffered_reader_memory_test () {
        let data : &[u8] = include_bytes!("buffered-reader-test.txt");
        let reader = BufferedReaderMemory::new(data);
        let mut reader = BufferedReaderDup::new(reader);

        buffered_reader_test_data_check(&mut reader);

        let consumed = reader.total_out();
        assert_eq!(consumed, data.len());

        // Since we haven't consumed the inner buffer, this should
        // still work.
        let mut reader = Box::new(reader).into_inner().unwrap();

        // Try to read consumed + 1 bytes (which shouldn't be
        // possible).
        assert_eq!(consumed, reader.data(consumed + 1).unwrap().len());

        buffered_reader_test_data_check(&mut reader);
    }

    // Test that buffer() returns the same data as data().
    #[test]
    fn buffer_test() {
        // Test vector.  A BufferedReaderDup returns all unconsumed
        // data.  So, use a relatively small buffer size.
        let size = DEFAULT_BUF_SIZE;
        let mut input = Vec::with_capacity(size);
        let mut v = 0u8;
        for _ in 0..size {
            input.push(v);
            if v == std::u8::MAX {
                v = 0;
            } else {
                v += 1;
            }
        }

        let reader = BufferedReaderMemory::new(&input[..]);
        let mut reader = BufferedReaderDup::new(reader);

        for i in 0..input.len() {
            let data = reader.data(DEFAULT_BUF_SIZE + 1).unwrap().to_vec();
            assert!(data.len() > 0);
            assert_eq!(data