/ libi2pd / Gzip.cpp
Gzip.cpp
  1  /*
  2  * Copyright (c) 2013-2022, The PurpleI2P Project
  3  *
  4  * This file is part of Purple i2pd project and licensed under BSD3
  5  *
  6  * See full license text in LICENSE file at top of project tree
  7  */
  8  
  9  #include <inttypes.h>
 10  #include <string.h> /* memset */
 11  #include <iostream>
 12  #include "Log.h"
 13  #include "I2PEndian.h"
 14  #include "Gzip.h"
 15  
 16  namespace i2p
 17  {
 18  namespace data
 19  {
 20  	const size_t GZIP_CHUNK_SIZE = 16384;
 21  
 22  	GzipInflator::GzipInflator (): m_IsDirty (false)
 23  	{
 24  		memset (&m_Inflator, 0, sizeof (m_Inflator));
 25  		inflateInit2 (&m_Inflator, MAX_WBITS + 16); // gzip
 26  	}
 27  
 28  	GzipInflator::~GzipInflator ()
 29  	{
 30  		inflateEnd (&m_Inflator);
 31  	}
 32  
 33  	size_t GzipInflator::Inflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
 34  	{
 35  		if (inLen < 23) return 0;
 36  		if (in[10] == 0x01) // non compressed
 37  		{
 38  			size_t len = bufle16toh (in + 11);
 39  			if (len + 23 < inLen)
 40  			{
 41  				LogPrint (eLogError, "Gzip: Incorrect length");
 42  				return 0;
 43  			}
 44  			if (len > outLen) len = outLen;
 45  			memcpy (out, in + 15, len);
 46  			return len;
 47  		}
 48  		else
 49  		{
 50  			if (m_IsDirty) inflateReset (&m_Inflator);
 51  			m_IsDirty = true;
 52  			m_Inflator.next_in = const_cast<uint8_t *>(in);
 53  			m_Inflator.avail_in = inLen;
 54  			m_Inflator.next_out = out;
 55  			m_Inflator.avail_out = outLen;
 56  			int err;
 57  			if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
 58  				return outLen - m_Inflator.avail_out;
 59  			// else
 60  			if (err)
 61  				LogPrint (eLogError, "Gzip: Inflate error ", err);
 62  			return 0;
 63  		}
 64  	}
 65  
 66  	void GzipInflator::Inflate (const uint8_t * in, size_t inLen, std::ostream& os)
 67  	{
 68  		m_IsDirty = true;
 69  		uint8_t * out = new uint8_t[GZIP_CHUNK_SIZE];
 70  		m_Inflator.next_in = const_cast<uint8_t *>(in);
 71  		m_Inflator.avail_in = inLen;
 72  		int ret;
 73  		do
 74  		{
 75  			m_Inflator.next_out = out;
 76  			m_Inflator.avail_out = GZIP_CHUNK_SIZE;
 77  			ret = inflate (&m_Inflator, Z_NO_FLUSH);
 78  			if (ret < 0)
 79  			{
 80  				inflateEnd (&m_Inflator);
 81  				os.setstate(std::ios_base::failbit);
 82  				break;
 83  			}
 84  			os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
 85  		}
 86  		while (!m_Inflator.avail_out); // more data to read
 87  		delete[] out;
 88  	}
 89  
 90  	void GzipInflator::Inflate (std::istream& in, std::ostream& out)
 91  	{
 92  		uint8_t * buf = new uint8_t[GZIP_CHUNK_SIZE];
 93  		while (!in.eof ())
 94  		{
 95  			in.read ((char *) buf, GZIP_CHUNK_SIZE);
 96  			Inflate (buf, in.gcount (), out);
 97  		}
 98  		delete[] buf;
 99  	}
100  
101  	GzipDeflator::GzipDeflator (): m_IsDirty (false)
102  	{
103  		memset (&m_Deflator, 0, sizeof (m_Deflator));
104  		deflateInit2 (&m_Deflator, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); // 15 + 16 sets gzip
105  	}
106  
107  	GzipDeflator::~GzipDeflator ()
108  	{
109  		deflateEnd (&m_Deflator);
110  	}
111  
112  	void GzipDeflator::SetCompressionLevel (int level)
113  	{
114  		deflateParams (&m_Deflator, level, Z_DEFAULT_STRATEGY);
115  	}
116  
117  	size_t GzipDeflator::Deflate (const uint8_t * in, size_t inLen, uint8_t * out, size_t outLen)
118  	{
119  		if (m_IsDirty) deflateReset (&m_Deflator);
120  		m_IsDirty = true;
121  		m_Deflator.next_in = const_cast<uint8_t *>(in);
122  		m_Deflator.avail_in = inLen;
123  		m_Deflator.next_out = out;
124  		m_Deflator.avail_out = outLen;
125  		int err;
126  		if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
127  		{
128  			out[9] = 0xff; // OS is always unknown
129  			return outLen - m_Deflator.avail_out;
130  		}
131  		// else
132  		if (err)
133  			LogPrint (eLogError, "Gzip: Deflate error ", err);
134  		return 0;
135  	}
136  
137  	size_t GzipDeflator::Deflate (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen)
138  	{
139  		if (m_IsDirty) deflateReset (&m_Deflator);
140  		m_IsDirty = true;
141  		size_t offset = 0;
142  		int err = 0;
143  		for (const auto& it: bufs)
144  		{
145  			m_Deflator.next_in = const_cast<uint8_t *>(it.first);
146  			m_Deflator.avail_in = it.second;
147  			m_Deflator.next_out = out + offset;
148  			m_Deflator.avail_out = outLen - offset;
149  			auto flush = (it == bufs.back ()) ? Z_FINISH : Z_NO_FLUSH;
150  			err = deflate (&m_Deflator, flush);
151  			if (err)
152  			{
153  				if (flush && err == Z_STREAM_END)
154  				{
155  					out[9] = 0xff; // OS is always unknown
156  					return outLen - m_Deflator.avail_out;
157  				}
158  				break;
159  			}
160  			offset = outLen - m_Deflator.avail_out;
161  		}
162  		// else
163  		if (err)
164  			LogPrint (eLogError, "Gzip: Deflate error ", err);
165  		return 0;
166  	}
167  
168  	size_t GzipNoCompression (const uint8_t * in, uint16_t inLen, uint8_t * out, size_t outLen)
169  	{
170  		static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 };
171  		if (outLen < (size_t)inLen + 23) return 0;
172  		memcpy (out, gzipHeader, 11);
173  		htole16buf (out + 11, inLen);
174  		htole16buf (out + 13, 0xffff - inLen);
175  		memcpy (out + 15, in, inLen);
176  		htole32buf (out + inLen + 15, crc32 (0, in, inLen));
177  		htole32buf (out + inLen + 19, inLen);
178  		return inLen + 23;
179  	}
180  
181  	size_t GzipNoCompression (const std::vector<std::pair<const uint8_t *, size_t> >& bufs, uint8_t * out, size_t outLen)
182  	{
183  		static const uint8_t gzipHeader[11] = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01 };
184  		memcpy (out, gzipHeader, 11);
185  		uint32_t crc = 0;
186  		size_t len = 0, len1;
187  		for (const auto& it: bufs)
188  		{
189  			len1 = len;
190  			len += it.second;
191  			if (outLen < len + 23) return 0;
192  			memcpy (out + 15 + len1, it.first, it.second);
193  			crc = crc32 (crc, it.first, it.second);
194  		}
195  		if (len > 0xffff) return 0;
196  		htole32buf (out + len + 15, crc);
197  		htole32buf (out + len + 19, len);
198  		htole16buf (out + 11, len);
199  		htole16buf (out + 13, 0xffff - len);
200  		return len + 23;
201  	}
202  
203  } // data
204  } // i2p