]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/stmem.cc
Simplify appending SBuf to String (#2108)
[thirdparty/squid.git] / src / stmem.cc
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 19 Store Memory Primitives */
10
11#include "squid.h"
12#include "Generic.h"
13#include "HttpReply.h"
14#include "mem_node.h"
15#include "MemObject.h"
16#include "stmem.h"
17
18/*
19 * NodeGet() is called to get the data buffer to pass to storeIOWrite().
20 * By setting the write_pending flag here we are assuming that there
21 * will be no other users of NodeGet(). The storeIOWrite() callback
22 * is memNodeWriteComplete(), which, for whatever reason, lives in
23 * mem_node.cc.
24 */
25char *
26mem_hdr::NodeGet(mem_node * aNode)
27{
28 assert(!aNode->write_pending);
29 aNode->write_pending = true;
30 return aNode->data;
31}
32
33int64_t
34mem_hdr::lowestOffset () const
35{
36 const SplayNode<mem_node *> *theStart = nodes.start();
37
38 if (theStart)
39 return theStart->data->nodeBuffer.offset;
40
41 return 0;
42}
43
44int64_t
45mem_hdr::endOffset () const
46{
47 int64_t result = 0;
48 const SplayNode<mem_node *> *theEnd = nodes.finish();
49
50 if (theEnd)
51 result = theEnd->data->dataRange().end;
52
53 assert (result == inmem_hi);
54
55 return result;
56}
57
58void
59mem_hdr::freeContent()
60{
61 nodes.destroy();
62 inmem_hi = 0;
63 debugs(19, 9, this << " hi: " << inmem_hi);
64}
65
66bool
67mem_hdr::unlink(mem_node *aNode)
68{
69 if (aNode->write_pending) {
70 debugs(0, DBG_CRITICAL, "ERROR: cannot unlink mem_node " << aNode << " while write_pending");
71 return false;
72 }
73
74 debugs(19, 8, this << " removing " << aNode);
75 nodes.remove (aNode, NodeCompare);
76 delete aNode;
77 return true;
78}
79
80int64_t
81mem_hdr::freeDataUpto(int64_t target_offset)
82{
83 debugs(19, 8, this << " up to " << target_offset);
84 /* keep the last one to avoid change to other part of code */
85 SplayNode<mem_node*> const * theStart;
86
87 while ((theStart = nodes.start())) {
88 if (theStart == nodes.finish())
89 break;
90
91 if (theStart->data->end() > target_offset )
92 break;
93
94 if (!unlink(theStart->data))
95 break;
96 }
97
98 return lowestOffset ();
99}
100
101size_t
102mem_hdr::writeAvailable(mem_node *aNode, int64_t location, size_t amount, char const *source)
103{
104 /* if we attempt to overwrite existing data or leave a gap within a node */
105 assert (location == aNode->nodeBuffer.offset + (int64_t)aNode->nodeBuffer.length);
106 /* And we are not at the end of the node */
107 assert (aNode->canAccept (location));
108
109 /* these two can go I think */
110 assert (location - aNode->nodeBuffer.offset == (int64_t)aNode->nodeBuffer.length);
111 size_t copyLen = min(amount, aNode->space());
112
113 memcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen);
114
115 debugs(19, 9, this << " hi: " << inmem_hi);
116 if (inmem_hi <= location)
117 inmem_hi = location + copyLen;
118
119 /* Adjust the ptr and len according to what was deposited in the page */
120 aNode->nodeBuffer.length += copyLen;
121
122 debugs(19, 9, this << " hi: " << inmem_hi);
123 debugs(19, 9, this << " hi: " << endOffset());
124 return copyLen;
125}
126
127void
128mem_hdr::appendNode (mem_node *aNode)
129{
130 nodes.insert (aNode, NodeCompare);
131}
132
133/* returns a mem_node that contains location..
134 * If no node contains the start, it returns NULL.
135 */
136mem_node *
137mem_hdr::getBlockContainingLocation (int64_t location) const
138{
139 // Optimize: do not create a whole mem_node just to store location
140 mem_node target (location);
141 target.nodeBuffer.length = 1;
142 mem_node *const *result = nodes.find (&target, NodeCompare);
143
144 if (result)
145 return *result;
146
147 return nullptr;
148}
149
150size_t
151mem_hdr::copyAvailable(mem_node *aNode, int64_t location, size_t amount, char *target) const
152{
153 if (aNode->nodeBuffer.offset > location)
154 return 0;
155
156 assert (aNode->nodeBuffer.offset <= location);
157
158 assert (aNode->end() > location);
159
160 size_t copyOffset = location - aNode->nodeBuffer.offset;
161
162 size_t copyLen = min(amount, aNode->nodeBuffer.length - copyOffset);
163
164 memcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen);
165
166 return copyLen;
167}
168
169void
170mem_hdr::debugDump() const
171{
172 debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << ".");
173 std::ostringstream result;
174 PointerPrinter<mem_node *> foo(result, " - ");
175 getNodes().visit(foo);
176 debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << ".");
177}
178
179/* XXX: how do we deal with sparse results -
180 * where we have (say)
181 * 0-500 and 1000-1500, but are asked for
182 * 0-2000
183 * Partial answer:
184 * we supply 0-500 and stop.
185 */
186ssize_t
187mem_hdr::copy(StoreIOBuffer const &target) const
188{
189
190 assert(target.range().end > target.range().start);
191 debugs(19, 6, "memCopy: " << this << " " << target.range());
192
193 /* we shouldn't ever ask for absent offsets */
194
195 if (nodes.size() == 0) {
196 debugs(19, DBG_IMPORTANT, "mem_hdr::copy: No data to read");
197 debugDump();
198 assert (0);
199 return 0;
200 }
201
202 /* RC: the next assert is nearly useless */
203 assert(target.length > 0);
204
205 /* Seek our way into store */
206 mem_node *p = getBlockContainingLocation(target.offset);
207
208 if (!p) {
209 debugs(19, DBG_IMPORTANT, "ERROR: memCopy: could not find start of " << target.range() <<
210 " in memory.");
211 debugDump();
212 fatal_dump("Squid has attempted to read data from memory that is not present. This is an indication of of (pre-3.0) code that hasn't been updated to deal with sparse objects in memory. Squid should coredump.allowing to review the cause. Immediately preceding this message is a dump of the available data in the format [start,end). The [ means from the value, the ) means up to the value. I.e. [1,5) means that there are 4 bytes of data, at offsets 1,2,3,4.\n");
213 return 0;
214 }
215
216 size_t bytes_to_go = target.length;
217 char *ptr_to_buf = target.data;
218 int64_t location = target.offset;
219
220 /* Start copying beginning with this block until
221 * we're satiated */
222
223 while (p && bytes_to_go > 0) {
224 size_t bytes_to_copy = copyAvailable (p,
225 location, bytes_to_go, ptr_to_buf);
226
227 /* hit a sparse patch */
228
229 if (bytes_to_copy == 0)
230 return target.length - bytes_to_go;
231
232 location += bytes_to_copy;
233
234 ptr_to_buf += bytes_to_copy;
235
236 bytes_to_go -= bytes_to_copy;
237
238 p = getBlockContainingLocation(location);
239 }
240
241 return target.length - bytes_to_go;
242}
243
244bool
245mem_hdr::hasContigousContentRange(Range<int64_t> const & range) const
246{
247 int64_t currentStart = range.start;
248
249 while (mem_node *curr = getBlockContainingLocation(currentStart)) {
250 currentStart = curr->end();
251
252 if (currentStart >= range.end)
253 return true;
254 }
255
256 return !range.size(); // empty range is contiguous
257}
258
259bool
260mem_hdr::unionNotEmpty(StoreIOBuffer const &candidate)
261{
262 assert (candidate.offset >= 0);
263 mem_node target(candidate.offset);
264 target.nodeBuffer.length = candidate.length;
265 return nodes.find (&target, NodeCompare);
266}
267
268mem_node *
269mem_hdr::nodeToRecieve(int64_t offset)
270{
271 /* case 1: Nothing in memory */
272
273 if (!nodes.size()) {
274 appendNode (new mem_node(offset));
275 return nodes.start()->data;
276 }
277
278 mem_node *candidate = nullptr;
279 /* case 2: location fits within an extant node */
280
281 if (offset > 0) {
282 mem_node search (offset - 1);
283 search.nodeBuffer.length = 1;
284 mem_node *const *leadup = nodes.find (&search, NodeCompare);
285
286 if (leadup)
287 candidate = *leadup;
288 }
289
290 if (candidate && candidate->canAccept(offset))
291 return candidate;
292
293 /* candidate can't accept, so we need a new node */
294 candidate = new mem_node(offset);
295
296 appendNode (candidate);
297
298 /* simpler to write than a indented if */
299 return candidate;
300}
301
302bool
303mem_hdr::write (StoreIOBuffer const &writeBuffer)
304{
305 debugs(19, 6, "mem_hdr::write: " << this << " " << writeBuffer.range() << " object end " << endOffset());
306
307 if (unionNotEmpty(writeBuffer)) {
308 debugs(19, DBG_CRITICAL, "mem_hdr::write: writeBuffer: " << writeBuffer.range());
309 debugDump();
310 fatal_dump("Attempt to overwrite already in-memory data. Preceding this there should be a mem_hdr::write output that lists the attempted write, and the currently present data. Please get a 'backtrace full' from this error - using the generated core, and file a bug report with the squid developers including the last 10 lines of cache.log and the backtrace.\n");
311 return false;
312 }
313
314 assert (writeBuffer.offset >= 0);
315
316 mem_node *target;
317 int64_t currentOffset = writeBuffer.offset;
318 char *currentSource = writeBuffer.data;
319 size_t len = writeBuffer.length;
320
321 while (len && (target = nodeToRecieve(currentOffset))) {
322 size_t wrote = writeAvailable(target, currentOffset, len, currentSource);
323 assert (wrote);
324 len -= wrote;
325 currentOffset += wrote;
326 currentSource += wrote;
327 }
328
329 return true;
330}
331
332mem_hdr::mem_hdr() : inmem_hi(0)
333{
334 debugs(19, 9, this << " hi: " << inmem_hi);
335}
336
337mem_hdr::~mem_hdr()
338{
339 freeContent();
340}
341
342/* splay of mem nodes:
343 * conditions:
344 * a = b if a.intersection(b).size > 0;
345 * a < b if a < b
346 */
347int
348mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right)
349{
350 // possibly Range can help us at some point.
351
352 if (left->dataRange().intersection(right->dataRange()).size() > 0)
353 return 0;
354
355 return *left < *right ? -1 : 1;
356}
357
358void
359mem_hdr::dump() const
360{
361 debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start());
362 debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish());
363}
364
365size_t
366mem_hdr::size() const
367{
368 return nodes.size();
369}
370
371const Splay<mem_node *> &
372mem_hdr::getNodes() const
373{
374 return nodes;
375}
376