]> git.ipfire.org Git - thirdparty/squid.git/blame - src/stmem.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / stmem.cc
CommitLineData
30a4f2a8 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
26ac0430 3 *
bbc27441
AJ
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.
019dd986 7 */
ed43818f 8
bbc27441
AJ
9/* DEBUG: section 19 Store Memory Primitives */
10
582c2af2
FC
11#include "squid.h"
12#include "Generic.h"
a0c227a9 13#include "HttpReply.h"
528b2c61 14#include "mem_node.h"
15#include "MemObject.h"
582c2af2
FC
16#include "profiler/Profiler.h"
17#include "stmem.h"
528b2c61 18
12d74f96 19/*
20 * NodeGet() is called to get the data buffer to pass to storeIOWrite().
21 * By setting the write_pending flag here we are assuming that there
22 * will be no other users of NodeGet(). The storeIOWrite() callback
23 * is memNodeWriteComplete(), which, for whatever reason, lives in
24 * mem_node.cc.
25 */
d06925a4 26char *
27mem_hdr::NodeGet(mem_node * aNode)
28{
12d74f96 29 assert(!aNode->write_pending);
3dd52a0b 30 aNode->write_pending = true;
d06925a4 31 return aNode->data;
32}
33
47f6e231 34int64_t
528b2c61 35mem_hdr::lowestOffset () const
36{
42a503bd 37 const SplayNode<mem_node *> *theStart = nodes.start();
38
39 if (theStart)
40 return theStart->data->nodeBuffer.offset;
62e76326 41
528b2c61 42 return 0;
43}
44
47f6e231 45int64_t
528b2c61 46mem_hdr::endOffset () const
47{
47f6e231 48 int64_t result = 0;
b8bad68c 49 const SplayNode<mem_node *> *theEnd = nodes.finish();
62e76326 50
42a503bd 51 if (theEnd)
52 result = theEnd->data->dataRange().end;
62e76326 53
528b2c61 54 assert (result == inmem_hi);
62e76326 55
528b2c61 56 return result;
57}
090089c4 58
d89d1fb6 59void
528b2c61 60mem_hdr::freeContent()
61{
552ec743 62 nodes.destroy();
528b2c61 63 inmem_hi = 0;
e2851fe7 64 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
528b2c61 65}
66
12d74f96 67bool
42a503bd 68mem_hdr::unlink(mem_node *aNode)
528b2c61 69{
12d74f96 70 if (aNode->write_pending) {
fa84c01d 71 debugs(0, DBG_CRITICAL, "cannot unlink mem_node " << aNode << " while write_pending");
12d74f96 72 return false;
73 }
d06925a4 74
97754f5a 75 debugs(19, 8, this << " removing " << aNode);
12d74f96 76 nodes.remove (aNode, NodeCompare);
77 delete aNode;
78 return true;
090089c4 79}
80
47f6e231 81int64_t
82mem_hdr::freeDataUpto(int64_t target_offset)
528b2c61 83{
97754f5a 84 debugs(19, 8, this << " up to " << target_offset);
528b2c61 85 /* keep the last one to avoid change to other part of code */
810635e3 86 SplayNode<mem_node*> const * theStart;
42a503bd 87
12d74f96 88 while ((theStart = nodes.start())) {
89 if (theStart == nodes.finish())
90 break;
91
47f6e231 92 if (theStart->data->end() > target_offset )
12d74f96 93 break;
94
95 if (!unlink(theStart->data))
96 break;
42a503bd 97 }
62e76326 98
528b2c61 99 assert (lowestOffset () <= target_offset);
62e76326 100
528b2c61 101 return lowestOffset ();
102}
103
62e76326 104int
528b2c61 105mem_hdr::appendToNode(mem_node *aNode, const char *data, int maxLength)
106{
9e167fa2 107 size_t result = writeAvailable (aNode, aNode->nodeBuffer.offset + aNode->nodeBuffer.length,maxLength, data);
528b2c61 108 return result;
109}
110
111size_t
47f6e231 112mem_hdr::writeAvailable(mem_node *aNode, int64_t location, size_t amount, char const *source)
528b2c61 113{
114 /* if we attempt to overwrite existing data or leave a gap within a node */
ed013b6c 115 assert (location == aNode->nodeBuffer.offset + (int64_t)aNode->nodeBuffer.length);
528b2c61 116 /* And we are not at the end of the node */
117 assert (aNode->canAccept (location));
62e76326 118
528b2c61 119 /* these two can go I think */
ed013b6c 120 assert (location - aNode->nodeBuffer.offset == (int64_t)aNode->nodeBuffer.length);
d85c3078 121 size_t copyLen = min(amount, aNode->space());
528b2c61 122
41d00cd3 123 memcpy(aNode->nodeBuffer.data + aNode->nodeBuffer.length, source, copyLen);
528b2c61 124
e2851fe7 125 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
47f6e231 126 if (inmem_hi <= location)
62e76326 127 inmem_hi = location + copyLen;
128
528b2c61 129 /* Adjust the ptr and len according to what was deposited in the page */
130 aNode->nodeBuffer.length += copyLen;
62e76326 131
e2851fe7
AR
132 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
133 debugs(19, 9, HERE << this << " hi: " << endOffset());
528b2c61 134 return copyLen;
135}
136
137void
138mem_hdr::appendNode (mem_node *aNode)
139{
42a503bd 140 nodes.insert (aNode, NodeCompare);
090089c4 141}
142
e6e5d90d 143void
528b2c61 144mem_hdr::makeAppendSpace()
145{
42a503bd 146 if (!nodes.size()) {
147 appendNode (new mem_node (0));
62e76326 148 return;
090089c4 149 }
62e76326 150
b8bad68c 151 if (!nodes.finish()->data->space())
62e76326 152 appendNode (new mem_node (endOffset()));
153
b8bad68c 154 assert (nodes.finish()->data->space());
528b2c61 155}
156
157void
158mem_hdr::internalAppend(const char *data, int len)
159{
e2851fe7 160 debugs(19, 6, "memInternalAppend: " << this << " len " << len);
62e76326 161
090089c4 162 while (len > 0) {
62e76326 163 makeAppendSpace();
b8bad68c 164 int copied = appendToNode (nodes.finish()->data, data, len);
62e76326 165 assert (copied);
166
167 len -= copied;
168 data += copied;
090089c4 169 }
090089c4 170}
171
528b2c61 172/* returns a mem_node that contains location..
173 * If no node contains the start, it returns NULL.
174 */
175mem_node *
47f6e231 176mem_hdr::getBlockContainingLocation (int64_t location) const
528b2c61 177{
a5c1e941 178 // Optimize: do not create a whole mem_node just to store location
42a503bd 179 mem_node target (location);
180 target.nodeBuffer.length = 1;
181 mem_node *const *result = nodes.find (&target, NodeCompare);
62e76326 182
42a503bd 183 if (result)
184 return *result;
62e76326 185
42a503bd 186 return NULL;
528b2c61 187}
188
189size_t
47f6e231 190mem_hdr::copyAvailable(mem_node *aNode, int64_t location, size_t amount, char *target) const
528b2c61 191{
47f6e231 192 if (aNode->nodeBuffer.offset > location)
62e76326 193 return 0;
194
47f6e231 195 assert (aNode->nodeBuffer.offset <= location);
62e76326 196
528b2c61 197 assert (aNode->end() > location);
62e76326 198
528b2c61 199 size_t copyOffset = location - aNode->nodeBuffer.offset;
62e76326 200
d85c3078 201 size_t copyLen = min(amount, aNode->nodeBuffer.length - copyOffset);
528b2c61 202
41d00cd3 203 memcpy(target, aNode->nodeBuffer.data + copyOffset, copyLen);
62e76326 204
528b2c61 205 return copyLen;
206}
207
b8bad68c 208void
209mem_hdr::debugDump() const
210{
db7778f1 211 debugs (19, 0, "mem_hdr::debugDump: lowest offset: " << lowestOffset() << " highest offset + 1: " << endOffset() << ".");
b8bad68c 212 std::ostringstream result;
213 PointerPrinter<mem_node *> foo(result, " - ");
97754f5a 214 getNodes().visit(foo);
db7778f1 215 debugs (19, 0, "mem_hdr::debugDump: Current available data is: " << result.str() << ".");
b8bad68c 216}
217
62e76326 218/* FIXME: how do we deal with sparse results -
528b2c61 219 * where we have (say)
26ac0430 220 * 0-500 and 1000-1500, but are asked for
528b2c61 221 * 0-2000
222 * Partial answer:
223 * we supply 0-500 and stop.
224 */
02be0294 225ssize_t
90703668 226mem_hdr::copy(StoreIOBuffer const &target) const
090089c4 227{
528b2c61 228
47f6e231 229 assert(target.range().end > target.range().start);
e2851fe7 230 debugs(19, 6, "memCopy: " << this << " " << target.range());
62e76326 231
b8bad68c 232 /* we shouldn't ever ask for absent offsets */
233
42a503bd 234 if (nodes.size() == 0) {
e0236918 235 debugs(19, DBG_IMPORTANT, "mem_hdr::copy: No data to read");
b8bad68c 236 debugDump();
42a503bd 237 assert (0);
62e76326 238 return 0;
42a503bd 239 }
62e76326 240
528b2c61 241 /* RC: the next assert is nearly useless */
90703668 242 assert(target.length > 0);
528b2c61 243
090089c4 244 /* Seek our way into store */
47f6e231 245 mem_node *p = getBlockContainingLocation(target.offset);
62e76326 246
528b2c61 247 if (!p) {
e0236918 248 debugs(19, DBG_IMPORTANT, "memCopy: could not find start of " << target.range() <<
e4049756 249 " in memory.");
b8bad68c 250 debugDump();
24fe24be 251 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");
62e76326 252 return 0;
090089c4 253 }
62e76326 254
90703668 255 size_t bytes_to_go = target.length;
256 char *ptr_to_buf = target.data;
47f6e231 257 int64_t location = target.offset;
62e76326 258
090089c4 259 /* Start copying begining with this block until
260 * we're satiated */
528b2c61 261
090089c4 262 while (p && bytes_to_go > 0) {
62e76326 263 size_t bytes_to_copy = copyAvailable (p,
264 location, bytes_to_go, ptr_to_buf);
265
266 /* hit a sparse patch */
267
268 if (bytes_to_copy == 0)
90703668 269 return target.length - bytes_to_go;
62e76326 270
271 location += bytes_to_copy;
272
273 ptr_to_buf += bytes_to_copy;
274
275 bytes_to_go -= bytes_to_copy;
276
42a503bd 277 p = getBlockContainingLocation(location);
090089c4 278 }
62e76326 279
90703668 280 return target.length - bytes_to_go;
090089c4 281}
528b2c61 282
283bool
47f6e231 284mem_hdr::hasContigousContentRange(Range<int64_t> const & range) const
528b2c61 285{
47f6e231 286 int64_t currentStart = range.start;
62e76326 287
528b2c61 288 while (mem_node *curr = getBlockContainingLocation(currentStart)) {
62e76326 289 currentStart = curr->end();
290
4c50505b 291 if (currentStart >= range.end)
62e76326 292 return true;
528b2c61 293 }
62e76326 294
9cc44692 295 return !range.size(); // empty range is contigous
528b2c61 296}
297
298bool
299mem_hdr::unionNotEmpty(StoreIOBuffer const &candidate)
300{
528b2c61 301 assert (candidate.offset >= 0);
42a503bd 302 mem_node target(candidate.offset);
303 target.nodeBuffer.length = candidate.length;
304 return nodes.find (&target, NodeCompare);
528b2c61 305}
306
307mem_node *
47f6e231 308mem_hdr::nodeToRecieve(int64_t offset)
528b2c61 309{
310 /* case 1: Nothing in memory */
62e76326 311
42a503bd 312 if (!nodes.size()) {
62e76326 313 appendNode (new mem_node(offset));
42a503bd 314 return nodes.start()->data;
528b2c61 315 }
316
42a503bd 317 mem_node *candidate = NULL;
528b2c61 318 /* case 2: location fits within an extant node */
62e76326 319
42a503bd 320 if (offset > 0) {
321 mem_node search (offset - 1);
322 search.nodeBuffer.length = 1;
323 mem_node *const *leadup = nodes.find (&search, NodeCompare);
324
325 if (leadup)
326 candidate = *leadup;
528b2c61 327 }
328
42a503bd 329 if (candidate && candidate->canAccept(offset))
62e76326 330 return candidate;
528b2c61 331
332 /* candidate can't accept, so we need a new node */
333 candidate = new mem_node(offset);
62e76326 334
528b2c61 335 appendNode (candidate);
62e76326 336
528b2c61 337 /* simpler to write than a indented if */
338 return candidate;
339}
340
528b2c61 341bool
342mem_hdr::write (StoreIOBuffer const &writeBuffer)
343{
1d5161bd 344 PROF_start(mem_hdr_write);
e2851fe7 345 debugs(19, 6, "mem_hdr::write: " << this << " " << writeBuffer.range() << " object end " << endOffset());
528b2c61 346
347 if (unionNotEmpty(writeBuffer)) {
fa84c01d 348 debugs(19, DBG_CRITICAL, "mem_hdr::write: writeBuffer: " << writeBuffer.range());
db7778f1 349 debugDump();
fbdf945d 350 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");
1d5161bd 351 PROF_stop(mem_hdr_write);
62e76326 352 return false;
528b2c61 353 }
354
355 assert (writeBuffer.offset >= 0);
356
357 mem_node *target;
47f6e231 358 int64_t currentOffset = writeBuffer.offset;
528b2c61 359 char *currentSource = writeBuffer.data;
360 size_t len = writeBuffer.length;
62e76326 361
528b2c61 362 while (len && (target = nodeToRecieve(currentOffset))) {
62e76326 363 size_t wrote = writeAvailable(target, currentOffset, len, currentSource);
364 assert (wrote);
365 len -= wrote;
366 currentOffset += wrote;
367 currentSource += wrote;
528b2c61 368 }
369
1d5161bd 370 PROF_stop(mem_hdr_write);
528b2c61 371 return true;
372}
4c50505b 373
42a503bd 374mem_hdr::mem_hdr() : inmem_hi(0)
e2851fe7
AR
375{
376 debugs(19, 9, HERE << this << " hi: " << inmem_hi);
377}
4c50505b 378
379mem_hdr::~mem_hdr()
380{
381 freeContent();
382}
42a503bd 383
384/* splay of mem nodes:
385 * conditions:
386 * a = b if a.intersection(b).size > 0;
387 * a < b if a < b
388 */
389int
390mem_hdr::NodeCompare(mem_node * const &left, mem_node * const &right)
391{
392 // possibly Range can help us at some point.
393
394 if (left->dataRange().intersection(right->dataRange()).size() > 0)
395 return 0;
396
397 return *left < *right ? -1 : 1;
398}
399
400void
401mem_hdr::dump() const
402{
e0236918
FC
403 debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.start() " << nodes.start());
404 debugs(20, DBG_IMPORTANT, "mem_hdr: " << (void *)this << " nodes.finish() " << nodes.finish());
42a503bd 405}
406
407size_t
408mem_hdr::size() const
409{
410 return nodes.size();
411}
412
413mem_node const *
414mem_hdr::start() const
415{
416 const SplayNode<mem_node *> * result = nodes.start();
417
418 if (result)
419 return result->data;
420
421 return NULL;
422}
b8bad68c 423
424const Splay<mem_node *> &
425mem_hdr::getNodes() const
426{
427 return nodes;
428}
f53969cc 429