stdex Additional custom or not Standard C++ covered algorithms

Loading...
Searching...
No Matches
zlib.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2024 Amebis
4*/
5
6#pragma once
7
8#include "assert.hpp"
9#include "compat.hpp"
10#include "stream.hpp"
11#if _MSC_VER
12#include <CodeAnalysis/Warnings.h>
13#pragma warning(push)
14#pragma warning(disable: ALL_CODE_ANALYSIS_WARNINGS)
15#endif
16#include <zlib.h>
17#if _MSC_VER
18#pragma warning(pop)
19#endif
20#include <memory>
21#include <stdexcept>
22
23#if defined(__GNUC__)
24#pragma GCC diagnostic push
25#pragma GCC diagnostic ignored "-Wunknown-pragmas"
26#endif
27
28 namespace stdex
29{
31 inline void throw_on_zlib_error(int result)
32 {
33 if (result >= 0)
34 return;
35 switch (result) {
36 case Z_ERRNO: throw std::system_error(errno, std::system_category(), "zlib failed with errno");
37 case Z_STREAM_ERROR: throw std::runtime_error("zlib stream error");
38 case Z_DATA_ERROR: throw std::runtime_error("zlib data error");
39 case Z_MEM_ERROR: throw std::bad_alloc();
40 case Z_BUF_ERROR: throw std::runtime_error("zlib buffer error");
41 case Z_VERSION_ERROR: throw std::runtime_error("zlib version error");
42 default: throw std::runtime_error("zlib unknown error");
43 }
44 }
46
50 class zlib_writer : public stdex::stream::converter
51 {
52 public:
53 zlib_writer(_Inout_ stdex::stream::basic& source, _In_ int compression_level = Z_BEST_COMPRESSION, _In_ uInt block_size = 0x10000) :
54 stdex::stream::converter(source),
55 m_block_size(block_size),
56 m_block(new Byte[block_size])
57 {
58 memset(&m_zlib, 0, sizeof(m_zlib));
59 throw_on_zlib_error(deflateInit(&m_zlib, compression_level));
60 }
61
62 virtual ~zlib_writer()
63 {
64 m_zlib.avail_in = 0;
65 m_zlib.next_in = NULL;
66 do {
67 m_zlib.avail_out = m_block_size;
68 m_zlib.next_out = m_block.get();
69 throw_on_zlib_error(deflate(&m_zlib, Z_FINISH));
70 m_source->write(m_block.get(), m_block_size - m_zlib.avail_out);
71 if (!m_source->ok()) _Unlikely_
72 throw std::system_error(sys_error(), std::system_category(), "failed to flush compressed stream"); // Data loss occured
73 } while (m_zlib.avail_out == 0);
74 // m_zlib.avail_out = m_block_size;
75 // m_zlib.next_out = m_block.get();
76 // deflateReset(&m_zlib);
77 deflateEnd(&m_zlib);
78 }
79
80 virtual _Success_(return != 0) size_t write(
81 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
82 {
83 stdex_assert(data || !length);
84 size_t num_written = 0;
85 while (length) {
86 uInt num_inflated = static_cast<uInt>(std::min<size_t>(length, UINT_MAX));
87 m_zlib.avail_in = num_inflated;
88 m_zlib.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(data));
89 do {
90 m_zlib.avail_out = m_block_size;
91 m_zlib.next_out = m_block.get();
92 throw_on_zlib_error(deflate(&m_zlib, Z_NO_FLUSH));
93 size_t num_deflated = m_block_size - m_zlib.avail_out;
94 if (num_deflated) {
95 m_source->write(m_block.get(), num_deflated);
96 if (!m_source->ok()) {
97 m_state = m_source->state();
98 return num_written;
99 }
100 }
101 } while (m_zlib.avail_out == 0);
102 num_written += num_inflated;
103 reinterpret_cast<const Bytef*&>(data) += num_inflated;
104 length -= num_inflated;
105 }
106 m_state = stdex::stream::state_t::ok;
107 return num_written;
108 }
109
110 protected:
111 z_stream m_zlib;
112 uInt m_block_size;
113 std::unique_ptr<Byte[]> m_block;
114 };
115
119 class zlib_reader : public stdex::stream::converter
120 {
121 public:
122 zlib_reader(_Inout_ stdex::stream::basic& source, _In_ uInt block_size = 0x10000) :
123 stdex::stream::converter(source),
124 m_block_size(block_size),
125 m_block(new Byte[block_size])
126 {
127 memset(&m_zlib, 0, sizeof(m_zlib));
128 throw_on_zlib_error(inflateInit(&m_zlib));
129 }
130
131 virtual ~zlib_reader()
132 {
133 inflateEnd(&m_zlib);
134 }
135
136#pragma warning(suppress: 6101) // See [1] below
137 virtual _Success_(return != 0 || length == 0) size_t read(
138 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
139 {
140 stdex_assert(data || !length);
141 size_t num_read = 0;
142 while (length) {
143 uInt num_deflated = static_cast<uInt>(std::min<size_t>(length, UINT_MAX));
144 m_zlib.avail_out = num_deflated;
145 m_zlib.next_out = reinterpret_cast<Bytef*>(data);
146 do {
147 if (m_zlib.avail_in == 0) {
148 m_zlib.next_in = m_block.get();
149 m_zlib.avail_in = static_cast<uInt>(m_source->read(m_block.get(), m_block_size));
150 if (!m_zlib.avail_in) {
151 num_read += num_deflated - m_zlib.avail_out; // [1] Code analysis misses `num_deflated - m_zlib.avail_out` bytes were written to data in previous loop iterations.
152 if (num_read) {
153 m_state = stdex::stream::state_t::ok;
154 return num_read;
155 }
156 m_state = m_source->state();
157 return 0;
158 }
159 }
160 throw_on_zlib_error(inflate(&m_zlib, Z_NO_FLUSH));
161 } while (m_zlib.avail_out);
162 num_read += num_deflated;
163 reinterpret_cast<Bytef*&>(data) += num_deflated;
164 length -= num_deflated;
165 }
166 m_state = stdex::stream::state_t::ok;
167 return num_read;
168 }
169
170 protected:
171 z_stream m_zlib;
172 uInt m_block_size;
173 std::unique_ptr<Byte[]> m_block;
174 };
175}
176
177#if defined(__GNUC__)
178#pragma GCC diagnostic pop
179#endif
stdex::stream::basic
Basic stream operations.
Definition stream.hpp:85
stdex::stream::basic::ok
bool ok() const
Returns true if the stream state is clean i.e. previous operation was successful.
Definition stream.hpp:181
stdex::stream::basic::state
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:176
stdex::stream::basic::read
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition stream.hpp:102
stdex::stream::basic::write
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition stream.hpp:120
stdex::stream::converter
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1020
stdex::zlib_reader
Decompresses data when reading from a stream.
Definition zlib.hpp:120
stdex::zlib_reader::read
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition zlib.hpp:137
stdex::zlib_writer
Compresses data when writing to a stream.
Definition zlib.hpp:51
stdex::zlib_writer::write
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition zlib.hpp:80