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