-
Notifications
You must be signed in to change notification settings - Fork 640
Expand file tree
/
Copy pathBase64Encoder.cc
More file actions
183 lines (155 loc) · 4.86 KB
/
Copy pathBase64Encoder.cc
File metadata and controls
183 lines (155 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
/*
* Copyright (C) 1996-2026 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
#include "squid.h"
#include "base/TextException.h"
#include "base64/Base64Encoder.h"
Base64Encoder::Base64Encoder(size_t maxEncodedSize)
: std::ostream(nullptr),
maxEncodedSize_(maxEncodedSize),
streamBuffer_(*this)
{
base64_encode_init(&ctx_);
rdbuf(&streamBuffer_);
clear();
}
Base64Encoder::Base64Encoder(const SBuf &input, size_t maxEncodedSize)
: Base64Encoder(maxEncodedSize)
{
// Encode the input immediately - will throw if too large
*this << input;
}
Base64Encoder::~Base64Encoder()
{
// Ensure encoding is finalized; log but don't propagate exceptions to avoid terminate during unwinding
try {
streamBuffer_.pubsync();
} catch (const std::exception &e) {
debugs(0, DBG_CRITICAL, "Base64Encoder dtor error: " << e.what());
} catch (...) {
debugs(0, DBG_CRITICAL, "Base64Encoder dtor unknown error");
}
}
SBuf
Base64Encoder::buf()
{
flush();
return sink_;
}
Base64Encoder&
Base64Encoder::clearBuf()
{
flush();
sink_.clear();
base64_encode_init(&ctx_);
finalized_ = false;
clear(); // Clear stream error state (badbit, failbit, etc.)
return *this;
}
std::ostream&
operator<<(std::ostream& os, Base64Encoder& encoder)
{
encoder.flush();
return encoder.sink_.print(os);
}
// --- Base64Encoder encoding implementation ---
void
Base64Encoder::checkSizeLimit(size_t newInputBytes)
{
// Since we sync after every append, sink_.length() is always up to date
// The additional encoded size for newInputBytes raw bytes is BASE64_ENCODE_RAW_LENGTH
const size_t additionalEncoded = BASE64_ENCODE_RAW_LENGTH(newInputBytes);
if (sink_.length() + additionalEncoded > maxEncodedSize_)
throw TextException("Base64Encoder output size limit exceeded", Here());
}
void
Base64Encoder::encodePending()
{
if (streamBuffer_.inputBufferPos_ == 0)
return;
checkSizeLimit(0); // No additional new input, just check pending
const size_t maxEncoded = BASE64_ENCODE_LENGTH(streamBuffer_.inputBufferPos_) + BASE64_ENCODE_FINAL_LENGTH;
sink_.reserveSpace(maxEncoded);
char *dst = sink_.rawAppendStart(maxEncoded);
size_t encoded = base64_encode_update(&ctx_, dst, streamBuffer_.inputBufferPos_,
reinterpret_cast<const uint8_t*>(streamBuffer_.inputBuffer_));
sink_.rawAppendFinish(dst, encoded);
streamBuffer_.inputBufferPos_ = 0;
streamBuffer_.setp(streamBuffer_.inputBuffer_, streamBuffer_.inputBuffer_ + 4096);
}
void
Base64Encoder::finalize()
{
if (finalized_)
return;
encodePending();
const size_t maxFinal = BASE64_ENCODE_FINAL_LENGTH;
sink_.reserveSpace(maxFinal);
char *dst = sink_.rawAppendStart(maxFinal);
size_t encoded = base64_encode_final(&ctx_, dst);
sink_.rawAppendFinish(dst, encoded);
finalized_ = true;
}
// --- Base64StreamBuf implementation ---
Base64Encoder::Base64StreamBuf::Base64StreamBuf(Base64Encoder &encoder)
: encoder_(encoder)
{
inputBuffer_ = static_cast<char*>(memAllocate(MEM_4K_BUF));
setp(inputBuffer_, inputBuffer_ + 4096);
}
Base64Encoder::Base64StreamBuf::~Base64StreamBuf()
{
memFree(inputBuffer_, MEM_4K_BUF);
inputBuffer_ = nullptr;
if (!encoder_.finalized_) {
try {
encoder_.finalize();
} catch (const std::exception &e) {
debugs(0, DBG_CRITICAL, "Base64StreamBuf dtor error: " << e.what());
} catch (...) {
debugs(0, DBG_CRITICAL, "Base64StreamBuf dtor unknown error");
}
}
}
int
Base64Encoder::Base64StreamBuf::overflow(int_type ch)
{
if (ch != traits_type::eof()) {
encoder_.checkSizeLimit(1);
inputBuffer_[inputBufferPos_++] = static_cast<char>(ch);
if (inputBufferPos_ >= 4096)
encoder_.encodePending();
}
encoder_.encodePending(); // Sync after every append
return ch;
}
int
Base64Encoder::Base64StreamBuf::sync()
{
encoder_.encodePending();
encoder_.finalize();
return 0;
}
std::streamsize
Base64Encoder::Base64StreamBuf::xsputn(const char *s, std::streamsize n)
{
std::streamsize written = 0;
while (n > 0) {
const size_t space = 4096 - inputBufferPos_;
const size_t toCopy = std::min<size_t>(static_cast<size_t>(n), space);
encoder_.checkSizeLimit(toCopy);
memcpy(inputBuffer_ + inputBufferPos_, s, toCopy);
inputBufferPos_ += toCopy;
s += toCopy;
n -= toCopy;
written += toCopy;
if (inputBufferPos_ >= 4096)
encoder_.encodePending();
}
encoder_.encodePending(); // Sync after every append
return written;
}