1 /*
2  * Copyright (C) 2020 Veloman Yunkan
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
11  * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
12  * NON-INFRINGEMENT.  See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  */
19 
20 #ifndef ZIM_DECODERSTREAMREADER_H
21 #define ZIM_DECODERSTREAMREADER_H
22 
23 #include "compression.h"
24 #include "istreamreader.h"
25 
26 namespace zim
27 {
28 
29 template<typename Decoder>
30 class DecoderStreamReader : public IStreamReader
31 {
32 private: // constants
33   enum { CHUNK_SIZE = 1024 };
34 
35 public: // functions
DecoderStreamReader(std::shared_ptr<const Reader> inputReader)36   DecoderStreamReader(std::shared_ptr<const Reader> inputReader)
37     : m_encodedDataReader(inputReader),
38       m_currentInputOffset(0),
39       m_inputBytesLeft(inputReader->size()),
40       m_encodedDataChunk(Buffer::makeBuffer(zsize_t(CHUNK_SIZE)))
41   {
42     Decoder::init_stream_decoder(&m_decoderState, nullptr);
43     readNextChunk();
44   }
45 
~DecoderStreamReader()46   ~DecoderStreamReader()
47   {
48     Decoder::stream_end_decode(&m_decoderState);
49   }
50 
51 private: // functions
readNextChunk()52   void readNextChunk()
53   {
54     const auto n = std::min(zsize_t(CHUNK_SIZE), m_inputBytesLeft);
55     m_encodedDataChunk = m_encodedDataReader->get_buffer(m_currentInputOffset, n);
56     m_currentInputOffset += n;
57     m_inputBytesLeft -= n;
58     // XXX: ugly C-style cast (casting away constness) on the next line
59     m_decoderState.next_in  = (unsigned char*)m_encodedDataChunk.data();
60     m_decoderState.avail_in = m_encodedDataChunk.size().v;
61   }
62 
decodeMoreBytes()63   CompStatus decodeMoreBytes()
64   {
65     CompStep step = CompStep::STEP;
66     if ( m_decoderState.avail_in == 0 )
67     {
68       if ( m_inputBytesLeft.v == 0 )
69         step = CompStep::FINISH;
70       else
71         readNextChunk();
72     }
73 
74     return Decoder::stream_run_decode(&m_decoderState, step);
75   }
76 
readImpl(char * buf,zsize_t nbytes)77   void readImpl(char* buf, zsize_t nbytes) override
78   {
79     m_decoderState.next_out = (unsigned char*)buf;
80     m_decoderState.avail_out = nbytes.v;
81     while ( m_decoderState.avail_out != 0 )
82     {
83       // We don't car of the return code of decodeMoreBytes.
84       // We feed (or stop feeding) the decoder based on what
85       // we need to decode and the `avail_in`.
86       // If there is a error somehow, a exception will be thrown.
87       decodeMoreBytes();
88     }
89   }
90 
91 private: // types
92   typedef typename Decoder::stream_t DecoderState;
93 
94 private: // data
95   std::shared_ptr<const Reader> m_encodedDataReader;
96   offset_t m_currentInputOffset;
97   zsize_t m_inputBytesLeft; // count of bytes left in the input stream
98   DecoderState m_decoderState;
99   Buffer m_encodedDataChunk;
100 };
101 
102 } // namespace zim
103 
104 #endif // ZIM_DECODERSTREAMREADER_H
105